* [PATCH v6] perf tools: add support for libpfm4
@ 2020-04-01 18:49 Ian Rogers
2020-04-29 14:49 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 3+ messages in thread
From: Ian Rogers @ 2020-04-01 18:49 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
Yonghong Song, Andrii Nakryiko, Greg Kroah-Hartman,
Thomas Gleixner, Igor Lubashev, Alexey Budankov,
Florian Fainelli, Adrian Hunter, Andi Kleen, Jiwei Sun,
yuzhoujian, Kan Liang, Jin Yao, Leo Yan, John Garry,
linux-kernel, netdev, bpf, linux-perf-users
Cc: Stephane Eranian
From: Stephane Eranian <eranian@google.com>
This patch links perf with the libpfm4 library if it is available and
NO_LIBPFM4 isn't 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 ....
v6 is a rebase.
v5 is a rebase.
v4 is a rebase on git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git
branch perf/core and re-adds the tools/build/feature/test-libpfm4.c
missed in v3.
v3 is against acme/perf/core and removes a diagnostic warning.
v2 of this patch makes the --pfm-events man page documentation
conditional on libpfm4 behing configured. It tidies some of the
documentation and adds the feature test missed in the v1 patch.
Reviewed-By:Ian Rogers <irogers@google.com>
---
tools/build/Makefile.feature | 6 +-
tools/build/feature/Makefile | 7 +-
tools/build/feature/test-libpfm4.c | 8 +
tools/perf/Documentation/Makefile | 4 +-
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 | 12 ++
tools/perf/Makefile.perf | 6 +-
tools/perf/builtin-list.c | 16 ++
tools/perf/builtin-record.c | 20 ++
tools/perf/builtin-stat.c | 21 ++
tools/perf/builtin-top.c | 20 ++
tools/perf/util/evsel.c | 2 +-
tools/perf/util/evsel.h | 1 +
tools/perf/util/parse-events.c | 246 +++++++++++++++++++++++
tools/perf/util/parse-events.h | 5 +
tools/perf/util/pmu.c | 11 +
tools/perf/util/pmu.h | 1 +
19 files changed, 410 insertions(+), 8 deletions(-)
create mode 100644 tools/build/feature/test-libpfm4.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 574c2e0b9d20..08c6fe5aee2d 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -72,7 +72,8 @@ FEATURE_TESTS_BASIC := \
setns \
libaio \
libzstd \
- disassembler-four-args
+ disassembler-four-args \
+ libpfm4
# FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
# of all feature tests
@@ -127,7 +128,8 @@ FEATURE_DISPLAY ?= \
bpf \
libaio \
libzstd \
- disassembler-four-args
+ disassembler-four-args \
+ libpfm4
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 7ac0d8088565..573072d32545 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -67,7 +67,9 @@ FILES= \
test-llvm.bin \
test-llvm-version.bin \
test-libaio.bin \
- test-libzstd.bin
+ test-libzstd.bin \
+ test-libpfm4.bin
+
FILES := $(addprefix $(OUTPUT),$(FILES))
@@ -321,6 +323,9 @@ $(OUTPUT)test-libaio.bin:
$(OUTPUT)test-libzstd.bin:
$(BUILD) -lzstd
+$(OUTPUT)test-libpfm4.bin:
+ $(BUILD) -lpfm
+
###############################
clean:
diff --git a/tools/build/feature/test-libpfm4.c b/tools/build/feature/test-libpfm4.c
new file mode 100644
index 000000000000..7f24df86cf09
--- /dev/null
+++ b/tools/build/feature/test-libpfm4.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <perfmon/pfmlib.h>
+
+int main(void)
+{
+ pfm_initialize();
+ return 0;
+}
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index 31824d5269cc..6e54979c2124 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -48,7 +48,7 @@ man5dir=$(mandir)/man5
man7dir=$(mandir)/man7
ASCIIDOC=asciidoc
-ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf
+ASCIIDOC_EXTRA += --unsafe -f asciidoc.conf
ASCIIDOC_HTML = xhtml11
MANPAGE_XSL = manpage-normal.xsl
XMLTO_EXTRA =
@@ -59,7 +59,7 @@ HTML_REF = origin/html
ifdef USE_ASCIIDOCTOR
ASCIIDOC = asciidoctor
-ASCIIDOC_EXTRA = -a compat-mode
+ASCIIDOC_EXTRA += -a compat-mode
ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
ASCIIDOC_EXTRA += -a mansource="perf" -a manmanual="perf Manual"
ASCIIDOC_HTML = xhtml5
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index b3f3b3f1c161..ad2c41f595c2 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -596,6 +596,17 @@ Make a copy of /proc/kcore and place it into a directory with the perf data file
Limit the sample data max size, <size> is expected to be a number with
appended unit character - B/K/M/G
+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 4d56586b2fb9..536952ad592c 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 487737a725e9..9858e3640b0c 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -319,6 +319,17 @@ Default is to monitor all CPUS.
go straight to the histogram browser, just like 'perf top' with no events
explicitely specified does.
+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 80e55e796be9..571aa6b1af40 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -999,6 +999,18 @@ ifdef LIBCLANGLLVM
endif
endif
+ifndef NO_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 d15a311408f1..9787eb3ca0a9 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 NO_LIBPFM4 to disable libpfm4 extension.
+#
# As per kernel Makefile, avoid funny character set dependencies
unexport LC_ALL
@@ -188,7 +190,7 @@ AWK = awk
# non-config cases
config := 1
-NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help install-doc install-man install-html install-info install-pdf doc man html info pdf
+NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help
ifdef MAKECMDGOALS
ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
@@ -832,7 +834,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
# 'make doc' should call 'make -C Documentation all'
$(DOC_TARGETS):
- $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all)
+ $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all) ASCIIDOC_EXTRA=$(ASCIIDOC_EXTRA)
TAG_FOLDERS= . ../lib ../include
TAG_FILES= ../../include/uapi/linux/perf_event.h
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 965ef017496f..5edeb428168a 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -18,6 +18,10 @@
#include <subcmd/parse-options.h>
#include <stdio.h>
+#ifdef HAVE_LIBPFM
+#include <perfmon/pfmlib.h>
+#endif
+
static bool desc_flag = true;
static bool details_flag;
@@ -56,6 +60,18 @@ int cmd_list(int argc, const char **argv)
if (!raw_dump && pager_in_use())
printf("\nList of pre-defined events (to be used in -e):\n\n");
+#ifdef HAVE_LIBPFM
+ {
+ int ret;
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS) {
+ fprintf(stderr,
+ "warning libpfm failed to initialize: %s\n",
+ pfm_strerror(ret));
+ }
+ }
+#endif
+
if (argc == 0) {
print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
details_flag, deprecated);
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 7d7912e121d6..7d2737f33c2e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -64,6 +64,10 @@
#include <linux/zalloc.h>
#include <linux/bitmap.h>
+#ifdef HAVE_LIBPFM
+#include <perfmon/pfmlib.h>
+#endif
+
struct switch_output {
bool enabled;
bool signal;
@@ -2415,6 +2419,11 @@ static struct option __record_options[] = {
#endif
OPT_CALLBACK(0, "max-size", &record.output_max_size,
"size", "Limit the maximum size of the output file", parse_output_max_size),
+#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()
};
@@ -2455,6 +2464,17 @@ int cmd_record(int argc, const char **argv)
if (rec->evlist == NULL)
return -ENOMEM;
+#ifdef HAVE_LIBPFM
+ {
+ int ret;
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS) {
+ ui__warning("warning libpfm failed to initialize: %s\n",
+ pfm_strerror(ret));
+ }
+ }
+#endif
+
err = perf_config(perf_record_config, rec);
if (err)
return err;
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index ec053dc1e35c..c47eaf238f0c 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -89,6 +89,10 @@
#include <linux/ctype.h>
#include <perf/evlist.h>
+#ifdef HAVE_LIBPFM
+#include <perfmon/pfmlib.h>
+#endif
+
#define DEFAULT_SEPARATOR " "
#define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi"
@@ -933,6 +937,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()
};
@@ -1871,6 +1880,18 @@ int cmd_stat(int argc, const char **argv)
unsigned int interval, timeout;
const char * const stat_subcommands[] = { "record", "report" };
+#ifdef HAVE_LIBPFM
+ {
+ int ret;
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS) {
+ fprintf(stderr,
+ "warning libpfm failed to initialize: %s\n",
+ pfm_strerror(ret));
+ }
+ }
+#endif
+
setlocale(LC_ALL, "");
evsel_list = evlist__new();
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 6684d94b1398..afec285f9877 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -84,6 +84,10 @@
#include <linux/ctype.h>
#include <perf/mmap.h>
+#ifdef HAVE_LIBPFM
+#include <perfmon/pfmlib.h>
+#endif
+
static volatile int done;
static volatile int resize;
@@ -1565,6 +1569,11 @@ int cmd_top(int argc, const char **argv)
"Sort the output by the event at the index n in group. "
"If n is invalid, sort by the first event. "
"WARNING: should be used on grouped events."),
+#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()
};
@@ -1578,6 +1587,17 @@ int cmd_top(int argc, const char **argv)
if (status < 0)
return status;
+#ifdef HAVE_LIBPFM
+ {
+ int ret;
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS) {
+ ui__warning("warning libpfm failed to initialize: %s\n",
+ pfm_strerror(ret));
+ }
+ }
+#endif
+
top.annotation_opts.min_pcnt = 5;
top.annotation_opts.context = 4;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index eb880efbce16..ca1b9cbf3355 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2448,7 +2448,7 @@ bool perf_evsel__fallback(struct evsel *evsel, int err,
/* 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 53187c501ee8..0b6eec714e6f 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 10107747b361..31ed184566c8 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -37,6 +37,11 @@
#include "util/evsel_config.h"
#include "util/event.h"
+#ifdef HAVE_LIBPFM
+#include <perfmon/pfmlib_perf_event.h>
+static void print_libpfm_events(bool name_only);
+#endif
+
#define MAX_NAME_LEN 100
#ifdef PARSER_DEBUG
@@ -2794,6 +2799,10 @@ 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);
+
+#ifdef HAVE_LIBPFM
+ print_libpfm_events(name_only);
+#endif
}
int parse_events__is_hardcoded_term(struct parse_events_term *term)
@@ -3042,3 +3051,240 @@ char *parse_events_formats_error_string(char *additional_terms)
fail:
return NULL;
}
+
+#ifdef HAVE_LIBPFM
+static int
+parse_libpfm_event(char *strp, struct perf_event_attr *attr)
+{
+ int ret;
+
+ ret = pfm_get_perf_event_encoding(strp, PFM_PLM0|PFM_PLM3,
+ attr, NULL, NULL);
+ return 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;
+
+ 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) {
+ fprintf(stderr, "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 = parse_libpfm_event(q, &attr);
+ if (ret != PFM_SUCCESS) {
+ fprintf(stderr, "failed to parse event %s : %s\n", str, pfm_strerror(ret));
+ goto error;
+ }
+
+ evsel = perf_evsel__new_idx(&attr, evlist->core.nr_entries);
+ if (evsel == NULL)
+ goto error;
+
+ evsel->name = strdup(q);
+ if (!evsel->name) {
+ evsel__delete(evsel);
+ goto error;
+ }
+ evsel->is_libpfm_event = true;
+
+ pmu = perf_pmu__find_by_type((unsigned)attr.type);
+ if (pmu)
+ evsel->core.own_cpus = perf_cpu_map__get(pmu->cpus);
+
+ 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) {
+ fprintf(stderr, "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_detailed_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t ainfo;
+ const char *src;
+ int j, ret;
+
+ ainfo.size = sizeof(ainfo);
+
+ printf("\nName : %s\n", info->name);
+ printf("PMU : %s\n", pinfo->name);
+ printf("Desc : %s\n", info->desc);
+ printf("Equiv : %s\n", info->equiv ? info->equiv : "None");
+ 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.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: [%s] : ", ainfo.code, src, ainfo.name);
+ print_attr_flags(&ainfo);
+ printf(": %s\n", ainfo.desc);
+ 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_simplified_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t ainfo;
+ int j, ret;
+ int um = 0;
+
+ 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);
+ um++;
+ }
+ if (um == 0)
+ printf("%s::%s\n", pinfo->name, info->name);
+}
+
+static void
+print_libpfm_events(bool name_only)
+{
+ pfm_event_info_t info;
+ pfm_pmu_info_t pinfo;
+ pfm_event_attr_info_t ainfo;
+ int i, p, ret;
+
+ /* initialize to zero to indicate ABI version */
+ info.size = sizeof(info);
+ pinfo.size = sizeof(pinfo);
+ ainfo.size = sizeof(ainfo);
+
+ putchar('\n');
+
+ pfm_for_all_pmus(p) {
+ ret = pfm_get_pmu_info(p, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ /* ony 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)
+ print_libpfm_detailed_events(&pinfo, &info);
+ else
+ print_libpfm_simplified_events(&pinfo, &info);
+ }
+ }
+}
+#endif
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 27596cbd0ba0..84d4799c9a31 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -37,6 +37,11 @@ int parse_events_terms(struct list_head *terms, const char *str);
int parse_filter(const struct option *opt, const char *str, int unset);
int exclude_perf(const struct option *opt, const char *arg, int unset);
+#ifdef HAVE_LIBPFM
+extern int parse_libpfm_events_option(const struct option *opt, const char *str,
+ int unset);
+#endif
+
#define EVENTS_HELP_MAX (128*1024)
enum perf_pmu_event_symbol_type {
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index ef6a63f3d386..5e918ca740c6 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -869,6 +869,17 @@ static struct perf_pmu *pmu_find(const char *name)
return NULL;
}
+struct perf_pmu *perf_pmu__find_by_type(unsigned int type)
+{
+ struct perf_pmu *pmu;
+
+ list_for_each_entry(pmu, &pmus, list)
+ if (pmu->type == type)
+ return pmu;
+
+ return NULL;
+}
+
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
{
/*
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 5fb3f16828df..de3b868d912c 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -65,6 +65,7 @@ struct perf_pmu_alias {
};
struct perf_pmu *perf_pmu__find(const char *name);
+struct perf_pmu *perf_pmu__find_by_type(unsigned int type);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms,
struct parse_events_error *error);
--
2.26.0.rc2.310.g2932bb562d-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v6] perf tools: add support for libpfm4
2020-04-01 18:49 [PATCH v6] perf tools: add support for libpfm4 Ian Rogers
@ 2020-04-29 14:49 ` Arnaldo Carvalho de Melo
2020-04-29 16:59 ` Ian Rogers
0 siblings, 1 reply; 3+ messages in thread
From: Arnaldo Carvalho de Melo @ 2020-04-29 14:49 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Namhyung Kim, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Yonghong Song, Andrii Nakryiko,
Greg Kroah-Hartman, Thomas Gleixner, Igor Lubashev,
Alexey Budankov, Florian Fainelli, Adrian Hunter, Andi Kleen,
Jiwei Sun, yuzhoujian, Kan Liang, Jin Yao, Leo Yan, John Garry,
linux-kernel, netdev, bpf, linux-perf-users, Stephane Eranian
Em Wed, Apr 01, 2020 at 11:49:13AM -0700, Ian Rogers escreveu:
> From: Stephane Eranian <eranian@google.com>
>
> This patch links perf with the libpfm4 library if it is available and
> NO_LIBPFM4 isn't passed to the build. The libpfm4 library contains hardware
Can we make this a explicit decision instead, i.e. for it to be linked
we would need to have its development files installed and -DLIBPFM4,
like we have for CORESIGHT?
$ grep CORESIGHT tools/perf/Makefile.perf
# Define CORESIGHT if you DO WANT support for CoreSight trace decoding.
$
- Arnaldo
> 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 ....
>
> v6 is a rebase.
> v5 is a rebase.
> v4 is a rebase on git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git
> branch perf/core and re-adds the tools/build/feature/test-libpfm4.c
> missed in v3.
> v3 is against acme/perf/core and removes a diagnostic warning.
> v2 of this patch makes the --pfm-events man page documentation
> conditional on libpfm4 behing configured. It tidies some of the
> documentation and adds the feature test missed in the v1 patch.
>
> Reviewed-By:Ian Rogers <irogers@google.com>
> ---
> tools/build/Makefile.feature | 6 +-
> tools/build/feature/Makefile | 7 +-
> tools/build/feature/test-libpfm4.c | 8 +
> tools/perf/Documentation/Makefile | 4 +-
> 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 | 12 ++
> tools/perf/Makefile.perf | 6 +-
> tools/perf/builtin-list.c | 16 ++
> tools/perf/builtin-record.c | 20 ++
> tools/perf/builtin-stat.c | 21 ++
> tools/perf/builtin-top.c | 20 ++
> tools/perf/util/evsel.c | 2 +-
> tools/perf/util/evsel.h | 1 +
> tools/perf/util/parse-events.c | 246 +++++++++++++++++++++++
> tools/perf/util/parse-events.h | 5 +
> tools/perf/util/pmu.c | 11 +
> tools/perf/util/pmu.h | 1 +
> 19 files changed, 410 insertions(+), 8 deletions(-)
> create mode 100644 tools/build/feature/test-libpfm4.c
>
> diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
> index 574c2e0b9d20..08c6fe5aee2d 100644
> --- a/tools/build/Makefile.feature
> +++ b/tools/build/Makefile.feature
> @@ -72,7 +72,8 @@ FEATURE_TESTS_BASIC := \
> setns \
> libaio \
> libzstd \
> - disassembler-four-args
> + disassembler-four-args \
> + libpfm4
>
> # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
> # of all feature tests
> @@ -127,7 +128,8 @@ FEATURE_DISPLAY ?= \
> bpf \
> libaio \
> libzstd \
> - disassembler-four-args
> + disassembler-four-args \
> + libpfm4
>
> # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
> # If in the future we need per-feature checks/flags for features not
> diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
> index 7ac0d8088565..573072d32545 100644
> --- a/tools/build/feature/Makefile
> +++ b/tools/build/feature/Makefile
> @@ -67,7 +67,9 @@ FILES= \
> test-llvm.bin \
> test-llvm-version.bin \
> test-libaio.bin \
> - test-libzstd.bin
> + test-libzstd.bin \
> + test-libpfm4.bin
> +
>
> FILES := $(addprefix $(OUTPUT),$(FILES))
>
> @@ -321,6 +323,9 @@ $(OUTPUT)test-libaio.bin:
> $(OUTPUT)test-libzstd.bin:
> $(BUILD) -lzstd
>
> +$(OUTPUT)test-libpfm4.bin:
> + $(BUILD) -lpfm
> +
> ###############################
>
> clean:
> diff --git a/tools/build/feature/test-libpfm4.c b/tools/build/feature/test-libpfm4.c
> new file mode 100644
> index 000000000000..7f24df86cf09
> --- /dev/null
> +++ b/tools/build/feature/test-libpfm4.c
> @@ -0,0 +1,8 @@
> +#include <sys/types.h>
> +#include <perfmon/pfmlib.h>
> +
> +int main(void)
> +{
> + pfm_initialize();
> + return 0;
> +}
> diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
> index 31824d5269cc..6e54979c2124 100644
> --- a/tools/perf/Documentation/Makefile
> +++ b/tools/perf/Documentation/Makefile
> @@ -48,7 +48,7 @@ man5dir=$(mandir)/man5
> man7dir=$(mandir)/man7
>
> ASCIIDOC=asciidoc
> -ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf
> +ASCIIDOC_EXTRA += --unsafe -f asciidoc.conf
> ASCIIDOC_HTML = xhtml11
> MANPAGE_XSL = manpage-normal.xsl
> XMLTO_EXTRA =
> @@ -59,7 +59,7 @@ HTML_REF = origin/html
>
> ifdef USE_ASCIIDOCTOR
> ASCIIDOC = asciidoctor
> -ASCIIDOC_EXTRA = -a compat-mode
> +ASCIIDOC_EXTRA += -a compat-mode
> ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
> ASCIIDOC_EXTRA += -a mansource="perf" -a manmanual="perf Manual"
> ASCIIDOC_HTML = xhtml5
> diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
> index b3f3b3f1c161..ad2c41f595c2 100644
> --- a/tools/perf/Documentation/perf-record.txt
> +++ b/tools/perf/Documentation/perf-record.txt
> @@ -596,6 +596,17 @@ Make a copy of /proc/kcore and place it into a directory with the perf data file
> Limit the sample data max size, <size> is expected to be a number with
> appended unit character - B/K/M/G
>
> +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 4d56586b2fb9..536952ad592c 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 487737a725e9..9858e3640b0c 100644
> --- a/tools/perf/Documentation/perf-top.txt
> +++ b/tools/perf/Documentation/perf-top.txt
> @@ -319,6 +319,17 @@ Default is to monitor all CPUS.
> go straight to the histogram browser, just like 'perf top' with no events
> explicitely specified does.
>
> +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 80e55e796be9..571aa6b1af40 100644
> --- a/tools/perf/Makefile.config
> +++ b/tools/perf/Makefile.config
> @@ -999,6 +999,18 @@ ifdef LIBCLANGLLVM
> endif
> endif
>
> +ifndef NO_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 d15a311408f1..9787eb3ca0a9 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 NO_LIBPFM4 to disable libpfm4 extension.
> +#
>
> # As per kernel Makefile, avoid funny character set dependencies
> unexport LC_ALL
> @@ -188,7 +190,7 @@ AWK = awk
> # non-config cases
> config := 1
>
> -NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help install-doc install-man install-html install-info install-pdf doc man html info pdf
> +NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help
>
> ifdef MAKECMDGOALS
> ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
> @@ -832,7 +834,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
>
> # 'make doc' should call 'make -C Documentation all'
> $(DOC_TARGETS):
> - $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all)
> + $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all) ASCIIDOC_EXTRA=$(ASCIIDOC_EXTRA)
>
> TAG_FOLDERS= . ../lib ../include
> TAG_FILES= ../../include/uapi/linux/perf_event.h
> diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
> index 965ef017496f..5edeb428168a 100644
> --- a/tools/perf/builtin-list.c
> +++ b/tools/perf/builtin-list.c
> @@ -18,6 +18,10 @@
> #include <subcmd/parse-options.h>
> #include <stdio.h>
>
> +#ifdef HAVE_LIBPFM
> +#include <perfmon/pfmlib.h>
> +#endif
> +
> static bool desc_flag = true;
> static bool details_flag;
>
> @@ -56,6 +60,18 @@ int cmd_list(int argc, const char **argv)
> if (!raw_dump && pager_in_use())
> printf("\nList of pre-defined events (to be used in -e):\n\n");
>
> +#ifdef HAVE_LIBPFM
> + {
> + int ret;
> + ret = pfm_initialize();
> + if (ret != PFM_SUCCESS) {
> + fprintf(stderr,
> + "warning libpfm failed to initialize: %s\n",
> + pfm_strerror(ret));
> + }
> + }
> +#endif
> +
> if (argc == 0) {
> print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
> details_flag, deprecated);
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index 7d7912e121d6..7d2737f33c2e 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -64,6 +64,10 @@
> #include <linux/zalloc.h>
> #include <linux/bitmap.h>
>
> +#ifdef HAVE_LIBPFM
> +#include <perfmon/pfmlib.h>
> +#endif
> +
> struct switch_output {
> bool enabled;
> bool signal;
> @@ -2415,6 +2419,11 @@ static struct option __record_options[] = {
> #endif
> OPT_CALLBACK(0, "max-size", &record.output_max_size,
> "size", "Limit the maximum size of the output file", parse_output_max_size),
> +#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()
> };
>
> @@ -2455,6 +2464,17 @@ int cmd_record(int argc, const char **argv)
> if (rec->evlist == NULL)
> return -ENOMEM;
>
> +#ifdef HAVE_LIBPFM
> + {
> + int ret;
> + ret = pfm_initialize();
> + if (ret != PFM_SUCCESS) {
> + ui__warning("warning libpfm failed to initialize: %s\n",
> + pfm_strerror(ret));
> + }
> + }
> +#endif
> +
> err = perf_config(perf_record_config, rec);
> if (err)
> return err;
> diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
> index ec053dc1e35c..c47eaf238f0c 100644
> --- a/tools/perf/builtin-stat.c
> +++ b/tools/perf/builtin-stat.c
> @@ -89,6 +89,10 @@
> #include <linux/ctype.h>
> #include <perf/evlist.h>
>
> +#ifdef HAVE_LIBPFM
> +#include <perfmon/pfmlib.h>
> +#endif
> +
> #define DEFAULT_SEPARATOR " "
> #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi"
>
> @@ -933,6 +937,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()
> };
>
> @@ -1871,6 +1880,18 @@ int cmd_stat(int argc, const char **argv)
> unsigned int interval, timeout;
> const char * const stat_subcommands[] = { "record", "report" };
>
> +#ifdef HAVE_LIBPFM
> + {
> + int ret;
> + ret = pfm_initialize();
> + if (ret != PFM_SUCCESS) {
> + fprintf(stderr,
> + "warning libpfm failed to initialize: %s\n",
> + pfm_strerror(ret));
> + }
> + }
> +#endif
> +
> setlocale(LC_ALL, "");
>
> evsel_list = evlist__new();
> diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
> index 6684d94b1398..afec285f9877 100644
> --- a/tools/perf/builtin-top.c
> +++ b/tools/perf/builtin-top.c
> @@ -84,6 +84,10 @@
> #include <linux/ctype.h>
> #include <perf/mmap.h>
>
> +#ifdef HAVE_LIBPFM
> +#include <perfmon/pfmlib.h>
> +#endif
> +
> static volatile int done;
> static volatile int resize;
>
> @@ -1565,6 +1569,11 @@ int cmd_top(int argc, const char **argv)
> "Sort the output by the event at the index n in group. "
> "If n is invalid, sort by the first event. "
> "WARNING: should be used on grouped events."),
> +#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()
> };
> @@ -1578,6 +1587,17 @@ int cmd_top(int argc, const char **argv)
> if (status < 0)
> return status;
>
> +#ifdef HAVE_LIBPFM
> + {
> + int ret;
> + ret = pfm_initialize();
> + if (ret != PFM_SUCCESS) {
> + ui__warning("warning libpfm failed to initialize: %s\n",
> + pfm_strerror(ret));
> + }
> + }
> +#endif
> +
> top.annotation_opts.min_pcnt = 5;
> top.annotation_opts.context = 4;
>
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index eb880efbce16..ca1b9cbf3355 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -2448,7 +2448,7 @@ bool perf_evsel__fallback(struct evsel *evsel, int err,
>
> /* 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 53187c501ee8..0b6eec714e6f 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 10107747b361..31ed184566c8 100644
> --- a/tools/perf/util/parse-events.c
> +++ b/tools/perf/util/parse-events.c
> @@ -37,6 +37,11 @@
> #include "util/evsel_config.h"
> #include "util/event.h"
>
> +#ifdef HAVE_LIBPFM
> +#include <perfmon/pfmlib_perf_event.h>
> +static void print_libpfm_events(bool name_only);
> +#endif
> +
> #define MAX_NAME_LEN 100
>
> #ifdef PARSER_DEBUG
> @@ -2794,6 +2799,10 @@ 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);
> +
> +#ifdef HAVE_LIBPFM
> + print_libpfm_events(name_only);
> +#endif
> }
>
> int parse_events__is_hardcoded_term(struct parse_events_term *term)
> @@ -3042,3 +3051,240 @@ char *parse_events_formats_error_string(char *additional_terms)
> fail:
> return NULL;
> }
> +
> +#ifdef HAVE_LIBPFM
> +static int
> +parse_libpfm_event(char *strp, struct perf_event_attr *attr)
> +{
> + int ret;
> +
> + ret = pfm_get_perf_event_encoding(strp, PFM_PLM0|PFM_PLM3,
> + attr, NULL, NULL);
> + return 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;
> +
> + 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) {
> + fprintf(stderr, "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 = parse_libpfm_event(q, &attr);
> + if (ret != PFM_SUCCESS) {
> + fprintf(stderr, "failed to parse event %s : %s\n", str, pfm_strerror(ret));
> + goto error;
> + }
> +
> + evsel = perf_evsel__new_idx(&attr, evlist->core.nr_entries);
> + if (evsel == NULL)
> + goto error;
> +
> + evsel->name = strdup(q);
> + if (!evsel->name) {
> + evsel__delete(evsel);
> + goto error;
> + }
> + evsel->is_libpfm_event = true;
> +
> + pmu = perf_pmu__find_by_type((unsigned)attr.type);
> + if (pmu)
> + evsel->core.own_cpus = perf_cpu_map__get(pmu->cpus);
> +
> + 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) {
> + fprintf(stderr, "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_detailed_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
> +{
> + pfm_event_attr_info_t ainfo;
> + const char *src;
> + int j, ret;
> +
> + ainfo.size = sizeof(ainfo);
> +
> + printf("\nName : %s\n", info->name);
> + printf("PMU : %s\n", pinfo->name);
> + printf("Desc : %s\n", info->desc);
> + printf("Equiv : %s\n", info->equiv ? info->equiv : "None");
> + 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.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: [%s] : ", ainfo.code, src, ainfo.name);
> + print_attr_flags(&ainfo);
> + printf(": %s\n", ainfo.desc);
> + 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_simplified_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
> +{
> + pfm_event_attr_info_t ainfo;
> + int j, ret;
> + int um = 0;
> +
> + 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);
> + um++;
> + }
> + if (um == 0)
> + printf("%s::%s\n", pinfo->name, info->name);
> +}
> +
> +static void
> +print_libpfm_events(bool name_only)
> +{
> + pfm_event_info_t info;
> + pfm_pmu_info_t pinfo;
> + pfm_event_attr_info_t ainfo;
> + int i, p, ret;
> +
> + /* initialize to zero to indicate ABI version */
> + info.size = sizeof(info);
> + pinfo.size = sizeof(pinfo);
> + ainfo.size = sizeof(ainfo);
> +
> + putchar('\n');
> +
> + pfm_for_all_pmus(p) {
> + ret = pfm_get_pmu_info(p, &pinfo);
> + if (ret != PFM_SUCCESS)
> + continue;
> +
> + /* ony 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)
> + print_libpfm_detailed_events(&pinfo, &info);
> + else
> + print_libpfm_simplified_events(&pinfo, &info);
> + }
> + }
> +}
> +#endif
> diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
> index 27596cbd0ba0..84d4799c9a31 100644
> --- a/tools/perf/util/parse-events.h
> +++ b/tools/perf/util/parse-events.h
> @@ -37,6 +37,11 @@ int parse_events_terms(struct list_head *terms, const char *str);
> int parse_filter(const struct option *opt, const char *str, int unset);
> int exclude_perf(const struct option *opt, const char *arg, int unset);
>
> +#ifdef HAVE_LIBPFM
> +extern int parse_libpfm_events_option(const struct option *opt, const char *str,
> + int unset);
> +#endif
> +
> #define EVENTS_HELP_MAX (128*1024)
>
> enum perf_pmu_event_symbol_type {
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index ef6a63f3d386..5e918ca740c6 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -869,6 +869,17 @@ static struct perf_pmu *pmu_find(const char *name)
> return NULL;
> }
>
> +struct perf_pmu *perf_pmu__find_by_type(unsigned int type)
> +{
> + struct perf_pmu *pmu;
> +
> + list_for_each_entry(pmu, &pmus, list)
> + if (pmu->type == type)
> + return pmu;
> +
> + return NULL;
> +}
> +
> struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
> {
> /*
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 5fb3f16828df..de3b868d912c 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -65,6 +65,7 @@ struct perf_pmu_alias {
> };
>
> struct perf_pmu *perf_pmu__find(const char *name);
> +struct perf_pmu *perf_pmu__find_by_type(unsigned int type);
> int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
> struct list_head *head_terms,
> struct parse_events_error *error);
> --
> 2.26.0.rc2.310.g2932bb562d-goog
>
--
- Arnaldo
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v6] perf tools: add support for libpfm4
2020-04-29 14:49 ` Arnaldo Carvalho de Melo
@ 2020-04-29 16:59 ` Ian Rogers
0 siblings, 0 replies; 3+ messages in thread
From: Ian Rogers @ 2020-04-29 16:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Namhyung Kim, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Yonghong Song, Andrii Nakryiko,
Greg Kroah-Hartman, Thomas Gleixner, Igor Lubashev,
Alexey Budankov, Florian Fainelli, Adrian Hunter, Andi Kleen,
Jiwei Sun, yuzhoujian, Kan Liang, Jin Yao, Leo Yan, John Garry,
LKML, netdev, bpf, linux-perf-users, Stephane Eranian
On Wed, Apr 29, 2020 at 7:49 AM Arnaldo Carvalho de Melo
<arnaldo.melo@gmail.com> wrote:
>
> Em Wed, Apr 01, 2020 at 11:49:13AM -0700, Ian Rogers escreveu:
> > From: Stephane Eranian <eranian@google.com>
> >
> > This patch links perf with the libpfm4 library if it is available and
> > NO_LIBPFM4 isn't passed to the build. The libpfm4 library contains hardware
>
> Can we make this a explicit decision instead, i.e. for it to be linked
> we would need to have its development files installed and -DLIBPFM4,
> like we have for CORESIGHT?
>
> $ grep CORESIGHT tools/perf/Makefile.perf
> # Define CORESIGHT if you DO WANT support for CoreSight trace decoding.
> $
Thanks Arnaldo, I'll do that.
Ian
> - Arnaldo
>
> > 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 ....
> >
> > v6 is a rebase.
> > v5 is a rebase.
> > v4 is a rebase on git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git
> > branch perf/core and re-adds the tools/build/feature/test-libpfm4.c
> > missed in v3.
> > v3 is against acme/perf/core and removes a diagnostic warning.
> > v2 of this patch makes the --pfm-events man page documentation
> > conditional on libpfm4 behing configured. It tidies some of the
> > documentation and adds the feature test missed in the v1 patch.
> >
> > Reviewed-By:Ian Rogers <irogers@google.com>
> > ---
> > tools/build/Makefile.feature | 6 +-
> > tools/build/feature/Makefile | 7 +-
> > tools/build/feature/test-libpfm4.c | 8 +
> > tools/perf/Documentation/Makefile | 4 +-
> > 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 | 12 ++
> > tools/perf/Makefile.perf | 6 +-
> > tools/perf/builtin-list.c | 16 ++
> > tools/perf/builtin-record.c | 20 ++
> > tools/perf/builtin-stat.c | 21 ++
> > tools/perf/builtin-top.c | 20 ++
> > tools/perf/util/evsel.c | 2 +-
> > tools/perf/util/evsel.h | 1 +
> > tools/perf/util/parse-events.c | 246 +++++++++++++++++++++++
> > tools/perf/util/parse-events.h | 5 +
> > tools/perf/util/pmu.c | 11 +
> > tools/perf/util/pmu.h | 1 +
> > 19 files changed, 410 insertions(+), 8 deletions(-)
> > create mode 100644 tools/build/feature/test-libpfm4.c
> >
> > diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
> > index 574c2e0b9d20..08c6fe5aee2d 100644
> > --- a/tools/build/Makefile.feature
> > +++ b/tools/build/Makefile.feature
> > @@ -72,7 +72,8 @@ FEATURE_TESTS_BASIC := \
> > setns \
> > libaio \
> > libzstd \
> > - disassembler-four-args
> > + disassembler-four-args \
> > + libpfm4
> >
> > # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
> > # of all feature tests
> > @@ -127,7 +128,8 @@ FEATURE_DISPLAY ?= \
> > bpf \
> > libaio \
> > libzstd \
> > - disassembler-four-args
> > + disassembler-four-args \
> > + libpfm4
> >
> > # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
> > # If in the future we need per-feature checks/flags for features not
> > diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
> > index 7ac0d8088565..573072d32545 100644
> > --- a/tools/build/feature/Makefile
> > +++ b/tools/build/feature/Makefile
> > @@ -67,7 +67,9 @@ FILES= \
> > test-llvm.bin \
> > test-llvm-version.bin \
> > test-libaio.bin \
> > - test-libzstd.bin
> > + test-libzstd.bin \
> > + test-libpfm4.bin
> > +
> >
> > FILES := $(addprefix $(OUTPUT),$(FILES))
> >
> > @@ -321,6 +323,9 @@ $(OUTPUT)test-libaio.bin:
> > $(OUTPUT)test-libzstd.bin:
> > $(BUILD) -lzstd
> >
> > +$(OUTPUT)test-libpfm4.bin:
> > + $(BUILD) -lpfm
> > +
> > ###############################
> >
> > clean:
> > diff --git a/tools/build/feature/test-libpfm4.c b/tools/build/feature/test-libpfm4.c
> > new file mode 100644
> > index 000000000000..7f24df86cf09
> > --- /dev/null
> > +++ b/tools/build/feature/test-libpfm4.c
> > @@ -0,0 +1,8 @@
> > +#include <sys/types.h>
> > +#include <perfmon/pfmlib.h>
> > +
> > +int main(void)
> > +{
> > + pfm_initialize();
> > + return 0;
> > +}
> > diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
> > index 31824d5269cc..6e54979c2124 100644
> > --- a/tools/perf/Documentation/Makefile
> > +++ b/tools/perf/Documentation/Makefile
> > @@ -48,7 +48,7 @@ man5dir=$(mandir)/man5
> > man7dir=$(mandir)/man7
> >
> > ASCIIDOC=asciidoc
> > -ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf
> > +ASCIIDOC_EXTRA += --unsafe -f asciidoc.conf
> > ASCIIDOC_HTML = xhtml11
> > MANPAGE_XSL = manpage-normal.xsl
> > XMLTO_EXTRA =
> > @@ -59,7 +59,7 @@ HTML_REF = origin/html
> >
> > ifdef USE_ASCIIDOCTOR
> > ASCIIDOC = asciidoctor
> > -ASCIIDOC_EXTRA = -a compat-mode
> > +ASCIIDOC_EXTRA += -a compat-mode
> > ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
> > ASCIIDOC_EXTRA += -a mansource="perf" -a manmanual="perf Manual"
> > ASCIIDOC_HTML = xhtml5
> > diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
> > index b3f3b3f1c161..ad2c41f595c2 100644
> > --- a/tools/perf/Documentation/perf-record.txt
> > +++ b/tools/perf/Documentation/perf-record.txt
> > @@ -596,6 +596,17 @@ Make a copy of /proc/kcore and place it into a directory with the perf data file
> > Limit the sample data max size, <size> is expected to be a number with
> > appended unit character - B/K/M/G
> >
> > +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 4d56586b2fb9..536952ad592c 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 487737a725e9..9858e3640b0c 100644
> > --- a/tools/perf/Documentation/perf-top.txt
> > +++ b/tools/perf/Documentation/perf-top.txt
> > @@ -319,6 +319,17 @@ Default is to monitor all CPUS.
> > go straight to the histogram browser, just like 'perf top' with no events
> > explicitely specified does.
> >
> > +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 80e55e796be9..571aa6b1af40 100644
> > --- a/tools/perf/Makefile.config
> > +++ b/tools/perf/Makefile.config
> > @@ -999,6 +999,18 @@ ifdef LIBCLANGLLVM
> > endif
> > endif
> >
> > +ifndef NO_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 d15a311408f1..9787eb3ca0a9 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 NO_LIBPFM4 to disable libpfm4 extension.
> > +#
> >
> > # As per kernel Makefile, avoid funny character set dependencies
> > unexport LC_ALL
> > @@ -188,7 +190,7 @@ AWK = awk
> > # non-config cases
> > config := 1
> >
> > -NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help install-doc install-man install-html install-info install-pdf doc man html info pdf
> > +NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help
> >
> > ifdef MAKECMDGOALS
> > ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
> > @@ -832,7 +834,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
> >
> > # 'make doc' should call 'make -C Documentation all'
> > $(DOC_TARGETS):
> > - $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all)
> > + $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all) ASCIIDOC_EXTRA=$(ASCIIDOC_EXTRA)
> >
> > TAG_FOLDERS= . ../lib ../include
> > TAG_FILES= ../../include/uapi/linux/perf_event.h
> > diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
> > index 965ef017496f..5edeb428168a 100644
> > --- a/tools/perf/builtin-list.c
> > +++ b/tools/perf/builtin-list.c
> > @@ -18,6 +18,10 @@
> > #include <subcmd/parse-options.h>
> > #include <stdio.h>
> >
> > +#ifdef HAVE_LIBPFM
> > +#include <perfmon/pfmlib.h>
> > +#endif
> > +
> > static bool desc_flag = true;
> > static bool details_flag;
> >
> > @@ -56,6 +60,18 @@ int cmd_list(int argc, const char **argv)
> > if (!raw_dump && pager_in_use())
> > printf("\nList of pre-defined events (to be used in -e):\n\n");
> >
> > +#ifdef HAVE_LIBPFM
> > + {
> > + int ret;
> > + ret = pfm_initialize();
> > + if (ret != PFM_SUCCESS) {
> > + fprintf(stderr,
> > + "warning libpfm failed to initialize: %s\n",
> > + pfm_strerror(ret));
> > + }
> > + }
> > +#endif
> > +
> > if (argc == 0) {
> > print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
> > details_flag, deprecated);
> > diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> > index 7d7912e121d6..7d2737f33c2e 100644
> > --- a/tools/perf/builtin-record.c
> > +++ b/tools/perf/builtin-record.c
> > @@ -64,6 +64,10 @@
> > #include <linux/zalloc.h>
> > #include <linux/bitmap.h>
> >
> > +#ifdef HAVE_LIBPFM
> > +#include <perfmon/pfmlib.h>
> > +#endif
> > +
> > struct switch_output {
> > bool enabled;
> > bool signal;
> > @@ -2415,6 +2419,11 @@ static struct option __record_options[] = {
> > #endif
> > OPT_CALLBACK(0, "max-size", &record.output_max_size,
> > "size", "Limit the maximum size of the output file", parse_output_max_size),
> > +#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()
> > };
> >
> > @@ -2455,6 +2464,17 @@ int cmd_record(int argc, const char **argv)
> > if (rec->evlist == NULL)
> > return -ENOMEM;
> >
> > +#ifdef HAVE_LIBPFM
> > + {
> > + int ret;
> > + ret = pfm_initialize();
> > + if (ret != PFM_SUCCESS) {
> > + ui__warning("warning libpfm failed to initialize: %s\n",
> > + pfm_strerror(ret));
> > + }
> > + }
> > +#endif
> > +
> > err = perf_config(perf_record_config, rec);
> > if (err)
> > return err;
> > diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
> > index ec053dc1e35c..c47eaf238f0c 100644
> > --- a/tools/perf/builtin-stat.c
> > +++ b/tools/perf/builtin-stat.c
> > @@ -89,6 +89,10 @@
> > #include <linux/ctype.h>
> > #include <perf/evlist.h>
> >
> > +#ifdef HAVE_LIBPFM
> > +#include <perfmon/pfmlib.h>
> > +#endif
> > +
> > #define DEFAULT_SEPARATOR " "
> > #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi"
> >
> > @@ -933,6 +937,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()
> > };
> >
> > @@ -1871,6 +1880,18 @@ int cmd_stat(int argc, const char **argv)
> > unsigned int interval, timeout;
> > const char * const stat_subcommands[] = { "record", "report" };
> >
> > +#ifdef HAVE_LIBPFM
> > + {
> > + int ret;
> > + ret = pfm_initialize();
> > + if (ret != PFM_SUCCESS) {
> > + fprintf(stderr,
> > + "warning libpfm failed to initialize: %s\n",
> > + pfm_strerror(ret));
> > + }
> > + }
> > +#endif
> > +
> > setlocale(LC_ALL, "");
> >
> > evsel_list = evlist__new();
> > diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
> > index 6684d94b1398..afec285f9877 100644
> > --- a/tools/perf/builtin-top.c
> > +++ b/tools/perf/builtin-top.c
> > @@ -84,6 +84,10 @@
> > #include <linux/ctype.h>
> > #include <perf/mmap.h>
> >
> > +#ifdef HAVE_LIBPFM
> > +#include <perfmon/pfmlib.h>
> > +#endif
> > +
> > static volatile int done;
> > static volatile int resize;
> >
> > @@ -1565,6 +1569,11 @@ int cmd_top(int argc, const char **argv)
> > "Sort the output by the event at the index n in group. "
> > "If n is invalid, sort by the first event. "
> > "WARNING: should be used on grouped events."),
> > +#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()
> > };
> > @@ -1578,6 +1587,17 @@ int cmd_top(int argc, const char **argv)
> > if (status < 0)
> > return status;
> >
> > +#ifdef HAVE_LIBPFM
> > + {
> > + int ret;
> > + ret = pfm_initialize();
> > + if (ret != PFM_SUCCESS) {
> > + ui__warning("warning libpfm failed to initialize: %s\n",
> > + pfm_strerror(ret));
> > + }
> > + }
> > +#endif
> > +
> > top.annotation_opts.min_pcnt = 5;
> > top.annotation_opts.context = 4;
> >
> > diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> > index eb880efbce16..ca1b9cbf3355 100644
> > --- a/tools/perf/util/evsel.c
> > +++ b/tools/perf/util/evsel.c
> > @@ -2448,7 +2448,7 @@ bool perf_evsel__fallback(struct evsel *evsel, int err,
> >
> > /* 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 53187c501ee8..0b6eec714e6f 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 10107747b361..31ed184566c8 100644
> > --- a/tools/perf/util/parse-events.c
> > +++ b/tools/perf/util/parse-events.c
> > @@ -37,6 +37,11 @@
> > #include "util/evsel_config.h"
> > #include "util/event.h"
> >
> > +#ifdef HAVE_LIBPFM
> > +#include <perfmon/pfmlib_perf_event.h>
> > +static void print_libpfm_events(bool name_only);
> > +#endif
> > +
> > #define MAX_NAME_LEN 100
> >
> > #ifdef PARSER_DEBUG
> > @@ -2794,6 +2799,10 @@ 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);
> > +
> > +#ifdef HAVE_LIBPFM
> > + print_libpfm_events(name_only);
> > +#endif
> > }
> >
> > int parse_events__is_hardcoded_term(struct parse_events_term *term)
> > @@ -3042,3 +3051,240 @@ char *parse_events_formats_error_string(char *additional_terms)
> > fail:
> > return NULL;
> > }
> > +
> > +#ifdef HAVE_LIBPFM
> > +static int
> > +parse_libpfm_event(char *strp, struct perf_event_attr *attr)
> > +{
> > + int ret;
> > +
> > + ret = pfm_get_perf_event_encoding(strp, PFM_PLM0|PFM_PLM3,
> > + attr, NULL, NULL);
> > + return 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;
> > +
> > + 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) {
> > + fprintf(stderr, "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 = parse_libpfm_event(q, &attr);
> > + if (ret != PFM_SUCCESS) {
> > + fprintf(stderr, "failed to parse event %s : %s\n", str, pfm_strerror(ret));
> > + goto error;
> > + }
> > +
> > + evsel = perf_evsel__new_idx(&attr, evlist->core.nr_entries);
> > + if (evsel == NULL)
> > + goto error;
> > +
> > + evsel->name = strdup(q);
> > + if (!evsel->name) {
> > + evsel__delete(evsel);
> > + goto error;
> > + }
> > + evsel->is_libpfm_event = true;
> > +
> > + pmu = perf_pmu__find_by_type((unsigned)attr.type);
> > + if (pmu)
> > + evsel->core.own_cpus = perf_cpu_map__get(pmu->cpus);
> > +
> > + 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) {
> > + fprintf(stderr, "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_detailed_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
> > +{
> > + pfm_event_attr_info_t ainfo;
> > + const char *src;
> > + int j, ret;
> > +
> > + ainfo.size = sizeof(ainfo);
> > +
> > + printf("\nName : %s\n", info->name);
> > + printf("PMU : %s\n", pinfo->name);
> > + printf("Desc : %s\n", info->desc);
> > + printf("Equiv : %s\n", info->equiv ? info->equiv : "None");
> > + 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.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: [%s] : ", ainfo.code, src, ainfo.name);
> > + print_attr_flags(&ainfo);
> > + printf(": %s\n", ainfo.desc);
> > + 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_simplified_events(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
> > +{
> > + pfm_event_attr_info_t ainfo;
> > + int j, ret;
> > + int um = 0;
> > +
> > + 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);
> > + um++;
> > + }
> > + if (um == 0)
> > + printf("%s::%s\n", pinfo->name, info->name);
> > +}
> > +
> > +static void
> > +print_libpfm_events(bool name_only)
> > +{
> > + pfm_event_info_t info;
> > + pfm_pmu_info_t pinfo;
> > + pfm_event_attr_info_t ainfo;
> > + int i, p, ret;
> > +
> > + /* initialize to zero to indicate ABI version */
> > + info.size = sizeof(info);
> > + pinfo.size = sizeof(pinfo);
> > + ainfo.size = sizeof(ainfo);
> > +
> > + putchar('\n');
> > +
> > + pfm_for_all_pmus(p) {
> > + ret = pfm_get_pmu_info(p, &pinfo);
> > + if (ret != PFM_SUCCESS)
> > + continue;
> > +
> > + /* ony 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)
> > + print_libpfm_detailed_events(&pinfo, &info);
> > + else
> > + print_libpfm_simplified_events(&pinfo, &info);
> > + }
> > + }
> > +}
> > +#endif
> > diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
> > index 27596cbd0ba0..84d4799c9a31 100644
> > --- a/tools/perf/util/parse-events.h
> > +++ b/tools/perf/util/parse-events.h
> > @@ -37,6 +37,11 @@ int parse_events_terms(struct list_head *terms, const char *str);
> > int parse_filter(const struct option *opt, const char *str, int unset);
> > int exclude_perf(const struct option *opt, const char *arg, int unset);
> >
> > +#ifdef HAVE_LIBPFM
> > +extern int parse_libpfm_events_option(const struct option *opt, const char *str,
> > + int unset);
> > +#endif
> > +
> > #define EVENTS_HELP_MAX (128*1024)
> >
> > enum perf_pmu_event_symbol_type {
> > diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> > index ef6a63f3d386..5e918ca740c6 100644
> > --- a/tools/perf/util/pmu.c
> > +++ b/tools/perf/util/pmu.c
> > @@ -869,6 +869,17 @@ static struct perf_pmu *pmu_find(const char *name)
> > return NULL;
> > }
> >
> > +struct perf_pmu *perf_pmu__find_by_type(unsigned int type)
> > +{
> > + struct perf_pmu *pmu;
> > +
> > + list_for_each_entry(pmu, &pmus, list)
> > + if (pmu->type == type)
> > + return pmu;
> > +
> > + return NULL;
> > +}
> > +
> > struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
> > {
> > /*
> > diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> > index 5fb3f16828df..de3b868d912c 100644
> > --- a/tools/perf/util/pmu.h
> > +++ b/tools/perf/util/pmu.h
> > @@ -65,6 +65,7 @@ struct perf_pmu_alias {
> > };
> >
> > struct perf_pmu *perf_pmu__find(const char *name);
> > +struct perf_pmu *perf_pmu__find_by_type(unsigned int type);
> > int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
> > struct list_head *head_terms,
> > struct parse_events_error *error);
> > --
> > 2.26.0.rc2.310.g2932bb562d-goog
> >
>
> --
>
> - Arnaldo
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2020-04-29 17:00 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-01 18:49 [PATCH v6] perf tools: add support for libpfm4 Ian Rogers
2020-04-29 14:49 ` Arnaldo Carvalho de Melo
2020-04-29 16:59 ` Ian Rogers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).