Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf pmus: Skip duplicate PMUs and don't print list suffix by default

Add a PMUs scan that ignores duplicates. When there are multiple PMUs
that differ only by suffix, by default just list the first one and
skip all others. The scan routine checks that the PMU names match but
doesn't enforce that the numbers are consecutive as for some PMUs
there are gaps. If "-v" is passed to "perf list" then list all PMUs.

With the previous change duplicate PMUs are no longer printed but the
suffix of the first is printed. When duplicate PMUs are being skipped
avoid printing the suffix.

Before:

$ perf list
...
uncore_imc_free_running_0/data_read/ [Kernel PMU event]
uncore_imc_free_running_0/data_total/ [Kernel PMU event]
uncore_imc_free_running_0/data_write/ [Kernel PMU event]
uncore_imc_free_running_1/data_read/ [Kernel PMU event]
uncore_imc_free_running_1/data_total/ [Kernel PMU event]
uncore_imc_free_running_1/data_write/ [Kernel PMU event]

After:

$ perf list
...
uncore_imc_free_running/data_read/ [Kernel PMU event]
uncore_imc_free_running/data_total/ [Kernel PMU event]
uncore_imc_free_running/data_write/ [Kernel PMU event]
...
$ perf list -v
uncore_imc_free_running_0/data_read/ [Kernel PMU event]
uncore_imc_free_running_0/data_total/ [Kernel PMU event]
uncore_imc_free_running_0/data_write/ [Kernel PMU event]
uncore_imc_free_running_1/data_read/ [Kernel PMU event]
uncore_imc_free_running_1/data_total/ [Kernel PMU event]
uncore_imc_free_running_1/data_write/ [Kernel PMU event]
...

Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Ian Rogers <irogers@google.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: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.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>
Link: https://lore.kernel.org/r/20230825135237.921058-3-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
cd4e1efb 8d9f5146

