perf list: Reorganize to use callbacks to allow honouring command line options

Rather than controlling the list output with passed flags, add
callbacks that are called when an event or metric are
encountered. State is passed to the callback so that command line
options can be respected, alternatively the callbacks can be changed.

Fix a few bugs:
 - wordwrap to columns metric descriptions and expressions;
 - remove unnecessary whitespace after PMU event names;
 - the metric filter is a glob but matched using strstr which will
   always fail, switch to using a proper globmatch,
 - the detail flag gives details for extra kernel PMU events like
   branch-instructions.

In metricgroup.c switch from struct mep being a rbtree of metricgroups
containing a list of metrics, to the tree directly containing all the
metrics. In general the alias for a name is passed to the print
routine rather than being contained in the name with OR.

Committer notes:

Check the asprint() return to address this on fedora 36:

  util/print-events.c: In function ‘print_sdt_events’:
  util/print-events.c:183:33: error: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
    183 |                                 asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid);
        |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  cc1: all warnings being treated as errors

  $ gcc --version | head -1
  gcc (GCC) 12.2.1 20220819 (Red Hat 12.2.1-2)
  $

Fix ps.pmu_glob setting when dealing with *:* events, it was being left
with a freed pointer that then at the end of cmd_list() would be double
freed.

Check if pmu_name is NULL in default_print_event() before calling
strglobmatch(pmu_name, ...) to avoid a segfault.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Xin Gao <gaoxin@cdjrlc.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: http://lore.kernel.org/lkml/20221114210723.2749751-10-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2022-11-14 13:07:22 -08:00 committed by Arnaldo Carvalho de Melo
parent a3720e969c
commit e5c6109f48
7 changed files with 620 additions and 508 deletions

View File

