[PATCH v3 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries

From: Arnaldo Carvalho de Melo

Date: Thu Oct 15 2026 - 20:23:51 EST


From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>

With more than one core PMU the requested events can't cover all the
CPUs, each cluster has its own PMU, so perf opens the software dummy
event to carry the tracking terms (mmap, comm, task, mmap_data), leaving
the requested events without them:

expected mmap=1, got 0
expected comm=1, got 0
expected task=1, got 0

That dummy event is not part of what the expectations are about, so
leave it out of the comparison, unless the expectations ask for one,
like system-wide-dummy and test-record-dummy-C0 do.

And when perf used it to carry the tracking terms, those terms can't be
matched on the requested events, leave them out of the match as well.

The terms are left out only where the dropped dummy actually carried them,
from the sample_type it was opened with, a dummy that didn't is no reason
to stop verifying them.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/tests/shell/lib/attr.py | 61 ++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index da4614068772eaec..c39f2603ec077b2c 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -77,6 +77,21 @@ PERF_TYPE_HARDWARE = 0
PERF_TYPE_HW_CACHE = 3
PERF_SAMPLE_ID = 1 << 6
PERF_SAMPLE_IDENTIFIER = 1 << 16
+PERF_TYPE_SOFTWARE = 1
+PERF_COUNT_SW_DUMMY = 9
+
+def is_dummy_tracking_event(event):
+ """
+ Is this the dummy event perf opens to carry the tracking events (mmap,
+ comm, task) when the requested ones can't, like when there is more than one
+ core PMU and each core PMU's events run on only part of the CPUs?
+ """
+ return (event.get('type') == str(PERF_TYPE_SOFTWARE) and
+ event.get('config') == str(PERF_COUNT_SW_DUMMY))
+
+# With more than one core PMU the tracking terms go to the dummy tracking event,
+# leaving the requested events without them, so they can't be matched there.
+TRACKING_TERMS = ['mmap', 'comm', 'task', 'mmap_data']

# 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.
@@ -259,11 +274,29 @@ class Event(dict):

return self if getattr(self, 'recorded', False) else None

+ def hide_tracking_via_dummy(self, term, other):
+ """
+ The tracking terms are set on the tracking event, which is the dummy
+ event when there is more than one core PMU, leaving the requested
+ events without them, nothing to match on then.
+ """
+ if term not in TRACKING_TERMS:
+ return False
+
+ for event in (self, other):
+ if term in getattr(event, 'tracking_moved_to_dummy', ()):
+ return True
+
+ return False
+
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 self.hide_tracking_via_dummy(t, other):
+ continue
+
recorded = self.recorded_side(other)
expected = other if recorded is self else self
exp = self.hide_core_pmu_type(t, expected[t], recorded or expected)
@@ -283,6 +316,9 @@ class Event(dict):
for t in Event.terms:
if t not in self or t not in other:
continue
+ if self.hide_tracking_via_dummy(t, other):
+ continue
+
recorded = self.recorded_side(other)
expected = other if recorded is self else self
exp = self.hide_core_pmu_type(t, expected[t], recorded or expected)
@@ -498,6 +534,29 @@ class Test(object):
else:
raise Unsup(self)

+ def drop_dummy_tracking_events(self):
+ """
+ Leave out the dummy event perf itself opened, see
+ is_dummy_tracking_event(), unless the expectations ask for one.
+ """
+ if any(is_dummy_tracking_event(e) for e in self.expect.values()):
+ return
+
+ if not any(is_dummy_tracking_event(e) for e in self.result.values()):
+ return
+
+ moved = set()
+ for name, event in list(self.result.items()):
+ if is_dummy_tracking_event(event):
+ log.debug(" dropping dummy tracking event [%s]", name)
+ moved.update(t for t in TRACKING_TERMS if event.get(t) == '1')
+ del self.result[name]
+
+ # The tracking terms the dropped dummies carried, flag the remaining
+ # events so that the match leaves only those out.
+ for event in self.result.values():
+ event.tracking_moved_to_dummy = moved
+
def compare(self, expect, result, strict_groups=True):
match = {}

@@ -579,6 +638,8 @@ class Test(object):
for event in self.result.values():
event.recorded = True

+ self.drop_dummy_tracking_events()
+
# resolve group_fd to event names
self.resolve_groups(self.expect)
self.resolve_groups(self.result)
--
2.55.0