[PATCH v1 26/41] perf pmus: Expose duplicate uncore PMU scanning helpers

From: Ian Rogers

Date: Tue Nov 03 2026 - 18:54:03 EST


Export perf_pmus__scan_skip_duplicates() and add
perf_pmu__num_duplicates() to count numbered PMU instances sharing a
common prefix (such as uncore_imc_0..uncore_imc_7) so subcommands like
'perf sysinfo' can collapse duplicate uncore PMUs by default and report
their instance count.

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/pmus.c | 25 ++++++++++++++++++++++++-
tools/perf/util/pmus.h | 19 +++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 908959abbd3d..aa088c9c9aec 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -484,7 +484,7 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmus *pmus,
return NULL;
}

-static struct perf_pmu *
+struct perf_pmu *
perf_pmus__scan_skip_duplicates(struct perf_pmus *pmus, struct perf_pmu *pmu)
{
bool use_core_pmus = !pmu || pmu->is_core;
@@ -522,6 +522,29 @@ perf_pmus__scan_skip_duplicates(struct perf_pmus *pmus, struct perf_pmu *pmu)
return NULL;
}

+size_t perf_pmu__num_duplicates(const struct perf_pmu *pmu)
+{
+ const struct list_head *head;
+ const struct perf_pmu *pos = pmu;
+ size_t base_len;
+ size_t count = 1;
+
+ if (!pmu || !pmu->pmus)
+ return 1;
+
+ head = pmu->is_core ? &pmu->pmus->core_pmus : &pmu->pmus->other_pmus;
+ base_len = pmu_name_len_no_suffix(pmu->name ?: "");
+ list_for_each_entry_continue(pos, head, list) {
+ size_t pos_len = pmu_name_len_no_suffix(pos->name ?: "");
+
+ if (pos_len != base_len ||
+ strncmp(pmu->name ?: "", pos->name ?: "", base_len))
+ break;
+ count++;
+ }
+ return count;
+}
+
struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmus *pmus,
struct perf_pmu *pmu, const char *compat)
{
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index 2b784cf648f3..9790be2d60a5 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -134,6 +134,24 @@ struct perf_pmu *perf_pmus__scan(struct perf_pmus *pmus, struct perf_pmu *pmu);
*/
struct perf_pmu *perf_pmus__scan_core(struct perf_pmus *pmus, struct perf_pmu *pmu);

+/**
+ * perf_pmus__scan_skip_duplicates() - Iterate over PMUs in @pmus, skipping
+ * numbered duplicate instances.
+ * @pmus: The PMU collection to scan.
+ * @pmu: Previous PMU returned, or NULL to start from the first PMU.
+ *
+ * Return: Next distinct prefix struct perf_pmu, or NULL when done.
+ */
+struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmus *pmus, struct perf_pmu *pmu);
+
+/**
+ * perf_pmu__num_duplicates() - Count consecutive PMUs sharing @pmu's prefix.
+ * @pmu: Representative PMU returned by perf_pmus__scan_skip_duplicates().
+ *
+ * Return: Number of PMU instances (including @pmu) sharing its base name.
+ */
+size_t perf_pmu__num_duplicates(const struct perf_pmu *pmu);
+
/**
* perf_pmus__scan_for_event() - Iterate over PMUs in @pmus that have @event.
* @pmus: The PMU collection to scan.
@@ -260,6 +278,7 @@ struct perf_pmu *perf_pmus__add_test_hwmon_pmu(struct perf_pmus *pmus,
* Return: Pointer to the fake struct perf_pmu.
*/
struct perf_pmu *perf_pmus__fake_pmu(void);
+
/**
* perf_pmus__find_core_pmu() - Return the first core PMU in @pmus.
* @pmus: The PMU collection to search.
--
2.56.0.385.gac671c819a-goog