diff options
Diffstat (limited to 'tools/perf/util/print-events.c')
| -rw-r--r-- | tools/perf/util/print-events.c | 398 |
1 files changed, 177 insertions, 221 deletions
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 2646ae18d9f9..8f3ed83853a9 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -4,9 +4,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <fcntl.h> #include <sys/param.h> +#include <unistd.h> #include <api/fs/tracing_path.h> +#include <api/io.h> #include <linux/stddef.h> #include <linux/perf_event.h> #include <linux/zalloc.h> @@ -18,13 +21,16 @@ #include "metricgroup.h" #include "parse-events.h" #include "pmu.h" +#include "pmus.h" #include "print-events.h" #include "probe-file.h" #include "string2.h" #include "strlist.h" #include "tracepoint.h" #include "pfm.h" -#include "pmu-hybrid.h" +#include "thread_map.h" +#include "tool_pmu.h" +#include "util.h" #define MAX_NAME_LEN 100 @@ -34,83 +40,10 @@ static const char * const event_type_descriptors[] = { "Software event", "Tracepoint event", "Hardware cache event", - "Raw hardware event descriptor", + "Raw event descriptor", "Hardware breakpoint", }; -static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = { - [PERF_TOOL_DURATION_TIME] = { - .symbol = "duration_time", - .alias = "", - }, - [PERF_TOOL_USER_TIME] = { - .symbol = "user_time", - .alias = "", - }, - [PERF_TOOL_SYSTEM_TIME] = { - .symbol = "system_time", - .alias = "", - }, -}; - -/* - * Print the events from <debugfs_mount_point>/tracing/events - */ -void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state) -{ - struct dirent **sys_namelist = NULL; - int sys_items = tracing_events__scandir_alphasort(&sys_namelist); - - for (int i = 0; i < sys_items; i++) { - struct dirent *sys_dirent = sys_namelist[i]; - struct dirent **evt_namelist = NULL; - char *dir_path; - int evt_items; - - if (sys_dirent->d_type != DT_DIR || - !strcmp(sys_dirent->d_name, ".") || - !strcmp(sys_dirent->d_name, "..")) - continue; - - dir_path = get_events_file(sys_dirent->d_name); - if (!dir_path) - continue; - - evt_items = scandir(dir_path, &evt_namelist, NULL, alphasort); - for (int j = 0; j < evt_items; j++) { - struct dirent *evt_dirent = evt_namelist[j]; - char evt_path[MAXPATHLEN]; - - if (evt_dirent->d_type != DT_DIR || - !strcmp(evt_dirent->d_name, ".") || - !strcmp(evt_dirent->d_name, "..")) - continue; - - if (tp_event_has_id(dir_path, evt_dirent) != 0) - continue; - - snprintf(evt_path, MAXPATHLEN, "%s:%s", - sys_dirent->d_name, evt_dirent->d_name); - print_cb->print_event(print_state, - /*topic=*/NULL, - /*pmu_name=*/NULL, - evt_path, - /*event_alias=*/NULL, - /*scale_unit=*/NULL, - /*deprecated=*/false, - "Tracepoint event", - /*desc=*/NULL, - /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); - } - free(dir_path); - free(evt_namelist); - } - free(sys_namelist); -} - void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) { struct strlist *bidlist, *sdtlist; @@ -188,6 +121,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_TRACEPOINT, evt_name ?: sdt_name->s, /*event_alias=*/NULL, /*deprecated=*/false, @@ -195,147 +129,194 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) "SDT event", /*desc=*/NULL, /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); + /*encoding_desc=*/NULL); free(evt_name); } strlist__delete(sdtlist); } -int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state) +bool is_event_supported(u8 type, u64 config) { - struct strlist *evt_name_list = strlist__new(NULL, NULL); - struct str_node *nd; + bool ret = true; + struct evsel *evsel; + struct perf_event_attr attr = { + .type = type, + .config = config, + .disabled = 1, + }; + struct perf_thread_map *tmap = thread_map__new_by_tid(0); + + if (tmap == NULL) + return false; + + evsel = evsel__new(&attr); + if (evsel) { + ret = evsel__open(evsel, NULL, tmap) >= 0; + + if (!ret) { + /* + * The event may fail to open if the paranoid value + * /proc/sys/kernel/perf_event_paranoid is set to 2 + * Re-run with exclude_kernel set; we don't do that by + * default as some ARM machines do not support it. + */ + evsel->core.attr.exclude_kernel = 1; + ret = evsel__open(evsel, NULL, tmap) >= 0; + } - if (!evt_name_list) { - pr_debug("Failed to allocate new strlist for hwcache events\n"); - return -ENOMEM; - } - for (int type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { - for (int op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { - /* skip invalid cache type */ - if (!evsel__is_cache_op_valid(type, op)) - continue; - - for (int i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { - struct perf_pmu *pmu = NULL; - char name[64]; - - __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name)); - if (!perf_pmu__has_hybrid()) { - if (is_event_supported(PERF_TYPE_HW_CACHE, - type | (op << 8) | (i << 16))) - strlist__add(evt_name_list, name); - continue; - } - perf_pmu__for_each_hybrid_pmu(pmu) { - if (is_event_supported(PERF_TYPE_HW_CACHE, - type | (op << 8) | (i << 16) | - ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT))) { - char new_name[128]; - snprintf(new_name, sizeof(new_name), - "%s/%s/", pmu->name, name); - strlist__add(evt_name_list, new_name); - } - } - } + if (!ret) { + /* + * The event may fail to open if the PMU requires + * exclude_guest to be set (e.g. as the Apple M1 PMU + * requires). + * Re-run with exclude_guest set; we don't do that by + * default as it's equally legitimate for another PMU + * driver to require that exclude_guest is clear. + */ + evsel->core.attr.exclude_guest = 1; + ret = evsel__open(evsel, NULL, tmap) >= 0; } - } - strlist__for_each_entry(nd, evt_name_list) { - print_cb->print_event(print_state, - "cache", - /*pmu_name=*/NULL, - nd->s, - /*event_alias=*/NULL, - /*scale_unit=*/NULL, - /*deprecated=*/false, - event_type_descriptors[PERF_TYPE_HW_CACHE], - /*desc=*/NULL, - /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); + evsel__close(evsel); + evsel__delete(evsel); } - strlist__delete(evt_name_list); - return 0; + + perf_thread_map__put(tmap); + return ret; } -void print_tool_events(const struct print_callbacks *print_cb, void *print_state) +/** struct mep - RB-tree node for building printing information. */ +struct mep { + /** nd - RB-tree element. */ + struct rb_node nd; + /** @metric_group: Owned metric group name, separated others with ';'. */ + char *metric_group; + const char *metric_name; + const char *metric_desc; + const char *metric_long_desc; + const char *metric_expr; + const char *metric_threshold; + const char *metric_unit; + const char *pmu_name; +}; + +static int mep_cmp(struct rb_node *rb_node, const void *entry) { - // Start at 1 because the first enum entry means no tool event. - for (int i = 1; i < PERF_TOOL_MAX; ++i) { - print_cb->print_event(print_state, - "tool", - /*pmu_name=*/NULL, - event_symbols_tool[i].symbol, - event_symbols_tool[i].alias, - /*scale_unit=*/NULL, - /*deprecated=*/false, - "Tool event", - /*desc=*/NULL, - /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); - } + struct mep *a = container_of(rb_node, struct mep, nd); + struct mep *b = (struct mep *)entry; + int ret; + + ret = strcmp(a->metric_group, b->metric_group); + if (ret) + return ret; + + return strcmp(a->metric_name, b->metric_name); } -void print_symbol_events(const struct print_callbacks *print_cb, void *print_state, - unsigned int type, const struct event_symbol *syms, - unsigned int max) +static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) { - struct strlist *evt_name_list = strlist__new(NULL, NULL); - struct str_node *nd; + struct mep *me = malloc(sizeof(struct mep)); - if (!evt_name_list) { - pr_debug("Failed to allocate new strlist for symbol events\n"); - return; - } - for (unsigned int i = 0; i < max; i++) { - /* - * New attr.config still not supported here, the latest - * example was PERF_COUNT_SW_CGROUP_SWITCHES - */ - if (syms[i].symbol == NULL) - continue; + if (!me) + return NULL; - if (!is_event_supported(type, i)) - continue; + memcpy(me, entry, sizeof(struct mep)); + return &me->nd; +} - if (strlen(syms[i].alias)) { - char name[MAX_NAME_LEN]; +static void mep_delete(struct rblist *rl __maybe_unused, + struct rb_node *nd) +{ + struct mep *me = container_of(nd, struct mep, nd); + + zfree(&me->metric_group); + free(me); +} - snprintf(name, MAX_NAME_LEN, "%s OR %s", syms[i].symbol, syms[i].alias); - strlist__add(evt_name_list, name); - } else - strlist__add(evt_name_list, syms[i].symbol); +static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, + const char *metric_name) +{ + struct rb_node *nd; + struct mep me = { + .metric_group = strdup(metric_group), + .metric_name = metric_name, + }; + nd = rblist__find(groups, &me); + if (nd) { + free(me.metric_group); + return container_of(nd, struct mep, nd); } + rblist__add_node(groups, &me); + nd = rblist__find(groups, &me); + if (nd) + return container_of(nd, struct mep, nd); + return NULL; +} - strlist__for_each_entry(nd, evt_name_list) { - char *alias = strstr(nd->s, " OR "); +static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *vdata) +{ + struct rblist *groups = vdata; + const char *g; + char *omg, *mg; - if (alias) { - *alias = '\0'; - alias += 4; + mg = strdup(pm->metric_group ?: pm->metric_name); + if (!mg) + return -ENOMEM; + omg = mg; + while ((g = strsep(&mg, ";")) != NULL) { + struct mep *me; + + g = skip_spaces(g); + if (strlen(g)) + me = mep_lookup(groups, g, pm->metric_name); + else + me = mep_lookup(groups, pm->metric_name, pm->metric_name); + + if (me) { + me->metric_desc = pm->desc; + me->metric_long_desc = pm->long_desc; + me->metric_expr = pm->metric_expr; + me->metric_threshold = pm->metric_threshold; + me->metric_unit = pm->unit; + me->pmu_name = pm->pmu; } - print_cb->print_event(print_state, - /*topic=*/NULL, - /*pmu_name=*/NULL, - nd->s, - alias, - /*scale_unit=*/NULL, - /*deprecated=*/false, - event_type_descriptors[type], - /*desc=*/NULL, - /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); } - strlist__delete(evt_name_list); + free(omg); + + return 0; +} + +void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) +{ + struct rblist groups; + struct rb_node *node, *next; + const struct pmu_metrics_table *table = pmu_metrics_table__find(); + + rblist__init(&groups); + groups.node_new = mep_new; + groups.node_cmp = mep_cmp; + groups.node_delete = mep_delete; + + metricgroup__for_each_metric(table, metricgroup__add_to_mep_groups_callback, &groups); + + for (node = rb_first_cached(&groups.entries); node; node = next) { + struct mep *me = container_of(node, struct mep, nd); + + print_cb->print_metric(print_state, + me->metric_group, + me->metric_name, + me->metric_desc, + me->metric_long_desc, + me->metric_expr, + me->metric_threshold, + me->metric_unit, + me->pmu_name); + next = rb_next(node); + rblist__remove_node(&groups, node); + } } /* @@ -343,20 +324,12 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta */ void print_events(const struct print_callbacks *print_cb, void *print_state) { - print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE, - event_symbols_hw, PERF_COUNT_HW_MAX); - print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE, - event_symbols_sw, PERF_COUNT_SW_MAX); - - print_tool_events(print_cb, print_state); - - print_hwcache_events(print_cb, print_state); - - print_pmu_events(print_cb, print_state); + perf_pmus__print_pmu_events(print_cb, print_state); print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_RAW, "rNNN", /*event_alias=*/NULL, /*scale_unit=*/NULL, @@ -364,27 +337,14 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) event_type_descriptors[PERF_TYPE_RAW], /*desc=*/NULL, /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); + /*encoding_desc=*/NULL); - print_cb->print_event(print_state, - /*topic=*/NULL, - /*pmu_name=*/NULL, - "cpu/t1=v1[,t2=v2,t3 ...]/modifier", - /*event_alias=*/NULL, - /*scale_unit=*/NULL, - /*deprecated=*/false, - event_type_descriptors[PERF_TYPE_RAW], - "(see 'man perf-list' on how to encode it)", - /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); + perf_pmus__print_raw_pmu_events(print_cb, print_state); print_cb->print_event(print_state, /*topic=*/NULL, /*pmu_name=*/NULL, + PERF_TYPE_BREAKPOINT, "mem:<addr>[/len][:access]", /*scale_unit=*/NULL, /*event_alias=*/NULL, @@ -392,11 +352,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) event_type_descriptors[PERF_TYPE_BREAKPOINT], /*desc=*/NULL, /*long_desc=*/NULL, - /*encoding_desc=*/NULL, - /*metric_name=*/NULL, - /*metric_expr=*/NULL); - - print_tracepoint_events(print_cb, print_state); + /*encoding_desc=*/NULL); print_sdt_events(print_cb, print_state); |
