On Tue, May 5, 2020 at 11:29 AM Ian Rogers <irogers@xxxxxxxxxx> wrote: > > From: Stephane Eranian <eranian@xxxxxxxxxx> > > This patch links perf with the libpfm4 library if it is available > and LIBPFM4 is passed to the build. The libpfm4 library > contains hardware event tables for all processors supported by > perf_events. It is a helper library that helps convert from a > symbolic event name to the event encoding required by the > underlying kernel interface. This library is open-source and > available from: http://perfmon2.sf.net. > > With this patch, it is possible to specify full hardware events > by name. Hardware filters are also supported. Events must be > specified via the --pfm-events and not -e option. Both options > are active at the same time and it is possible to mix and match: > > $ perf stat --pfm-events inst_retired:any_p:c=1:i -e cycles .... > > Signed-off-by: Stephane Eranian <eranian@xxxxxxxxxx> > Reviewed-by: Ian Rogers <irogers@xxxxxxxxxx> Ping. Thanks, Ian > --- > tools/perf/Documentation/perf-record.txt | 11 + > tools/perf/Documentation/perf-stat.txt | 10 + > tools/perf/Documentation/perf-top.txt | 11 + > tools/perf/Makefile.config | 13 ++ > tools/perf/Makefile.perf | 2 + > tools/perf/builtin-record.c | 6 + > tools/perf/builtin-stat.c | 6 + > tools/perf/builtin-top.c | 6 + > tools/perf/tests/Build | 1 + > tools/perf/tests/builtin-test.c | 9 + > tools/perf/tests/pfm.c | 203 ++++++++++++++++ > tools/perf/tests/tests.h | 3 + > tools/perf/util/Build | 2 + > tools/perf/util/evsel.c | 2 +- > tools/perf/util/evsel.h | 1 + > tools/perf/util/parse-events.c | 30 ++- > tools/perf/util/parse-events.h | 4 + > tools/perf/util/pfm.c | 281 +++++++++++++++++++++++ > tools/perf/util/pfm.h | 37 +++ > 19 files changed, 630 insertions(+), 8 deletions(-) > create mode 100644 tools/perf/tests/pfm.c > create mode 100644 tools/perf/util/pfm.c > create mode 100644 tools/perf/util/pfm.h > > diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt > index 561ef55743e2..492b6b6f2b77 100644 > --- a/tools/perf/Documentation/perf-record.txt > +++ b/tools/perf/Documentation/perf-record.txt > @@ -613,6 +613,17 @@ appended unit character - B/K/M/G > The number of threads to run when synthesizing events for existing processes. > By default, the number of threads equals 1. > > +ifdef::HAVE_LIBPFM[] > +--pfm-events events:: > +Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net) > +including support for event filters. For example '--pfm-events > +inst_retired:any_p:u:c=1:i'. More than one event can be passed to the > +option using the comma separator. Hardware events and generic hardware > +events cannot be mixed together. The latter must be used with the -e > +option. The -e option and this one can be mixed and matched. Events > +can be grouped using the {} notation. > +endif::HAVE_LIBPFM[] > + > SEE ALSO > -------- > linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1] > diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt > index 3fb5028aef08..b69af18dccd0 100644 > --- a/tools/perf/Documentation/perf-stat.txt > +++ b/tools/perf/Documentation/perf-stat.txt > @@ -71,6 +71,16 @@ report:: > --tid=<tid>:: > stat events on existing thread id (comma separated list) > > +ifdef::HAVE_LIBPFM[] > +--pfm-events events:: > +Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net) > +including support for event filters. For example '--pfm-events > +inst_retired:any_p:u:c=1:i'. More than one event can be passed to the > +option using the comma separator. Hardware events and generic hardware > +events cannot be mixed together. The latter must be used with the -e > +option. The -e option and this one can be mixed and matched. Events > +can be grouped using the {} notation. > +endif::HAVE_LIBPFM[] > > -a:: > --all-cpus:: > diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt > index 20227dabc208..ee2024691d46 100644 > --- a/tools/perf/Documentation/perf-top.txt > +++ b/tools/perf/Documentation/perf-top.txt > @@ -329,6 +329,17 @@ Default is to monitor all CPUS. > The known limitations include exception handing such as > setjmp/longjmp will have calls/returns not match. > > +ifdef::HAVE_LIBPFM[] > +--pfm-events events:: > +Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net) > +including support for event filters. For example '--pfm-events > +inst_retired:any_p:u:c=1:i'. More than one event can be passed to the > +option using the comma separator. Hardware events and generic hardware > +events cannot be mixed together. The latter must be used with the -e > +option. The -e option and this one can be mixed and matched. Events > +can be grouped using the {} notation. > +endif::HAVE_LIBPFM[] > + > INTERACTIVE PROMPTING KEYS > -------------------------- > > diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config > index 12a8204d63c6..b67804fee1e3 100644 > --- a/tools/perf/Makefile.config > +++ b/tools/perf/Makefile.config > @@ -1012,6 +1012,19 @@ ifdef LIBCLANGLLVM > endif > endif > > +ifdef LIBPFM4 > + $(call feature_check,libpfm4) > + ifeq ($(feature-libpfm4), 1) > + CFLAGS += -DHAVE_LIBPFM > + EXTLIBS += -lpfm > + ASCIIDOC_EXTRA = -aHAVE_LIBPFM=1 > + $(call detected,CONFIG_LIBPFM4) > + else > + msg := $(warning libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev); > + NO_LIBPFM4 := 1 > + endif > +endif > + > # Among the variables below, these: > # perfexecdir > # perf_include_dir > diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf > index 94a495594e99..dc82578c8773 100644 > --- a/tools/perf/Makefile.perf > +++ b/tools/perf/Makefile.perf > @@ -118,6 +118,8 @@ include ../scripts/utilities.mak > # > # Define LIBBPF_DYNAMIC to enable libbpf dynamic linking. > # > +# Define LIBPFM4 to enable libpfm4 events extension. > +# > > # As per kernel Makefile, avoid funny character set dependencies > unexport LC_ALL > diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c > index e4efdbf1a81e..98387cce3207 100644 > --- a/tools/perf/builtin-record.c > +++ b/tools/perf/builtin-record.c > @@ -45,6 +45,7 @@ > #include "util/units.h" > #include "util/bpf-event.h" > #include "util/util.h" > +#include "util/pfm.h" > #include "asm/bug.h" > #include "perf.h" > > @@ -2506,6 +2507,11 @@ static struct option __record_options[] = { > OPT_UINTEGER(0, "num-thread-synthesize", > &record.opts.nr_threads_synthesize, > "number of threads to run for event synthesis"), > +#ifdef HAVE_LIBPFM > + OPT_CALLBACK(0, "pfm-events", &record.evlist, "event", > + "libpfm4 event selector. use 'perf list' to list available events", > + parse_libpfm_events_option), > +#endif > OPT_END() > }; > > diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c > index e0c1ad23c768..f6e2dd57f48e 100644 > --- a/tools/perf/builtin-stat.c > +++ b/tools/perf/builtin-stat.c > @@ -66,6 +66,7 @@ > #include "util/time-utils.h" > #include "util/top.h" > #include "util/affinity.h" > +#include "util/pfm.h" > #include "asm/bug.h" > > #include <linux/time64.h> > @@ -935,6 +936,11 @@ static struct option stat_options[] = { > "Use with 'percore' event qualifier to show the event " > "counts of one hardware thread by sum up total hardware " > "threads of same physical core"), > +#ifdef HAVE_LIBPFM > + OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", > + "libpfm4 event selector. use 'perf list' to list available events", > + parse_libpfm_events_option), > +#endif > OPT_END() > }; > > diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c > index 372c38254654..20c41d9040ee 100644 > --- a/tools/perf/builtin-top.c > +++ b/tools/perf/builtin-top.c > @@ -53,6 +53,7 @@ > > #include "util/debug.h" > #include "util/ordered-events.h" > +#include "util/pfm.h" > > #include <assert.h> > #include <elf.h> > @@ -1575,6 +1576,11 @@ int cmd_top(int argc, const char **argv) > "WARNING: should be used on grouped events."), > OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr, > "Enable LBR callgraph stitching approach"), > +#ifdef HAVE_LIBPFM > + OPT_CALLBACK(0, "pfm-events", &top.evlist, "event", > + "libpfm4 event selector. use 'perf list' to list available events", > + parse_libpfm_events_option), > +#endif > OPTS_EVSWITCH(&top.evswitch), > OPT_END() > }; > diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build > index c75557aeef0e..4e74a363b0b0 100644 > --- a/tools/perf/tests/Build > +++ b/tools/perf/tests/Build > @@ -57,6 +57,7 @@ perf-y += maps.o > perf-y += time-utils-test.o > perf-y += genelf.o > perf-y += api-io.o > +perf-y += pfm.o > > $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build > $(call rule_mkdir) > diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c > index 3471ec52ea11..57c6f8b31624 100644 > --- a/tools/perf/tests/builtin-test.c > +++ b/tools/perf/tests/builtin-test.c > @@ -317,6 +317,15 @@ static struct test generic_tests[] = { > .desc = "maps__merge_in", > .func = test__maps__merge_in, > }, > + { > + .desc = "Test libpfm4 support", > + .func = test__pfm, > + .subtest = { > + .skip_if_fail = true, > + .get_nr = test__pfm_subtest_get_nr, > + .get_desc = test__pfm_subtest_get_desc, > + } > + }, > { > .func = NULL, > }, > diff --git a/tools/perf/tests/pfm.c b/tools/perf/tests/pfm.c > new file mode 100644 > index 000000000000..76a53126efdf > --- /dev/null > +++ b/tools/perf/tests/pfm.c > @@ -0,0 +1,203 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Test support for libpfm4 event encodings. > + * > + * Copyright 2020 Google LLC. > + */ > +#include "tests.h" > +#include "util/debug.h" > +#include "util/evlist.h" > +#include "util/pfm.h" > + > +#include <linux/kernel.h> > + > +#ifdef HAVE_LIBPFM > +static int test__pfm_events(void); > +static int test__pfm_group(void); > +#endif > + > +static const struct { > + int (*func)(void); > + const char *desc; > +} pfm_testcase_table[] = { > +#ifdef HAVE_LIBPFM > + { > + .func = test__pfm_events, > + .desc = "test of individual --pfm-events", > + }, > + { > + .func = test__pfm_group, > + .desc = "test groups of --pfm-events", > + }, > +#endif > +}; > + > +#ifdef HAVE_LIBPFM > +static int count_pfm_events(struct perf_evlist *evlist) > +{ > + struct perf_evsel *evsel; > + int count = 0; > + > + perf_evlist__for_each_entry(evlist, evsel) { > + count++; > + } > + return count; > +} > + > +static int test__pfm_events(void) > +{ > + struct evlist *evlist; > + struct option opt; > + size_t i; > + const struct { > + const char *events; > + int nr_events; > + } table[] = { > + { > + .events = "", > + .nr_events = 0, > + }, > + { > + .events = "instructions", > + .nr_events = 1, > + }, > + { > + .events = "instructions,cycles", > + .nr_events = 2, > + }, > + { > + .events = "stereolab", > + .nr_events = 0, > + }, > + { > + .events = "instructions,instructions", > + .nr_events = 2, > + }, > + { > + .events = "stereolab,instructions", > + .nr_events = 0, > + }, > + { > + .events = "instructions,stereolab", > + .nr_events = 1, > + }, > + }; > + > + for (i = 0; i < ARRAY_SIZE(table); i++) { > + evlist = evlist__new(); > + if (evlist == NULL) > + return -ENOMEM; > + > + opt.value = evlist; > + parse_libpfm_events_option(&opt, > + table[i].events, > + 0); > + TEST_ASSERT_EQUAL(table[i].events, > + count_pfm_events(&evlist->core), > + table[i].nr_events); > + TEST_ASSERT_EQUAL(table[i].events, > + evlist->nr_groups, > + 0); > + > + evlist__delete(evlist); > + } > + return 0; > +} > + > +static int test__pfm_group(void) > +{ > + struct evlist *evlist; > + struct option opt; > + size_t i; > + const struct { > + const char *events; > + int nr_events; > + int nr_groups; > + } table[] = { > + { > + .events = "{},", > + .nr_events = 0, > + .nr_groups = 0, > + }, > + { > + .events = "{instructions}", > + .nr_events = 1, > + .nr_groups = 1, > + }, > + { > + .events = "{instructions},{}", > + .nr_events = 1, > + .nr_groups = 1, > + }, > + { > + .events = "{},{instructions}", > + .nr_events = 0, > + .nr_groups = 0, > + }, > + { > + .events = "{instructions},{instructions}", > + .nr_events = 2, > + .nr_groups = 2, > + }, > + { > + .events = "{instructions,cycles},{instructions,cycles}", > + .nr_events = 4, > + .nr_groups = 2, > + }, > + { > + .events = "{stereolab}", > + .nr_events = 0, > + .nr_groups = 0, > + }, > + { > + .events = > + "{instructions,cycles},{instructions,stereolab}", > + .nr_events = 3, > + .nr_groups = 1, > + }, > + }; > + > + for (i = 0; i < ARRAY_SIZE(table); i++) { > + evlist = evlist__new(); > + if (evlist == NULL) > + return -ENOMEM; > + > + opt.value = evlist; > + parse_libpfm_events_option(&opt, > + table[i].events, > + 0); > + TEST_ASSERT_EQUAL(table[i].events, > + count_pfm_events(&evlist->core), > + table[i].nr_events); > + TEST_ASSERT_EQUAL(table[i].events, > + evlist->nr_groups, > + table[i].nr_groups); > + > + evlist__delete(evlist); > + } > + return 0; > +} > +#endif > + > +const char *test__pfm_subtest_get_desc(int i) > +{ > + if (i < 0 || i >= (int)ARRAY_SIZE(pfm_testcase_table)) > + return NULL; > + return pfm_testcase_table[i].desc; > +} > + > +int test__pfm_subtest_get_nr(void) > +{ > + return (int)ARRAY_SIZE(pfm_testcase_table); > +} > + > +int test__pfm(struct test *test __maybe_unused, int i __maybe_unused) > +{ > +#ifdef HAVE_LIBPFM > + if (i < 0 || i >= (int)ARRAY_SIZE(pfm_testcase_table)) > + return TEST_FAIL; > + return pfm_testcase_table[i].func(); > +#else > + return TEST_SKIP; > +#endif > +} > diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h > index d6d4ac34eeb7..a2ae0d2e6087 100644 > --- a/tools/perf/tests/tests.h > +++ b/tools/perf/tests/tests.h > @@ -113,6 +113,9 @@ int test__maps__merge_in(struct test *t, int subtest); > int test__time_utils(struct test *t, int subtest); > int test__jit_write_elf(struct test *test, int subtest); > int test__api_io(struct test *test, int subtest); > +int test__pfm(struct test *test, int subtest); > +const char *test__pfm_subtest_get_desc(int subtest); > +int test__pfm_subtest_get_nr(void); > > bool test__bp_signal_is_supported(void); > bool test__bp_account_is_supported(void); > diff --git a/tools/perf/util/Build b/tools/perf/util/Build > index ca07a162d602..dfda916f0b4c 100644 > --- a/tools/perf/util/Build > +++ b/tools/perf/util/Build > @@ -179,6 +179,8 @@ perf-$(CONFIG_LIBBPF) += bpf-event.o > > perf-$(CONFIG_CXX) += c++/ > > +perf-$(CONFIG_LIBPFM4) += pfm.o > + > CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" > CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))" > > diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c > index f3e60c45d59a..d7ea1a7e74cd 100644 > --- a/tools/perf/util/evsel.c > +++ b/tools/perf/util/evsel.c > @@ -2416,7 +2416,7 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize) > > /* Is there already the separator in the name. */ > if (strchr(name, '/') || > - strchr(name, ':')) > + (strchr(name, ':') && !evsel->is_libpfm_event)) > sep = ""; > > if (asprintf(&new_name, "%s%su", name, sep) < 0) > diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h > index 351c0aaf2a11..5a4f20ddeb49 100644 > --- a/tools/perf/util/evsel.h > +++ b/tools/perf/util/evsel.h > @@ -76,6 +76,7 @@ struct evsel { > bool ignore_missing_thread; > bool forced_leader; > bool use_uncore_alias; > + bool is_libpfm_event; > /* parse modifier helper */ > int exclude_GH; > int sample_read; > diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c > index b7a0518d607d..0ee3338a0449 100644 > --- a/tools/perf/util/parse-events.c > +++ b/tools/perf/util/parse-events.c > @@ -36,6 +36,7 @@ > #include "metricgroup.h" > #include "util/evsel_config.h" > #include "util/event.h" > +#include "util/pfm.h" > > #define MAX_NAME_LEN 100 > > @@ -344,6 +345,7 @@ static char *get_config_name(struct list_head *head_terms) > static struct evsel * > __add_event(struct list_head *list, int *idx, > struct perf_event_attr *attr, > + bool init_attr, > char *name, struct perf_pmu *pmu, > struct list_head *config_terms, bool auto_merge_stats, > const char *cpu_list) > @@ -352,7 +354,8 @@ __add_event(struct list_head *list, int *idx, > struct perf_cpu_map *cpus = pmu ? pmu->cpus : > cpu_list ? perf_cpu_map__new(cpu_list) : NULL; > > - event_attr_init(attr); > + if (init_attr) > + event_attr_init(attr); > > evsel = perf_evsel__new_idx(attr, *idx); > if (!evsel) > @@ -370,15 +373,25 @@ __add_event(struct list_head *list, int *idx, > if (config_terms) > list_splice(config_terms, &evsel->config_terms); > > - list_add_tail(&evsel->core.node, list); > + if (list) > + list_add_tail(&evsel->core.node, list); > + > return evsel; > } > > +struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, > + char *name, struct perf_pmu *pmu) > +{ > + return __add_event(NULL, &idx, attr, false, name, pmu, NULL, false, > + NULL); > +} > + > static int add_event(struct list_head *list, int *idx, > struct perf_event_attr *attr, char *name, > struct list_head *config_terms) > { > - return __add_event(list, idx, attr, name, NULL, config_terms, false, NULL) ? 0 : -ENOMEM; > + return __add_event(list, idx, attr, true, name, NULL, config_terms, > + false, NULL) ? 0 : -ENOMEM; > } > > static int add_event_tool(struct list_head *list, int *idx, > @@ -390,7 +403,8 @@ static int add_event_tool(struct list_head *list, int *idx, > .config = PERF_COUNT_SW_DUMMY, > }; > > - evsel = __add_event(list, idx, &attr, NULL, NULL, NULL, false, "0"); > + evsel = __add_event(list, idx, &attr, true, NULL, NULL, NULL, false, > + "0"); > if (!evsel) > return -ENOMEM; > evsel->tool_event = tool_event; > @@ -1446,8 +1460,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, > > if (!head_config) { > attr.type = pmu->type; > - evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, > - auto_merge_stats, NULL); > + evsel = __add_event(list, &parse_state->idx, &attr, true, NULL, > + pmu, NULL, auto_merge_stats, NULL); > if (evsel) { > evsel->pmu_name = name ? strdup(name) : NULL; > evsel->use_uncore_alias = use_uncore_alias; > @@ -1488,7 +1502,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, > return -EINVAL; > } > > - evsel = __add_event(list, &parse_state->idx, &attr, > + evsel = __add_event(list, &parse_state->idx, &attr, true, > get_config_name(head_config), pmu, > &config_terms, auto_merge_stats, NULL); > if (evsel) { > @@ -2817,6 +2831,8 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, > print_sdt_events(NULL, NULL, name_only); > > metricgroup__print(true, true, NULL, name_only, details_flag); > + > + print_libpfm_events(name_only, long_desc); > } > > int parse_events__is_hardcoded_term(struct parse_events_term *term) > diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h > index 6ead9661238c..04e3f627c081 100644 > --- a/tools/perf/util/parse-events.h > +++ b/tools/perf/util/parse-events.h > @@ -17,6 +17,7 @@ struct evlist; > struct parse_events_error; > > struct option; > +struct perf_pmu; > > struct tracepoint_path { > char *system; > @@ -187,6 +188,9 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, > bool auto_merge_stats, > bool use_alias); > > +struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, > + char *name, struct perf_pmu *pmu); > + > int parse_events_multi_pmu_add(struct parse_events_state *parse_state, > char *str, > struct list_head **listp); > diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c > new file mode 100644 > index 000000000000..d735acb6c29c > --- /dev/null > +++ b/tools/perf/util/pfm.c > @@ -0,0 +1,281 @@ > +// 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 <string.h> > +#include <linux/kernel.h> > +#include <perfmon/pfmlib_perf_event.h> > + > +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') > + 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->leader = 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->nr_groups++; > + grp_leader = NULL; > + grp_evt = -1; > + } > + } > + 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'); > + } > +} > diff --git a/tools/perf/util/pfm.h b/tools/perf/util/pfm.h > new file mode 100644 > index 000000000000..7d70dda87012 > --- /dev/null > +++ b/tools/perf/util/pfm.h > @@ -0,0 +1,37 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Support for libpfm4 event encoding. > + * > + * Copyright 2020 Google LLC. > + */ > +#ifndef __PERF_PFM_H > +#define __PERF_PFM_H > + > +#include <subcmd/parse-options.h> > + > +#ifdef HAVE_LIBPFM > +int parse_libpfm_events_option(const struct option *opt, const char *str, > + int unset); > + > +void print_libpfm_events(bool name_only, bool long_desc); > + > +#else > +#include <linux/compiler.h> > + > +static inline int parse_libpfm_events_option( > + const struct option *opt __maybe_unused, > + const char *str __maybe_unused, > + int unset __maybe_unused) > +{ > + return 0; > +} > + > +static inline void print_libpfm_events(bool name_only __maybe_unused, > + bool long_desc __maybe_unused) > +{ > +} > + > +#endif > + > + > +#endif /* __PERF_PFM_H */ > -- > 2.26.2.526.g744177e7f7-goog >