Linux Perf Users
 help / color / mirror / Atom feed
From: Besar Wicaksono <bwicaksono@nvidia.com>
To: <acme@kernel.org>, <namhyung@kernel.org>, <irogers@google.com>,
	<ctshao@google.com>
Cc: <linux-tegra@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-perf-users@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <vsethi@nvidia.com>,
	<rwiley@nvidia.com>, <skelley@nvidia.com>, <mochs@nvidia.com>,
	<rajjoshi@nvidia.com>, <treding@nvidia.com>,
	<jonathanh@nvidia.com>, <john.garry@linux.dev>, <will@kernel.org>,
	<james.clark@linaro.org>, <mike.leach@arm.com>,
	<leo.yan@linux.dev>, <mark.rutland@arm.com>,
	<alexander.shishkin@linux.intel.com>, <jolsa@kernel.org>,
	<adrian.hunter@intel.com>,
	Besar Wicaksono <bwicaksono@nvidia.com>
Subject: [PATCH v2 8/9] perf evsel: Name a merged uncore counter after all of its PMUs
Date: Fri, 30 Oct 2026 17:48:15 +0000	[thread overview]
Message-ID: <20261030174816.1544471-9-bwicaksono@nvidia.com> (raw)
In-Reply-To: <20261030174816.1544471-1-bwicaksono@nvidia.com>

When an uncore event is opened on several instances of a PMU, perf stat
merges the counts of the other instances into the evsel of the first
one. If the event name has to be made unique, for example because the
event matches more than one PMU type, the name of the first instance is
put in front of it, although the count is the sum of all instances.

For example, on a dual-socket NVIDIA Tegra410 with six PCIe root
complexes per socket, the socket 1 rows count nvidia_pcie_pmu_1_rc_<rc>
but are labelled with nvidia_pcie_pmu_0_rc_0:

Before:
  $ perf stat -a --per-socket -e 'nvidia_pcie_*rc*/rd_req/' -- sleep 1
  S268435456    6            235,037      nvidia_pcie_pmu_0_rc_0/nvidia_pcie_*rc*,rd_req/
  S268435456    6            665,297      nvidia_pcie_tgt_pmu_0_rc_0/nvidia_pcie_*rc*,rd_req/
  S285212672    6              3,967      nvidia_pcie_pmu_0_rc_0/nvidia_pcie_*rc*,rd_req/
  S285212672    6                 45      nvidia_pcie_tgt_pmu_0_rc_0/nvidia_pcie_*rc*,rd_req/

Name a merged uncore counter after all of its PMUs instead. If the PMU
names differ only in the suffix, use the name without the suffix, like
nvidia_ucf_pmu. Otherwise, replace each number that differs with '*',
like nvidia_pcie_pmu_*_rc_*.

After:
  $ perf stat -a --per-socket -e 'nvidia_pcie_*rc*/rd_req/' -- sleep 1
  S268435456    6            234,973      nvidia_pcie_pmu_*_rc_*/nvidia_pcie_*rc*,rd_req/
  S268435456    6            666,448      nvidia_pcie_tgt_pmu_*_rc_*/nvidia_pcie_*rc*,rd_req/
  S285212672    6              3,184      nvidia_pcie_pmu_*_rc_*/nvidia_pcie_*rc*,rd_req/
  S285212672    6                 31      nvidia_pcie_tgt_pmu_*_rc_*/nvidia_pcie_*rc*,rd_req/

Output with --no-aggr, where the counters are not merged, is unchanged.

Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com>
---
 tools/perf/tests/pmu.c   | 62 ++++++++++++++++++++++++++++++
 tools/perf/util/evlist.c |  2 +-
 tools/perf/util/evsel.c  | 83 ++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/evsel.h  |  2 +-
 tools/perf/util/pmus.c   | 47 +++++++++++++++++++++++
 tools/perf/util/pmus.h   |  1 +
 6 files changed, 192 insertions(+), 5 deletions(-)

diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index e722cd08e30a..e31fdc25aa4c 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -547,6 +547,67 @@ static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __
 	return TEST_OK;
 }
 
