forked from Minki/linux
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:
parent
82352ae28f
commit
7094349078
@ -615,6 +615,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]
|
||||
|
@ -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::
|
||||
|
@ -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
|
||||
--------------------------
|
||||
|
||||
|
@ -1022,6 +1022,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
|
||||
|
@ -121,6 +121,9 @@ include ../scripts/utilities.mak
|
||||
# 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
|
||||
# for doing the conversions to/from strings/id.
|
||||
#
|
||||
# Define LIBPFM4 to enable libpfm4 events extension.
|
||||
#
|
||||
|
||||
# As per kernel Makefile, avoid funny character set dependencies
|
||||
unexport LC_ALL
|
||||
|
@ -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"
|
||||
|
||||
@ -2555,6 +2556,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()
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
@ -1012,6 +1013,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()
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -58,6 +58,7 @@ perf-y += time-utils-test.o
|
||||
perf-y += genelf.o
|
||||
perf-y += api-io.o
|
||||
perf-y += demangle-java-test.o
|
||||
perf-y += pfm.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
@ -316,6 +316,15 @@ static struct test generic_tests[] = {
|
||||
.desc = "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",
|
||||
.func = test__api_io,
|
||||
|
203
tools/perf/tests/pfm.c
Normal file
203
tools/perf/tests/pfm.c
Normal 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
|
||||
}
|
@ -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__api_io(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_account_is_supported(void);
|
||||
|
@ -183,6 +183,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))"
|
||||
|
||||
|
@ -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. */
|
||||
if (strchr(name, '/') ||
|
||||
strchr(name, ':'))
|
||||
(strchr(name, ':') && !evsel->is_libpfm_event))
|
||||
sep = "";
|
||||
|
||||
if (asprintf(&new_name, "%s%su", name, sep) < 0)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
@ -345,6 +346,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)
|
||||
@ -353,7 +355,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 = evsel__new_idx(attr, *idx);
|
||||
if (!evsel)
|
||||
@ -371,15 +374,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,
|
||||
@ -391,7 +404,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;
|
||||
@ -1458,8 +1472,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;
|
||||
@ -1514,7 +1528,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) {
|
||||
@ -2846,6 +2860,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)
|
||||
|
@ -17,6 +17,7 @@ struct evlist;
|
||||
struct parse_events_error;
|
||||
|
||||
struct option;
|
||||
struct perf_pmu;
|
||||
|
||||
struct tracepoint_path {
|
||||
char *system;
|
||||
@ -188,6 +189,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);
|
||||
|
281
tools/perf/util/pfm.c
Normal file
281
tools/perf/util/pfm.c
Normal 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
37
tools/perf/util/pfm.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user