| // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | |
| #include "tp_pmu.h" | |
| #include "pmus.h" | |
| #include <api/fs/fs.h> | |
| #include <api/fs/tracing_path.h> | |
| #include <api/io_dir.h> | |
| #include <linux/kernel.h> | |
| #include <errno.h> | |
| #include <string.h> | |
| int tp_pmu__id(const char *sys, const char *name) | |
| { | |
| char *tp_dir = get_events_file(sys); | |
| char path[PATH_MAX]; | |
| int id, err; | |
| if (!tp_dir) | |
| return -1; | |
| scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name); | |
| put_events_file(tp_dir); | |
| err = filename__read_int(path, &id); | |
| if (err) | |
| return err; | |
| return id; | |
| } | |
| int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb) | |
| { | |
| char *evt_path; | |
| struct io_dirent64 *evt_ent; | |
| struct io_dir evt_dir; | |
| int ret = 0; | |
| evt_path = get_events_file(sys); | |
| if (!evt_path) | |
| return -errno; | |
| io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); | |
| if (evt_dir.dirfd < 0) { | |
| ret = -errno; | |
| put_events_file(evt_path); | |
| return ret; | |
| } | |
| put_events_file(evt_path); | |
| while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) { | |
| if (!strcmp(evt_ent->d_name, ".") | |
| || !strcmp(evt_ent->d_name, "..") | |
| || !strcmp(evt_ent->d_name, "enable") | |
| || !strcmp(evt_ent->d_name, "filter")) | |
| continue; | |
| ret = cb(state, sys, evt_ent->d_name); | |
| if (ret) | |
| break; | |
| } | |
| close(evt_dir.dirfd); | |
| return ret; | |
| } | |
| int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb) | |
| { | |
| struct io_dirent64 *events_ent; | |
| struct io_dir events_dir; | |
| int ret = 0; | |
| char *events_dir_path = get_tracing_file("events"); | |
| if (!events_dir_path) | |
| return -errno; | |
| io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); | |
| if (events_dir.dirfd < 0) { | |
| ret = -errno; | |
| put_events_file(events_dir_path); | |
| return ret; | |
| } | |
| put_events_file(events_dir_path); | |
| while (!ret && (events_ent = io_dir__readdir(&events_dir))) { | |
| if (!strcmp(events_ent->d_name, ".") || | |
| !strcmp(events_ent->d_name, "..") || | |
| !strcmp(events_ent->d_name, "enable") || | |
| !strcmp(events_ent->d_name, "header_event") || | |
| !strcmp(events_ent->d_name, "header_page")) | |
| continue; | |
| ret = cb(state, events_ent->d_name); | |
| } | |
| close(events_dir.dirfd); | |
| return ret; | |
| } | |
| bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu) | |
| { | |
| return pmu->type == PERF_TYPE_TRACEPOINT; | |
| } | |
| struct for_each_event_args { | |
| void *state; | |
| pmu_event_callback cb; | |
| const struct perf_pmu *pmu; | |
| }; | |
| static int for_each_event_cb(void *state, const char *sys_name, const char *evt_name) | |
| { | |
| struct for_each_event_args *args = state; | |
| char name[2 * FILENAME_MAX + 2]; | |
| /* 16 possible hex digits and 22 other characters and \0. */ | |
| char encoding[16 + 22]; | |
| char *format = NULL; | |
| size_t format_size; | |
| struct pmu_event_info info = { | |
| .pmu = args->pmu, | |
| .pmu_name = args->pmu->name, | |
| .event_type_desc = "Tracepoint event", | |
| }; | |
| char *tp_dir = get_events_file(sys_name); | |
| char path[PATH_MAX]; | |
| int id, err; | |
| if (!tp_dir) | |
| return -1; | |
| scnprintf(path, sizeof(path), "%s/%s/id", tp_dir, evt_name); | |
| err = filename__read_int(path, &id); | |
| if (err == 0) { | |
| snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%x/", id); | |
| info.encoding_desc = encoding; | |
| } | |
| scnprintf(path, sizeof(path), "%s/%s/format", tp_dir, evt_name); | |
| put_events_file(tp_dir); | |
| err = filename__read_str(path, &format, &format_size); | |
| if (err == 0) { | |
| info.long_desc = format; | |
| for (size_t i = 0 ; i < format_size; i++) { | |
| /* Swap tabs to spaces due to some rendering issues. */ | |
| if (format[i] == '\t') | |
| format[i] = ' '; | |
| } | |
| } | |
| snprintf(name, sizeof(name), "%s:%s", sys_name, evt_name); | |
| info.name = name; | |
| err = args->cb(args->state, &info); | |
| free(format); | |
| return err; | |
| } | |
| static int for_each_event_sys_cb(void *state, const char *sys_name) | |
| { | |
| return tp_pmu__for_each_tp_event(sys_name, state, for_each_event_cb); | |
| } | |
| int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb) | |
| { | |
| struct for_each_event_args args = { | |
| .state = state, | |
| .cb = cb, | |
| .pmu = pmu, | |
| }; | |
| return tp_pmu__for_each_tp_sys(&args, for_each_event_sys_cb); | |
| } | |
| static int num_events_cb(void *state, const char *sys_name __maybe_unused, | |
| const char *evt_name __maybe_unused) | |
| { | |
| size_t *count = state; | |
| (*count)++; | |
| return 0; | |
| } | |
| static int num_events_sys_cb(void *state, const char *sys_name) | |
| { | |
| return tp_pmu__for_each_tp_event(sys_name, state, num_events_cb); | |
| } | |
| size_t tp_pmu__num_events(struct perf_pmu *pmu __maybe_unused) | |
| { | |
| size_t count = 0; | |
| tp_pmu__for_each_tp_sys(&count, num_events_sys_cb); | |
| return count; | |
| } | |
| bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name) | |
| { | |
| char *dup_name, *colon; | |
| int id; | |
| colon = strchr((char *)name, ':'); | |
| if (colon == NULL) | |
| return false; | |
| dup_name = strdup(name); | |
| if (!dup_name) | |
| return false; | |
| colon = dup_name + (colon - name); | |
| *colon = '\0'; | |
| id = tp_pmu__id(dup_name, colon + 1); | |
| free(dup_name); | |
| return id >= 0; | |
| } |