mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-11-08 18:43:21 +01:00
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 <[email protected]> Reviewed-by: Ian Rogers <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Alexey Budankov <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Daniel Borkmann <[email protected]> Cc: Florian Fainelli <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Igor Lubashev <[email protected]> Cc: Jin Yao <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Jiwei Sun <[email protected]> Cc: John Garry <[email protected]> Cc: Kan Liang <[email protected]> Cc: Leo Yan <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Martin KaFai Lau <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Yonghong Song <[email protected]> Cc: [email protected] Cc: [email protected] Cc: yuzhoujian <[email protected]> Link: http://lore.kernel.org/lkml/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
282 lines
5.7 KiB
C
282 lines
5.7 KiB
C
// 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');
|
|
}
|
|
}
|