+static int test__name_mask_numbers(struct test_suite *test __maybe_unused,
+				   int subtest __maybe_unused)
+{
+/*
+ * Check that pmu_name_mask_numbers() turns _pattern into expect after
+ * matching _name, or fails if expect is NULL.
+ */
+#define TEST_MASK(msg, _pattern, _name, _expect)				\
+	do {								\
+		const char *expect = _expect;				\
+		char buf[64];						\
+		bool ok;						\
+									\
+		snprintf(buf, sizeof(buf), "%s", _pattern);		\
+		ok = pmu_name_mask_numbers(buf, _name);			\
+		TEST_ASSERT_VAL(msg, expect ? ok && !strcmp(buf, expect) : !ok); \
+	} while (0)
+
+	TEST_MASK("Same name", "nvidia_ucf_pmu_0", "nvidia_ucf_pmu_0",
+		  "nvidia_ucf_pmu_0");
+	TEST_MASK("Differ in suffix number", "nvidia_ucf_pmu_0", "nvidia_ucf_pmu_1",
+		  "nvidia_ucf_pmu_*");
+	TEST_MASK("Differ in suffix number, with a number inside the name",
+		  "nvidia_nvlink_c2c_pmu_0", "nvidia_nvlink_c2c_pmu_1",
+		  "nvidia_nvlink_c2c_pmu_*");
+	TEST_MASK("Differ in both numbers", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_pmu_1_rc_5", "nvidia_pcie_pmu_*_rc_*");
+	TEST_MASK("Differ in last number only", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_pmu_0_rc_5", "nvidia_pcie_pmu_0_rc_*");
+	TEST_MASK("Differ in first number only", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_pmu_1_rc_0", "nvidia_pcie_pmu_*_rc_0");
+	TEST_MASK("Same number length, different number", "nvidia_pcie_pmu_0_rc_10",
+		  "nvidia_pcie_pmu_0_rc_12", "nvidia_pcie_pmu_0_rc_*");
+	TEST_MASK("Longer last number in name", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_pmu_0_rc_12", "nvidia_pcie_pmu_0_rc_*");
+	TEST_MASK("Longer first number in name", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_pmu_10_rc_0", "nvidia_pcie_pmu_*_rc_0");
+	TEST_MASK("Longer first number in pattern", "nvidia_pcie_pmu_10_rc_0",
+		  "nvidia_pcie_pmu_0_rc_0", "nvidia_pcie_pmu_*_rc_0");
+	TEST_MASK("Same longer number after a masked one", "nvidia_pcie_pmu_10_rc_12",
+		  "nvidia_pcie_pmu_0_rc_12", "nvidia_pcie_pmu_*_rc_12");
+	TEST_MASK("Number is a prefix of the other", "nvidia_pcie_pmu_0_rc_1",
+		  "nvidia_pcie_pmu_0_rc_10", "nvidia_pcie_pmu_0_rc_*");
+	TEST_MASK("'*' matches any number", "nvidia_pcie_pmu_*_rc_*",
+		  "nvidia_pcie_pmu_1_rc_10", "nvidia_pcie_pmu_*_rc_*");
+	TEST_MASK("Numbers without _", "hisi_sccl1_ddrc0", "hisi_sccl3_ddrc0",
+		  "hisi_sccl*_ddrc0");
+	TEST_MASK("Hex suffix, differ in digits", "smmuv3_pmcg_fe0004030",
+		  "smmuv3_pmcg_fe0004620", "smmuv3_pmcg_fe*");
+	TEST_MASK("Hex suffix, differ in a letter", "smmuv3_pmcg_fe0004030",
+		  "smmuv3_pmcg_fe00053a0", NULL);
+	TEST_MASK("Differ before the numbers", "nvidia_pcie_pmu_0_rc_0",
+		  "nvidia_pcie_tgt_pmu_0_rc_0", NULL);
+	TEST_MASK("Name ends early", "nvidia_pcie_pmu_0_rc_0", "nvidia_pcie_pmu_0",
+		  NULL);
+	TEST_MASK("Name is longer", "nvidia_pcie_pmu_0", "nvidia_pcie_pmu_0_rc_0",
+		  NULL);
+#undef TEST_MASK
+	return TEST_OK;
+}
+
 /**
  * Test perf_pmu__wildcard_match() that's used to search for a PMU given a name passed
  * on the command line. The name that's passed may also be a filename type glob
@@ -706,6 +767,7 @@ static struct test_case tests__pmu[] = {
 	TEST_CASE("PMU event names", pmu_event_names),
 	TEST_CASE("PMU name combining", name_len),
 	TEST_CASE("PMU name comparison", name_cmp),
+	TEST_CASE("PMU name number masking", name_mask_numbers),
 	TEST_CASE("PMU cmdline match", pmu_match),
 	TEST_CASE("PMU instance match", pmu_instance_match),
 	TEST_CASE("PMU config helpers", pmu_config_helpers),
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index c2402e4791b6..6ece8fb78a2f 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -2977,7 +2977,7 @@ void evlist__uniquify_evsel_names(struct evlist *evlist, const struct perf_stat_
 		struct evsel *pos;
 
 		evlist__for_each_entry(evlist, pos)
-			evsel__uniquify_counter(pos);
+			evsel__uniquify_counter(pos, config->aggr_mode != AGGR_NONE);
 	}
 }
 
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d060fca8090f..7256a315e7df 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -4911,10 +4911,84 @@ bool evsel__set_needs_uniquify(struct evsel *counter, const struct perf_stat_con
 	return false;
 }
 
-void evsel__uniquify_counter(struct evsel *counter)
+/*
+ * Check whether the PMUs merged into an uncore counter differ from its PMU
+ * only in the suffix, like nvidia_ucf_pmu_0 and nvidia_ucf_pmu_1. Set merged
+ * if any PMU is merged.
+ */
+static bool evsel__merged_pmus_suffix_only(struct evsel *counter, bool *merged)
+{
+	struct evsel *alias;
+
+	*merged = false;
+	evlist__for_each_entry(counter->evlist, alias) {
+		if (alias->first_wildcard_match != counter)
+			continue;
+
+		*merged = true;
+		if (!perf_pmu__name_no_suffix_match(alias->pmu, counter->pmu->name))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * The PMU name of an uncore counter with each number that differs in the
+ * PMUs merged into it replaced by '*', like nvidia_pcie_pmu_*_rc_*, or NULL
+ * if the names differ in anything but numbers.
+ */
+static char *evsel__merged_pmus_mask_numbers(struct evsel *counter)
+{
+	struct evsel *alias;
+	char *name;
+
+	name = strdup(counter->pmu->name);
+	if (!name)
+		return NULL;
+
+	evlist__for_each_entry(counter->evlist, alias) {
+		if (alias->first_wildcard_match != counter)
+			continue;
+
+		if (!pmu_name_mask_numbers(name, alias->pmu->name)) {
+			free(name);
+			return NULL;
+		}
+	}
+
+	return name;
+}
+
+/*
+ * The name of the PMUs whose counts are merged into an uncore counter, or
+ * NULL if none are. It is the PMU name without the suffix if the PMUs
+ * differ only in the suffix, like nvidia_ucf_pmu, otherwise the PMU name
+ * with each number that differs replaced by '*', like
+ * nvidia_pcie_pmu_*_rc_*.
+ */
+static char *evsel__merged_pmu_name(struct evsel *counter)
+{
+	const char *pmu_name = counter->pmu->name;
+	bool merged;
+
+	if (counter->pmu->is_core)
+		return NULL;
+
+	if (evsel__merged_pmus_suffix_only(counter, &merged)) {
+		if (!merged)
+			return NULL;
+
+		return strndup(pmu_name, pmu_name_len_no_suffix(pmu_name));
+	}
+
+	return evsel__merged_pmus_mask_numbers(counter);
+}
+
+void evsel__uniquify_counter(struct evsel *counter, bool merged)
 {
 	const char *name, *pmu_name, *config;
-	char *new_name;
+	char *new_name, *merged_pmu_name;
 	int len, ret;
 
 	/* No uniquification necessary. */
@@ -4930,7 +5004,8 @@ void evsel__uniquify_counter(struct evsel *counter)
 
 	name = evsel__name(counter);
 	config = strchr(name, '/');
-	pmu_name = counter->pmu->name;
+	merged_pmu_name = merged ? evsel__merged_pmu_name(counter) : NULL;
+	pmu_name = merged_pmu_name ?: counter->pmu->name;
 
 	/* Already prefixed by the PMU name? */
 	len = pmu_name_len_no_suffix(pmu_name);
@@ -4965,6 +5040,8 @@ void evsel__uniquify_counter(struct evsel *counter)
 			ret = asprintf(&new_name, "%s/%s/", pmu_name, name);
 		}
 	}
+	free(merged_pmu_name);
+
 	if (ret > 0) {
 		free(counter->name);
 		counter->name = new_name;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 53f3641b66e1..0c46b95ab777 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -615,7 +615,7 @@ void evsel__remove_from_group(struct evsel *evsel, struct evsel *leader);
 bool arch_evsel__must_be_in_group(const struct evsel *evsel);
 
 bool evsel__set_needs_uniquify(struct evsel *counter, const struct perf_stat_config *config);
-void evsel__uniquify_counter(struct evsel *counter);
+void evsel__uniquify_counter(struct evsel *counter, bool merged);
 
 /*
  * Macro to swap the bit-field postition and size.
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 1355b317f3ed..f80ba16a5007 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -90,6 +90,53 @@ size_t pmu_name_len_no_suffix(const char *str)
 	return orig_len;
 }
 
+/*
+ * Replace each number in pattern that differs from the number in the same
+ * place in name with '*'. A '*' already in pattern matches any number.
+ * Return false, with pattern partly changed, if the names differ in
+ * anything but numbers.
+ */
+bool pmu_name_mask_numbers(char *pattern, const char *name)
+{
+	const char *p = pattern;
+	/* Masking never lengthens pattern, so write the result in place. */
+	char *out = pattern;
+
+	while (*p || *name) {
+		size_t p_len, name_len;
+
+		/* Unless both names have a number here, the characters must match. */
+		if (!(isdigit(*p) || *p == '*') || !isdigit(*name)) {
+			if (*p != *name)
+				return false;
+
+			*out++ = *p++;
+			name++;
+			continue;
+		}
+
+		/*
+		 * Both names have a number here. A '*' in pattern is a number
+		 * that differed in an earlier name, so it stays '*'.
+		 */
+		p_len = *p == '*' ? 1 : strspn(p, "0123456789");
+		name_len = strspn(name, "0123456789");
+
+		/* Keep the number if it is the same in both names, else mask it. */
+		if (*p != '*' && p_len == name_len && !strncmp(p, name, p_len)) {
+			for (size_t i = 0; i < p_len; i++)
+				*out++ = *p++;
+		} else {
+			*out++ = '*';
+			p += p_len;
+		}
+		name += name_len;
+	}
+
+	*out = '\0';
+	return true;
+}
+
 int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name)
 {
 	unsigned long long lhs_num = 0, rhs_num = 0;
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index 0d55edb3f2fc..535b79b1c750 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -10,6 +10,7 @@ struct perf_pmu;
 struct print_callbacks;
 
 size_t pmu_name_len_no_suffix(const char *str);
+bool pmu_name_mask_numbers(char *pattern, const char *name);
 /* Exposed for testing only. */
 int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name);
 
-- 
2.43.0


  parent reply	other threads:[~2026-10-30 17:51 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 17:48 [PATCH v2 0/9] perf: Add NVIDIA Tegra410 uncore PMU events and metrics Besar Wicaksono
2026-10-30 17:48 ` [PATCH v2 1/9] perf jevents: Add support for NVIDIA Tegra410 UCF PMU aliasing Besar Wicaksono
2026-10-30 18:01   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 2/9] perf jevents: Add support for NVIDIA Tegra410 PCIE " Besar Wicaksono
2026-10-30 17:58   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 3/9] perf jevents: Add support for NVIDIA Tegra410 PCIE-TGT " Besar Wicaksono
2026-10-30 17:56   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 4/9] perf jevents: Add support for NVIDIA Tegra410 CPU Memory Latency " Besar Wicaksono
2026-10-30 17:55   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 5/9] perf jevents: Add support for NVIDIA Tegra410 C2C " Besar Wicaksono
2026-10-30 17:57   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 6/9] perf stat: Check merged PMU instance's cpumasks when skipping zero counts Besar Wicaksono
2026-10-30 17:58   ` sashiko-bot
2026-10-30 17:48 ` [PATCH v2 7/9] perf pmu: Merge PMU instances that match the same comma-separated json Unit Besar Wicaksono
2026-10-30 17:58   ` sashiko-bot
2026-10-30 17:48 ` Besar Wicaksono [this message]
2026-10-30 18:09   ` [PATCH v2 8/9] perf evsel: Name a merged uncore counter after all of its PMUs sashiko-bot
2026-10-30 17:48 ` [PATCH v2 9/9] perf Documentation: Describe PMU names numbered in more than one place Besar Wicaksono
2026-10-30 17:58   ` sashiko-bot
2026-10-30 18:07 ` [PATCH v2 0/9] perf: Add NVIDIA Tegra410 uncore PMU events and metrics Ian Rogers
2026-10-30 22:18   ` Besar Wicaksono

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261030174816.1544471-9-bwicaksono@nvidia.com \
    --to=bwicaksono@nvidia.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=ctshao@google.com \
    --cc=irogers@google.com \
    --cc=james.clark@linaro.org \
    --cc=john.garry@linux.dev \
    --cc=jolsa@kernel.org \
    --cc=jonathanh@nvidia.com \
    --cc=leo.yan@linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mike.leach@arm.com \
    --cc=mochs@nvidia.com \
    --cc=namhyung@kernel.org \
    --cc=rajjoshi@nvidia.com \
    --cc=rwiley@nvidia.com \
    --cc=skelley@nvidia.com \
    --cc=treding@nvidia.com \
    --cc=vsethi@nvidia.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox