[PATCH v4 08/21] perf test attr: Ignore the core PMU type perf puts in 'config'
From: Arnaldo Carvalho de Melo
Date: Fri Oct 16 2026 - 17:22:28 EST
From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Since
commit 55bcf6ef314a ("perf: Extend PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE")
in v5.13, and arm_pmu opted in with
commit 5c816728651a ("arm_pmu: Add PERF_PMU_CAP_EXTENDED_HW_TYPE capability")
in v6.6, the kernel can
be told which core PMU a legacy type event is meant for, with its type in
the top 32 bits of 'config', see
perf_pmus__supports_extended_type(), which __parse_events_add_numeric()
does for HARDWARE and HW_CACHE events, but only when there is more than
one core PMU, i.e. on heterogeneous machines, where asking for one event
ends up being one event per core PMU:
$ perf stat -e cycles -a -- sleep 1
20,520,998 armv8_cortex_a53/cycles/
13,029,350 armv8_cortex_a73/cycles/
and what lands in PERF_TEST_ATTR is the extended encoding:
type=0
config=38654705664 # core PMU type 9, PERF_COUNT_HW_CPU_CYCLES
so the expectations written for the single core PMU case fail with:
expected config=0|1, got 38654705664
expected config=0, got 38654705664 # 'perf stat -d' L1-dcache events
which is what now happens on an Amlogic A311D (2 Cortex-A53 + 4
Cortex-A73), a machine that just got the CPU PMU nodes it was missing.
Take those bits out when comparing the 'config' of legacy type events,
keeping what these expectations mean on machines with a single core PMU:
which of the PMUs is counting is not what we're asking here.
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/tests/shell/lib/attr.py | 53 ++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)
diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 087bfa4532b69c58..fb866fb75175dc95 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -73,6 +73,32 @@ def core_pmu_probe():
return True
+PERF_TYPE_HARDWARE = 0
+PERF_TYPE_HW_CACHE = 3
+
+# The legacy event types, where perf may qualify 'config' with the core PMU
+# type in its top 32 bits when there is more than one core PMU.
+LEGACY_TYPES = (str(PERF_TYPE_HARDWARE), str(PERF_TYPE_HW_CACHE))
+
+def hide_core_pmu_type(value):
+ """
+ The kernel can be told which core PMU a legacy type event is meant for by
+ putting its type in the top 32 bits of 'config', which perf only does when
+ there is more than one core PMU, so take those bits out to keep the
+ expectations written for the single core PMU case matching on
+ heterogeneous machines.
+ """
+ hidden = []
+
+ for alternative in value.split('|'):
+ try:
+ alternative = str(int(alternative, 0) & 0xffffffff)
+ except ValueError:
+ pass
+ hidden.append(alternative)
+
+ return '|'.join(hidden)
+
class Fail(Exception):
def __init__(self, test, msg):
self.msg = msg
@@ -146,12 +172,33 @@ class Event(dict):
self.add(base)
self.add(data)
+ def hide_core_pmu_type_terms(self, other):
+ """
+ Is this a legacy type event? Then the core PMU type that perf may have
+ put in the top 32 bits of 'config' to tell the kernel which core PMU it
+ wants isn't part of what we asked for, see hide_core_pmu_type().
+ """
+ for event in (self, other):
+ for type in event.get('type', '').split('|'):
+ if type in LEGACY_TYPES:
+ return True
+
+ return False
+
+ def hide_core_pmu_type(self, term, value, other):
+ if term == 'config' and self.hide_core_pmu_type_terms(other):
+ return hide_core_pmu_type(value)
+
+ return value
+
def equal(self, other):
for t in Event.terms:
log.debug(" [%s] %s %s", t, self[t], other[t])
if t not in self or t not in other:
return False
- if not data_equal(self[t], other[t]):
+ exp = self.hide_core_pmu_type(t, self[t], other)
+ got = self.hide_core_pmu_type(t, other[t], other)
+ if not data_equal(exp, got):
return False
return True
@@ -164,7 +211,9 @@ class Event(dict):
for t in Event.terms:
if t not in self or t not in other:
continue
- if not data_equal(self[t], other[t]):
+ exp = self.hide_core_pmu_type(t, self[t], other)
+ got = self.hide_core_pmu_type(t, other[t], other)
+ if not data_equal(exp, got):
log.warning("expected %s=%s, got %s", t, self[t], other[t])
def parse_version(version):
--
2.55.0