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

perf parse-events: Wildcard most "numeric" events

Numeric events are either raw events or those with ABI defined numbers
matched by the lexer. PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE events
should wildcard match on hybrid systems. So "cycles" should match each
PMU type with an extended type, not just PERF_TYPE_HARDWARE.

Change wildcard matching to add the event even if wildcard PMU
scanning fails, there will be no extended type but this best matches
previous behavior.

Only set the extended type when the event type supports it and when
perf_pmus__supports_extended_type is true. This new function returns
true if >1 core PMU and avoids potential errors on older kernels.

Modify evsel__compute_group_pmu_name using a helper
perf_pmu__is_software to determine when grouping should occur. Try to
use PMUs, and evsel__find_pmu, as being more dependable than
evsel->pmu_name.

Set a parse events error if a hardware term's PMU lookup fails, to
provide extra diagnostics.

Fixes: 8bc75f69 ("perf parse-events: Support wildcards on raw events")
Reported-by: default avatarKan Liang <kan.liang@linux.intel.com>
Signed-off-by: default avatarIan Rogers <irogers@google.com>
Tested-by: default avatarKan Liang <kan.liang@linux.intel.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
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>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: https://lore.kernel.org/r/20230601082954.754318-4-irogers@google.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 1f4326bf
...@@ -372,7 +372,7 @@ static int config_attr(struct perf_event_attr *attr, ...@@ -372,7 +372,7 @@ static int config_attr(struct perf_event_attr *attr,
* contain hyphens and the longest name * contain hyphens and the longest name
* should always be selected. * should always be selected.
*/ */
int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config) int parse_events__decode_legacy_cache(const char *name, int extended_pmu_type, __u64 *config)
{ {
int len, cache_type = -1, cache_op = -1, cache_result = -1; int len, cache_type = -1, cache_op = -1, cache_result = -1;
const char *name_end = &name[strlen(name) + 1]; const char *name_end = &name[strlen(name) + 1];
...@@ -423,8 +423,9 @@ int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *con ...@@ -423,8 +423,9 @@ int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *con
if (cache_result == -1) if (cache_result == -1)
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
*config = ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT) | *config = cache_type | (cache_op << 8) | (cache_result << 16);
cache_type | (cache_op << 8) | (cache_result << 16); if (perf_pmus__supports_extended_type())
*config |= (__u64)extended_pmu_type << PERF_PMU_TYPE_SHIFT;
return 0; return 0;
} }
...@@ -1204,11 +1205,17 @@ static int config_term_pmu(struct perf_event_attr *attr, ...@@ -1204,11 +1205,17 @@ static int config_term_pmu(struct perf_event_attr *attr,
const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);
if (!pmu) { if (!pmu) {
pr_debug("Failed to find PMU for type %d", attr->type); char *err_str;
if (asprintf(&err_str, "Failed to find PMU for type %d", attr->type) >= 0)
parse_events_error__handle(err, term->err_term,
err_str, /*help=*/NULL);
return -EINVAL; return -EINVAL;
} }
attr->type = PERF_TYPE_HARDWARE; attr->type = PERF_TYPE_HARDWARE;
attr->config = ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT) | term->val.num; attr->config = term->val.num;
if (perf_pmus__supports_extended_type())
attr->config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
return 0; return 0;
} }
if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER || if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER ||
...@@ -1435,8 +1442,8 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx, ...@@ -1435,8 +1442,8 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
static int __parse_events_add_numeric(struct parse_events_state *parse_state, static int __parse_events_add_numeric(struct parse_events_state *parse_state,
struct list_head *list, struct list_head *list,
struct perf_pmu *pmu, u32 type, u64 config, struct perf_pmu *pmu, u32 type, u32 extended_type,
struct list_head *head_config) u64 config, struct list_head *head_config)
{ {
struct perf_event_attr attr; struct perf_event_attr attr;
LIST_HEAD(config_terms); LIST_HEAD(config_terms);
...@@ -1446,6 +1453,10 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state, ...@@ -1446,6 +1453,10 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
attr.type = type; attr.type = type;
attr.config = config; attr.config = config;
if (extended_type && (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)) {
assert(perf_pmus__supports_extended_type());
attr.config |= (u64)extended_type << PERF_PMU_TYPE_SHIFT;
};
if (head_config) { if (head_config) {
if (config_attr(&attr, head_config, parse_state->error, if (config_attr(&attr, head_config, parse_state->error,
...@@ -1474,24 +1485,26 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, ...@@ -1474,24 +1485,26 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
struct perf_pmu *pmu = NULL; struct perf_pmu *pmu = NULL;
bool found_supported = false; bool found_supported = false;
if (!wildcard)
return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL,
type, config, head_config);
/* Wildcards on numeric values are only supported by core PMUs. */ /* Wildcards on numeric values are only supported by core PMUs. */
if (wildcard && perf_pmus__supports_extended_type()) {
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
int ret; int ret;
found_supported = true;
if (parse_events__filter_pmu(parse_state, pmu)) if (parse_events__filter_pmu(parse_state, pmu))
continue; continue;
found_supported = true; ret = __parse_events_add_numeric(parse_state, list, pmu,
ret = __parse_events_add_numeric(parse_state, list, pmu, pmu->type, type, pmu->type,
config, head_config); config, head_config);
if (ret) if (ret)
return ret; return ret;
} }
return found_supported ? 0 : -EINVAL; if (found_supported)
return 0;
}
return __parse_events_add_numeric(parse_state, list, perf_pmus__find_by_type(type),
type, /*extended_type=*/0, config, head_config);
} }
int parse_events_add_tool(struct parse_events_state *parse_state, int parse_events_add_tool(struct parse_events_state *parse_state,
...@@ -1989,8 +2002,22 @@ static int evsel__compute_group_pmu_name(struct evsel *evsel, ...@@ -1989,8 +2002,22 @@ static int evsel__compute_group_pmu_name(struct evsel *evsel,
{ {
struct evsel *leader = evsel__leader(evsel); struct evsel *leader = evsel__leader(evsel);
struct evsel *pos; struct evsel *pos;
const char *group_pmu_name = evsel->pmu_name ?: "cpu"; const char *group_pmu_name;
struct perf_pmu *pmu = evsel__find_pmu(evsel);
if (!pmu) {
/*
* For PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE types the PMU
* is a core PMU, but in heterogeneous systems this is
* unknown. For now pick the first core PMU.
*/
pmu = perf_pmus__scan_core(NULL);
}
if (!pmu) {
pr_debug("No PMU found for '%s'", evsel__name(evsel));
return -EINVAL;
}
group_pmu_name = pmu->name;
/* /*
* Software events may be in a group with other uncore PMU events. Use * Software events may be in a group with other uncore PMU events. Use
* the pmu_name of the first non-software event to avoid breaking the * the pmu_name of the first non-software event to avoid breaking the
...@@ -1999,24 +2026,41 @@ static int evsel__compute_group_pmu_name(struct evsel *evsel, ...@@ -1999,24 +2026,41 @@ static int evsel__compute_group_pmu_name(struct evsel *evsel,
* Aux event leaders, like intel_pt, expect a group with events from * Aux event leaders, like intel_pt, expect a group with events from
* other PMUs, so substitute the AUX event's PMU in this case. * other PMUs, so substitute the AUX event's PMU in this case.
*/ */
if (evsel->core.attr.type == PERF_TYPE_SOFTWARE || evsel__is_aux_event(leader)) { if (perf_pmu__is_software(pmu) || evsel__is_aux_event(leader)) {
struct perf_pmu *leader_pmu = evsel__find_pmu(leader);
if (!leader_pmu) {
/* As with determining pmu above. */
leader_pmu = perf_pmus__scan_core(NULL);
}
/* /*
* Starting with the leader, find the first event with a named * Starting with the leader, find the first event with a named
* PMU. for_each_group_(member|evsel) isn't used as the list * non-software PMU. for_each_group_(member|evsel) isn't used as
* isn't yet sorted putting evsel's in the same group together. * the list isn't yet sorted putting evsel's in the same group
* together.
*/ */
if (leader->pmu_name) { if (leader_pmu && !perf_pmu__is_software(leader_pmu)) {
group_pmu_name = leader->pmu_name; group_pmu_name = leader_pmu->name;
} else if (leader->core.nr_members > 1) { } else if (leader->core.nr_members > 1) {
list_for_each_entry(pos, head, core.node) { list_for_each_entry(pos, head, core.node) {
if (evsel__leader(pos) == leader && pos->pmu_name) { struct perf_pmu *pos_pmu;
group_pmu_name = pos->pmu_name;
if (pos == leader || evsel__leader(pos) != leader)
continue;
pos_pmu = evsel__find_pmu(pos);
if (!pos_pmu) {
/* As with determining pmu above. */
pos_pmu = perf_pmus__scan_core(NULL);
}
if (pos_pmu && !perf_pmu__is_software(pos_pmu)) {
group_pmu_name = pos_pmu->name;
break; break;
} }
} }
} }
} }
evsel->group_pmu_name = strdup(group_pmu_name); /* Assign the actual name taking care that the fake PMU lacks a name. */
evsel->group_pmu_name = strdup(group_pmu_name ?: "fake");
return evsel->group_pmu_name ? 0 : -ENOMEM; return evsel->group_pmu_name ? 0 : -ENOMEM;
} }
......
...@@ -445,11 +445,11 @@ value_sym '/' event_config '/' ...@@ -445,11 +445,11 @@ value_sym '/' event_config '/'
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
int err; int err;
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
list = alloc_list(); list = alloc_list();
ABORT_ON(!list); ABORT_ON(!list);
err = parse_events_add_numeric(_parse_state, list, type, config, $3, err = parse_events_add_numeric(_parse_state, list, type, config, $3, wildcard);
/*wildcard=*/false);
parse_events_terms__delete($3); parse_events_terms__delete($3);
if (err) { if (err) {
free_list_evsel(list); free_list_evsel(list);
...@@ -463,12 +463,12 @@ value_sym sep_slash_slash_dc ...@@ -463,12 +463,12 @@ value_sym sep_slash_slash_dc
struct list_head *list; struct list_head *list;
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
list = alloc_list(); list = alloc_list();
ABORT_ON(!list); ABORT_ON(!list);
ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config, ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config,
/*head_config=*/NULL, /*head_config=*/NULL, wildcard));
/*wildcard=*/false));
$$ = list; $$ = list;
} }
| |
...@@ -635,7 +635,7 @@ PE_RAW opt_event_config ...@@ -635,7 +635,7 @@ PE_RAW opt_event_config
ABORT_ON(errno); ABORT_ON(errno);
free($1); free($1);
err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2, err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2,
/*wildcard=*/true); /*wildcard=*/false);
parse_events_terms__delete($2); parse_events_terms__delete($2);
if (err) { if (err) {
free(list); free(list);
......
...@@ -1438,6 +1438,22 @@ bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) ...@@ -1438,6 +1438,22 @@ bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
return false; return false;
} }
bool perf_pmu__is_software(const struct perf_pmu *pmu)
{
if (pmu->is_core || pmu->is_uncore || pmu->auxtrace)
return false;
switch (pmu->type) {
case PERF_TYPE_HARDWARE: return false;
case PERF_TYPE_SOFTWARE: return true;
case PERF_TYPE_TRACEPOINT: return true;
case PERF_TYPE_HW_CACHE: return false;
case PERF_TYPE_RAW: return false;
case PERF_TYPE_BREAKPOINT: return true;
default: break;
}
return !strcmp(pmu->name, "kprobe") || !strcmp(pmu->name, "uprobe");
}
FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
......
...@@ -224,6 +224,11 @@ bool is_pmu_core(const char *name); ...@@ -224,6 +224,11 @@ 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);
/**
* perf_pmu_is_software - is the PMU a software PMU as in it uses the
* perf_sw_context in the kernel?
*/
bool perf_pmu__is_software(const struct perf_pmu *pmu);
FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name); FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name); FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name);
......
...@@ -477,6 +477,11 @@ int perf_pmus__num_core_pmus(void) ...@@ -477,6 +477,11 @@ int perf_pmus__num_core_pmus(void)
return count; return count;
} }
bool perf_pmus__supports_extended_type(void)
{
return perf_pmus__num_core_pmus() > 1;
}
struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
{ {
struct perf_pmu *pmu = evsel->pmu; struct perf_pmu *pmu = evsel->pmu;
......
...@@ -19,5 +19,6 @@ int perf_pmus__num_mem_pmus(void); ...@@ -19,5 +19,6 @@ int perf_pmus__num_mem_pmus(void);
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);
bool perf_pmus__have_event(const char *pname, const char *name); bool perf_pmus__have_event(const char *pname, const char *name);
int perf_pmus__num_core_pmus(void); int perf_pmus__num_core_pmus(void);
bool perf_pmus__supports_extended_type(void);
#endif /* __PMUS_H */ #endif /* __PMUS_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