+76 -11
+8
tools/perf/builtin-list.c
··· 434 434 strbuf_release(&buf); 435 435 } 436 436 437 + static bool default_skip_duplicate_pmus(void *ps) 438 + { 439 + struct print_state *print_state = ps; 440 + 441 + return !print_state->long_desc; 442 + } 443 + 437 444 int cmd_list(int argc, const char **argv) 438 445 { 439 446 int i, ret = 0; ··· 452 445 .print_end = default_print_end, 453 446 .print_event = default_print_event, 454 447 .print_metric = default_print_metric, 448 + .skip_duplicate_pmus = default_skip_duplicate_pmus, 455 449 }; 456 450 const char *cputype = NULL; 457 451 const char *unit_name = NULL;
+12 -5
tools/perf/util/pmu.c
··· 1578 1578 .cb = cb, 1579 1579 }; 1580 1580 1581 - return perf_pmu__for_each_event(pmu, &args, find_event_callback); 1581 + /* Sub-optimal, but function is only used by tests. */ 1582 + return perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ false, 1583 + &args, find_event_callback); 1582 1584 } 1583 1585 1584 1586 static void perf_pmu__del_formats(struct list_head *formats) ··· 1654 1652 } 1655 1653 1656 1654 static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, 1657 - const struct perf_pmu_alias *alias) 1655 + const struct perf_pmu_alias *alias, bool skip_duplicate_pmus) 1658 1656 { 1659 1657 struct parse_events_term *term; 1660 - int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); 1658 + int pmu_name_len = skip_duplicate_pmus 1659 + ? pmu_name_len_no_suffix(pmu->name, /*num=*/NULL) 1660 + : (int)strlen(pmu->name); 1661 + int used = snprintf(buf, len, "%.*s/%s", pmu_name_len, pmu->name, alias->name); 1661 1662 1662 1663 list_for_each_entry(term, &alias->terms, list) { 1663 1664 if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) ··· 1682 1677 return buf; 1683 1678 } 1684 1679 1685 - int perf_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb) 1680 + int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, 1681 + void *state, pmu_event_callback cb) 1686 1682 { 1687 1683 char buf[1024]; 1688 1684 struct perf_pmu_alias *event; ··· 1702 1696 info.name = event->name; 1703 1697 buf_used = 0; 1704 1698 } else { 1705 - info.name = format_alias(buf, sizeof(buf), pmu, event); 1699 + info.name = format_alias(buf, sizeof(buf), pmu, event, 1700 + skip_duplicate_pmus); 1706 1701 if (pmu->is_core) { 1707 1702 info.alias = info.name; 1708 1703 info.name = event->name;
+2 -1
tools/perf/util/pmu.h
··· 212 212 bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); 213 213 bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name); 214 214 size_t perf_pmu__num_events(struct perf_pmu *pmu); 215 - int perf_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb); 215 + int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, 216 + void *state, pmu_event_callback cb); 216 217 bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name); 217 218 218 219 /**
+49 -4
tools/perf/util/pmus.c
··· 36 36 static bool read_sysfs_core_pmus; 37 37 static bool read_sysfs_all_pmus; 38 38 39 - static int pmu_name_len_no_suffix(const char *str, unsigned long *num) 39 + int pmu_name_len_no_suffix(const char *str, unsigned long *num) 40 40 { 41 41 int orig_len, len; 42 42 ··· 276 276 return NULL; 277 277 } 278 278 279 + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) 280 + { 281 + bool use_core_pmus = !pmu || pmu->is_core; 282 + int last_pmu_name_len = 0; 283 + const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : ""; 284 + 285 + if (!pmu) { 286 + pmu_read_sysfs(/*core_only=*/false); 287 + pmu = list_prepare_entry(pmu, &core_pmus, list); 288 + } else 289 + last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", NULL); 290 + 291 + if (use_core_pmus) { 292 + list_for_each_entry_continue(pmu, &core_pmus, list) { 293 + int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); 294 + 295 + if (last_pmu_name_len == pmu_name_len && 296 + !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) 297 + continue; 298 + 299 + return pmu; 300 + } 301 + pmu = NULL; 302 + pmu = list_prepare_entry(pmu, &other_pmus, list); 303 + } 304 + list_for_each_entry_continue(pmu, &other_pmus, list) { 305 + int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); 306 + 307 + if (last_pmu_name_len == pmu_name_len && 308 + !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) 309 + continue; 310 + 311 + return pmu; 312 + } 313 + return NULL; 314 + } 315 + 279 316 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) 280 317 { 281 318 struct perf_pmu *pmu = NULL; ··· 438 401 int len; 439 402 struct sevent *aliases; 440 403 struct events_callback_state state; 404 + bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state); 405 + struct perf_pmu *(*scan_fn)(struct perf_pmu *); 406 + 407 + if (skip_duplicate_pmus) 408 + scan_fn = perf_pmus__scan_skip_duplicates; 409 + else 410 + scan_fn = perf_pmus__scan; 441 411 442 412 pmu = NULL; 443 413 len = 0; 444 - while ((pmu = perf_pmus__scan(pmu)) != NULL) 414 + while ((pmu = scan_fn(pmu)) != NULL) 445 415 len += perf_pmu__num_events(pmu); 446 416 447 417 aliases = zalloc(sizeof(struct sevent) * len); ··· 462 418 .aliases_len = len, 463 419 .index = 0, 464 420 }; 465 - while ((pmu = perf_pmus__scan(pmu)) != NULL) { 466 - perf_pmu__for_each_event(pmu, &state, perf_pmus__print_pmu_events__callback); 421 + while ((pmu = scan_fn(pmu)) != NULL) { 422 + perf_pmu__for_each_event(pmu, skip_duplicate_pmus, &state, 423 + perf_pmus__print_pmu_events__callback); 467 424 } 468 425 qsort(aliases, len, sizeof(struct sevent), cmp_sevent); 469 426 for (int j = 0; j < len; j++) {
+2
tools/perf/util/pmus.h
··· 5 5 struct perf_pmu; 6 6 struct print_callbacks; 7 7 8 + int pmu_name_len_no_suffix(const char *str, unsigned long *num); 9 + 8 10 void perf_pmus__destroy(void); 9 11 10 12 struct perf_pmu *perf_pmus__find(const char *name);
+1
tools/perf/util/print-events.h
··· 26 26 const char *expr, 27 27 const char *threshold, 28 28 const char *unit); 29 + bool (*skip_duplicate_pmus)(void *print_state); 29 30 }; 30 31 31 32 /** Print all events, the default when no options are specified. */
+2 -1
tools/perf/util/s390-sample-raw.c
··· 171 171 if (!pmu) 172 172 return NULL; 173 173 174 - perf_pmu__for_each_event(pmu, &data, get_counter_name_callback); 174 + perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ true, 175 + &data, get_counter_name_callback); 175 176 return data.result; 176 177 } 177 178