// SPDX-License-Identifier: GPL-2.0 /* * Support for libpfm4 event encoding. * * Copyright 2020 Google LLC. */ #include "util/cpumap.h" #include "util/debug.h" #include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/parse-events.h" #include "util/pmu.h" #include "util/pfm.h" #include #include #include static void libpfm_initialize(void) { int ret; ret = pfm_initialize(); if (ret != PFM_SUCCESS) { ui__warning("libpfm failed to initialize: %s\n", pfm_strerror(ret)); } } int parse_libpfm_events_option(const struct option *opt, const char *str, int unset __maybe_unused) { struct evlist *evlist = *(struct evlist **)opt->value; struct perf_event_attr attr; struct perf_pmu *pmu; struct evsel *evsel, *grp_leader = NULL; char *p, *q, *p_orig; const char *sep; int grp_evt = -1; int ret; libpfm_initialize(); p_orig = p = strdup(str); if (!p) return -1; /* * force loading of the PMU list */ perf_pmu__scan(NULL); for (q = p; strsep(&p, ",{}"); q = p) { sep = p ? str + (p - p_orig - 1) : ""; if (*sep == '{') { if (grp_evt > -1) { ui__error( "nested event groups not supported\n"); goto error; } grp_evt++; } /* no event */ if (*q == '\0') { if (*sep == '}') { if (grp_evt < 0) { ui__error("cannot close a non-existing event group\n"); goto error; } grp_evt--; } continue; } memset(&attr, 0, sizeof(attr)); event_attr_init(&attr); ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3, &attr, NULL, NULL); if (ret != PFM_SUCCESS) { ui__error("failed to parse event %s : %s\n", str, pfm_strerror(ret)); goto error; } pmu = perf_pmu__find_by_type((unsigned int)attr.type); evsel = parse_events__add_event(evlist->core.nr_entries, &attr, q, pmu); if (evsel == NULL) goto error; evsel->is_libpfm_event = true; evlist__add(evlist, evsel); if (grp_evt == 0) grp_leader = evsel; if (grp_evt > -1) { evsel__set_leader(evsel, grp_leader); grp_leader->core.nr_members++; grp_evt++; } if (*sep == '}') { if (grp_evt < 0) { ui__error( "cannot close a non-existing event group\n"); goto error; } evlist->core.nr_groups++; grp_leader = NULL; grp_evt = -1; } } free(p_orig); return 0; error: free(p_orig); return -1; } static const char *srcs[PFM_ATTR_CTRL_MAX] = { [PFM_ATTR_CTRL_UNKNOWN] = "???", [PFM_ATTR_CTRL_PMU] = "PMU", [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event", }; static void print_attr_flags(pfm_event_attr_info_t *info) { int n = 0; if (info->is_dfl) { printf("[default] "); n++; } if (info->is_precise) { printf("[precise] "); n++; } if (!n) printf("- "); } static void print_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc) { pfm_event_attr_info_t ainfo; const char *src; int j, ret; ainfo.size = sizeof(ainfo); printf(" %s\n", info->name); printf(" [%s]\n", info->desc); if (long_desc) { if (info->equiv) printf(" Equiv: %s\n", info->equiv); printf(" Code : 0x%"PRIx64"\n", info->code); } pfm_for_each_event_attr(j, info) { ret = pfm_get_event_attr_info(info->idx, j, PFM_OS_PERF_EVENT_EXT, &ainfo); if (ret != PFM_SUCCESS) continue; if (ainfo.type == PFM_ATTR_UMASK) { printf(" %s:%s\n", info->name, ainfo.name); printf(" [%s]\n", ainfo.desc); } if (!long_desc) continue; if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN; src = srcs[ainfo.ctrl]; switch (ainfo.type) { case PFM_ATTR_UMASK: printf(" Umask : 0x%02"PRIx64" : %s: ", ainfo.code, src); print_attr_flags(&ainfo); putchar('\n'); break; case PFM_ATTR_MOD_BOOL: printf(" Modif : %s: [%s] : %s (boolean)\n", src, ainfo.name, ainfo.desc); break; case PFM_ATTR_MOD_INTEGER: printf(" Modif : %s: [%s] : %s (integer)\n", src, ainfo.name, ainfo.desc); break; case PFM_ATTR_NONE: case PFM_ATTR_RAW_UMASK: case PFM_ATTR_MAX: default: printf(" Attr : %s: [%s] : %s\n", src, ainfo.name, ainfo.desc); } } } /* * list all pmu::event:umask, pmu::event * printed events may not be all valid combinations of umask for an event */ static void print_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info) { pfm_event_attr_info_t ainfo; int j, ret; bool has_umask = false; ainfo.size = sizeof(ainfo); pfm_for_each_event_attr(j, info) { ret = pfm_get_event_attr_info(info->idx, j, PFM_OS_PERF_EVENT_EXT, &ainfo); if (ret != PFM_SUCCESS) continue; if (ainfo.type != PFM_ATTR_UMASK) continue; printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name); has_umask = true; } if (!has_umask) printf("%s::%s\n", pinfo->name, info->name); } void print_libpfm_events(bool name_only, bool long_desc) { pfm_event_info_t info; pfm_pmu_info_t pinfo; int i, p, ret; libpfm_initialize(); /* initialize to zero to indicate ABI version */ info.size = sizeof(info); pinfo.size = sizeof(pinfo); if (!name_only) puts("\nList of pre-defined events (to be used in --pfm-events):\n"); pfm_for_all_pmus(p) { bool printed_pmu = false; ret = pfm_get_pmu_info(p, &pinfo); if (ret != PFM_SUCCESS) continue; /* only print events that are supported by host HW */ if (!pinfo.is_present) continue; /* handled by perf directly */ if (pinfo.pmu == PFM_PMU_PERF_EVENT) continue; for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) { ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT, &info); if (ret != PFM_SUCCESS) continue; if (!name_only && !printed_pmu) { printf("%s:\n", pinfo.name); printed_pmu = true; } if (!name_only) print_libpfm_events_detailed(&info, long_desc); else print_libpfm_events_raw(&pinfo, &info); } if (!name_only && printed_pmu) putchar('\n'); } }