@ -15,31 +15,240 @@
#include "util/pmu-hybrid.h"
#include "util/debug.h"
#include "util/metricgroup.h"
#include "util/string2.h"
#include "util/strlist.h"
#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include <stdio.h>
static bool desc_flag = true;
static bool details_flag;
/**
* struct print_state - State and configuration passed to the default_print
* functions.
*/
struct print_state {
/**
* @pmu_glob: Optionally restrict PMU and metric matching to PMU or
* debugfs subsystem name.
*/
char *pmu_glob;
/** @event_glob: Optional pattern matching glob. */
char *event_glob;
/** @name_only: Print event or metric names only. */
bool name_only;
/** @desc: Print the event or metric description. */
bool desc;
/** @long_desc: Print longer event or metric description. */
bool long_desc;
/** @deprecated: Print deprecated events or metrics. */
bool deprecated;
/**
* @detailed: Print extra information on the perf event such as names
* and expressions used internally by events.
*/
bool detailed;
/** @metrics: Controls printing of metric and metric groups. */
bool metrics;
/** @metricgroups: Controls printing of metric and metric groups. */
bool metricgroups;
/** @last_topic: The last printed event topic. */
char *last_topic;
/** @last_metricgroups: The last printed metric group. */
char *last_metricgroups;
/** @visited_metrics: Metrics that are printed to avoid duplicates. */
struct strlist *visited_metrics;
};
static void default_print_start(void *ps)
{
struct print_state *print_state = ps;
if (!print_state->name_only && pager_in_use())
printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
}
static void default_print_end(void *print_state __maybe_unused) {}
static void wordwrap(const char *s, int start, int max, int corr)
{
int column = start;
int n;
while (*s) {
int wlen = strcspn(s, " \t");
if (column + wlen >= max && column > start) {
printf("\n%*s", start, "");
column = start + corr;
}
n = printf("%s%.*s", column > start ? " " : "", wlen, s);
if (n <= 0)
break;
s += wlen;
column += n;
s = skip_spaces(s);
}
}
static void default_print_event(void *ps, const char *pmu_name, const char *topic,
const char *event_name, const char *event_alias,
const char *scale_unit __maybe_unused,
bool deprecated, const char *event_type_desc,
const char *desc, const char *long_desc,
const char *encoding_desc,
const char *metric_name, const char *metric_expr)
{
struct print_state *print_state = ps;
int pos;
if (deprecated && !print_state->deprecated)
return;
if (print_state->pmu_glob && pmu_name && !strglobmatch(pmu_name, print_state->pmu_glob))
return;
if (print_state->event_glob &&
(!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
(!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
(!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
return;
if (print_state->name_only) {
if (event_alias && strlen(event_alias))
printf("%s ", event_alias);
else
printf("%s ", event_name);
return;
}
if (strcmp(print_state->last_topic, topic ?: "")) {
if (topic)
printf("\n%s:\n", topic);
free(print_state->last_topic);
print_state->last_topic = strdup(topic ?: "");
}
if (event_alias && strlen(event_alias))
pos = printf(" %s OR %s", event_name, event_alias);
else
pos = printf(" %s", event_name);
if (!topic && event_type_desc) {
for (; pos < 53; pos++)
putchar(' ');
printf("[%s]\n", event_type_desc);
} else
putchar('\n');
if (desc && print_state->desc) {
printf("%*s", 8, "[");
wordwrap(desc, 8, pager_get_columns(), 0);
printf("]\n");
}
if (long_desc && print_state->long_desc) {
printf("%*s", 8, "[");
wordwrap(long_desc, 8, pager_get_columns(), 0);
printf("]\n");
}
if (print_state->detailed && encoding_desc) {
printf("%*s%s", 8, "", encoding_desc);
if (metric_name)
printf(" MetricName: %s", metric_name);
if (metric_expr)
printf(" MetricExpr: %s", metric_expr);
putchar('\n');
}
}
static void default_print_metric(void *ps,
const char *group,
const char *name,
const char *desc,
const char *long_desc,
const char *expr,
const char *unit __maybe_unused)
{
struct print_state *print_state = ps;
if (print_state->event_glob &&
(!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
(!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
return;
if (!print_state->name_only && !print_state->last_metricgroups) {
if (print_state->metricgroups) {
printf("\nMetric Groups:\n");
if (!print_state->metrics)
putchar('\n');
} else {
printf("\nMetrics:\n\n");
}
}
if (!print_state->last_metricgroups ||
strcmp(print_state->last_metricgroups, group ?: "")) {
if (group && print_state->metricgroups) {
if (print_state->name_only)
printf("%s ", group);
else if (print_state->metrics)
printf("\n%s:\n", group);
else
printf("%s\n", group);
}
free(print_state->last_metricgroups);
print_state->last_metricgroups = strdup(group ?: "");
}
if (!print_state->metrics)
return;
if (print_state->name_only) {
if (print_state->metrics &&
!strlist__has_entry(print_state->visited_metrics, name)) {
printf("%s ", name);
strlist__add(print_state->visited_metrics, name);
}
return;
}
printf(" %s\n", name);
if (desc && print_state->desc) {
printf("%*s", 8, "[");
wordwrap(desc, 8, pager_get_columns(), 0);
printf("]\n");
}
if (long_desc && print_state->long_desc) {
printf("%*s", 8, "[");
wordwrap(long_desc, 8, pager_get_columns(), 0);
printf("]\n");
}
if (expr && print_state->detailed) {
printf("%*s", 8, "[");
wordwrap(expr, 8, pager_get_columns(), 0);
printf("]\n");
}
}
int cmd_list(int argc, const char **argv)
{
int i, ret = 0;
bool raw_dump = false;
bool long_desc_flag = false;
bool deprecated = false;
char *pmu_name = NULL;
struct print_state ps = {};
struct print_callbacks print_cb = {
.print_start = default_print_start,
.print_end = default_print_end,
.print_event = default_print_event,
.print_metric = default_print_metric,
};
const char *hybrid_name = NULL;
const char *unit_name = NULL;
struct option list_options[] = {
OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
OPT_BOOLEAN('d', "desc", &desc_flag,
OPT_BOOLEAN(0, "raw-dump", &ps.name_only, "Dump raw events"),
OPT_BOOLEAN('d', "desc", &ps.desc,
"Print extra event descriptions. --no-desc to not print."),
OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
OPT_BOOLEAN('v', "long-desc", &ps.long_desc,
"Print longer event descriptions."),
OPT_BOOLEAN(0, "details", &details_flag,
OPT_BOOLEAN(0, "details", &ps.detailed,
"Print information on the perf event names and expressions used internally by events."),
OPT_BOOLEAN(0, "deprecated", &deprecated,
OPT_BOOLEAN(0, "deprecated", &ps.deprecated,
"Print deprecated events."),
OPT_STRING(0, "cputype", &hybrid_name, "hybrid cpu type",
"Limit PMU or metric printing to the given hybrid PMU (e.g. core or atom)."),
@ -63,20 +272,28 @@ int cmd_list(int argc, const char **argv)
setup_pager();
if (!raw_dump && pager_in_use())
printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
if (!ps.name_only)
setup_pager();
ps.desc = !ps.long_desc;
ps.last_topic = strdup("");
assert(ps.last_topic);
ps.visited_metrics = strlist__new(NULL, NULL);
assert(ps.visited_metrics);
if (unit_name)
pmu_name = strdup(unit_name);
ps.pmu_glob = strdup(unit_name);
else if (hybrid_name) {
pmu_name = perf_pmu__hybrid_type_to_pmu(hybrid_name);
if (!pmu_name)
ps.pmu_glob = perf_pmu__hybrid_type_to_pmu(hybrid_name);
if (!ps.pmu_glob)
pr_warning("WARNING: hybrid cputype is not supported!\n");
}
print_cb.print_start(&ps);
if (argc == 0) {
print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
details_flag, deprecated, pmu_name);
ps.metrics = true;
ps.metricgroups = true;
print_events(&print_cb, &ps);
goto out;
}
@ -84,31 +301,35 @@ int cmd_list(int argc, const char **argv)
char *sep, *s;
if (strcmp(argv[i], "tracepoint") == 0)
print_tracepoint_events(NULL, NULL, raw_dump);
print_tracepoint_events(&print_cb, &ps);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
print_symbol_events(NULL, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0) {
print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
print_tool_events(NULL, raw_dump);
print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
print_tool_events(&print_cb, &ps);
} else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, raw_dump);
print_hwcache_events(&print_cb, &ps);
else if (strcmp(argv[i], "pmu") == 0)
print_pmu_events(NULL, raw_dump, !desc_flag,
long_desc_flag, details_flag,
deprecated, pmu_name);
print_pmu_events(&print_cb, &ps);
else if (strcmp(argv[i], "sdt") == 0)
print_sdt_events(NULL, NULL, raw_dump);
else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0)
metricgroup__print(true, false, NULL, raw_dump, details_flag, pmu_name);
else if (strcmp(argv[i], "metricgroup") == 0 || strcmp(argv[i], "metricgroups") == 0)
metricgroup__print(false, true, NULL, raw_dump, details_flag, pmu_name);
else if ((sep = strchr(argv[i], ':')) != NULL) {
print_sdt_events(&print_cb, &ps);
else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
ps.metricgroups = false;
ps.metrics = true;
metricgroup__print(&print_cb, &ps);
} else if (strcmp(argv[i], "metricgroup") == 0 ||
strcmp(argv[i], "metricgroups") == 0) {
ps.metricgroups = true;
ps.metrics = false;
metricgroup__print(&print_cb, &ps);
} else if ((sep = strchr(argv[i], ':')) != NULL) {
int sep_idx;
char *old_pmu_glob = ps.pmu_glob;
sep_idx = sep - argv[i];
s = strdup(argv[i]);
@ -118,34 +339,42 @@ int cmd_list(int argc, const char **argv)
}
s[sep_idx] = '\0';
print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
print_sdt_events(s, s + sep_idx + 1, raw_dump);
metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
ps.pmu_glob = s;
ps.event_glob = s + sep_idx + 1;
print_tracepoint_events(&print_cb, &ps);
print_sdt_events(&print_cb, &ps);
ps.metrics = true;
ps.metricgroups = true;
metricgroup__print(&print_cb, &ps);
free(s);
ps.pmu_glob = old_pmu_glob;
} else {
if (asprintf(&s, "*%s*", argv[i]) < 0) {
printf("Critical: Not enough memory! Trying to continue...\n");
continue;
}
print_symbol_events(s, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
print_symbol_events(s, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
print_tool_events(s, raw_dump);
print_hwcache_events(s, raw_dump);
print_pmu_events(s, raw_dump, !desc_flag,
long_desc_flag,
details_flag,
deprecated,
pmu_name);
print_tracepoint_events(NULL, s, raw_dump);
print_sdt_events(NULL, s, raw_dump);
metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
ps.event_glob = s;
print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX);
print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
print_tool_events(&print_cb, &ps);
print_hwcache_events(&print_cb, &ps);
print_pmu_events(&print_cb, &ps);
print_tracepoint_events(&print_cb, &ps);
print_sdt_events(&print_cb, &ps);
ps.metrics = true;
ps.metricgroups = true;
metricgroup__print(&print_cb, &ps);
free(s);
}
}
out:
free(pmu_name);
print_cb.print_end(&ps);
free(ps.pmu_glob);
free(ps.last_topic);
free(ps.last_metricgroups);
strlist__delete(ps.visited_metrics);
return ret;
}

View File

@ -12,6 +12,7 @@
#include "strbuf.h"
#include "pmu.h"
#include "pmu-hybrid.h"
#include "print-events.h"
#include "expr.h"
#include "rblist.h"
#include <string.h>
@ -353,56 +354,41 @@ static bool match_pe_metric(const struct pmu_event *pe, const char *metric)
match_metric(pe->metric_name, metric);
}
/** struct mep - RB-tree node for building printing information. */
struct mep {
/** nd - RB-tree element. */
struct rb_node nd;
const char *name;
struct strlist *metrics;
/** @metric_group: Owned metric group name, separated others with ';'. */
char *metric_group;
const char *metric_name;
const char *metric_desc;
const char *metric_long_desc;
const char *metric_expr;
const char *metric_unit;
};
static int mep_cmp(struct rb_node *rb_node, const void *entry)
{
struct mep *a = container_of(rb_node, struct mep, nd);
struct mep *b = (struct mep *)entry;
int ret;
return strcmp(a->name, b->name);
ret = strcmp(a->metric_group, b->metric_group);
if (ret)
return ret;
return strcmp(a->metric_name, b->metric_name);
}
static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
const void *entry)
static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
{
struct mep *me = malloc(sizeof(struct mep));
if (!me)
return NULL;
memcpy(me, entry, sizeof(struct mep));
me->name = strdup(me->name);
if (!me->name)
goto out_me;
me->metrics = strlist__new(NULL, NULL);
if (!me->metrics)
goto out_name;
return &me->nd;
out_name:
zfree(&me->name);
out_me:
free(me);
return NULL;
}
static struct mep *mep_lookup(struct rblist *groups, const char *name)
{
struct rb_node *nd;
struct mep me = {
.name = name
};
nd = rblist__find(groups, &me);
if (nd)
return container_of(nd, struct mep, nd);
rblist__add_node(groups, &me);
nd = rblist__find(groups, &me);
if (nd)
return container_of(nd, struct mep, nd);
return NULL;
memcpy(me, entry, sizeof(struct mep));
return &me->nd;
}
static void mep_delete(struct rblist *rl __maybe_unused,
@ -410,102 +396,61 @@ static void mep_delete(struct rblist *rl __maybe_unused,
{
struct mep *me = container_of(nd, struct mep, nd);
strlist__delete(me->metrics);
zfree(&me->name);
zfree(&me->metric_group);
free(me);
}
static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
const char *metric_name)
{
struct str_node *sn;
int n = 0;
strlist__for_each_entry (sn, metrics) {
if (raw)
printf("%s%s", n > 0 ? " " : "", sn->s);
else
printf(" %s\n", sn->s);
n++;
struct rb_node *nd;
struct mep me = {
.metric_group = strdup(metric_group),
.metric_name = metric_name,
};
nd = rblist__find(groups, &me);
if (nd) {
free(me.metric_group);
return container_of(nd, struct mep, nd);
}
if (raw)
putchar('\n');
rblist__add_node(groups, &me);
nd = rblist__find(groups, &me);
if (nd)
return container_of(nd, struct mep, nd);
return NULL;
}
static int metricgroup__print_pmu_event(const struct pmu_event *pe,
bool metricgroups, char *filter,
bool raw, bool details,
struct rblist *groups,
struct strlist *metriclist)
static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
struct rblist *groups)
{
const char *g;
char *omg, *mg;
g = pe->metric_group;
if (!g && pe->metric_name) {
if (pe->name)
return 0;
g = "No_group";
}
if (!g)
return 0;
mg = strdup(g);
mg = strdup(pe->metric_group ?: "No_group");
if (!mg)
return -ENOMEM;
omg = mg;
while ((g = strsep(&mg, ";")) != NULL) {
struct mep *me;
char *s;
g = skip_spaces(g);
if (*g == 0)
g = "No_group";
if (filter && !strstr(g, filter))
continue;
if (raw)
s = (char *)pe->metric_name;
else {
if (asprintf(&s, "%s\n%*s%s]",
pe->metric_name, 8, "[", pe->desc) < 0)
return -1;
if (details) {
if (asprintf(&s, "%s\n%*s%s]",
s, 8, "[", pe->metric_expr) < 0)
return -1;
}
if (strlen(g))
me = mep_lookup(groups, g, pe->metric_name);
else
me = mep_lookup(groups, "No_group", pe->metric_name);
if (me) {
me->metric_desc = pe->desc;
me->metric_long_desc = pe->long_desc;
me->metric_expr = pe->metric_expr;
me->metric_unit = pe->unit;
}
if (!s)
continue;
if (!metricgroups) {
strlist__add(metriclist, s);
} else {
me = mep_lookup(groups, g);
if (!me)
continue;
strlist__add(me->metrics, s);
}
if (!raw)
free(s);
}
free(omg);
return 0;
}
struct metricgroup_print_sys_idata {
struct strlist *metriclist;
char *filter;
struct rblist *groups;
bool metricgroups;
bool raw;
bool details;
};
struct metricgroup_iter_data {
pmu_event_iter_fn fn;
void *data;
@ -528,61 +473,26 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe,
return d->fn(pe, table, d->data);
}
return 0;
}
static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
const struct pmu_events_table *table __maybe_unused,
void *data)
static int metricgroup__add_to_mep_groups_callback(const struct pmu_event *pe,
const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct metricgroup_print_sys_idata *d = data;
struct rblist *groups = vdata;
return metricgroup__print_pmu_event(pe, d->metricgroups, d->filter, d->raw,
d->details, d->groups, d->metriclist);
}
struct metricgroup_print_data {
const char *pmu_name;
struct strlist *metriclist;
char *filter;
struct rblist *groups;
bool metricgroups;
bool raw;
bool details;
};
static int metricgroup__print_callback(const struct pmu_event *pe,
const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct metricgroup_print_data *data = vdata;
const char *pmu = pe->pmu ?: "cpu";
if (!pe->metric_expr)
if (!pe->metric_name)
return 0;
if (data->pmu_name && strcmp(data->pmu_name, pmu))
return 0;
return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
data->raw, data->details, data->groups,
data->metriclist);
return metricgroup__add_to_mep_groups(pe, groups);
}
void metricgroup__print(bool metrics, bool metricgroups, char *filter,
bool raw, bool details, const char *pmu_name)
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
{
struct rblist groups;
struct rb_node *node, *next;
struct strlist *metriclist = NULL;
const struct pmu_events_table *table;
if (!metricgroups) {
metriclist = strlist__new(NULL, NULL);
if (!metriclist)
return;
}
struct rb_node *node, *next;
rblist__init(&groups);
groups.node_new = mep_new;
@ -590,56 +500,31 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
groups.node_delete = mep_delete;
table = pmu_events_table__find();
if (table) {
struct metricgroup_print_data data = {
.pmu_name = pmu_name,
.metriclist = metriclist,
.metricgroups = metricgroups,
.filter = filter,
.raw = raw,
.details = details,
.groups = &groups,
};
pmu_events_table_for_each_event(table,
metricgroup__print_callback,
&data);
metricgroup__add_to_mep_groups_callback,
&groups);
}
{
struct metricgroup_iter_data data = {
.fn = metricgroup__print_sys_event_iter,
.data = (void *) &(struct metricgroup_print_sys_idata){
.metriclist = metriclist,
.metricgroups = metricgroups,
.filter = filter,
.raw = raw,
.details = details,
.groups = &groups,
},
.fn = metricgroup__add_to_mep_groups_callback,
.data = &groups,
};
pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
}
if (!filter || !rblist__empty(&groups)) {
if (metricgroups && !raw)
printf("\nMetric Groups:\n\n");
else if (metrics && !raw)
printf("\nMetrics:\n\n");
}
for (node = rb_first_cached(&groups.entries); node; node = next) {
struct mep *me = container_of(node, struct mep, nd);
if (metricgroups)
printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
if (metrics)
metricgroup__print_strlist(me->metrics, raw);
print_cb->print_metric(print_state,
me->metric_group,
me->metric_name,
me->metric_desc,
me->metric_long_desc,
me->metric_expr,
me->metric_unit);
next = rb_next(node);
rblist__remove_node(&groups, node);
}
if (!metricgroups)
metricgroup__print_strlist(metriclist, raw);
strlist__delete(metriclist);
}
static const char *code_characters = ",-=@";

View File

@ -10,6 +10,7 @@
struct evlist;
struct evsel;
struct option;
struct print_callbacks;
struct rblist;
struct cgroup;
@ -78,8 +79,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
bool metric_no_merge,
struct rblist *metric_events);
void metricgroup__print(bool metrics, bool groups, char *filter,
bool raw, bool details, const char *pmu_name);
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state);
bool metricgroup__has_metric(const char *metric);
int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused);
void metricgroup__rblist_exit(struct rblist *metric_events);

