Commit e5c6109f authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo

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: default avatarIan 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent a3720e96
This diff is collapsed.
......@@ -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,51 +354,65 @@ 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:
}
static void mep_delete(struct rblist *rl __maybe_unused,
struct rb_node *nd)
{
struct mep *me = container_of(nd, struct mep, nd);
zfree(&me->metric_group);
free(me);
return NULL;
}
static struct mep *mep_lookup(struct rblist *groups, const char *name)
static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
const char *metric_name)
{
struct rb_node *nd;
struct mep me = {
.name = name
.metric_group = strdup(metric_group),
.metric_name = metric_name,
};
nd = rblist__find(groups, &me);
if (nd)
if (nd) {
free(me.metric_group);
return container_of(nd, struct mep, nd);
}
rblist__add_node(groups, &me);
nd = rblist__find(groups, &me);
if (nd)
......@@ -405,107 +420,37 @@ static struct mep *mep_lookup(struct rblist *groups, const char *name)
return NULL;
}
static void mep_delete(struct rblist *rl __maybe_unused,
struct rb_node *nd)
{
struct mep *me = container_of(nd, struct mep, nd);
strlist__delete(me->metrics);
zfree(&me->name);
free(me);
}
static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
{
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++;
}
if (raw)
putchar('\n');
}
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 (!s)
continue;
if (strlen(g))
me = mep_lookup(groups, g, pe->metric_name);
else
me = mep_lookup(groups, "No_group", pe->metric_name);
if (!metricgroups) {
strlist__add(metriclist, s);
} else {
me = mep_lookup(groups, g);
if (!me)
continue;
strlist__add(me->metrics, s);
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 (!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)
{
struct metricgroup_print_sys_idata *d = data;
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)
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_data *data = vdata;
const char *pmu = pe->pmu ?: "cpu";
struct rblist *groups = vdata;
if (!pe->metric_expr)
return 0;
if (data->pmu_name && strcmp(data->pmu_name, pmu))
if (!pe->metric_name)
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 = ",-=@";
......
......@@ -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);
......
......@@ -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");
......
......@@ -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);
......
This diff is collapsed.
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment