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

perf pmu: Abstract alias/event struct

In order to be able to lazily compute aliases/events for a PMU, move
the struct perf_pmu_alias into pmu.c.

Add perf_pmu__find_event and perf_pmu__for_each_event that take a
callback that is called for the found event or for each event.

The layout of struct pmu and the event/alias list is unchanged but the
API is altered so that aliases are no longer directly accessed, allowing
for later changes.
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: Gaosheng Cui <cuigaosheng1@huawei.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20230824041330.266337-3-irogers@google.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 50402641
...@@ -57,9 +57,7 @@ static int save_result(void) ...@@ -57,9 +57,7 @@ static int save_result(void)
r->is_core = pmu->is_core; r->is_core = pmu->is_core;
r->nr_caps = pmu->nr_caps; r->nr_caps = pmu->nr_caps;
r->nr_aliases = 0; r->nr_aliases = perf_pmu__num_events(pmu);
list_for_each(list, &pmu->aliases)
r->nr_aliases++;
r->nr_formats = 0; r->nr_formats = 0;
list_for_each(list, &pmu->format) list_for_each(list, &pmu->format)
...@@ -98,9 +96,7 @@ static int check_result(bool core_only) ...@@ -98,9 +96,7 @@ static int check_result(bool core_only)
return -1; return -1;
} }
nr = 0; nr = perf_pmu__num_events(pmu);
list_for_each(list, &pmu->aliases)
nr++;
if (nr != r->nr_aliases) { if (nr != r->nr_aliases) {
pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n", pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
pmu->name, r->nr_aliases, nr); pmu->name, r->nr_aliases, nr);
......
...@@ -341,7 +341,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event ...@@ -341,7 +341,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event
return 0; return 0;
} }
static int compare_alias_to_test_event(struct perf_pmu_alias *alias, static int compare_alias_to_test_event(struct pmu_event_info *alias,
struct perf_pmu_test_event const *test_event, struct perf_pmu_test_event const *test_event,
char const *pmu_name) char const *pmu_name)
{ {
...@@ -496,6 +496,23 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused, ...@@ -496,6 +496,23 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
return 0; return 0;
} }
struct test_core_pmu_event_aliases_cb_args {
struct perf_pmu_test_event const *test_event;
int *count;
};
static int test_core_pmu_event_aliases_cb(void *state, struct pmu_event_info *alias)
{
struct test_core_pmu_event_aliases_cb_args *args = state;
if (compare_alias_to_test_event(alias, args->test_event, alias->pmu->name))
return -1;
(*args->count)++;
pr_debug2("testing aliases core PMU %s: matched event %s\n",
alias->pmu_name, alias->name);
return 0;
}
/* Verify aliases are as expected */ /* Verify aliases are as expected */
static int __test_core_pmu_event_aliases(char *pmu_name, int *count) static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
{ {
...@@ -522,25 +539,19 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count) ...@@ -522,25 +539,19 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
pmu_add_cpu_aliases_table(pmu, table); pmu_add_cpu_aliases_table(pmu, table);
for (; *test_event_table; test_event_table++) { for (; *test_event_table; test_event_table++) {
struct perf_pmu_test_event const *test_event = *test_event_table; struct perf_pmu_test_event test_event = **test_event_table;
struct pmu_event const *event = &test_event->event; struct pmu_event const *event = &test_event.event;
struct perf_pmu_alias *alias = perf_pmu__find_alias(pmu, event->name); struct test_core_pmu_event_aliases_cb_args args = {
.test_event = &test_event,
if (!alias) { .count = count,
pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n", };
pmu_name, event->name); int err;
res = -1;
break; test_event.event.pmu = pmu_name;
} err = perf_pmu__find_event(pmu, event->name, &args,
test_core_pmu_event_aliases_cb);
if (compare_alias_to_test_event(alias, test_event, pmu_name)) { if (err)
res = -1; res = err;
break;
}
(*count)++;
pr_debug2("testing aliases core PMU %s: matched event %s\n",
pmu_name, alias->name);
} }
perf_pmu__delete(pmu); perf_pmu__delete(pmu);
...@@ -553,7 +564,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) ...@@ -553,7 +564,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
struct perf_pmu_test_event const **table; struct perf_pmu_test_event const **table;
struct perf_pmu *pmu = &test_pmu->pmu; struct perf_pmu *pmu = &test_pmu->pmu;
const char *pmu_name = pmu->name; const char *pmu_name = pmu->name;
struct perf_pmu_alias *a, *tmp, *alias;
const struct pmu_events_table *events_table; const struct pmu_events_table *events_table;
int res = 0; int res = 0;
...@@ -564,8 +574,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) ...@@ -564,8 +574,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
pmu_add_sys_aliases(pmu); pmu_add_sys_aliases(pmu);
/* Count how many aliases we generated */ /* Count how many aliases we generated */
list_for_each_entry(alias, &pmu->aliases, list) alias_count = perf_pmu__num_events(pmu);
alias_count++;
/* Count how many aliases we expect from the known table */ /* Count how many aliases we expect from the known table */
for (table = &test_pmu->aliases[0]; *table; table++) for (table = &test_pmu->aliases[0]; *table; table++)
...@@ -574,33 +583,25 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) ...@@ -574,33 +583,25 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
if (alias_count != to_match_count) { if (alias_count != to_match_count) {
pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n", pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n",
pmu_name, to_match_count, alias_count); pmu_name, to_match_count, alias_count);
res = -1; return -1;
goto out;
} }
list_for_each_entry(alias, &pmu->aliases, list) { for (table = &test_pmu->aliases[0]; *table; table++) {
bool matched = false; struct perf_pmu_test_event test_event = **table;
struct pmu_event const *event = &test_event.event;
for (table = &test_pmu->aliases[0]; *table; table++) { int err;
struct perf_pmu_test_event const *test_event = *table; struct test_core_pmu_event_aliases_cb_args args = {
struct pmu_event const *event = &test_event->event; .test_event = &test_event,
.count = &matched_count,
if (!strcmp(event->name, alias->name)) { };
if (compare_alias_to_test_event(alias,
test_event, err = perf_pmu__find_event(pmu, event->name, &args,
pmu_name)) { test_core_pmu_event_aliases_cb);
continue; if (err) {
} res = err;
matched = true;
matched_count++;
}
}
if (matched == false) {
pr_debug("testing aliases uncore PMU %s: could not match alias %s\n", pr_debug("testing aliases uncore PMU %s: could not match alias %s\n",
pmu_name, alias->name); pmu_name, event->name);
res = -1; return -1;
goto out;
} }
} }
...@@ -609,12 +610,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) ...@@ -609,12 +610,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
pmu_name, matched_count, alias_count); pmu_name, matched_count, alias_count);
res = -1; res = -1;
} }
out:
list_for_each_entry_safe(a, tmp, &pmu->aliases, list) {
list_del(&a->list);
perf_pmu_free_alias(a);
}
return res; return res;
} }
......
...@@ -193,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu) ...@@ -193,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
struct parse_events_term *term; struct parse_events_term *term;
list_for_each_entry(term, config_terms, list) { list_for_each_entry(term, config_terms, list) {
struct perf_pmu_alias *alias; u64 num;
bool matched = false;
if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW) if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
continue; continue;
list_for_each_entry(alias, &pmu->aliases, list) { if (perf_pmu__have_event(pmu, term->val.str)) {
if (!strcmp(alias->name, term->val.str)) {
free(term->config);
term->config = term->val.str;
term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
term->val.num = 1;
term->no_value = true;
matched = true;
break;
}
}
if (!matched) {
u64 num;
free(term->config); free(term->config);
term->config = strdup("config"); term->config = term->val.str;
errno = 0;
num = strtoull(term->val.str + 1, NULL, 16);
assert(errno == 0);
free(term->val.str);
term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG; term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
term->val.num = num; term->val.num = 1;
term->no_value = false; term->no_value = true;
continue;
} }
free(term->config);
term->config = strdup("config");
errno = 0;
num = strtoull(term->val.str + 1, NULL, 16);
assert(errno == 0);
free(term->val.str);
term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
term->val.num = num;
term->no_value = false;
} }
} }
...@@ -1458,28 +1451,22 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, ...@@ -1458,28 +1451,22 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
INIT_LIST_HEAD(list); INIT_LIST_HEAD(list);
while ((pmu = perf_pmus__scan(pmu)) != NULL) { while ((pmu = perf_pmus__scan(pmu)) != NULL) {
struct perf_pmu_alias *alias;
bool auto_merge_stats; bool auto_merge_stats;
if (parse_events__filter_pmu(parse_state, pmu)) if (parse_events__filter_pmu(parse_state, pmu))
continue; continue;
auto_merge_stats = perf_pmu__auto_merge_stats(pmu); if (!perf_pmu__have_event(pmu, str))
continue;
list_for_each_entry(alias, &pmu->aliases, list) { auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
if (!strcasecmp(alias->name, str)) { parse_events_copy_term_list(head, &orig_head);
parse_events_copy_term_list(head, &orig_head); if (!parse_events_add_pmu(parse_state, list, pmu->name,
if (!parse_events_add_pmu(parse_state, list, orig_head, auto_merge_stats, loc)) {
pmu->name, orig_head, pr_debug("%s -> %s/%s/\n", str, pmu->name, str);
auto_merge_stats, loc)) { ok++;
pr_debug("%s -> %s/%s/\n", str,
pmu->name, alias->str);
parse_state->wild_card_pmus = true;
ok++;
}
parse_events_terms__delete(orig_head);
}
} }
parse_events_terms__delete(orig_head);
} }
if (parse_state->fake_pmu) { if (parse_state->fake_pmu) {
......
...@@ -31,6 +31,61 @@ ...@@ -31,6 +31,61 @@
struct perf_pmu perf_pmu__fake; struct perf_pmu perf_pmu__fake;
#define UNIT_MAX_LEN 31 /* max length for event unit name */
/**
* struct perf_pmu_alias - An event either read from sysfs or builtin in
* pmu-events.c, created by parsing the pmu-events json files.
*/
struct perf_pmu_alias {
/** @name: Name of the event like "mem-loads". */
char *name;
/** @desc: Optional short description of the event. */
char *desc;
/** @long_desc: Optional long description. */
char *long_desc;
/**
* @topic: Optional topic such as cache or pipeline, particularly for
* json events.
*/
char *topic;
/**
* @str: Comma separated parameter list like
* "event=0xcd,umask=0x1,ldlat=0x3".
*/
char *str;
/** @terms: Owned list of the original parsed parameters. */
struct list_head terms;
/** @list: List element of struct perf_pmu aliases. */
struct list_head list;
/** @unit: Units for the event, such as bytes or cache lines. */
char unit[UNIT_MAX_LEN+1];
/** @scale: Value to scale read counter values by. */
double scale;
/**
* @per_pkg: Does the file
* <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
* equivalent json value exist and have the value 1.
*/
bool per_pkg;
/**
* @snapshot: Does the file
* <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
* exist and have the value 1.
*/
bool snapshot;
/**
* @deprecated: Is the event hidden and so not shown in perf list by
* default.
*/
bool deprecated;
/**
* @pmu_name: The name copied from the json struct pmu_event. This can
* differ from the PMU name as it won't have suffixes.
*/
char *pmu_name;
};
/** /**
* struct perf_pmu_format - Values from a format file read from * struct perf_pmu_format - Values from a format file read from
* <sysfs>/devices/cpu/format/ held in struct perf_pmu. * <sysfs>/devices/cpu/format/ held in struct perf_pmu.
...@@ -355,7 +410,7 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old, ...@@ -355,7 +410,7 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old,
} }
/* Delete an alias entry. */ /* Delete an alias entry. */
void perf_pmu_free_alias(struct perf_pmu_alias *newalias) static void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
{ {
zfree(&newalias->name); zfree(&newalias->name);
zfree(&newalias->desc); zfree(&newalias->desc);
...@@ -1349,10 +1404,20 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, ...@@ -1349,10 +1404,20 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
return perf_pmu__config_terms(pmu, attr, head_terms, zero, err); return perf_pmu__config_terms(pmu, attr, head_terms, zero, err);
} }
static struct perf_pmu_alias *perf_pmu__find_alias(const struct perf_pmu *pmu, const char *str)
{
struct perf_pmu_alias *alias;
list_for_each_entry(alias, &pmu->aliases, list) {
if (!strcasecmp(alias->name, str))
return alias;
}
return NULL;
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
struct parse_events_term *term) struct parse_events_term *term)
{ {
struct perf_pmu_alias *alias;
char *name; char *name;
if (parse_events__is_hardcoded_term(term)) if (parse_events__is_hardcoded_term(term))
...@@ -1364,6 +1429,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, ...@@ -1364,6 +1429,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
if (pmu_find_format(&pmu->format, term->config)) if (pmu_find_format(&pmu->format, term->config))
return NULL; return NULL;
name = term->config; name = term->config;
} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcasecmp(term->config, "event")) if (strcasecmp(term->config, "event"))
return NULL; return NULL;
...@@ -1372,11 +1438,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, ...@@ -1372,11 +1438,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
return NULL; return NULL;
} }
list_for_each_entry(alias, &pmu->aliases, list) { return perf_pmu__find_alias(pmu, name);
if (!strcasecmp(alias->name, name))
return alias;
}
return NULL;
} }
...@@ -1459,16 +1521,33 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, ...@@ -1459,16 +1521,33 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
return 0; return 0;
} }
struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event) struct find_event_args {
const char *event;
void *state;
pmu_event_callback cb;
};
static int find_event_callback(void *state, struct pmu_event_info *info)
{ {
struct perf_pmu_alias *alias; struct find_event_args *args = state;
list_for_each_entry(alias, &pmu->aliases, list) if (!strcmp(args->event, info->name))
if (!strcmp(event, alias->name)) return args->cb(args->state, info);
return alias;
return NULL; return 0;
} }
int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb)
{
struct find_event_args args = {
.event = event,
.state = state,
.cb = cb,
};
return perf_pmu__for_each_event(pmu, &args, find_event_callback);
}
static void perf_pmu__del_formats(struct list_head *formats) static void perf_pmu__del_formats(struct list_head *formats)
{ {
struct perf_pmu_format *fmt, *tmp; struct perf_pmu_format *fmt, *tmp;
...@@ -1508,13 +1587,110 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) ...@@ -1508,13 +1587,110 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
{ {
struct perf_pmu_alias *alias; return perf_pmu__find_alias(pmu, name) != NULL;
}
list_for_each_entry(alias, &pmu->aliases, list) { size_t perf_pmu__num_events(const struct perf_pmu *pmu)
if (!strcmp(alias->name, name)) {
return true; struct list_head *list;
size_t nr = 0;
list_for_each(list, &pmu->aliases)
nr++;
return pmu->selectable ? nr + 1 : nr;
}
static int sub_non_neg(int a, int b)
{
if (b > a)
return 0;
return a - b;
}
static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
const struct perf_pmu_alias *alias)
{
struct parse_events_term *term;
int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
list_for_each_entry(term, &alias->terms, list) {
if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
used += snprintf(buf + used, sub_non_neg(len, used),
",%s=%s", term->config,
term->val.str);
} }
return false;
if (sub_non_neg(len, used) > 0) {
buf[used] = '/';
used++;
}
if (sub_non_neg(len, used) > 0) {
buf[used] = '\0';
used++;
} else
buf[len - 1] = '\0';
return buf;
}
int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb)
{
char buf[1024];
struct perf_pmu_alias *event;
struct pmu_event_info info = {
.pmu = pmu,
};
int ret = 0;
list_for_each_entry(event, &pmu->aliases, list) {
size_t buf_used;
info.pmu_name = event->pmu_name ?: pmu->name;
info.alias = NULL;
if (event->desc) {
info.name = event->name;
buf_used = 0;
} else {
info.name = format_alias(buf, sizeof(buf), pmu, event);
if (pmu->is_core) {
info.alias = info.name;
info.name = event->name;
}
buf_used = strlen(buf) + 1;
}
info.scale_unit = NULL;
if (strlen(event->unit) || event->scale != 1.0) {
info.scale_unit = buf + buf_used;
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%G%s", event->scale, event->unit) + 1;
}
info.desc = event->desc;
info.long_desc = event->long_desc;
info.encoding_desc = buf + buf_used;
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%s/%s/", info.pmu_name, event->str) + 1;
info.topic = event->topic;
info.str = event->str;
info.deprecated = event->deprecated;
ret = cb(state, &info);
if (ret)
return ret;
}
if (pmu->selectable) {
info.name = buf;
snprintf(buf, sizeof(buf), "%s//", pmu->name);
info.alias = NULL;
info.scale_unit = NULL;
info.desc = NULL;
info.long_desc = NULL;
info.encoding_desc = NULL;
info.topic = NULL;
info.pmu_name = pmu->name;
info.deprecated = false;
ret = cb(state, &info);
}
return ret;
} }
bool perf_pmu__is_software(const struct perf_pmu *pmu) bool perf_pmu__is_software(const struct perf_pmu *pmu)
......
...@@ -158,61 +158,22 @@ struct perf_pmu_info { ...@@ -158,61 +158,22 @@ struct perf_pmu_info {
bool snapshot; bool snapshot;
}; };
#define UNIT_MAX_LEN 31 /* max length for event unit name */ struct pmu_event_info {
const struct perf_pmu *pmu;
/** const char *name;
* struct perf_pmu_alias - An event either read from sysfs or builtin in const char* alias;
* pmu-events.c, created by parsing the pmu-events json files. const char *scale_unit;
*/ const char *desc;
struct perf_pmu_alias { const char *long_desc;
/** @name: Name of the event like "mem-loads". */ const char *encoding_desc;
char *name; const char *topic;
/** @desc: Optional short description of the event. */ const char *pmu_name;
char *desc; const char *str;
/** @long_desc: Optional long description. */
char *long_desc;
/**
* @topic: Optional topic such as cache or pipeline, particularly for
* json events.
*/
char *topic;
/**
* @str: Comma separated parameter list like
* "event=0xcd,umask=0x1,ldlat=0x3".
*/
char *str;
/** @terms: Owned list of the original parsed parameters. */
struct list_head terms;
/** @list: List element of struct perf_pmu aliases. */
struct list_head list;
/** @unit: Units for the event, such as bytes or cache lines. */
char unit[UNIT_MAX_LEN+1];
/** @scale: Value to scale read counter values by. */
double scale;
/**
* @per_pkg: Does the file
* <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
* equivalent json value exist and have the value 1.
*/
bool per_pkg;
/**
* @snapshot: Does the file
* <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
* exist and have the value 1.
*/
bool snapshot;
/**
* @deprecated: Is the event hidden and so not shown in perf list by
* default.
*/
bool deprecated; bool deprecated;
/**
* @pmu_name: The name copied from the json struct pmu_event. This can
* differ from the PMU name as it won't have suffixes.
*/
char *pmu_name;
}; };
typedef int (*pmu_event_callback)(void *state, struct pmu_event_info *info);
void pmu_add_sys_aliases(struct perf_pmu *pmu); void pmu_add_sys_aliases(struct perf_pmu *pmu);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms, struct list_head *head_terms,
...@@ -225,7 +186,7 @@ __u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name); ...@@ -225,7 +186,7 @@ __u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name);
int perf_pmu__format_type(struct perf_pmu *pmu, const char *name); int perf_pmu__format_type(struct perf_pmu *pmu, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
struct perf_pmu_info *info); struct perf_pmu_info *info);
struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event); int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb);
int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load); int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load);
void perf_pmu_format__set_value(void *format, int config, unsigned long *bits); void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);
...@@ -235,6 +196,9 @@ bool is_pmu_core(const char *name); ...@@ -235,6 +196,9 @@ bool is_pmu_core(const char *name);
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name); bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);
size_t perf_pmu__num_events(const struct perf_pmu *pmu);
int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb);
/** /**
* perf_pmu_is_software - is the PMU a software PMU as in it uses the * perf_pmu_is_software - is the PMU a software PMU as in it uses the
* perf_sw_context in the kernel? * perf_sw_context in the kernel?
...@@ -259,7 +223,6 @@ void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, ...@@ -259,7 +223,6 @@ void pmu_add_cpu_aliases_table(struct perf_pmu *pmu,
char *perf_pmu__getcpuid(struct perf_pmu *pmu); char *perf_pmu__getcpuid(struct perf_pmu *pmu);
const struct pmu_events_table *pmu_events_table__find(void); const struct pmu_events_table *pmu_events_table__find(void);
const struct pmu_metrics_table *pmu_metrics_table__find(void); const struct pmu_metrics_table *pmu_metrics_table__find(void);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval); int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
......
...@@ -258,219 +258,145 @@ int __weak perf_pmus__num_mem_pmus(void) ...@@ -258,219 +258,145 @@ int __weak perf_pmus__num_mem_pmus(void)
struct sevent { struct sevent {
/** PMU for event. */ /** PMU for event. */
const struct perf_pmu *pmu; const struct perf_pmu *pmu;
/** const char *name;
* Optional event for name, desc, etc. If not present then this is a const char* alias;
* selectable PMU and the event name is shown as "//". const char *scale_unit;
*/ const char *desc;
const struct perf_pmu_alias *event; const char *long_desc;
/** Is the PMU for the CPU? */ const char *encoding_desc;
bool is_cpu; const char *topic;
const char *pmu_name;
bool deprecated;
}; };
static int cmp_sevent(const void *a, const void *b) static int cmp_sevent(const void *a, const void *b)
{ {
const struct sevent *as = a; const struct sevent *as = a;
const struct sevent *bs = b; const struct sevent *bs = b;
const char *a_pmu_name = NULL, *b_pmu_name = NULL; bool a_iscpu, b_iscpu;
const char *a_name = "//", *a_desc = NULL, *a_topic = "";
const char *b_name = "//", *b_desc = NULL, *b_topic = "";
int ret; int ret;
if (as->event) {
a_name = as->event->name;
a_desc = as->event->desc;
a_topic = as->event->topic ?: "";
a_pmu_name = as->event->pmu_name;
}
if (bs->event) {
b_name = bs->event->name;
b_desc = bs->event->desc;
b_topic = bs->event->topic ?: "";
b_pmu_name = bs->event->pmu_name;
}
/* Put extra events last. */ /* Put extra events last. */
if (!!a_desc != !!b_desc) if (!!as->desc != !!bs->desc)
return !!a_desc - !!b_desc; return !!as->desc - !!bs->desc;
/* Order by topics. */ /* Order by topics. */
ret = strcmp(a_topic, b_topic); ret = strcmp(as->topic ?: "", bs->topic ?: "");
if (ret) if (ret)
return ret; return ret;
/* Order CPU core events to be first */ /* Order CPU core events to be first */
if (as->is_cpu != bs->is_cpu) a_iscpu = as->pmu ? as->pmu->is_core : true;
return as->is_cpu ? -1 : 1; b_iscpu = bs->pmu ? bs->pmu->is_core : true;
if (a_iscpu != b_iscpu)
return a_iscpu ? -1 : 1;
/* Order by PMU name. */ /* Order by PMU name. */
if (as->pmu != bs->pmu) { if (as->pmu != bs->pmu) {
a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: "");
b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
ret = strcmp(a_pmu_name, b_pmu_name);
if (ret) if (ret)
return ret; return ret;
} }
/* Order by event name. */ /* Order by event name. */
return strcmp(a_name, b_name); return strcmp(as->name, bs->name);
} }
static bool pmu_alias_is_duplicate(struct sevent *alias_a, static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *b)
struct sevent *alias_b)
{ {
const char *a_pmu_name = NULL, *b_pmu_name = NULL;
const char *a_name = "//", *b_name = "//";
if (alias_a->event) {
a_name = alias_a->event->name;
a_pmu_name = alias_a->event->pmu_name;
}
if (alias_b->event) {
b_name = alias_b->event->name;
b_pmu_name = alias_b->event->pmu_name;
}
/* Different names -> never duplicates */ /* Different names -> never duplicates */
if (strcmp(a_name, b_name)) if (strcmp(a->name ?: "//", b->name ?: "//"))
return false; return false;
/* Don't remove duplicates for different PMUs */ /* Don't remove duplicates for different PMUs */
a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); return strcmp(a->pmu_name, b->pmu_name) == 0;
b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
return strcmp(a_pmu_name, b_pmu_name) == 0;
} }
static int sub_non_neg(int a, int b) struct events_callback_state {
{ struct sevent *aliases;
if (b > a) size_t aliases_len;
return 0; size_t index;
return a - b; };
}
static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, static int perf_pmus__print_pmu_events__callback(void *vstate,
const struct perf_pmu_alias *alias) struct pmu_event_info *info)
{ {
struct parse_events_term *term; struct events_callback_state *state = vstate;
int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); struct sevent *s;
list_for_each_entry(term, &alias->terms, list) {
if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
used += snprintf(buf + used, sub_non_neg(len, used),
",%s=%s", term->config,
term->val.str);
}
if (sub_non_neg(len, used) > 0) { if (state->index >= state->aliases_len) {
buf[used] = '/'; pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);
used++; return 1;
} }
if (sub_non_neg(len, used) > 0) { s = &state->aliases[state->index];
buf[used] = '\0'; s->pmu = info->pmu;
used++; #define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL
} else COPY_STR(name);
buf[len - 1] = '\0'; COPY_STR(alias);
COPY_STR(scale_unit);
return buf; COPY_STR(desc);
COPY_STR(long_desc);
COPY_STR(encoding_desc);
COPY_STR(topic);
COPY_STR(pmu_name);
#undef COPY_STR
s->deprecated = info->deprecated;
state->index++;
return 0;
} }
void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state) void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
{ {
struct perf_pmu *pmu; struct perf_pmu *pmu;
struct perf_pmu_alias *event;
char buf[1024];
int printed = 0; int printed = 0;
int len, j; int len;
struct sevent *aliases; struct sevent *aliases;
struct events_callback_state state;
pmu = NULL; pmu = NULL;
len = 0; len = 0;
while ((pmu = perf_pmus__scan(pmu)) != NULL) { while ((pmu = perf_pmus__scan(pmu)) != NULL)
list_for_each_entry(event, &pmu->aliases, list) len += perf_pmu__num_events(pmu);
len++;
if (pmu->selectable)
len++;
}
aliases = zalloc(sizeof(struct sevent) * len); aliases = zalloc(sizeof(struct sevent) * len);
if (!aliases) { if (!aliases) {
pr_err("FATAL: not enough memory to print PMU events\n"); pr_err("FATAL: not enough memory to print PMU events\n");
return; return;
} }
pmu = NULL; pmu = NULL;
j = 0; state = (struct events_callback_state) {
.aliases = aliases,
.aliases_len = len,
.index = 0,
};
while ((pmu = perf_pmus__scan(pmu)) != NULL) { while ((pmu = perf_pmus__scan(pmu)) != NULL) {
bool is_cpu = pmu->is_core; perf_pmu__for_each_event(pmu, &state, perf_pmus__print_pmu_events__callback);
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) {
aliases[j].event = NULL;
aliases[j].pmu = pmu;
aliases[j].is_cpu = is_cpu;
j++;
}
} }
len = j;
qsort(aliases, len, sizeof(struct sevent), cmp_sevent); qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) { for (int j = 0; j < len; j++) {
const char *name, *alias = NULL, *scale_unit = NULL,
*desc = NULL, *long_desc = NULL,
*encoding_desc = NULL, *topic = NULL,
*pmu_name = NULL;
bool deprecated = false;
size_t buf_used;
/* Skip duplicates */ /* Skip duplicates */
if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
continue; continue;
if (!aliases[j].event) {
/* A selectable event. */
pmu_name = aliases[j].pmu->name;
buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
name = buf;
} else {
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;
}
pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
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/", pmu_name, aliases[j].event->str) + 1;
deprecated = aliases[j].event->deprecated;
}
print_cb->print_event(print_state, print_cb->print_event(print_state,
pmu_name, aliases[j].pmu_name,
topic, aliases[j].topic,
name, aliases[j].name,
alias, aliases[j].alias,
scale_unit, aliases[j].scale_unit,
deprecated, aliases[j].deprecated,
"Kernel PMU event", "Kernel PMU event",
desc, aliases[j].desc,
long_desc, aliases[j].long_desc,
encoding_desc); aliases[j].encoding_desc);
zfree(&aliases[j].name);
zfree(&aliases[j].alias);
zfree(&aliases[j].scale_unit);
zfree(&aliases[j].desc);
zfree(&aliases[j].long_desc);
zfree(&aliases[j].encoding_desc);
zfree(&aliases[j].topic);
zfree(&aliases[j].pmu_name);
} }
if (printed && pager_in_use()) if (printed && pager_in_use())
printf("\n"); printf("\n");
......
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