perf tools: Add optional support for libpfm4

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 ....

One needs to explicitely ask for its inclusion by using the LIBPFM4 make
command line option, ie its opt-in rather than opt-out of feature
detection and build support.

Signed-off-by: Stephane Eranian <eranian@google.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Alexey Budankov <alexey.budankov@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Igor Lubashev <ilubashe@akamai.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Jiwei Sun <jiwei.sun@windriver.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: yuzhoujian <yuzhoujian@didichuxing.com>
Link: http://lore.kernel.org/lkml/20200505182943.218248-2-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Stephane Eranian 2020-05-05 11:29:43 -07:00 committed by Arnaldo Carvalho de Melo
parent 82352ae28f
commit 7094349078
19 changed files with 631 additions and 8 deletions

View File

@ -615,6 +615,17 @@ appended unit character - B/K/M/G
The number of threads to run when synthesizing events for existing processes. The number of threads to run when synthesizing events for existing processes.
By default, the number of threads equals 1. 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 SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1] linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1]

View File

@ -71,6 +71,16 @@ report::
--tid=<tid>:: --tid=<tid>::
stat events on existing thread id (comma separated list) 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:: -a::
--all-cpus:: --all-cpus::

View File

@ -329,6 +329,17 @@ Default is to monitor all CPUS.
The known limitations include exception handing such as The known limitations include exception handing such as
setjmp/longjmp will have calls/returns not match. 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 INTERACTIVE PROMPTING KEYS
-------------------------- --------------------------

View File

@ -1022,6 +1022,19 @@ ifdef LIBCLANGLLVM
endif endif
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: # Among the variables below, these:
# perfexecdir # perfexecdir
# perf_include_dir # perf_include_dir

View File

@ -121,6 +121,9 @@ include ../scripts/utilities.mak
# Define NO_SYSCALL_TABLE=1 to disable the use of syscall id to/from name tables # Define NO_SYSCALL_TABLE=1 to disable the use of syscall id to/from name tables
# generated from the kernel .tbl or unistd.h files and use, if available, libaudit # generated from the kernel .tbl or unistd.h files and use, if available, libaudit
# for doing the conversions to/from strings/id. # for doing the conversions to/from strings/id.
#
# Define LIBPFM4 to enable libpfm4 events extension.
#
# As per kernel Makefile, avoid funny character set dependencies # As per kernel Makefile, avoid funny character set dependencies
unexport LC_ALL unexport LC_ALL

View File

@ -45,6 +45,7 @@
#include "util/units.h" #include "util/units.h"
#include "util/bpf-event.h" #include "util/bpf-event.h"
#include "util/util.h" #include "util/util.h"
#include "util/pfm.h"
#include "asm/bug.h" #include "asm/bug.h"
#include "perf.h" #include "perf.h"
@ -2555,6 +2556,11 @@ static struct option __record_options[] = {
OPT_UINTEGER(0, "num-thread-synthesize", OPT_UINTEGER(0, "num-thread-synthesize",
&record.opts.nr_threads_synthesize, &record.opts.nr_threads_synthesize,
"number of threads to run for event synthesis"), "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() OPT_END()
}; };

View File

@ -66,6 +66,7 @@
#include "util/time-utils.h" #include "util/time-utils.h"
#include "util/top.h" #include "util/top.h"
#include "util/affinity.h" #include "util/affinity.h"
#include "util/pfm.h"
#include "asm/bug.h" #include "asm/bug.h"
#include <linux/time64.h> #include <linux/time64.h>
@ -1012,6 +1013,11 @@ static struct option stat_options[] = {
"Use with 'percore' event qualifier to show the event " "Use with 'percore' event qualifier to show the event "
"counts of one hardware thread by sum up total hardware " "counts of one hardware thread by sum up total hardware "
"threads of same physical core"), "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() OPT_END()
}; };

View File

@ -53,6 +53,7 @@
#include "util/debug.h" #include "util/debug.h"
#include "util/ordered-events.h" #include "util/ordered-events.h"
#include "util/pfm.h"
#include <assert.h> #include <assert.h>
#include <elf.h> #include <elf.h>
@ -1575,6 +1576,11 @@ int cmd_top(int argc, const char **argv)
"WARNING: should be used on grouped events."), "WARNING: should be used on grouped events."),
OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr, OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
"Enable LBR callgraph stitching approach"), "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), OPTS_EVSWITCH(&top.evswitch),
OPT_END() OPT_END()
}; };

View File

@ -58,6 +58,7 @@ perf-y += time-utils-test.o
perf-y += genelf.o perf-y += genelf.o
perf-y += api-io.o perf-y += api-io.o
perf-y += demangle-java-test.o perf-y += demangle-java-test.o
perf-y += pfm.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir) $(call rule_mkdir)

View File

@ -316,6 +316,15 @@ static struct test generic_tests[] = {
.desc = "Test jit_write_elf", .desc = "Test jit_write_elf",
.func = test__jit_write_elf, .func = test__jit_write_elf,
}, },
{
.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,
}
},
{ {
.desc = "Test api io", .desc = "Test api io",
.func = test__api_io, .func = test__api_io,

203
tools/perf/tests/pfm.c Normal file
View File

@ -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
}

View File

@ -118,6 +118,9 @@ int test__time_utils(struct test *t, int subtest);
int test__jit_write_elf(struct test *test, int subtest); int test__jit_write_elf(struct test *test, int subtest);
int test__api_io(struct test *test, int subtest); int test__api_io(struct test *test, int subtest);
int test__demangle_java(struct test *test, int subtest); int test__demangle_java(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_signal_is_supported(void);
bool test__bp_account_is_supported(void); bool test__bp_account_is_supported(void);

View File

@ -183,6 +183,8 @@ perf-$(CONFIG_LIBBPF) += bpf-event.o
perf-$(CONFIG_CXX) += c++/ perf-$(CONFIG_CXX) += c++/
perf-$(CONFIG_LIBPFM4) += pfm.o
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))" CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))"

View File

@ -2417,7 +2417,7 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize)
/* Is there already the separator in the name. */ /* Is there already the separator in the name. */
if (strchr(name, '/') || if (strchr(name, '/') ||
strchr(name, ':')) (strchr(name, ':') && !evsel->is_libpfm_event))
sep = ""; sep = "";
if (asprintf(&new_name, "%s%su", name, sep) < 0) if (asprintf(&new_name, "%s%su", name, sep) < 0)

View File

@ -76,6 +76,7 @@ struct evsel {
bool ignore_missing_thread; bool ignore_missing_thread;
bool forced_leader; bool forced_leader;
bool use_uncore_alias; bool use_uncore_alias;
bool is_libpfm_event;
/* parse modifier helper */ /* parse modifier helper */
int exclude_GH; int exclude_GH;
int sample_read; int sample_read;

View File

@ -36,6 +36,7 @@
#include "metricgroup.h" #include "metricgroup.h"
#include "util/evsel_config.h" #include "util/evsel_config.h"
#include "util/event.h" #include "util/event.h"
#include "util/pfm.h"
#define MAX_NAME_LEN 100 #define MAX_NAME_LEN 100
@ -345,6 +346,7 @@ static char *get_config_name(struct list_head *head_terms)
static struct evsel * static struct evsel *
__add_event(struct list_head *list, int *idx, __add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr, struct perf_event_attr *attr,
bool init_attr,
char *name, struct perf_pmu *pmu, char *name, struct perf_pmu *pmu,
struct list_head *config_terms, bool auto_merge_stats, struct list_head *config_terms, bool auto_merge_stats,
const char *cpu_list) const char *cpu_list)
@ -353,6 +355,7 @@ __add_event(struct list_head *list, int *idx,
struct perf_cpu_map *cpus = pmu ? pmu->cpus : struct perf_cpu_map *cpus = pmu ? pmu->cpus :
cpu_list ? perf_cpu_map__new(cpu_list) : NULL; cpu_list ? perf_cpu_map__new(cpu_list) : NULL;
if (init_attr)
event_attr_init(attr); event_attr_init(attr);
evsel = evsel__new_idx(attr, *idx); evsel = evsel__new_idx(attr, *idx);
@ -371,15 +374,25 @@ __add_event(struct list_head *list, int *idx,
if (config_terms) if (config_terms)
list_splice(config_terms, &evsel->config_terms); list_splice(config_terms, &evsel->config_terms);
if (list)
list_add_tail(&evsel->core.node, list); list_add_tail(&evsel->core.node, list);
return evsel; 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, static int add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr, char *name, struct perf_event_attr *attr, char *name,
struct list_head *config_terms) 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, static int add_event_tool(struct list_head *list, int *idx,
@ -391,7 +404,8 @@ static int add_event_tool(struct list_head *list, int *idx,
.config = PERF_COUNT_SW_DUMMY, .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) if (!evsel)
return -ENOMEM; return -ENOMEM;
evsel->tool_event = tool_event; evsel->tool_event = tool_event;
@ -1458,8 +1472,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
if (!head_config) { if (!head_config) {
attr.type = pmu->type; attr.type = pmu->type;
evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, evsel = __add_event(list, &parse_state->idx, &attr, true, NULL,
auto_merge_stats, NULL); pmu, NULL, auto_merge_stats, NULL);
if (evsel) { if (evsel) {
evsel->pmu_name = name ? strdup(name) : NULL; evsel->pmu_name = name ? strdup(name) : NULL;
evsel->use_uncore_alias = use_uncore_alias; evsel->use_uncore_alias = use_uncore_alias;
@ -1514,7 +1528,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
return -EINVAL; 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, get_config_name(head_config), pmu,
&config_terms, auto_merge_stats, NULL); &config_terms, auto_merge_stats, NULL);
if (evsel) { if (evsel) {
@ -2846,6 +2860,8 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag,
print_sdt_events(NULL, NULL, name_only); print_sdt_events(NULL, NULL, name_only);
metricgroup__print(true, true, NULL, name_only, details_flag); 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) int parse_events__is_hardcoded_term(struct parse_events_term *term)

View File

@ -17,6 +17,7 @@ struct evlist;
struct parse_events_error; struct parse_events_error;
struct option; struct option;
struct perf_pmu;
struct tracepoint_path { struct tracepoint_path {
char *system; char *system;
@ -188,6 +189,9 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
bool auto_merge_stats, bool auto_merge_stats,
bool use_alias); 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, int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
char *str, char *str,
struct list_head **listp); struct list_head **listp);

281
tools/perf/util/pfm.c Normal file
View File

@ -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');
}
}

37
tools/perf/util/pfm.h Normal file
View File

@ -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 */