View File

@ -23,6 +23,7 @@
#include "evsel.h"
#include "pmu.h"
#include "parse-events.h"
#include "print-events.h"
#include "header.h"
#include "string2.h"
#include "strbuf.h"
@ -1579,13 +1580,6 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
return buf;
}
static char *format_alias_or(char *buf, int len, const struct perf_pmu *pmu,
const struct perf_pmu_alias *alias)
{
snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name);
return buf;
}
/** Struct for ordering events as output in perf list. */
struct sevent {
/** PMU for event. */
@ -1629,7 +1623,7 @@ static int cmp_sevent(const void *a, const void *b)
/* Order CPU core events to be first */
if (as->is_cpu != bs->is_cpu)
return bs->is_cpu - as->is_cpu;
return as->is_cpu ? -1 : 1;
/* Order by PMU name. */
a_pmu_name = as->pmu->name ?: "";
@ -1642,27 +1636,6 @@ static int cmp_sevent(const void *a, const void *b)
return strcmp(a_name, b_name);
}
static void wordwrap(char *s, int start, int max, int corr)
{
int column = start;
int n;
while (*s) {
int wlen = strcspn(s, " \t");
if (column + wlen >= max && column > start) {
printf("\n%*s", start, "");
column = start + corr;
}
n = printf("%s%.*s", column > start ? " " : "", wlen, s);
if (n <= 0)
break;
s += wlen;
column += n;
s = skip_spaces(s);
}
}
bool is_pmu_core(const char *name)
{
return !strcmp(name, "cpu") || is_arm_pmu_core(name);
@ -1685,24 +1658,19 @@ static bool pmu_alias_is_duplicate(struct sevent *alias_a,
return strcmp(a_pmu_name, b_pmu_name) == 0;
}
void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
bool long_desc, bool details_flag, bool deprecated,
const char *pmu_name)
void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
{
struct perf_pmu *pmu;
struct perf_pmu_alias *alias;
struct perf_pmu_alias *event;
char buf[1024];
int printed = 0;
int len, j;
struct sevent *aliases;
int numdesc = 0;
int columns = pager_get_columns();
char *topic = NULL;
pmu = NULL;
len = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list)
list_for_each_entry(event, &pmu->aliases, list)
len++;
if (pmu->selectable)
len++;
@ -1715,32 +1683,15 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
pmu = NULL;
j = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
bool is_cpu;
bool is_cpu = is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name);
if (pmu_name && pmu->name && strcmp(pmu_name, pmu->name))
continue;
is_cpu = is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name);
list_for_each_entry(alias, &pmu->aliases, list) {
if (alias->deprecated && !deprecated)
continue;
if (event_glob != NULL &&
!(strglobmatch_nocase(alias->name, event_glob) ||
(!is_cpu &&
strglobmatch_nocase(alias->name, event_glob)) ||
(alias->topic &&
strglobmatch_nocase(alias->topic, event_glob))))
continue;
aliases[j].event = alias;
list_for_each_entry(event, &pmu->aliases, list) {
aliases[j].event = event;
aliases[j].pmu = pmu;
aliases[j].is_cpu = is_cpu;
j++;
}
if (pmu->selectable &&
(event_glob == NULL || strglobmatch(pmu->name, event_glob))) {
if (pmu->selectable) {
aliases[j].event = NULL;
aliases[j].pmu = pmu;
aliases[j].is_cpu = is_cpu;
@ -1750,7 +1701,12 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
len = j;
qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) {
char *name, *desc;
const char *name, *alias = NULL, *scale_unit = NULL,
*desc = NULL, *long_desc = NULL,
*encoding_desc = NULL, *topic = NULL,
*metric_name = NULL, *metric_expr = NULL;
bool deprecated = false;
size_t buf_used;
/* Skip duplicates */
if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
@ -1758,48 +1714,51 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
if (!aliases[j].event) {
/* A selectable event. */
snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name);
buf_used = snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name) + 1;
name = buf;
} else if (aliases[j].event->desc) {
name = aliases[j].event->name;
} else {
if (!name_only && aliases[j].is_cpu) {
name = format_alias_or(buf, sizeof(buf), aliases[j].pmu,
aliases[j].event);
if (aliases[j].event->desc) {
name = aliases[j].event->name;
buf_used = 0;
} else {
name = format_alias(buf, sizeof(buf), aliases[j].pmu,
aliases[j].event);
if (aliases[j].is_cpu) {
alias = name;
name = aliases[j].event->name;
}
buf_used = strlen(buf) + 1;
}
}
if (name_only) {
printf("%s ", name);
continue;
}
printed++;
if (!aliases[j].event || !aliases[j].event->desc || quiet_flag) {
printf(" %-50s [Kernel PMU event]\n", name);
continue;
}
if (numdesc++ == 0)
printf("\n");
if (aliases[j].event->topic && (!topic ||
strcmp(topic, aliases[j].event->topic))) {
printf("%s%s:\n", topic ? "\n" : "", aliases[j].event->topic);
if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
scale_unit = buf + buf_used;
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%G%s", aliases[j].event->scale,
aliases[j].event->unit) + 1;
}
desc = aliases[j].event->desc;
long_desc = aliases[j].event->long_desc;
topic = aliases[j].event->topic;
encoding_desc = buf + buf_used;
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%s/%s/", aliases[j].pmu->name,
aliases[j].event->str) + 1;
metric_name = aliases[j].event->metric_name;
metric_expr = aliases[j].event->metric_expr;
deprecated = aliases[j].event->deprecated;
}
printf(" %-50s\n", name);
printf("%*s", 8, "[");
desc = long_desc ? aliases[j].event->long_desc : aliases[j].event->desc;
wordwrap(desc, 8, columns, 0);
printf("]\n");
if (details_flag) {
printf("%*s%s/%s/ ", 8, "", aliases[j].pmu->name, aliases[j].event->str);
if (aliases[j].event->metric_name)
printf(" MetricName: %s", aliases[j].event->metric_name);
if (aliases[j].event->metric_expr)
printf(" MetricExpr: %s", aliases[j].event->metric_expr);
putchar('\n');
}
print_cb->print_event(print_state,
aliases[j].pmu->name,
topic,
name,
alias,
scale_unit,
deprecated,
"Kernel PMU event",
desc,
long_desc,
encoding_desc,
metric_name,
metric_expr);
}
if (printed && pager_in_use())
printf("\n");

