Skip to content

Commit 251aa04

Browse files
captain5050acmel
authored andcommitted
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: 8bc75f6 ("perf parse-events: Support wildcards on raw events") Reported-by: Kan Liang <kan.liang@linux.intel.com> Signed-off-by: Ian Rogers <irogers@google.com> Tested-by: Kan 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.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent 1f4326b commit 251aa04

File tree

6 files changed

+106
-35
lines changed

6 files changed

+106
-35
lines changed

tools/perf/util/parse-events.c

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ static int config_attr(struct perf_event_attr *attr,
372372
* contain hyphens and the longest name
373373
* should always be selected.
374374
*/
375-
int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config)
375+
int parse_events__decode_legacy_cache(const char *name, int extended_pmu_type, __u64 *config)
376376
{
377377
int len, cache_type = -1, cache_op = -1, cache_result = -1;
378378
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
423423
if (cache_result == -1)
424424
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
425425

426-
*config = ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT) |
427-
cache_type | (cache_op << 8) | (cache_result << 16);
426+
*config = cache_type | (cache_op << 8) | (cache_result << 16);
427+
if (perf_pmus__supports_extended_type())
428+
*config |= (__u64)extended_pmu_type << PERF_PMU_TYPE_SHIFT;
428429
return 0;
429430
}
430431

@@ -1204,11 +1205,17 @@ static int config_term_pmu(struct perf_event_attr *attr,
12041205
const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);
12051206

12061207
if (!pmu) {
1207-
pr_debug("Failed to find PMU for type %d", attr->type);
1208+
char *err_str;
1209+
1210+
if (asprintf(&err_str, "Failed to find PMU for type %d", attr->type) >= 0)
1211+
parse_events_error__handle(err, term->err_term,
1212+
err_str, /*help=*/NULL);
12081213
return -EINVAL;
12091214
}
12101215
attr->type = PERF_TYPE_HARDWARE;
1211-
attr->config = ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT) | term->val.num;
1216+
attr->config = term->val.num;
1217+
if (perf_pmus__supports_extended_type())
1218+
attr->config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
12121219
return 0;
12131220
}
12141221
if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER ||
@@ -1435,8 +1442,8 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
14351442

