@@ -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
@@ -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:
@@ -587,6 +587,16 @@ 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
+--pfm-events events::
+this option is only available when perf is linked with the libpfm4 library
+(see http://perfmon2.sf.net). It allows passing hardware events as strings
+for all support processors. Event filters can also be used. As an example:
+perf stat --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.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
@@ -71,6 +71,15 @@ report::
--tid=<tid>::
stat events on existing thread id (comma separated list)
+--pfm-events events::
+this option is only available when perf is linked with the libpfm4 library
+(see http://perfmon2.sf.net). It allows passing hardware events as strings
+for all support processors. Event filters can also be used. As an example:
+perf stat --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.
-a::
--all-cpus::
@@ -310,6 +310,16 @@ Default is to monitor all CPUS.
go straight to the histogram browser, just like 'perf top' with no events
explicitely specified does.
+--pfm-events events::
+this option is only available when perf is linked with the libpfm4 library
+(see http://perfmon2.sf.net). It allows passing hardware events as strings
+for all support processors. Event filters can also be used. As an example:
+perf stat --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.
+
INTERACTIVE PROMPTING KEYS
--------------------------
@@ -999,6 +999,17 @@ ifdef LIBCLANGLLVM
endif
endif
+ifndef NO_LIBPFM4
+ ifeq ($(feature-libpfm4), 1)
+ CFLAGS += -DHAVE_LIBPFM
+ EXTLIBS += -lpfm
+ $(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
@@ -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
@@ -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);
@@ -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;
@@ -2405,6 +2409,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()
};
@@ -2445,6 +2454,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;
@@ -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"
@@ -929,6 +933,11 @@ static struct option stat_options[] = {
OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user,
"Configure all used events to run in user space.",
PARSE_OPT_EXCLUSIVE),
+#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()
};
@@ -1867,6 +1876,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();
@@ -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;
@@ -1545,6 +1549,11 @@ int cmd_top(int argc, const char **argv)
"number of thread to run event synthesize"),
OPT_BOOLEAN(0, "namespaces", &opts->record_namespaces,
"Record namespaces 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()
};
@@ -1558,6 +1567,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;
@@ -2418,7 +2418,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)
@@ -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;
@@ -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
@@ -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 {
@@ -864,6 +864,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)
{
/*
@@ -64,6 +64,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);
This patch links perf with the libpfm4 library. This library contains all the hardware event tables for all processors supported by perf_events. This is a helper library that help 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 event by names. 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 .... You need to have libpfm4-dev package installed on your system. Author: Stephane Eranian <eranian@google.com> Signed-off-by: Ian Rogers <irogers@google.com> --- tools/build/Makefile.feature | 6 +- tools/build/feature/Makefile | 7 +- tools/perf/Documentation/perf-record.txt | 10 + tools/perf/Documentation/perf-stat.txt | 9 + tools/perf/Documentation/perf-top.txt | 10 + tools/perf/Makefile.config | 11 + tools/perf/Makefile.perf | 2 + 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 + 17 files changed, 394 insertions(+), 4 deletions(-)