mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
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: Ian 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.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
5040264121
commit
c3245d2093
@ -57,9 +57,7 @@ static int save_result(void)
|
||||
r->is_core = pmu->is_core;
|
||||
r->nr_caps = pmu->nr_caps;
|
||||
|
||||
r->nr_aliases = 0;
|
||||
list_for_each(list, &pmu->aliases)
|
||||
r->nr_aliases++;
|
||||
r->nr_aliases = perf_pmu__num_events(pmu);
|
||||
|
||||
r->nr_formats = 0;
|
||||
list_for_each(list, &pmu->format)
|
||||
@ -98,9 +96,7 @@ static int check_result(bool core_only)
|
||||
return -1;
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
list_for_each(list, &pmu->aliases)
|
||||
nr++;
|
||||
nr = perf_pmu__num_events(pmu);
|
||||
if (nr != r->nr_aliases) {
|
||||
pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
|
||||
pmu->name, r->nr_aliases, nr);
|
||||
|
@ -341,7 +341,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event
|
||||
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,
|
||||
char const *pmu_name)
|
||||
{
|
||||
@ -496,6 +496,23 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
|
||||
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 */
|
||||
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);
|
||||
|
||||
for (; *test_event_table; test_event_table++) {
|
||||
struct perf_pmu_test_event const *test_event = *test_event_table;
|
||||
struct pmu_event const *event = &test_event->event;
|
||||
struct perf_pmu_alias *alias = perf_pmu__find_alias(pmu, event->name);
|
||||
struct perf_pmu_test_event test_event = **test_event_table;
|
||||
struct pmu_event const *event = &test_event.event;
|
||||
struct test_core_pmu_event_aliases_cb_args args = {
|
||||
.test_event = &test_event,
|
||||
.count = count,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (!alias) {
|
||||
pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n",
|
||||
pmu_name, event->name);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (compare_alias_to_test_event(alias, test_event, pmu_name)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
pr_debug2("testing aliases core PMU %s: matched event %s\n",
|
||||
pmu_name, alias->name);
|
||||
test_event.event.pmu = pmu_name;
|
||||
err = perf_pmu__find_event(pmu, event->name, &args,
|
||||
test_core_pmu_event_aliases_cb);
|
||||
if (err)
|
||||
res = err;
|
||||
}
|
||||
perf_pmu__delete(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 *pmu = &test_pmu->pmu;
|
||||
const char *pmu_name = pmu->name;
|
||||
struct perf_pmu_alias *a, *tmp, *alias;
|
||||
const struct pmu_events_table *events_table;
|
||||
int res = 0;
|
||||
|
||||
@ -564,8 +574,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
|
||||
pmu_add_sys_aliases(pmu);
|
||||
|
||||
/* Count how many aliases we generated */
|
||||
list_for_each_entry(alias, &pmu->aliases, list)
|
||||
alias_count++;
|
||||
alias_count = perf_pmu__num_events(pmu);
|
||||
|
||||
/* Count how many aliases we expect from the known 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)
|
||||
if (alias_count != to_match_count) {
|
||||
pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n",
|
||||
pmu_name, to_match_count, alias_count);
|
||||
res = -1;
|
||||
goto out;
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
bool matched = false;
|
||||
for (table = &test_pmu->aliases[0]; *table; table++) {
|
||||
struct perf_pmu_test_event test_event = **table;
|
||||
struct pmu_event const *event = &test_event.event;
|
||||
int err;
|
||||
struct test_core_pmu_event_aliases_cb_args args = {
|
||||
.test_event = &test_event,
|
||||
.count = &matched_count,
|
||||
};
|
||||
|
||||
for (table = &test_pmu->aliases[0]; *table; table++) {
|
||||
struct perf_pmu_test_event const *test_event = *table;
|
||||
struct pmu_event const *event = &test_event->event;
|
||||
|
||||
if (!strcmp(event->name, alias->name)) {
|
||||
if (compare_alias_to_test_event(alias,
|
||||
test_event,
|
||||
pmu_name)) {
|
||||
continue;
|
||||
}
|
||||
matched = true;
|
||||
matched_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched == false) {
|
||||
err = perf_pmu__find_event(pmu, event->name, &args,
|
||||
test_core_pmu_event_aliases_cb);
|
||||
if (err) {
|
||||
res = err;
|
||||
pr_debug("testing aliases uncore PMU %s: could not match alias %s\n",
|
||||
pmu_name, alias->name);
|
||||
res = -1;
|
||||
goto out;
|
||||
pmu_name, event->name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,12 +610,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
|
||||
pmu_name, matched_count, alias_count);
|
||||
res = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
list_for_each_entry_safe(a, tmp, &pmu->aliases, list) {
|
||||
list_del(&a->list);
|
||||
perf_pmu_free_alias(a);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -193,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
|
||||
struct parse_events_term *term;
|
||||
|
||||
list_for_each_entry(term, config_terms, list) {
|
||||
struct perf_pmu_alias *alias;
|
||||
bool matched = false;
|
||||
u64 num;
|
||||
|
||||
if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
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;
|
||||
|
||||
if (perf_pmu__have_event(pmu, term->val.str)) {
|
||||
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->config = 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;
|
||||
term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
|
||||
term->val.num = 1;
|
||||
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,
|
||||
INIT_LIST_HEAD(list);
|
||||
|
||||
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
|
||||
struct perf_pmu_alias *alias;
|
||||
bool auto_merge_stats;
|
||||
|
||||
if (parse_events__filter_pmu(parse_state, pmu))
|
||||
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) {
|
||||
if (!strcasecmp(alias->name, str)) {
|
||||
parse_events_copy_term_list(head, &orig_head);
|
||||
if (!parse_events_add_pmu(parse_state, list,
|
||||
pmu->name, orig_head,
|
||||
auto_merge_stats, loc)) {
|
||||
pr_debug("%s -> %s/%s/\n", str,
|
||||
pmu->name, alias->str);
|
||||
parse_state->wild_card_pmus = true;
|
||||
ok++;
|
||||
}
|
||||
parse_events_terms__delete(orig_head);
|
||||
}
|
||||
auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
|
||||
parse_events_copy_term_list(head, &orig_head);
|
||||
if (!parse_events_add_pmu(parse_state, list, pmu->name,
|
||||
orig_head, auto_merge_stats, loc)) {
|
||||
pr_debug("%s -> %s/%s/\n", str, pmu->name, str);
|
||||
ok++;
|
||||
}
|
||||
parse_events_terms__delete(orig_head);
|
||||
}
|
||||
|
||||
if (parse_state->fake_pmu) {
|
||||
|
@ -31,6 +31,61 @@
|
||||
|
||||
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
|
||||
* <sysfs>/devices/cpu/format/ held in struct perf_pmu.
|
||||
@ -355,7 +410,7 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old,
|
||||
}
|
||||
|
||||
/* 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->desc);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
struct parse_events_term *term)
|
||||
{
|
||||
struct perf_pmu_alias *alias;
|
||||
char *name;
|
||||
|
||||
if (parse_events__is_hardcoded_term(term))
|
||||
@ -1364,6 +1429,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
|
||||
if (pmu_find_format(&pmu->format, term->config))
|
||||
return NULL;
|
||||
name = term->config;
|
||||
|
||||
} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
|
||||
if (strcasecmp(term->config, "event"))
|
||||
return NULL;
|
||||
@ -1372,11 +1438,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
if (!strcasecmp(alias->name, name))
|
||||
return alias;
|
||||
}
|
||||
return NULL;
|
||||
return perf_pmu__find_alias(pmu, name);
|
||||
}
|
||||
|
||||
|
||||
@ -1459,16 +1521,33 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
|
||||
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(event, alias->name))
|
||||
return alias;
|
||||
if (!strcmp(args->event, info->name))
|
||||
return args->cb(args->state, info);
|
||||
|
||||
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)
|
||||
{
|
||||
struct perf_pmu_format *fmt, *tmp;
|
||||
@ -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)
|
||||
{
|
||||
struct perf_pmu_alias *alias;
|
||||
return perf_pmu__find_alias(pmu, name) != NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
if (!strcmp(alias->name, name))
|
||||
return true;
|
||||
size_t perf_pmu__num_events(const struct perf_pmu *pmu)
|
||||
{
|
||||
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)
|
||||
|
@ -158,61 +158,22 @@ struct perf_pmu_info {
|
||||
bool snapshot;
|
||||
};
|
||||
|
||||
#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.
|
||||
*/
|
||||
struct pmu_event_info {
|
||||
const struct perf_pmu *pmu;
|
||||
const char *name;
|
||||
const char* alias;
|
||||
const char *scale_unit;
|
||||
const char *desc;
|
||||
const char *long_desc;
|
||||
const char *encoding_desc;
|
||||
const char *topic;
|
||||
const char *pmu_name;
|
||||
const char *str;
|
||||
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);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms,
|
||||
@ -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__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
|
||||
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);
|
||||
void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);
|
||||
@ -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__auto_merge_stats(const struct perf_pmu *pmu);
|
||||
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_sw_context in the kernel?
|
||||
@ -259,7 +223,6 @@ void pmu_add_cpu_aliases_table(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_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);
|
||||
|
||||
|
@ -258,219 +258,145 @@ int __weak perf_pmus__num_mem_pmus(void)
|
||||
struct sevent {
|
||||
/** PMU for event. */
|
||||
const struct perf_pmu *pmu;
|
||||
/**
|
||||
* Optional event for name, desc, etc. If not present then this is a
|
||||
* selectable PMU and the event name is shown as "//".
|
||||
*/
|
||||
const struct perf_pmu_alias *event;
|
||||
/** Is the PMU for the CPU? */
|
||||
bool is_cpu;
|
||||
const char *name;
|
||||
const char* alias;
|
||||
const char *scale_unit;
|
||||
const char *desc;
|
||||
const char *long_desc;
|
||||
const char *encoding_desc;
|
||||
const char *topic;
|
||||
const char *pmu_name;
|
||||
bool deprecated;
|
||||
};
|
||||
|
||||
static int cmp_sevent(const void *a, const void *b)
|
||||
{
|
||||
const struct sevent *as = a;
|
||||
const struct sevent *bs = b;
|
||||
const char *a_pmu_name = NULL, *b_pmu_name = NULL;
|
||||
const char *a_name = "//", *a_desc = NULL, *a_topic = "";
|
||||
const char *b_name = "//", *b_desc = NULL, *b_topic = "";
|
||||
bool a_iscpu, b_iscpu;
|
||||
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. */
|
||||
if (!!a_desc != !!b_desc)
|
||||
return !!a_desc - !!b_desc;
|
||||
if (!!as->desc != !!bs->desc)
|
||||
return !!as->desc - !!bs->desc;
|
||||
|
||||
/* Order by topics. */
|
||||
ret = strcmp(a_topic, b_topic);
|
||||
ret = strcmp(as->topic ?: "", bs->topic ?: "");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Order CPU core events to be first */
|
||||
if (as->is_cpu != bs->is_cpu)
|
||||
return as->is_cpu ? -1 : 1;
|
||||
a_iscpu = as->pmu ? as->pmu->is_core : true;
|
||||
b_iscpu = bs->pmu ? bs->pmu->is_core : true;
|
||||
if (a_iscpu != b_iscpu)
|
||||
return a_iscpu ? -1 : 1;
|
||||
|
||||
/* Order by PMU name. */
|
||||
if (as->pmu != bs->pmu) {
|
||||
a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
|
||||
b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
|
||||
ret = strcmp(a_pmu_name, b_pmu_name);
|
||||
ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: "");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
struct sevent *alias_b)
|
||||
static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *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 */
|
||||
if (strcmp(a_name, b_name))
|
||||
if (strcmp(a->name ?: "//", b->name ?: "//"))
|
||||
return false;
|
||||
|
||||
/* Don't remove duplicates for different PMUs */
|
||||
a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
|
||||
b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
|
||||
return strcmp(a_pmu_name, b_pmu_name) == 0;
|
||||
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;
|
||||
size_t aliases_len;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
static int perf_pmus__print_pmu_events__callback(void *vstate,
|
||||
struct pmu_event_info *info)
|
||||
{
|
||||
if (b > a)
|
||||
return 0;
|
||||
return a - b;
|
||||
}
|
||||
struct events_callback_state *state = vstate;
|
||||
struct sevent *s;
|
||||
|
||||
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);
|
||||
if (state->index >= state->aliases_len) {
|
||||
pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
s = &state->aliases[state->index];
|
||||
s->pmu = info->pmu;
|
||||
#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL
|
||||
COPY_STR(name);
|
||||
COPY_STR(alias);
|
||||
COPY_STR(scale_unit);
|
||||
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)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
struct perf_pmu_alias *event;
|
||||
char buf[1024];
|
||||
int printed = 0;
|
||||
int len, j;
|
||||
int len;
|
||||
struct sevent *aliases;
|
||||
struct events_callback_state state;
|
||||
|
||||
pmu = NULL;
|
||||
len = 0;
|
||||
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
|
||||
list_for_each_entry(event, &pmu->aliases, list)
|
||||
len++;
|
||||
if (pmu->selectable)
|
||||
len++;
|
||||
}
|
||||
while ((pmu = perf_pmus__scan(pmu)) != NULL)
|
||||
len += perf_pmu__num_events(pmu);
|
||||
|
||||
aliases = zalloc(sizeof(struct sevent) * len);
|
||||
if (!aliases) {
|
||||
pr_err("FATAL: not enough memory to print PMU events\n");
|
||||
return;
|
||||
}
|
||||
pmu = NULL;
|
||||
j = 0;
|
||||
state = (struct events_callback_state) {
|
||||
.aliases = aliases,
|
||||
.aliases_len = len,
|
||||
.index = 0,
|
||||
};
|
||||
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
|
||||
bool is_cpu = pmu->is_core;
|
||||
|
||||
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++;
|
||||
}
|
||||
perf_pmu__for_each_event(pmu, &state, perf_pmus__print_pmu_events__callback);
|
||||
}
|
||||
len = j;
|
||||
qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
|
||||
for (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;
|
||||
|
||||
for (int j = 0; j < len; j++) {
|
||||
/* Skip duplicates */
|
||||
if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
|
||||
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,
|
||||
pmu_name,
|
||||
topic,
|
||||
name,
|
||||
alias,
|
||||
scale_unit,
|
||||
deprecated,
|
||||
aliases[j].pmu_name,
|
||||
aliases[j].topic,
|
||||
aliases[j].name,
|
||||
aliases[j].alias,
|
||||
aliases[j].scale_unit,
|
||||
aliases[j].deprecated,
|
||||
"Kernel PMU event",
|
||||
desc,
|
||||
long_desc,
|
||||
encoding_desc);
|
||||
aliases[j].desc,
|
||||
aliases[j].long_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())
|
||||
printf("\n");
|
||||
|
Loading…
Reference in New Issue
Block a user