diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index ffa6f0a90741..b1c2f0a20306 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2103,7 +2103,7 @@ static int test_event_fake_pmu(const char *str) parse_events_error__init(&err); perf_pmu__test_parse_init(); - ret = __parse_events(evlist, str, &err, &perf_pmu__fake); + ret = __parse_events(evlist, str, &err, &perf_pmu__fake, /*warn_if_reordered=*/true); if (ret) { pr_debug("failed to parse event '%s', err %d, str '%s'\n", str, ret, err.str); diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 6ccd413b5983..7f8e86452527 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -785,7 +785,7 @@ static int check_parse_id(const char *id, struct parse_events_error *error, */ perf_pmu__test_parse_init(); } - ret = __parse_events(evlist, dup, error, fake_pmu); + ret = __parse_events(evlist, dup, error, fake_pmu, /*warn_if_reordered=*/true); free(dup); evlist__delete(evlist); diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index de6dd527a2ba..5783f4c2d1ef 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -1441,7 +1441,8 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, } pr_debug("Parsing metric events '%s'\n", events.buf); parse_events_error__init(&parse_error); - ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu); + ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu, + /*warn_if_reordered=*/false); if (ret) { parse_events_error__print(&parse_error, events.buf); goto err_out; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9ec3c1dc81e0..3b2e5bb3e852 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2157,11 +2157,13 @@ static int evlist__cmp(void *state, const struct list_head *l, const struct list return arch_evlist__cmp(lhs, rhs); } -static void parse_events__sort_events_and_fix_groups(struct list_head *list) +static bool parse_events__sort_events_and_fix_groups(struct list_head *list) { - int idx = -1; + int idx = 0, unsorted_idx = -1; struct evsel *pos, *cur_leader = NULL; struct perf_evsel *cur_leaders_grp = NULL; + bool idx_changed = false; + int orig_num_leaders = 0, num_leaders = 0; /* * Compute index to insert ungrouped events at. Place them where the @@ -2170,15 +2172,22 @@ static void parse_events__sort_events_and_fix_groups(struct list_head *list) list_for_each_entry(pos, list, core.node) { const struct evsel *pos_leader = evsel__leader(pos); - if (pos != pos_leader || pos->core.nr_members > 1) - continue; + if (pos == pos_leader) + orig_num_leaders++; - idx = pos->core.idx; - break; + /* + * Ensure indexes are sequential, in particular for multiple + * event lists being merged. The indexes are used to detect when + * the user order is modified. + */ + pos->core.idx = idx++; + + if (unsorted_idx == -1 && pos == pos_leader && pos->core.nr_members < 2) + unsorted_idx = pos->core.idx; } /* Sort events. */ - list_sort(&idx, list, evlist__cmp); + list_sort(&unsorted_idx, list, evlist__cmp); /* * Recompute groups, splitting for PMUs and adding groups for events @@ -2192,6 +2201,8 @@ static void parse_events__sort_events_and_fix_groups(struct list_head *list) bool force_grouped = arch_evsel__must_be_in_group(pos); /* Reset index and nr_members. */ + if (pos->core.idx != idx) + idx_changed = true; pos->core.idx = idx++; pos->core.nr_members = 0; @@ -2225,12 +2236,18 @@ static void parse_events__sort_events_and_fix_groups(struct list_head *list) } } list_for_each_entry(pos, list, core.node) { - pos->core.leader->nr_members++; + struct evsel *pos_leader = evsel__leader(pos); + + if (pos == pos_leader) + num_leaders++; + pos_leader->core.nr_members++; } + return idx_changed || num_leaders != orig_num_leaders; } int __parse_events(struct evlist *evlist, const char *str, - struct parse_events_error *err, struct perf_pmu *fake_pmu) + struct parse_events_error *err, struct perf_pmu *fake_pmu, + bool warn_if_reordered) { struct parse_events_state parse_state = { .list = LIST_HEAD_INIT(parse_state.list), @@ -2250,7 +2267,9 @@ int __parse_events(struct evlist *evlist, const char *str, return -1; } - parse_events__sort_events_and_fix_groups(&parse_state.list); + if (parse_events__sort_events_and_fix_groups(&parse_state.list) && + warn_if_reordered && !parse_state.wild_card_pmus) + pr_warning("WARNING: events were regrouped to match PMUs\n"); /* * Add list to the evlist even with errors to allow callers to clean up. diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 767ad1729228..46204c1a7916 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -26,13 +26,13 @@ int parse_events_option(const struct option *opt, const char *str, int unset); int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset); __attribute__((nonnull(1, 2, 3))) int __parse_events(struct evlist *evlist, const char *str, struct parse_events_error *error, - struct perf_pmu *fake_pmu); + struct perf_pmu *fake_pmu, bool warn_if_reordered); -__attribute__((nonnull)) +__attribute__((nonnull(1, 2, 3))) static inline int parse_events(struct evlist *evlist, const char *str, struct parse_events_error *err) { - return __parse_events(evlist, str, err, NULL); + return __parse_events(evlist, str, err, /*fake_pmu=*/NULL, /*warn_if_reordered=*/true); } int parse_event(struct evlist *evlist, const char *str); @@ -128,6 +128,7 @@ struct parse_events_state { int stoken; struct perf_pmu *fake_pmu; char *hybrid_pmu_name; + bool wild_card_pmus; }; void parse_events__shrink_config_terms(void); diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 3a04602d2982..4488443e506e 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -323,6 +323,7 @@ event_pmu_name opt_pmu_config if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, /*auto_merge_stats=*/true)) { ok++; + parse_state->wild_card_pmus = true; } parse_events_terms__delete(terms); }