14361443
static int __parse_events_add_numeric(struct parse_events_state *parse_state,
14371444
struct list_head *list,
1438-
struct perf_pmu *pmu, u32 type, u64 config,
1439-
struct list_head *head_config)
1445+
struct perf_pmu *pmu, u32 type, u32 extended_type,
1446+
u64 config, struct list_head *head_config)
14401447
{
14411448
struct perf_event_attr attr;
14421449
LIST_HEAD(config_terms);
@@ -1446,6 +1453,10 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
14461453
memset(&attr, 0, sizeof(attr));
14471454
attr.type = type;
14481455
attr.config = config;
1456+
if (extended_type && (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)) {
1457+
assert(perf_pmus__supports_extended_type());
1458+
attr.config |= (u64)extended_type << PERF_PMU_TYPE_SHIFT;
1459+
};
14491460

14501461
if (head_config) {
14511462
if (config_attr(&attr, head_config, parse_state->error,
@@ -1474,24 +1485,26 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
14741485
struct perf_pmu *pmu = NULL;
14751486
bool found_supported = false;
14761487

1477-
if (!wildcard)
1478-
return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL,
1479-
type, config, head_config);
1480-
14811488
/* Wildcards on numeric values are only supported by core PMUs. */
1482-
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
1483-
int ret;
1489+
if (wildcard && perf_pmus__supports_extended_type()) {
1490+
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
1491+
int ret;
14841492

1485-
if (parse_events__filter_pmu(parse_state, pmu))
1486-
continue;
1493+
found_supported = true;
1494+
if (parse_events__filter_pmu(parse_state, pmu))
1495+
continue;
14871496

1488-
found_supported = true;
1489-
ret = __parse_events_add_numeric(parse_state, list, pmu, pmu->type,
1490-
config, head_config);
1491-
if (ret)
1492-
return ret;
1497+
ret = __parse_events_add_numeric(parse_state, list, pmu,
1498+
type, pmu->type,
1499+
config, head_config);
1500+
if (ret)
1501+
return ret;
1502+
}
1503+
if (found_supported)
1504+
return 0;
14931505
}
1494-
return found_supported ? 0 : -EINVAL;
1506+
return __parse_events_add_numeric(parse_state, list, perf_pmus__find_by_type(type),
1507+
type, /*extended_type=*/0, config, head_config);
14951508
}
14961509

14971510
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,
19892002
{
19902003
struct evsel *leader = evsel__leader(evsel);
19912004
struct evsel *pos;
1992-
const char *group_pmu_name = evsel->pmu_name ?: "cpu";
2005+
const char *group_pmu_name;
2006+
struct perf_pmu *pmu = evsel__find_pmu(evsel);
19932007

2008+
if (!pmu) {
2009+
/*
2010+
* For PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE types the PMU
2011+
* is a core PMU, but in heterogeneous systems this is
2012+
* unknown. For now pick the first core PMU.
2013+
*/
2014+
pmu = perf_pmus__scan_core(NULL);
2015+
}
2016+
if (!pmu) {
2017+
pr_debug("No PMU found for '%s'", evsel__name(evsel));
2018+
return -EINVAL;
2019+
}
2020+
group_pmu_name = pmu->name;
19942021
/*
19952022
* Software events may be in a group with other uncore PMU events. Use
19962023
* 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,
19992026
* Aux event leaders, like intel_pt, expect a group with events from
20002027
* other PMUs, so substitute the AUX event's PMU in this case.
20012028
*/
2002-
if (evsel->core.attr.type == PERF_TYPE_SOFTWARE || evsel__is_aux_event(leader)) {
2029+
if (perf_pmu__is_software(pmu) || evsel__is_aux_event(leader)) {
2030+
struct perf_pmu *leader_pmu = evsel__find_pmu(leader);
2031+
2032+
if (!leader_pmu) {
2033+
/* As with determining pmu above. */
2034+
leader_pmu = perf_pmus__scan_core(NULL);
2035+
}
20032036
/*
20042037
* Starting with the leader, find the first event with a named
2005-
* PMU. for_each_group_(member|evsel) isn't used as the list
2006-
* isn't yet sorted putting evsel's in the same group together.
2038+
* non-software PMU. for_each_group_(member|evsel) isn't used as
2039+
* the list isn't yet sorted putting evsel's in the same group
2040+
* together.
20072041
*/
2008-
if (leader->pmu_name) {
2009-
group_pmu_name = leader->pmu_name;
2042+
if (leader_pmu && !perf_pmu__is_software(leader_pmu)) {
2043+
group_pmu_name = leader_pmu->name;
20102044
} else if (leader->core.nr_members > 1) {
20112045
list_for_each_entry(pos, head, core.node) {
2012-
if (evsel__leader(pos) == leader && pos->pmu_name) {
2013-
group_pmu_name = pos->pmu_name;
2046+
struct perf_pmu *pos_pmu;
2047+
2048+
if (pos == leader || evsel__leader(pos) != leader)
2049+
continue;
2050+
pos_pmu = evsel__find_pmu(pos);
2051+
if (!pos_pmu) {
2052+
/* As with determining pmu above. */
2053+
pos_pmu = perf_pmus__scan_core(NULL);
2054+
}
2055+
if (pos_pmu && !perf_pmu__is_software(pos_pmu)) {
2056+
group_pmu_name = pos_pmu->name;
20142057
break;
20152058
}
20162059
}
20172060
}
20182061
}
2019-
evsel->group_pmu_name = strdup(group_pmu_name);
2062+
/* Assign the actual name taking care that the fake PMU lacks a name. */
2063+
evsel->group_pmu_name = strdup(group_pmu_name ?: "fake");
20202064
return evsel->group_pmu_name ? 0 : -ENOMEM;
20212065
}
20222066

tools/perf/util/parse-events.y

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,11 @@ value_sym '/' event_config '/'
445445
int type = $1 >> 16;
446446
int config = $1 & 255;
447447
int err;
448+
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
448449

449450
list = alloc_list();
450451
ABORT_ON(!list);
451-
err = parse_events_add_numeric(_parse_state, list, type, config, $3,
452-
/*wildcard=*/false);
452+
err = parse_events_add_numeric(_parse_state, list, type, config, $3, wildcard);
453453
parse_events_terms__delete($3);
454454
if (err) {
455455
free_list_evsel(list);
@@ -463,12 +463,12 @@ value_sym sep_slash_slash_dc
463463
struct list_head *list;
464464
int type = $1 >> 16;
465465
int config = $1 & 255;
466+
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
466467

467468
list = alloc_list();
468469
ABORT_ON(!list);
469470
ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config,
470-
/*head_config=*/NULL,
471-
/*wildcard=*/false));
471+
/*head_config=*/NULL, wildcard));
472472
$$ = list;
473473
}
474474
|
@@ -635,7 +635,7 @@ PE_RAW opt_event_config
635635
ABORT_ON(errno);
636636
free($1);
637637
err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2,
638-
/*wildcard=*/true);
638+
/*wildcard=*/false);
639639
parse_events_terms__delete($2);
640640
if (err) {
641641
free(list);

tools/perf/util/pmu.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,22 @@ bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
14381438
return false;
14391439
}
14401440

1441+
bool perf_pmu__is_software(const struct perf_pmu *pmu)
1442+
{
1443+
if (pmu->is_core || pmu->is_uncore || pmu->auxtrace)
1444+
return false;
1445+
switch (pmu->type) {
1446+
case PERF_TYPE_HARDWARE: return false;
1447+
case PERF_TYPE_SOFTWARE: return true;
1448+
case PERF_TYPE_TRACEPOINT: return true;
1449+
case PERF_TYPE_HW_CACHE: return false;
1450+
case PERF_TYPE_RAW: return false;
1451+
case PERF_TYPE_BREAKPOINT: return true;
1452+
default: break;
1453+
}
1454+
return !strcmp(pmu->name, "kprobe") || !strcmp(pmu->name, "uprobe");
1455+
}
1456+
14411457
FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
14421458
{
14431459
char path[PATH_MAX];

tools/perf/util/pmu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ bool is_pmu_core(const char *name);
224224
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
225225
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
226226
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);
227+
/**
228+
* perf_pmu_is_software - is the PMU a software PMU as in it uses the
229+
* perf_sw_context in the kernel?
230+
*/
231+
bool perf_pmu__is_software(const struct perf_pmu *pmu);
227232

228233
FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
229234
FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name);

tools/perf/util/pmus.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,11 @@ int perf_pmus__num_core_pmus(void)
477477
return count;
478478
}
479479

480+
bool perf_pmus__supports_extended_type(void)
481+
{
482+
return perf_pmus__num_core_pmus() > 1;
483+
}
484+
480485
struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
481486
{
482487
struct perf_pmu *pmu = evsel->pmu;

tools/perf/util/pmus.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ int perf_pmus__num_mem_pmus(void);
1919
void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
2020
bool perf_pmus__have_event(const char *pname, const char *name);
2121
int perf_pmus__num_core_pmus(void);
22+
bool perf_pmus__supports_extended_type(void);
2223

2324
#endif /* __PMUS_H */

0 commit comments

Comments
 (0)