View File

@ -12,6 +12,7 @@
struct evsel_config_term;
struct perf_cpu_map;
struct print_callbacks;
enum {
PERF_PMU_FORMAT_VALUE_CONFIG,
@ -225,9 +226,7 @@ void perf_pmu__del_formats(struct list_head *formats);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
bool is_pmu_core(const char *name);
void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
bool long_desc, bool details_flag,
bool deprecated, const char *pmu_name);
void print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
bool pmu_have_event(const char *pname, const char *name);
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);

View File

@ -28,6 +28,7 @@
#define MAX_NAME_LEN 100
/** Strings corresponding to enum perf_type_id. */
static const char * const event_type_descriptors[] = {
"Hardware event",
"Software event",
@ -55,11 +56,9 @@ static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
/*
* Print the events from <debugfs_mount_point>/tracing/events
*/
void print_tracepoint_events(const char *subsys_glob,
const char *event_glob, bool name_only)
void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state)
{
struct dirent **sys_namelist = NULL;
bool printed = false;
int sys_items = tracing_events__scandir_alphasort(&sys_namelist);
for (int i = 0; i < sys_items; i++) {
@ -73,10 +72,6 @@ void print_tracepoint_events(const char *subsys_glob,
!strcmp(sys_dirent->d_name, ".."))
continue;
if (subsys_glob != NULL &&
!strglobmatch(sys_dirent->d_name, subsys_glob))
continue;
dir_path = get_events_file(sys_dirent->d_name);
if (!dir_path)
continue;
@ -94,41 +89,41 @@ void print_tracepoint_events(const char *subsys_glob,
if (tp_event_has_id(dir_path, evt_dirent) != 0)
continue;
if (event_glob != NULL &&
!strglobmatch(evt_dirent->d_name, event_glob))
continue;
snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent->d_name, evt_dirent->d_name);
if (name_only)
printf("%s ", evt_path);
else {
printf(" %-50s [%s]\n", evt_path,
event_type_descriptors[PERF_TYPE_TRACEPOINT]);
}
printed = true;
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
evt_path,
/*event_alias=*/NULL,
/*scale_unit=*/NULL,
/*deprecated=*/false,
"Tracepoint event",
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
}
free(dir_path);
free(evt_namelist);
}
free(sys_namelist);
if (printed && pager_in_use())
printf("\n");
}
void print_sdt_events(const char *subsys_glob, const char *event_glob,
bool name_only)
void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
{
struct probe_cache *pcache;
struct probe_cache_entry *ent;
struct strlist *bidlist, *sdtlist;
struct strlist_config cfg = {.dont_dupstr = true};
struct str_node *nd, *nd2;
char *buf, *path, *ptr = NULL;
bool show_detail = false;
int ret;
struct str_node *bid_nd, *sdt_name, *next_sdt_name;
const char *last_sdt_name = NULL;
sdtlist = strlist__new(NULL, &cfg);
/*
* The implicitly sorted sdtlist will hold the tracepoint name followed
* by @<buildid>. If the tracepoint name is unique (determined by
* looking at the adjacent nodes) the @<buildid> is dropped otherwise
* the executable path and buildid are added to the name.
*/
sdtlist = strlist__new(NULL, NULL);
if (!sdtlist) {
pr_debug("Failed to allocate new strlist for SDT\n");
return;
@ -138,65 +133,78 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
pr_debug("Failed to get buildids: %d\n", errno);
return;
}
strlist__for_each_entry(nd, bidlist) {
pcache = probe_cache__new(nd->s, NULL);
strlist__for_each_entry(bid_nd, bidlist) {
struct probe_cache *pcache;
struct probe_cache_entry *ent;
pcache = probe_cache__new(bid_nd->s, NULL);
if (!pcache)
continue;
list_for_each_entry(ent, &pcache->entries, node) {
if (!ent->sdt)
continue;
if (subsys_glob &&
!strglobmatch(ent->pev.group, subsys_glob))
continue;
if (event_glob &&
!strglobmatch(ent->pev.event, event_glob))
continue;
ret = asprintf(&buf, "%s:%s@%s", ent->pev.group,
ent->pev.event, nd->s);
if (ret > 0)
strlist__add(sdtlist, buf);
char buf[1024];
snprintf(buf, sizeof(buf), "%s:%s@%s",
ent->pev.group, ent->pev.event, bid_nd->s);
strlist__add(sdtlist, buf);
}
probe_cache__delete(pcache);
}
strlist__delete(bidlist);
strlist__for_each_entry(nd, sdtlist) {
buf = strchr(nd->s, '@');
if (buf)
*(buf++) = '\0';
if (name_only) {
printf("%s ", nd->s);
continue;
}
nd2 = strlist__next(nd);
if (nd2) {
ptr = strchr(nd2->s, '@');
if (ptr)
*ptr = '\0';
if (strcmp(nd->s, nd2->s) == 0)
show_detail = true;
}
if (show_detail) {
path = build_id_cache__origname(buf);
ret = asprintf(&buf, "%s@%s(%.12s)", nd->s, path, buf);
if (ret > 0) {
printf(" %-50s [%s]\n", buf, "SDT event");
free(buf);
strlist__for_each_entry(sdt_name, sdtlist) {
bool show_detail = false;
char *bid = strchr(sdt_name->s, '@');
char *evt_name = NULL;
if (bid)
*(bid++) = '\0';
if (last_sdt_name && !strcmp(last_sdt_name, sdt_name->s)) {
show_detail = true;
} else {
next_sdt_name = strlist__next(sdt_name);
if (next_sdt_name) {
char *bid2 = strchr(next_sdt_name->s, '@');
if (bid2)
*bid2 = '\0';
if (strcmp(sdt_name->s, next_sdt_name->s) == 0)
show_detail = true;
if (bid2)
*bid2 = '@';
}
free(path);
} else
printf(" %-50s [%s]\n", nd->s, "SDT event");
if (nd2) {
if (strcmp(nd->s, nd2->s) != 0)
show_detail = false;
if (ptr)
*ptr = '@';
}
last_sdt_name = sdt_name->s;
if (show_detail) {
char *path = build_id_cache__origname(bid);
if (path) {
if (asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid) < 0)
evt_name = NULL;
free(path);
}
}
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
evt_name ?: sdt_name->s,
/*event_alias=*/NULL,
/*deprecated=*/false,
/*scale_unit=*/NULL,
"SDT event",
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
free(evt_name);
}
strlist__delete(sdtlist);
}
int print_hwcache_events(const char *event_glob, bool name_only)
int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state)
{
struct strlist *evt_name_list = strlist__new(NULL, NULL);
struct str_node *nd;
@ -216,9 +224,6 @@ int print_hwcache_events(const char *event_glob, bool name_only)
char name[64];
__evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
if (event_glob != NULL && !strglobmatch(name, event_glob))
continue;
if (!perf_pmu__has_hybrid()) {
if (is_event_supported(PERF_TYPE_HW_CACHE,
type | (op << 8) | (i << 16)))
@ -240,55 +245,47 @@ int print_hwcache_events(const char *event_glob, bool name_only)
}
strlist__for_each_entry(nd, evt_name_list) {
if (name_only) {
printf("%s ", nd->s);
continue;
}
printf(" %-50s [%s]\n", nd->s, event_type_descriptors[PERF_TYPE_HW_CACHE]);
print_cb->print_event(print_state,
"cache",
/*pmu_name=*/NULL,
nd->s,
/*event_alias=*/NULL,
/*scale_unit=*/NULL,
/*deprecated=*/false,
event_type_descriptors[PERF_TYPE_HW_CACHE],
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
}
if (!strlist__empty(evt_name_list) && pager_in_use())
printf("\n");
strlist__delete(evt_name_list);
return 0;
}
static void print_tool_event(const struct event_symbol *syms, const char *event_glob,
bool name_only)
void print_tool_events(const struct print_callbacks *print_cb, void *print_state)
{
if (syms->symbol == NULL)
return;
if (event_glob && !(strglobmatch(syms->symbol, event_glob) ||
(syms->alias && strglobmatch(syms->alias, event_glob))))
return;
if (name_only)
printf("%s ", syms->symbol);
else {
char name[MAX_NAME_LEN];
if (syms->alias && strlen(syms->alias))
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else
strlcpy(name, syms->symbol, MAX_NAME_LEN);
printf(" %-50s [%s]\n", name, "Tool event");
// Start at 1 because the first enum entry means no tool event.
for (int i = 1; i < PERF_TOOL_MAX; ++i) {
print_cb->print_event(print_state,
"tool",
/*pmu_name=*/NULL,
event_symbols_tool[i].symbol,
event_symbols_tool[i].alias,
/*scale_unit=*/NULL,
/*deprecated=*/false,
"Tool event",
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
}
}
void print_tool_events(const char *event_glob, bool name_only)
{
// Start at 1 because the first enum entry means no tool event.
for (int i = 1; i < PERF_TOOL_MAX; ++i)
print_tool_event(event_symbols_tool + i, event_glob, name_only);
if (pager_in_use())
printf("\n");
}
void print_symbol_events(const char *event_glob, unsigned int type,
struct event_symbol *syms, unsigned int max,
bool name_only)
void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
unsigned int type, const struct event_symbol *syms,
unsigned int max)
{
struct strlist *evt_name_list = strlist__new(NULL, NULL);
struct str_node *nd;
@ -305,10 +302,6 @@ void print_symbol_events(const char *event_glob, unsigned int type,
if (syms[i].symbol == NULL)
continue;
if (event_glob != NULL && !(strglobmatch(syms[i].symbol, event_glob) ||
(syms[i].alias && strglobmatch(syms[i].alias, event_glob))))
continue;
if (!is_event_supported(type, i))
continue;
@ -322,63 +315,92 @@ void print_symbol_events(const char *event_glob, unsigned int type,
}
strlist__for_each_entry(nd, evt_name_list) {
if (name_only) {
printf("%s ", nd->s);
continue;
}
printf(" %-50s [%s]\n", nd->s, event_type_descriptors[type]);
}
if (!strlist__empty(evt_name_list) && pager_in_use())
printf("\n");
char *alias = strstr(nd->s, " OR ");
if (alias) {
*alias = '\0';
alias += 4;
}
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
nd->s,
alias,
/*scale_unit=*/NULL,
/*deprecated=*/false,
event_type_descriptors[type],
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
}
strlist__delete(evt_name_list);
}
/*
* Print the help text for the event symbols:
*/
void print_events(const char *event_glob, bool name_only, bool quiet_flag,
bool long_desc, bool details_flag, bool deprecated,
const char *pmu_name)
void print_events(const struct print_callbacks *print_cb, void *print_state)
{
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX);
print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
print_symbol_events(event_glob, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX, name_only);
print_tool_events(event_glob, name_only);
print_tool_events(print_cb, print_state);
print_hwcache_events(event_glob, name_only);
print_hwcache_events(print_cb, print_state);
print_pmu_events(event_glob, name_only, quiet_flag, long_desc,
details_flag, deprecated, pmu_name);
print_pmu_events(print_cb, print_state);
if (event_glob != NULL)
return;
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
"rNNN",
/*event_alias=*/NULL,
/*scale_unit=*/NULL,
/*deprecated=*/false,
event_type_descriptors[PERF_TYPE_RAW],
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
if (!name_only) {
printf(" %-50s [%s]\n",
"rNNN",
event_type_descriptors[PERF_TYPE_RAW]);
printf(" %-50s [%s]\n",
"cpu/t1=v1[,t2=v2,t3 ...]/modifier",
event_type_descriptors[PERF_TYPE_RAW]);
if (pager_in_use())
printf(" (see 'man perf-list' on how to encode it)\n\n");
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
"cpu/t1=v1[,t2=v2,t3 ...]/modifier",
/*event_alias=*/NULL,
/*scale_unit=*/NULL,
/*deprecated=*/false,
event_type_descriptors[PERF_TYPE_RAW],
"(see 'man perf-list' on how to encode it)",
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
printf(" %-50s [%s]\n",
"mem:<addr>[/len][:access]",
event_type_descriptors[PERF_TYPE_BREAKPOINT]);
if (pager_in_use())
printf("\n");
}
print_cb->print_event(print_state,
/*topic=*/NULL,
/*pmu_name=*/NULL,
"mem:<addr>[/len][:access]",
/*scale_unit=*/NULL,
/*event_alias=*/NULL,
/*deprecated=*/false,
event_type_descriptors[PERF_TYPE_BREAKPOINT],
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL,
/*metric_name=*/NULL,
/*metric_expr=*/NULL);
print_tracepoint_events(NULL, NULL, name_only);
print_tracepoint_events(print_cb, print_state);
print_sdt_events(NULL, NULL, name_only);
print_sdt_events(print_cb, print_state);
metricgroup__print(true, true, NULL, name_only, details_flag,
pmu_name);
metricgroup__print(print_cb, print_state);
print_libpfm_events(name_only, long_desc);
print_libpfm_events(print_cb, print_state);
}

View File

@ -2,21 +2,39 @@
#ifndef __PERF_PRINT_EVENTS_H
#define __PERF_PRINT_EVENTS_H
#include <linux/perf_event.h>
#include <stdbool.h>
struct event_symbol;
void print_events(const char *event_glob, bool name_only, bool quiet_flag,
bool long_desc, bool details_flag, bool deprecated,
const char *pmu_name);
int print_hwcache_events(const char *event_glob, bool name_only);
void print_sdt_events(const char *subsys_glob, const char *event_glob,
bool name_only);
void print_symbol_events(const char *event_glob, unsigned int type,
struct event_symbol *syms, unsigned int max,
bool name_only);
void print_tool_events(const char *event_glob, bool name_only);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only);
struct print_callbacks {
void (*print_start)(void *print_state);
void (*print_end)(void *print_state);
void (*print_event)(void *print_state, const char *topic,
const char *pmu_name,
const char *event_name, const char *event_alias,
const char *scale_unit,
bool deprecated, const char *event_type_desc,
const char *desc, const char *long_desc,
const char *encoding_desc,
const char *metric_name, const char *metric_expr);
void (*print_metric)(void *print_state,
const char *group,
const char *name,
const char *desc,
const char *long_desc,
const char *expr,
const char *unit);
};
/** Print all events, the default when no options are specified. */
void print_events(const struct print_callbacks *print_cb, void *print_state);
int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state);
void print_sdt_events(const struct print_callbacks *print_cb, void *print_state);
void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
unsigned int type, const struct event_symbol *syms,
unsigned int max);
void print_tool_events(const struct print_callbacks *print_cb, void *print_state);
void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state);
#endif /* __PERF_PRINT_EVENTS_H */