linux-perf-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU
@ 2026-10-13 20:13 Arnaldo Carvalho de Melo
  2026-10-13 20:13 ` [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation Arnaldo Carvalho de Melo
                   ` (18 more replies)
  0 siblings, 19 replies; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo, Michael Petlan

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Hello all,

perf test 82 stopped reporting failures when it got its shell wrapper
(8519e4f44c2af722), so expectations that no longer match perf went
unverified.  The prerequisite fixes are upstream now, so they are not
repeated here.

This series updates those expectations and makes the test cope with
machines with no core PMU, or with more than one.  Asking for one event
on the latter gives one event per core PMU.  The expectations ignore
what that adds; the commit messages say what and why.

The tests that map ring buffers take turns on the per user mlock budget
(kernel.perf_event_mlock_kb) through a robust, process-shared mutex.
The sysctl is left alone.  A test killed while holding the budget does
not leave the next one waiting.

Tested: 'perf test -vv attribute' Ok on an Amlogic A311D (aarch64, as
root), an Intel i7-14700K (hybrid, two core PMUs), x86_64 (AMD Ryzen 9
9950X3D, kernel 7.2.7) and riscv64 (Orange Pi RV).  On riscv64 the stat
tests run and the record tests report unsupp, as sampling is not
available there (no Sscofpmf).

Best regards,

- Arnaldo

What changed from v1:

  - Namhyung's python fix and Michael Petlan's two fixes are upstream
    now, so they are dropped from the series.
  - Namhyung's v1 report (test-record-branch-filter-any_call): probe
    events are no longer dumped, and the core PMU fallout is masked.
  - New, not from review: the 'requires = core-pmu' skip, the skip
    reasons and the mlock budget part.
  - New, not from review: the events of the other core PMUs are left
    out, when the expectations name a core PMU.

 tools/build/Makefile.feature                  |   1 +
 tools/build/feature/Makefile                  |   4 +
 tools/build/feature/test-all.c                |   5 +
 .../build/feature/test-pthread-mutex-robust.c |  19 +
 tools/perf/Documentation/perf-test.txt        |  28 +-
 tools/perf/Makefile.config                    |   4 +
 tools/perf/tests/backward-ring-buffer.c       |   2 +-
 tools/perf/tests/builtin-test.c               | 138 +++++-
 tools/perf/tests/code-reading.c               |   2 +-
 tools/perf/tests/keep-tracking.c              |   2 +-
 tools/perf/tests/mmap-basic.c                 |   1 +
 tools/perf/tests/openat-syscall-tp-fields.c   |   1 +
 tools/perf/tests/perf-record.c                |   1 +
 tools/perf/tests/perf-time-to-tsc.c           |   1 +
 tools/perf/tests/shell/attr/README            |  10 +-
 tools/perf/tests/shell/attr/base-stat-group   |  45 ++
 .../tests/shell/attr/test-record-graph-dwarf  |   2 +-
 .../shell/attr/test-record-group-sampling     |   5 +-
 .../shell/attr/test-record-group-sampling1    |   5 +-
 .../shell/attr/test-record-group-sampling2    |   5 +-
 .../perf/tests/shell/attr/test-record-group1  |   3 +-
 .../perf/tests/shell/attr/test-record-group2  |   3 +-
 .../perf/tests/shell/attr/test-record-group3  |   3 +-
 tools/perf/tests/shell/attr/test-stat-C0      |   1 +
 tools/perf/tests/shell/attr/test-stat-basic   |   1 +
 tools/perf/tests/shell/attr/test-stat-default |  91 ++--
 .../tests/shell/attr/test-stat-detailed-1     |  99 +++--
 .../tests/shell/attr/test-stat-detailed-2     | 111 +++--
 .../tests/shell/attr/test-stat-detailed-3     | 115 +++--
 tools/perf/tests/shell/attr/test-stat-group1  |   3 +-
 .../tests/shell/attr/test-stat-no-inherit     |   1 +
 tools/perf/tests/shell/drm_pmu.sh             |   2 +-
 tools/perf/tests/shell/ftrace.sh              |   2 +-
 tools/perf/tests/shell/kallsyms.sh            |   2 +-
 tools/perf/tests/shell/lib/attr.py            | 415 ++++++++++++++++--
 tools/perf/tests/shell/list.sh                |   2 +-
 tools/perf/tests/shell/perf_sched_stats.sh    |   2 +-
 tools/perf/tests/shell/probe_vfs_getname.sh   |   2 +-
 tools/perf/tests/shell/python-use.sh          |   2 +-
 tools/perf/tests/shell/schedstat_snapshots.sh |   2 +-
 tools/perf/tests/shell/stat+csv_output.sh     |   2 +-
 tools/perf/tests/shell/stat+csv_summary.sh    |   2 +-
 .../tests/shell/stat+event_uniquifying.sh     |   2 +-
 tools/perf/tests/shell/stat+json_output.sh    |   2 +-
 tools/perf/tests/shell/stat+shadow_stat.sh    |   2 +-
 tools/perf/tests/shell/stat+std_output.sh     |   2 +-
 tools/perf/tests/shell/stat.sh                |   2 +-
 .../perf/tests/shell/stat_all_metricgroups.sh |   2 +-
 tools/perf/tests/shell/stat_all_metrics.sh    |   2 +-
 tools/perf/tests/shell/stat_all_pfm.sh        |   2 +-
 .../tests/shell/stat_bpf_counters_cgrp.sh     |   2 +-
 tools/perf/tests/shell/stat_metrics_cgrp.sh   |   2 +-
 tools/perf/tests/shell/stat_metrics_values.sh |   2 +-
 tools/perf/tests/shell/test_ilist_python.sh   |   2 +-
 .../perf/tests/shell/test_stat_cpi_python.sh  |   2 +-
 .../tests/shell/test_test_junit_output.sh     |   2 +-
 .../shell/test_uprobe_from_different_cu.sh    |   2 +-
 tools/perf/tests/sw-clock.c                   |   2 +-
 tools/perf/tests/task-exit.c                  |   1 +
 tools/perf/tests/tests-scripts.c              |  47 +-
 tools/perf/tests/tests.h                      |  17 +
 tools/perf/util/evsel.c                       |  12 +-
 tools/perf/util/evsel.h                       |   1 +
 tools/perf/util/mutex.c                       |  86 +++-
 tools/perf/util/mutex.h                       |   5 +
 tools/perf/util/pfm.c                         |   1 +
 tools/perf/util/print-events.c                |   1 +
 67 files changed, 1092 insertions(+), 258 deletions(-)

base-commit: 3b05843302202598a6d25f3e2543c8182e6a930c
v1-head: b1300831ecc4503d

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
@ 2026-10-13 20:13 ` Arnaldo Carvalho de Melo
  2026-10-13 20:21   ` sashiko-bot
  2026-10-13 20:13 ` [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU Arnaldo Carvalho de Melo
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Since commit bdd051e24914 ("perf record: Split --data-mmap option"),
record_opts__parse_callchain() sets record_data_mmap instead of
record->sample_address for the DWARF unwind mode, so -d/--data and its
implied PERF_SAMPLE_DATA_SRC are not enabled implicitly anymore.

As a result, --call-graph dwarf no longer sets PERF_SAMPLE_ADDR and
PERF_SAMPLE_DATA_SRC, making the test fail:

  expected sample_type=45359, got 12583

where 45359 (0xb12f) is:

  IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC

and 12583 (0x3127) is:

  IP|TID|TIME|CALLCHAIN|PERIOD|REGS_USER|STACK_USER

i.e. the values differ exactly by PERF_SAMPLE_ADDR (0x8) and
PERF_SAMPLE_DATA_SRC (0x8000).  The mmap_data=1 expectation is still
correct as DWARF now enables record_data_mmap.

This went unnoticed until tests/attr.sh stopped ignoring attr.py's
return value.

Fixes: bdd051e249141c79 ("perf record: Split --data-mmap option")
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/shell/attr/test-record-graph-dwarf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/tests/shell/attr/test-record-graph-dwarf b/tools/perf/tests/shell/attr/test-record-graph-dwarf
index ae92061d611ded68..fbe0faa8cdb6a8f4 100644
--- a/tools/perf/tests/shell/attr/test-record-graph-dwarf
+++ b/tools/perf/tests/shell/attr/test-record-graph-dwarf
@@ -4,7 +4,7 @@ args    = --no-bpf-event --call-graph dwarf -- kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
-sample_type=45359
+sample_type=12583
 exclude_callchain_user=1
 sample_stack_user=8192
 # TODO different for each arch, no support for that now
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
  2026-10-13 20:13 ` [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation Arnaldo Carvalho de Melo
@ 2026-10-13 20:13 ` Arnaldo Carvalho de Melo
  2026-10-13 20:28   ` sashiko-bot
  2026-10-13 20:13 ` [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests Arnaldo Carvalho de Melo
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

On machines without a usable core PMU, like an aarch64 box with no DT
node for the ARM PMU, the expectations that pin the legacy hardware
encoding can't be checked: without a core PMU at all 'cycles' doesn't
even resolve, and with one that can't be opened 'perf record' takes the
software fallback and records attributes they can never match:

  expected type=0, got 1
  expected config=0, got 1
  FAILED '.../attr/test-record-group-sampling1' - match failure

And since attr.py stops at the first failure, every expectation that
would come after that one ends up not being run at all.

Add a 'requires' limitation, modelled after the existing 'arch', 'auxv'
and 'kernel_since/until' ones, with 'core-pmu' as its first value, that
is checked by asking perf itself to open 'cycles' via its python
binding: a parse failure or an ENOENT means there is no PMU that can
count it, hence nothing to check and the test gets skipped, while any
other error means we can't tell, so the test keeps running as it does
today, where there is no binding to ask either.

An old distro python package may have a 'perf' module without
parse_events(), i.e. there is nothing to ask with, so that
AttributeError gets the same treatment as the ImportError: let the
expectations speak.

The expectations that have a strict 'type=0', plus the group tests,
where the fallback drops a member and leaves nothing to compare the
group leader attributes with, now state that they require a core PMU.
They are marked here, before the commit that has the group tests ask for
the legacy encodings, so the requisite comes with that dependency.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 .../shell/attr/test-record-group-sampling     |  1 +
 .../shell/attr/test-record-group-sampling1    |  1 +
 .../shell/attr/test-record-group-sampling2    |  1 +
 .../perf/tests/shell/attr/test-record-group1  |  1 +
 .../perf/tests/shell/attr/test-record-group2  |  1 +
 .../perf/tests/shell/attr/test-record-group3  |  1 +
 tools/perf/tests/shell/attr/test-stat-C0      |  1 +
 tools/perf/tests/shell/attr/test-stat-basic   |  1 +
 tools/perf/tests/shell/attr/test-stat-default |  1 +
 .../tests/shell/attr/test-stat-detailed-1     |  1 +
 .../tests/shell/attr/test-stat-detailed-2     |  1 +
 .../tests/shell/attr/test-stat-detailed-3     |  1 +
 tools/perf/tests/shell/attr/test-stat-group1  |  1 +
 .../tests/shell/attr/test-stat-no-inherit     |  1 +
 tools/perf/tests/shell/lib/attr.py            | 44 +++++++++++++++++++
 15 files changed, 58 insertions(+)

diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling b/tools/perf/tests/shell/attr/test-record-group-sampling
index 86a940d7895d7404..b43d9fe3af75c096 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling
@@ -3,6 +3,7 @@ command = record
 args    = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_until = 6.12
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling1 b/tools/perf/tests/shell/attr/test-record-group-sampling1
index 4748ab7bf68437c7..3def72161820dca3 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling1
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling1
@@ -3,6 +3,7 @@ command = record
 args    = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling2 b/tools/perf/tests/shell/attr/test-record-group-sampling2
index e0432244a0eb7f6c..227c18bb221c4b2f 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling2
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling2
@@ -3,6 +3,7 @@ command = record
 args    = --no-bpf-event -c 10000 -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-record-group1 b/tools/perf/tests/shell/attr/test-record-group1
index eeb1db392bc9ceac..a57c7ee8aaa4f9c9 100644
--- a/tools/perf/tests/shell/attr/test-record-group1
+++ b/tools/perf/tests/shell/attr/test-record-group1
@@ -2,6 +2,7 @@
 command = record
 args    = --no-bpf-event -e '{cycles,instructions}' kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-record-group2 b/tools/perf/tests/shell/attr/test-record-group2
index 891d41a7bddf785a..fb6e664eeacc10ef 100644
--- a/tools/perf/tests/shell/attr/test-record-group2
+++ b/tools/perf/tests/shell/attr/test-record-group2
@@ -3,6 +3,7 @@ command = record
 args    = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_until = 6.12
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-record-group3 b/tools/perf/tests/shell/attr/test-record-group3
index 249be884959eaaba..7b90b4411d8f4b90 100644
--- a/tools/perf/tests/shell/attr/test-record-group3
+++ b/tools/perf/tests/shell/attr/test-record-group3
@@ -3,6 +3,7 @@ command = record
 args    = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
+requires = core-pmu
 
 [event-1:base-record]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-stat-C0 b/tools/perf/tests/shell/attr/test-stat-C0
index a2c76d10b2bb2a15..9d242e93e3a92009 100644
--- a/tools/perf/tests/shell/attr/test-stat-C0
+++ b/tools/perf/tests/shell/attr/test-stat-C0
@@ -2,6 +2,7 @@
 command = stat
 args    = -e cycles -C 0 kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 [event:base-stat]
 # events are disabled by default when attached to cpu
diff --git a/tools/perf/tests/shell/attr/test-stat-basic b/tools/perf/tests/shell/attr/test-stat-basic
index 69867d049fda926f..277897bd2626a41f 100644
--- a/tools/perf/tests/shell/attr/test-stat-basic
+++ b/tools/perf/tests/shell/attr/test-stat-basic
@@ -2,6 +2,7 @@
 command = stat
 args    = -e cycles kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 [event:base-stat]
 optional=1
diff --git a/tools/perf/tests/shell/attr/test-stat-default b/tools/perf/tests/shell/attr/test-stat-default
index 8dd27c1fb661fb77..74691188e859a169 100644
--- a/tools/perf/tests/shell/attr/test-stat-default
+++ b/tools/perf/tests/shell/attr/test-stat-default
@@ -2,6 +2,7 @@
 command = stat
 args    = kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
 [event1:base-stat]
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-1 b/tools/perf/tests/shell/attr/test-stat-detailed-1
index 12a2ebf4e64a0077..d9a6236054f2dfb1 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-1
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-1
@@ -2,6 +2,7 @@
 command = stat
 args    = -d kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-2 b/tools/perf/tests/shell/attr/test-stat-detailed-2
index 66ea25b7d38f8d59..582a30289ccfa6df 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-2
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-2
@@ -2,6 +2,7 @@
 command = stat
 args    = -dd kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-3 b/tools/perf/tests/shell/attr/test-stat-detailed-3
index 4a27bbfb9f87a538..2d0fca8b5020f504 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-3
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-3
@@ -2,6 +2,7 @@
 command = stat
 args    = -ddd kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
diff --git a/tools/perf/tests/shell/attr/test-stat-group1 b/tools/perf/tests/shell/attr/test-stat-group1
index 1746751123dcb2c4..3d8b44ba42d6ae19 100644
--- a/tools/perf/tests/shell/attr/test-stat-group1
+++ b/tools/perf/tests/shell/attr/test-stat-group1
@@ -2,6 +2,7 @@
 command = stat
 args    = -e '{cycles,instructions}' kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 [event-1:base-stat]
 fd=1
diff --git a/tools/perf/tests/shell/attr/test-stat-no-inherit b/tools/perf/tests/shell/attr/test-stat-no-inherit
index 924fbb9300d16259..38f746855b1b3e11 100644
--- a/tools/perf/tests/shell/attr/test-stat-no-inherit
+++ b/tools/perf/tests/shell/attr/test-stat-no-inherit
@@ -2,6 +2,7 @@
 command = stat
 args    = -i -e cycles kill >/dev/null 2>&1
 ret     = 1
+requires = core-pmu
 
 [event:base-stat]
 inherit=0
diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 68f06df9565a0811..b55c9b881e59852f 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
 import configparser
+import errno
 import os
 import sys
 import glob
@@ -28,6 +29,34 @@ def data_equal(a, b):
 
     return False
 
+def core_pmu_available():
+    """
+    Is there a core PMU on this machine? Without one hardware events aren't
+    supported and get replaced by the software fallback, so there's no way to
+    check the expectations that pin their legacy encodings.
+    """
+    try:
+        import perf
+        parse_events = perf.parse_events
+    except (ImportError, AttributeError):
+        # Nothing to ask about it, let the expectations speak.
+        return True
+
+    try:
+        evlist = parse_events('cycles')
+    except OSError:
+        # No PMU that can count it, the same as the ENOENT below.
+        return False
+
+    try:
+        evlist.open()
+    except OSError as err:
+        # Anything else is a permission problem or alike, not our business here.
+        return err.errno != errno.ENOENT
+    evlist.close()
+
+    return True
+
 class Fail(Exception):
     def __init__(self, test, msg):
         self.msg = msg
@@ -138,6 +167,9 @@ def parse_version(version):
 #     'arch'    - architecture specific test (optional)
 #                 comma separated list, ! at the beginning
 #                 negates it.
+#     'requires' - Comma separated list of things the test needs, it is
+#                 skipped when any of them isn't there, currently:
+#                   core-pmu - the machine can count hardware events.
 #     'auxv'    - Truthy statement that is evaled in the scope of the auxv map. When false,
 #                 the test is skipped. For example 'auxv["AT_HWCAP"] == 10'. (optional)
 #     'kernel_since' - Inclusive kernel version from which the test will start running. Only the
@@ -173,6 +205,7 @@ class Test(object):
             self.arch  = ''
 
         self.auxv = parser.get('config', 'auxv', fallback=None)
+        self.requires = parser.get('config', 'requires', fallback='')
         self.kernel_since = parse_version(parser.get('config', 'kernel_since', fallback=None))
         self.kernel_until = parse_version(parser.get('config', 'kernel_until', fallback=None))
         self.expect   = {}
@@ -242,6 +275,14 @@ class Test(object):
                 return False
         return True
 
+    def skip_test_requires(self):
+        for requirement in filter(None, map(str.strip, self.requires.split(','))):
+            if requirement == 'core-pmu' and not core_pmu_available():
+                log.warning("test limitation 'requires %s'", requirement)
+                return True
+
+        return False
+
     def restore_sample_rate(self, value=10000):
         try:
             # Check value of sample_rate
@@ -296,6 +337,9 @@ class Test(object):
         if self.skip_test_kernel_until():
             raise Notest(self, "new kernel skip")
 
+        if self.skip_test_requires():
+            raise Notest(self, "requirements skip")
+
         self.restore_sample_rate()
         cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir,
               self.perf, self.command, tempdir, self.args)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
  2026-10-13 20:13 ` [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation Arnaldo Carvalho de Melo
  2026-10-13 20:13 ` [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU Arnaldo Carvalho de Melo
@ 2026-10-13 20:13 ` Arnaldo Carvalho de Melo
  2026-10-13 20:22   ` sashiko-bot
  2026-10-13 20:13 ` [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations Arnaldo Carvalho de Melo
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Since commit b1c5efbfd92e ("perf parse-events: Remove hard coded legacy
hardware and cache parsing"), event names are resolved with sysfs/JSON
priority over the legacy encodings, so bare cache-misses and
instructions now become the core PMU sysfs/JSON events (on x86 type 4,
configs 0x412e and 0xc0) rather than the legacy
PERF_TYPE_HARDWARE/config=3 and config=1 events the tests expect, e.g.:

  expected config=3, got 0
  expected disabled=0, got 1
  expected mmap=0, got 1
  expected comm=0, got 1
  expected freq=0, got 1
  expected enable_on_exec=0, got 1
  expected task=0, got 1
  FAILED '.../test-record-group-sampling1' - match failure

The shown diff is against the cycles group leader, as no result event
matches the expected cache-misses member anymore.

Request the legacy encodings explicitly with the legacy-hardware-config
term, introduced for this purpose, keeping the original attribute
expectations unchanged, in:

  test-record-group1
  test-record-group2
  test-record-group3
  test-record-group-sampling
  test-record-group-sampling1
  test-record-group-sampling2
  test-stat-group1

This went unnoticed until tests/attr.sh stopped ignoring attr.py's
return value.

Fixes: b1c5efbfd92eb84d ("perf parse-events: Remove hard coded legacy hardware and cache parsing")
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/shell/attr/README                     | 10 +++++-----
 tools/perf/tests/shell/attr/test-record-group-sampling |  4 ++--
 .../perf/tests/shell/attr/test-record-group-sampling1  |  4 ++--
 .../perf/tests/shell/attr/test-record-group-sampling2  |  4 ++--
 tools/perf/tests/shell/attr/test-record-group1         |  2 +-
 tools/perf/tests/shell/attr/test-record-group2         |  2 +-
 tools/perf/tests/shell/attr/test-record-group3         |  2 +-
 tools/perf/tests/shell/attr/test-stat-group1           |  2 +-
 8 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/tools/perf/tests/shell/attr/README b/tools/perf/tests/shell/attr/README
index 67c4ca76b85d5611..c7b49cdf87297177 100644
--- a/tools/perf/tests/shell/attr/README
+++ b/tools/perf/tests/shell/attr/README
@@ -49,10 +49,10 @@ Following tests are defined (with perf commands):
   perf record --call-graph dwarf kill		(test-record-graph-dwarf)
   perf record --call-graph fp kill              (test-record-graph-fp)
   perf record --call-graph fp kill              (test-record-graph-fp-aarch64)
-  perf record -e '{cycles,instructions}' kill   (test-record-group1)
-  perf record -e '{cycles/period=1/,instructions/period=2/}:S' kill (test-record-group2)
-  perf record -e '{cycles,cache-misses}:S' kill (test-record-group-sampling1)
-  perf record -c 10000 -e '{cycles,cache-misses}:S' kill (test-record-group-sampling2)
+  perf record -e '{cycles,default_core/legacy-hardware-config=1/}' kill (test-record-group1)
+  perf record -e '{cycles/period=1234000/,default_core/legacy-hardware-config=1,period=6789000/}:S' kill (test-record-group2)
+  perf record -e '{cycles,default_core/legacy-hardware-config=3/}:S' kill (test-record-group-sampling1)
+  perf record -c 10000 -e '{cycles,default_core/legacy-hardware-config=3/}:S' kill (test-record-group-sampling2)
   perf record -D kill                           (test-record-no-delay)
   perf record -i kill                           (test-record-no-inherit)
   perf record -n kill                           (test-record-no-samples)
@@ -67,5 +67,5 @@ Following tests are defined (with perf commands):
   perf stat -d kill                             (test-stat-detailed-1)
   perf stat -dd kill                            (test-stat-detailed-2)
   perf stat -ddd kill                           (test-stat-detailed-3)
-  perf stat -e '{cycles,instructions}' kill     (test-stat-group1)
+  perf stat -e '{cycles,default_core/legacy-hardware-config=1/}' kill (test-stat-group1)
   perf stat -i -e cycles kill                   (test-stat-no-inherit)
diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling b/tools/perf/tests/shell/attr/test-record-group-sampling
index b43d9fe3af75c096..ed47dd1e5984535b 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles,default_core/legacy-hardware-config=3/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_until = 6.12
 requires = core-pmu
@@ -16,7 +16,7 @@ inherit=0
 fd=2
 group_fd=1
 
-# cache-misses
+# cache-misses, explicitly requested with the legacy hardware encoding
 type=0
 config=3
 
diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling1 b/tools/perf/tests/shell/attr/test-record-group-sampling1
index 3def72161820dca3..9cf013dc03dd0309 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling1
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling1
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles,default_core/legacy-hardware-config=3/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
 requires = core-pmu
@@ -31,7 +31,7 @@ inherit=1
 fd=2
 group_fd=1
 
-# cache-misses
+# cache-misses, explicitly requested with the legacy hardware encoding
 type=0
 config=3
 
diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling2 b/tools/perf/tests/shell/attr/test-record-group-sampling2
index 227c18bb221c4b2f..bd1eb4161e4aee46 100644
--- a/tools/perf/tests/shell/attr/test-record-group-sampling2
+++ b/tools/perf/tests/shell/attr/test-record-group-sampling2
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -c 10000 -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -c 10000 -e '{cycles,default_core/legacy-hardware-config=3/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
 requires = core-pmu
@@ -37,7 +37,7 @@ write_backward=0
 fd=2
 group_fd=1
 
-# cache-misses
+# cache-misses, explicitly requested with the legacy hardware encoding
 type=0
 config=3
 
diff --git a/tools/perf/tests/shell/attr/test-record-group1 b/tools/perf/tests/shell/attr/test-record-group1
index a57c7ee8aaa4f9c9..5fdd5288555f32ea 100644
--- a/tools/perf/tests/shell/attr/test-record-group1
+++ b/tools/perf/tests/shell/attr/test-record-group1
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -e '{cycles,instructions}' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles,default_core/legacy-hardware-config=1/}' kill >/dev/null 2>&1
 ret     = 1
 requires = core-pmu
 
diff --git a/tools/perf/tests/shell/attr/test-record-group2 b/tools/perf/tests/shell/attr/test-record-group2
index fb6e664eeacc10ef..e7274108f5fd7e7b 100644
--- a/tools/perf/tests/shell/attr/test-record-group2
+++ b/tools/perf/tests/shell/attr/test-record-group2
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles/period=1234000/,default_core/legacy-hardware-config=1,period=6789000/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_until = 6.12
 requires = core-pmu
diff --git a/tools/perf/tests/shell/attr/test-record-group3 b/tools/perf/tests/shell/attr/test-record-group3
index 7b90b4411d8f4b90..e74fd3a48ac9094f 100644
--- a/tools/perf/tests/shell/attr/test-record-group3
+++ b/tools/perf/tests/shell/attr/test-record-group3
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles/period=1234000/,default_core/legacy-hardware-config=1,period=6789000/}:S' kill >/dev/null 2>&1
 ret     = 1
 kernel_since = 6.12
 requires = core-pmu
diff --git a/tools/perf/tests/shell/attr/test-stat-group1 b/tools/perf/tests/shell/attr/test-stat-group1
index 3d8b44ba42d6ae19..7a31b27933f11959 100644
--- a/tools/perf/tests/shell/attr/test-stat-group1
+++ b/tools/perf/tests/shell/attr/test-stat-group1
@@ -1,6 +1,6 @@
 [config]
 command = stat
-args    = -e '{cycles,instructions}' kill >/dev/null 2>&1
+args    = -e '{cycles,default_core/legacy-hardware-config=1/}' kill >/dev/null 2>&1
 ret     = 1
 requires = core-pmu
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (2 preceding siblings ...)
  2026-10-13 20:13 ` [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests Arnaldo Carvalho de Melo
@ 2026-10-13 20:13 ` Arnaldo Carvalho de Melo
  2026-10-13 20:37   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations Arnaldo Carvalho de Melo
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Since commit c7adeb0974f1 ("perf jevents: Add set of common metrics based
on default ones") and commit 2dfc0cab3da4 ("perf stat: Add detail -d,-dd,-ddd
metrics") the perf stat default metrics open their events in groups, so
group leaders and members have read_format=15 and members are enabled
without enable_on_exec, while standalone events keep read_format=3 and
are disabled until exec.

Also, since commit b1c5efbfd92e ("perf parse-events: Remove hard coded
legacy hardware and cache parsing") bare hardware events resolve to the
core PMU sysfs/JSON events (type 4), so add the raw encodings seen on
Intel and AMD for instructions, branch-misses, cpu-cycles and
stalled-cycles-frontend/backend to test-stat-default and
test-stat-detailed-1/2/3.

Add base-stat-group, that accepts both the standalone and the group
variants of read_format, disabled and enable_on_exec, and point the four
default/detailed expectations at it.  test-stat-basic, test-stat-C0 and
test-stat-no-inherit keep base-stat, whose events aren't opened in a
group: for those, a change that groups or enables them differently is
still something to catch.  test-stat-group1 does open its events in a
group, but pins read_format, disabled and enable_on_exec itself, the very
terms base-stat-group loosens, so it doesn't need it either.

Fixes: b1c5efbfd92eb84d ("perf parse-events: Remove hard coded legacy hardware and cache parsing")
Fixes: c7adeb0974f18da4 ("perf jevents: Add set of common metrics based on default ones")
Fixes: 2dfc0cab3da4a205 ("perf stat: Add detail -d,-dd,-ddd metrics")
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/shell/attr/base-stat-group   |  45 +++++++
 tools/perf/tests/shell/attr/test-stat-default |  90 +++++++++-----
 .../tests/shell/attr/test-stat-detailed-1     |  98 +++++++++------
 .../tests/shell/attr/test-stat-detailed-2     | 110 ++++++++++-------
 .../tests/shell/attr/test-stat-detailed-3     | 114 +++++++++++-------
 5 files changed, 307 insertions(+), 150 deletions(-)
 create mode 100644 tools/perf/tests/shell/attr/base-stat-group

diff --git a/tools/perf/tests/shell/attr/base-stat-group b/tools/perf/tests/shell/attr/base-stat-group
new file mode 100644
index 0000000000000000..9bcb8ab3687ff9fb
--- /dev/null
+++ b/tools/perf/tests/shell/attr/base-stat-group
@@ -0,0 +1,45 @@
+[event]
+fd=1
+group_fd=-1
+# 0 or PERF_FLAG_FD_CLOEXEC flag
+flags=0|8
+cpu=*
+type=0
+size=176
+config=0
+sample_period=0
+sample_type=65536
+# The default and -d/-dd/-ddd metrics open part of their events in groups:
+# the members get read_format=15 and are enabled right away, the standalone
+# ones keep read_format=3 and wait for the exec, and the leaders get 15 when
+# their group has members, 3 when alone, as create_perf_stat_counter() goes.
+read_format=3|15
+disabled=0|1
+inherit=1
+pinned=0
+exclusive=0
+exclude_user=0
+exclude_kernel=0|1
+exclude_hv=0|1
+exclude_idle=0
+mmap=0
+comm=0
+freq=0
+inherit_stat=0
+enable_on_exec=0|1
+task=0
+watermark=0
+precise_ip=0
+mmap_data=0
+sample_id_all=0
+exclude_host=0|1
+exclude_guest=0|1
+exclude_callchain_kernel=0
+exclude_callchain_user=0
+wakeup_events=0
+bp_type=0
+config1=0
+config2=0
+branch_sample_type=0
+sample_regs_user=0
+sample_stack_user=0
diff --git a/tools/perf/tests/shell/attr/test-stat-default b/tools/perf/tests/shell/attr/test-stat-default
index 74691188e859a169..addeb2ce7f1412b9 100644
--- a/tools/perf/tests/shell/attr/test-stat-default
+++ b/tools/perf/tests/shell/attr/test-stat-default
@@ -5,72 +5,72 @@ ret     = 1
 requires = core-pmu
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
-[event1:base-stat]
+[event1:base-stat-group]
 fd=1
 type=1
 config=1
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
-[event2:base-stat]
+[event2:base-stat-group]
 fd=2
 type=1
 config=3
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
-[event3:base-stat]
+[event3:base-stat-group]
 fd=3
 type=1
 config=4
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
-[event4:base-stat]
+[event4:base-stat-group]
 fd=4
 type=1
 config=2
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
-[event5:base-stat]
+[event5:base-stat-group]
 fd=5
 type=0
 config=0
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
-[event6:base-stat]
+[event6:base-stat-group]
 fd=6
 type=0
 config=7
 optional=1
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
-[event7:base-stat]
+[event7:base-stat-group]
 fd=7
 type=0
 config=8
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
-[event8:base-stat]
+[event8:base-stat-group]
 fd=8
 type=0
 config=1
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
-[event9:base-stat]
+[event9:base-stat-group]
 fd=9
 type=0
 config=4
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
-[event10:base-stat]
+[event10:base-stat-group]
 fd=10
 type=0
 config=5
 optional=1
 
 # PERF_TYPE_RAW / slots (0x400)
-[event11:base-stat]
+[event11:base-stat-group]
 fd=11
 group_fd=-1
 type=4
@@ -79,7 +79,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-retiring (0x8000)
-[event12:base-stat]
+[event12:base-stat-group]
 fd=12
 group_fd=11
 type=4
@@ -90,7 +90,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-bad-spec (0x8100)
-[event13:base-stat]
+[event13:base-stat-group]
 fd=13
 group_fd=11
 type=4
@@ -101,7 +101,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fe-bound (0x8200)
-[event14:base-stat]
+[event14:base-stat-group]
 fd=14
 group_fd=11
 type=4
@@ -112,7 +112,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-be-bound (0x8300)
-[event15:base-stat]
+[event15:base-stat-group]
 fd=15
 group_fd=11
 type=4
@@ -123,7 +123,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-heavy-ops (0x8400)
-[event16:base-stat]
+[event16:base-stat-group]
 fd=16
 group_fd=11
 type=4
@@ -134,7 +134,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-br-mispredict (0x8500)
-[event17:base-stat]
+[event17:base-stat-group]
 fd=17
 group_fd=11
 type=4
@@ -145,7 +145,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fetch-lat (0x8600)
-[event18:base-stat]
+[event18:base-stat-group]
 fd=18
 group_fd=11
 type=4
@@ -156,7 +156,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-mem-bound (0x8700)
-[event19:base-stat]
+[event19:base-stat-group]
 fd=19
 group_fd=11
 type=4
@@ -167,71 +167,99 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event20:base-stat]
+[event20:base-stat-group]
 fd=20
 type=4
 config=4109
 optional=1
 
 # PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/
-[event21:base-stat]
+[event21:base-stat-group]
 fd=21
 type=4
 config=17039629
 optional=1
 
-# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD
-[event22:base-stat]
+# PERF_TYPE_RAW / cpu-cycles (core PMU sysfs event; Intel 0x3c, AMD 0x76)
+[event22:base-stat-group]
 fd=22
 type=4
-config=60
+config=60|118
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY
-[event23:base-stat]
+[event23:base-stat-group]
 fd=23
 type=4
 config=2097421
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK
-[event24:base-stat]
+[event24:base-stat-group]
 fd=24
 type=4
 config=316
 optional=1
 
 # PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE
-[event25:base-stat]
+[event25:base-stat-group]
 fd=25
 type=4
 config=412
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE
-[event26:base-stat]
+[event26:base-stat-group]
 fd=26
 type=4
 config=572
 optional=1
 
 # PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS
-[event27:base-stat]
+[event27:base-stat-group]
 fd=27
 type=4
 config=706
 optional=1
 
 # PERF_TYPE_RAW / UOPS_ISSUED.ANY
-[event28:base-stat]
+[event28:base-stat-group]
 fd=28
 type=4
 config=270
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event29:base-stat]
+[event29:base-stat-group]
 fd=29
 type=4
 config=4269
+optional=1
+
+# PERF_TYPE_RAW / instructions (core PMU sysfs event)
+[event30:base-stat-group]
+fd=30
+type=4
+config=192
+optional=1
+
+# PERF_TYPE_RAW / branch-misses (core PMU sysfs event; Intel 0xc5, AMD 0xc3)
+[event31:base-stat-group]
+fd=31
+type=4
+config=197|195
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-frontend (core PMU sysfs event; AMD 0xd0/0x287/0xa9)
+[event32:base-stat-group]
+fd=32
+type=4
+config=208|647|169
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-backend (core PMU sysfs event; AMD 0xd1/0x187)
+[event33:base-stat-group]
+fd=33
+type=4
+config=209|391
 optional=1
\ No newline at end of file
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-1 b/tools/perf/tests/shell/attr/test-stat-detailed-1
index d9a6236054f2dfb1..d5a155b4de4c4e6b 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-1
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-1
@@ -6,73 +6,73 @@ requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
-[event1:base-stat]
+[event1:base-stat-group]
 fd=1
 type=1
 config=1
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
-[event2:base-stat]
+[event2:base-stat-group]
 fd=2
 type=1
 config=3
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
-[event3:base-stat]
+[event3:base-stat-group]
 fd=3
 type=1
 config=4
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
-[event4:base-stat]
+[event4:base-stat-group]
 fd=4
 type=1
 config=2
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
-[event5:base-stat]
+[event5:base-stat-group]
 fd=5
 type=0
 config=0
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
-[event6:base-stat]
+[event6:base-stat-group]
 fd=6
 type=0
 config=7
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
-[event7:base-stat]
+[event7:base-stat-group]
 fd=7
 type=0
 config=8
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
-[event8:base-stat]
+[event8:base-stat-group]
 fd=8
 type=0
 config=1
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
-[event9:base-stat]
+[event9:base-stat-group]
 fd=9
 type=0
 config=4
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
-[event10:base-stat]
+[event10:base-stat-group]
 fd=10
 type=0
 config=5
 optional=1
 
 # PERF_TYPE_RAW / slots (0x400)
-[event11:base-stat]
+[event11:base-stat-group]
 fd=11
 group_fd=-1
 type=4
@@ -81,7 +81,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-retiring (0x8000)
-[event12:base-stat]
+[event12:base-stat-group]
 fd=12
 group_fd=11
 type=4
@@ -92,7 +92,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-bad-spec (0x8100)
-[event13:base-stat]
+[event13:base-stat-group]
 fd=13
 group_fd=11
 type=4
@@ -103,7 +103,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fe-bound (0x8200)
-[event14:base-stat]
+[event14:base-stat-group]
 fd=14
 group_fd=11
 type=4
@@ -114,7 +114,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-be-bound (0x8300)
-[event15:base-stat]
+[event15:base-stat-group]
 fd=15
 group_fd=11
 type=4
@@ -125,7 +125,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-heavy-ops (0x8400)
-[event16:base-stat]
+[event16:base-stat-group]
 fd=16
 group_fd=11
 type=4
@@ -136,7 +136,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-br-mispredict (0x8500)
-[event17:base-stat]
+[event17:base-stat-group]
 fd=17
 group_fd=11
 type=4
@@ -147,7 +147,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fetch-lat (0x8600)
-[event18:base-stat]
+[event18:base-stat-group]
 fd=18
 group_fd=11
 type=4
@@ -158,7 +158,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-mem-bound (0x8700)
-[event19:base-stat]
+[event19:base-stat-group]
 fd=19
 group_fd=11
 type=4
@@ -169,63 +169,63 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event20:base-stat]
+[event20:base-stat-group]
 fd=20
 type=4
 config=4109
 optional=1
 
 # PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/
-[event21:base-stat]
+[event21:base-stat-group]
 fd=21
 type=4
 config=17039629
 optional=1
 
-# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD
-[event22:base-stat]
+# PERF_TYPE_RAW / cpu-cycles (core PMU sysfs event; Intel 0x3c, AMD 0x76)
+[event22:base-stat-group]
 fd=22
 type=4
-config=60
+config=60|118
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY
-[event23:base-stat]
+[event23:base-stat-group]
 fd=23
 type=4
 config=2097421
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK
-[event24:base-stat]
+[event24:base-stat-group]
 fd=24
 type=4
 config=316
 optional=1
 
 # PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE
-[event25:base-stat]
+[event25:base-stat-group]
 fd=25
 type=4
 config=412
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE
-[event26:base-stat]
+[event26:base-stat-group]
 fd=26
 type=4
 config=572
 optional=1
 
 # PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS
-[event27:base-stat]
+[event27:base-stat-group]
 fd=27
 type=4
 config=706
 optional=1
 
 # PERF_TYPE_RAW / UOPS_ISSUED.ANY
-[event28:base-stat]
+[event28:base-stat-group]
 fd=28
 type=4
 config=270
@@ -235,7 +235,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event29:base-stat]
+[event29:base-stat-group]
 fd=29
 type=3
 config=0
@@ -245,7 +245,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event30:base-stat]
+[event30:base-stat-group]
 fd=30
 type=3
 config=65536
@@ -255,7 +255,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event31:base-stat]
+[event31:base-stat-group]
 fd=31
 type=3
 config=2
@@ -265,15 +265,43 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event32:base-stat]
+[event32:base-stat-group]
 fd=32
 type=3
 config=65538
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event33:base-stat]
+[event33:base-stat-group]
 fd=33
 type=4
 config=4269
+optional=1
+
+# PERF_TYPE_RAW / instructions (core PMU sysfs event)
+[event34:base-stat-group]
+fd=34
+type=4
+config=192
+optional=1
+
+# PERF_TYPE_RAW / branch-misses (core PMU sysfs event; Intel 0xc5, AMD 0xc3)
+[event35:base-stat-group]
+fd=35
+type=4
+config=197|195
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-frontend (core PMU sysfs event; AMD 0xd0/0x287/0xa9)
+[event36:base-stat-group]
+fd=36
+type=4
+config=208|647|169
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-backend (core PMU sysfs event; AMD 0xd1/0x187)
+[event37:base-stat-group]
+fd=37
+type=4
+config=209|391
 optional=1
\ No newline at end of file
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-2 b/tools/perf/tests/shell/attr/test-stat-detailed-2
index 582a30289ccfa6df..740269dab5884a11 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-2
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-2
@@ -6,73 +6,73 @@ requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
-[event1:base-stat]
+[event1:base-stat-group]
 fd=1
 type=1
 config=1
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
-[event2:base-stat]
+[event2:base-stat-group]
 fd=2
 type=1
 config=3
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
-[event3:base-stat]
+[event3:base-stat-group]
 fd=3
 type=1
 config=4
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
-[event4:base-stat]
+[event4:base-stat-group]
 fd=4
 type=1
 config=2
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
-[event5:base-stat]
+[event5:base-stat-group]
 fd=5
 type=0
 config=0
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
-[event6:base-stat]
+[event6:base-stat-group]
 fd=6
 type=0
 config=7
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
-[event7:base-stat]
+[event7:base-stat-group]
 fd=7
 type=0
 config=8
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
-[event8:base-stat]
+[event8:base-stat-group]
 fd=8
 type=0
 config=1
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
-[event9:base-stat]
+[event9:base-stat-group]
 fd=9
 type=0
 config=4
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
-[event10:base-stat]
+[event10:base-stat-group]
 fd=10
 type=0
 config=5
 optional=1
 
 # PERF_TYPE_RAW / slots (0x400)
-[event11:base-stat]
+[event11:base-stat-group]
 fd=11
 group_fd=-1
 type=4
@@ -81,7 +81,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-retiring (0x8000)
-[event12:base-stat]
+[event12:base-stat-group]
 fd=12
 group_fd=11
 type=4
@@ -92,7 +92,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-bad-spec (0x8100)
-[event13:base-stat]
+[event13:base-stat-group]
 fd=13
 group_fd=11
 type=4
@@ -103,7 +103,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fe-bound (0x8200)
-[event14:base-stat]
+[event14:base-stat-group]
 fd=14
 group_fd=11
 type=4
@@ -114,7 +114,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-be-bound (0x8300)
-[event15:base-stat]
+[event15:base-stat-group]
 fd=15
 group_fd=11
 type=4
@@ -125,7 +125,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-heavy-ops (0x8400)
-[event16:base-stat]
+[event16:base-stat-group]
 fd=16
 group_fd=11
 type=4
@@ -136,7 +136,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-br-mispredict (0x8500)
-[event17:base-stat]
+[event17:base-stat-group]
 fd=17
 group_fd=11
 type=4
@@ -147,7 +147,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fetch-lat (0x8600)
-[event18:base-stat]
+[event18:base-stat-group]
 fd=18
 group_fd=11
 type=4
@@ -158,7 +158,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-mem-bound (0x8700)
-[event19:base-stat]
+[event19:base-stat-group]
 fd=19
 group_fd=11
 type=4
@@ -169,63 +169,63 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event20:base-stat]
+[event20:base-stat-group]
 fd=20
 type=4
 config=4109
 optional=1
 
 # PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/
-[event21:base-stat]
+[event21:base-stat-group]
 fd=21
 type=4
 config=17039629
 optional=1
 
-# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD
-[event22:base-stat]
+# PERF_TYPE_RAW / cpu-cycles (core PMU sysfs event; Intel 0x3c, AMD 0x76)
+[event22:base-stat-group]
 fd=22
 type=4
-config=60
+config=60|118
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY
-[event23:base-stat]
+[event23:base-stat-group]
 fd=23
 type=4
 config=2097421
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK
-[event24:base-stat]
+[event24:base-stat-group]
 fd=24
 type=4
 config=316
 optional=1
 
 # PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE
-[event25:base-stat]
+[event25:base-stat-group]
 fd=25
 type=4
 config=412
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE
-[event26:base-stat]
+[event26:base-stat-group]
 fd=26
 type=4
 config=572
 optional=1
 
 # PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS
-[event27:base-stat]
+[event27:base-stat-group]
 fd=27
 type=4
 config=706
 optional=1
 
 # PERF_TYPE_RAW / UOPS_ISSUED.ANY
-[event28:base-stat]
+[event28:base-stat-group]
 fd=28
 type=4
 config=270
@@ -235,7 +235,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event29:base-stat]
+[event29:base-stat-group]
 fd=29
 type=3
 config=0
@@ -245,7 +245,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event30:base-stat]
+[event30:base-stat-group]
 fd=30
 type=3
 config=65536
@@ -255,7 +255,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event31:base-stat]
+[event31:base-stat-group]
 fd=31
 type=3
 config=2
@@ -265,7 +265,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event32:base-stat]
+[event32:base-stat-group]
 fd=32
 type=3
 config=65538
@@ -275,7 +275,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1I                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event33:base-stat]
+[event33:base-stat-group]
 fd=33
 type=3
 config=1
@@ -285,7 +285,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1I                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event34:base-stat]
+[event34:base-stat-group]
 fd=34
 type=3
 config=65537
@@ -295,7 +295,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_DTLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event35:base-stat]
+[event35:base-stat-group]
 fd=35
 type=3
 config=3
@@ -305,7 +305,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_DTLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event36:base-stat]
+[event36:base-stat-group]
 fd=36
 type=3
 config=65539
@@ -315,7 +315,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_ITLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event37:base-stat]
+[event37:base-stat-group]
 fd=37
 type=3
 config=4
@@ -325,15 +325,43 @@ optional=1
 #  PERF_COUNT_HW_CACHE_ITLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event38:base-stat]
+[event38:base-stat-group]
 fd=38
 type=3
 config=65540
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event39:base-stat]
+[event39:base-stat-group]
 fd=39
 type=4
 config=4269
+optional=1
+
+# PERF_TYPE_RAW / instructions (core PMU sysfs event)
+[event40:base-stat-group]
+fd=40
+type=4
+config=192
+optional=1
+
+# PERF_TYPE_RAW / branch-misses (core PMU sysfs event; Intel 0xc5, AMD 0xc3)
+[event41:base-stat-group]
+fd=41
+type=4
+config=197|195
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-frontend (core PMU sysfs event; AMD 0xd0/0x287/0xa9)
+[event42:base-stat-group]
+fd=42
+type=4
+config=208|647|169
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-backend (core PMU sysfs event; AMD 0xd1/0x187)
+[event43:base-stat-group]
+fd=43
+type=4
+config=209|391
 optional=1
\ No newline at end of file
diff --git a/tools/perf/tests/shell/attr/test-stat-detailed-3 b/tools/perf/tests/shell/attr/test-stat-detailed-3
index 2d0fca8b5020f504..fb061b97e93f5b21 100644
--- a/tools/perf/tests/shell/attr/test-stat-detailed-3
+++ b/tools/perf/tests/shell/attr/test-stat-detailed-3
@@ -6,73 +6,73 @@ requires = core-pmu
 
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
-[event1:base-stat]
+[event1:base-stat-group]
 fd=1
 type=1
 config=1
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
-[event2:base-stat]
+[event2:base-stat-group]
 fd=2
 type=1
 config=3
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
-[event3:base-stat]
+[event3:base-stat-group]
 fd=3
 type=1
 config=4
 
 # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
-[event4:base-stat]
+[event4:base-stat-group]
 fd=4
 type=1
 config=2
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
-[event5:base-stat]
+[event5:base-stat-group]
 fd=5
 type=0
 config=0
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
-[event6:base-stat]
+[event6:base-stat-group]
 fd=6
 type=0
 config=7
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
-[event7:base-stat]
+[event7:base-stat-group]
 fd=7
 type=0
 config=8
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
-[event8:base-stat]
+[event8:base-stat-group]
 fd=8
 type=0
 config=1
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
-[event9:base-stat]
+[event9:base-stat-group]
 fd=9
 type=0
 config=4
 optional=1
 
 # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
-[event10:base-stat]
+[event10:base-stat-group]
 fd=10
 type=0
 config=5
 optional=1
 
 # PERF_TYPE_RAW / slots (0x400)
-[event11:base-stat]
+[event11:base-stat-group]
 fd=11
 group_fd=-1
 type=4
@@ -81,7 +81,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-retiring (0x8000)
-[event12:base-stat]
+[event12:base-stat-group]
 fd=12
 group_fd=11
 type=4
@@ -92,7 +92,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-bad-spec (0x8100)
-[event13:base-stat]
+[event13:base-stat-group]
 fd=13
 group_fd=11
 type=4
@@ -103,7 +103,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fe-bound (0x8200)
-[event14:base-stat]
+[event14:base-stat-group]
 fd=14
 group_fd=11
 type=4
@@ -114,7 +114,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-be-bound (0x8300)
-[event15:base-stat]
+[event15:base-stat-group]
 fd=15
 group_fd=11
 type=4
@@ -125,7 +125,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-heavy-ops (0x8400)
-[event16:base-stat]
+[event16:base-stat-group]
 fd=16
 group_fd=11
 type=4
@@ -136,7 +136,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-br-mispredict (0x8500)
-[event17:base-stat]
+[event17:base-stat-group]
 fd=17
 group_fd=11
 type=4
@@ -147,7 +147,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-fetch-lat (0x8600)
-[event18:base-stat]
+[event18:base-stat-group]
 fd=18
 group_fd=11
 type=4
@@ -158,7 +158,7 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / topdown-mem-bound (0x8700)
-[event19:base-stat]
+[event19:base-stat-group]
 fd=19
 group_fd=11
 type=4
@@ -169,63 +169,63 @@ read_format=15
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event20:base-stat]
+[event20:base-stat-group]
 fd=20
 type=4
 config=4109
 optional=1
 
 # PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/
-[event21:base-stat]
+[event21:base-stat-group]
 fd=21
 type=4
 config=17039629
 optional=1
 
-# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD
-[event22:base-stat]
+# PERF_TYPE_RAW / cpu-cycles (core PMU sysfs event; Intel 0x3c, AMD 0x76)
+[event22:base-stat-group]
 fd=22
 type=4
-config=60
+config=60|118
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY
-[event23:base-stat]
+[event23:base-stat-group]
 fd=23
 type=4
 config=2097421
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK
-[event24:base-stat]
+[event24:base-stat-group]
 fd=24
 type=4
 config=316
 optional=1
 
 # PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE
-[event25:base-stat]
+[event25:base-stat-group]
 fd=25
 type=4
 config=412
 optional=1
 
 # PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE
-[event26:base-stat]
+[event26:base-stat-group]
 fd=26
 type=4
 config=572
 optional=1
 
 # PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS
-[event27:base-stat]
+[event27:base-stat-group]
 fd=27
 type=4
 config=706
 optional=1
 
 # PERF_TYPE_RAW / UOPS_ISSUED.ANY
-[event28:base-stat]
+[event28:base-stat-group]
 fd=28
 type=4
 config=270
@@ -235,7 +235,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event29:base-stat]
+[event29:base-stat-group]
 fd=29
 type=3
 config=0
@@ -245,7 +245,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event30:base-stat]
+[event30:base-stat-group]
 fd=30
 type=3
 config=65536
@@ -255,7 +255,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event31:base-stat]
+[event31:base-stat-group]
 fd=31
 type=3
 config=2
@@ -265,7 +265,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_LL                 <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event32:base-stat]
+[event32:base-stat-group]
 fd=32
 type=3
 config=65538
@@ -275,7 +275,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1I                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event33:base-stat]
+[event33:base-stat-group]
 fd=33
 type=3
 config=1
@@ -285,7 +285,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1I                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event34:base-stat]
+[event34:base-stat-group]
 fd=34
 type=3
 config=65537
@@ -295,7 +295,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_DTLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event35:base-stat]
+[event35:base-stat-group]
 fd=35
 type=3
 config=3
@@ -305,7 +305,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_DTLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event36:base-stat]
+[event36:base-stat-group]
 fd=36
 type=3
 config=65539
@@ -315,7 +315,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_ITLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event37:base-stat]
+[event37:base-stat-group]
 fd=37
 type=3
 config=4
@@ -325,7 +325,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_ITLB               <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_READ            <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event38:base-stat]
+[event38:base-stat-group]
 fd=38
 type=3
 config=65540
@@ -335,7 +335,7 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_PREFETCH        <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_ACCESS      << 16)
-[event39:base-stat]
+[event39:base-stat-group]
 fd=39
 type=3
 config=512
@@ -345,15 +345,43 @@ optional=1
 #  PERF_COUNT_HW_CACHE_L1D                <<  0  |
 # (PERF_COUNT_HW_CACHE_OP_PREFETCH        <<  8) |
 # (PERF_COUNT_HW_CACHE_RESULT_MISS        << 16)
-[event40:base-stat]
+[event40:base-stat-group]
 fd=40
 type=3
 config=66048
 optional=1
 
 # PERF_TYPE_RAW / INT_MISC.UOP_DROPPING
-[event41:base-stat]
+[event41:base-stat-group]
 fd=41
 type=4
 config=4269
+optional=1
+
+# PERF_TYPE_RAW / instructions (core PMU sysfs event)
+[event42:base-stat-group]
+fd=42
+type=4
+config=192
+optional=1
+
+# PERF_TYPE_RAW / branch-misses (core PMU sysfs event; Intel 0xc5, AMD 0xc3)
+[event43:base-stat-group]
+fd=43
+type=4
+config=197|195
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-frontend (core PMU sysfs event; AMD 0xd0/0x287/0xa9)
+[event44:base-stat-group]
+fd=44
+type=4
+config=208|647|169
+optional=1
+
+# PERF_TYPE_RAW / stalled-cycles-backend (core PMU sysfs event; AMD 0xd1/0x187)
+[event45:base-stat-group]
+fd=45
+type=4
+config=209|391
 optional=1
\ No newline at end of file
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (3 preceding siblings ...)
  2026-10-13 20:13 ` [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:18   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open Arnaldo Carvalho de Melo
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

When matching the recorded events back against the expectations,
attr.py requires every event opened in a group to match an expectation
that declares the same group.  The perf stat default metrics open the
events of each metric in groups, but the test expectations only verify
the event attributes, so test-stat-default fails with:

  FAILED '.../test-stat-default' - group failure

Only enforce the group of a recorded event when the expectation it
matches declares one.  Expectations that ask for a group are still
verified in both directions.

Fixes: c7adeb0974f18da4 ("perf jevents: Add set of common metrics based on default ones")
Fixes: 2dfc0cab3da4a205 ("perf stat: Add detail -d,-dd,-ddd metrics")
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/shell/lib/attr.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index b55c9b881e59852f..f254d03c1b253c7a 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -353,7 +353,7 @@ class Test(object):
             else:
                 raise Unsup(self)
 
-    def compare(self, expect, result):
+    def compare(self, expect, result, strict_groups=True):
         match = {}
 
         log.debug("  compare")
@@ -387,8 +387,9 @@ class Test(object):
 
             match[exp_name] = exp_list
 
-        # For each defined group in the expected events
-        # check we match the same group in the result.
+        # Check that each expected group is matched in the result; an event
+        # opened in a group may match an expectation that doesn't declare
+        # one, as those only verify the event attributes.
         for exp_name, exp_event in expect.items():
             group = exp_event.group
 
@@ -397,6 +398,8 @@ class Test(object):
 
             for res_name in match[exp_name]:
                 res_group = result[res_name].group
+                if res_group == '' and not strict_groups:
+                    continue
                 if res_group not in match[group]:
                     raise Fail(self, 'group failure')
 
@@ -435,7 +438,7 @@ class Test(object):
 
             # do the expectation - results matching - both ways
             self.compare(self.expect, self.result)
-            self.compare(self.result, self.expect)
+            self.compare(self.result, self.expect, strict_groups=False)
 
         finally:
             # cleanup
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (4 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:23   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config' Arnaldo Carvalho de Melo
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Opening a group with perf record on a machine with perf_event_paranoid >
1 and no CAP_PERFMON fails the first perf_event_open() with EACCES, after
which evsel__open_cpu() clears evsel->supported; evsel__fallback() then
adds exclude_kernel and record__open() retries, and the retry succeeds
with supported left false.  get_group_fd() treats unsupported events as
ungroupable, so the retry opens the group members without their leader:

  sys_perf_event_open: pid 12345 cpu 0 group_fd -1
  FAILED '.../test-record-group-sampling1' - group failure

which fails the group subtests of "perf test 82", the Perf attribute
expectations test, when it is run as a regular user, while it passes as
root or with CAP_PERFMON, where the first open doesn't fail.

commit 2cc7aa995ce9 ("perf stat: Refactor retry/skip/fatal error handling")
dropped the pos->supported = true that record__open() did after a
successful open and moved the resets into the stat code only, while
saying "When an event is tried the supported is set to true again".  Do
that in evsel__open_cpu(), which every open entry point funnels through,
so that a retry describes its own try instead of the previous failure.

Fixes: 2cc7aa995ce9ecd8 ("perf stat: Refactor retry/skip/fatal error handling")
Cc: Ian Rogers <irogers@google.com>
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/evsel.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 9c5e7510f0c019c3..e61f309e93dccc7f 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -3014,6 +3014,12 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
 	enum rlimit_action set_rlimit = NO_CHANGE;
 	struct perf_cpu cpu;
 
+	/*
+	 * A failed attempt being retried after evsel__fallback() would
+	 * otherwise keep group members out of their group in get_group_fd().
+	 */
+	evsel->supported = true;
+
 	if (evsel__is_retire_lat(evsel)) {
 		err = evsel__tpebs_open(evsel);
 		goto out;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config'
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (5 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:25   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR Arnaldo Carvalho de Melo
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

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@redhat.com>
---
 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 f254d03c1b253c7a..70ff73585cb3ffa9 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -57,6 +57,32 @@ def core_pmu_available():
 
     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
@@ -130,12 +156,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
 
@@ -148,7 +195,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (6 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config' Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:26   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type() Arnaldo Carvalho de Melo
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Events opened just to ask if they are supported, i.e. the ones in
is_event_supported() and is_libpfm_event_supported(), go through the
regular evsel__open() path and thus end up in the event files written to
$PERF_TEST_ATTR, with everything but type, config and disabled zeroed:

  [event-0-38654705664-3]
  fd=3
  cpu=-1
  pid=0
  type=0
  size=0
  config=38654705664
  sample_type=0
  ...

which 'perf test attr' then takes as one of the events that were asked
for, failing with:

  expected cpu=0, got -1
  expected size=176, got 0
  expected sample_type=65927, got 0
  expected freq=1, got 0

This started showing up when there is more than one core PMU, as
perf_pmus__supports_extended_type() probes one such event per core PMU,
but these records were always bogus: nothing asked for them.

Flag those evsels as support probes, keeping the name clear of the
kprobe/uprobe meaning probe already has in evsel, and leave them out of
the dump.

Reported-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/linux-perf-users/asWQvXRdqp_RK85c@google.com/
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/evsel.c        | 6 +++++-
 tools/perf/util/evsel.h        | 1 +
 tools/perf/util/pfm.c          | 1 +
 tools/perf/util/print-events.c | 1 +
 4 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index e61f309e93dccc7f..59318fef03cb17ff 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -3104,7 +3104,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
 
 			bpf_counter__install_pe(evsel, idx, fd);
 
-			if (unlikely(test_attr__enabled())) {
+			/*
+			 * Support probes aren't what was asked for, leave them
+			 * out of the PERF_TEST_ATTR dump.
+			 */
+			if (unlikely(test_attr__enabled() && !evsel->support_probe)) {
 				test_attr__open(&evsel->core.attr, pid, cpu,
 						fd, group_fd, evsel->open_flags);
 			}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 174f3414fd3cfa0d..53f3641b66e18674 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -120,6 +120,7 @@ struct evsel {
 	u64			db_id;
 	bool			uniquified_name;
 	bool 			supported;
+	bool			support_probe;	/* Opened just to check for support. */
 	bool 			needs_swap;
 	bool 			disabled;
 	bool			no_aux_samples;
diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c
index f80d6b0df47ae64d..ad9bc481da70604e 100644
--- a/tools/perf/util/pfm.c
+++ b/tools/perf/util/pfm.c
@@ -141,6 +141,7 @@ static bool is_libpfm_event_supported(const char *name, struct perf_cpu_map *cpu
 		return false;
 
 	evsel->is_libpfm_event = true;
+	evsel->support_probe = true;
 
 	ret = evsel__open(evsel, cpus, threads);
 	if (ret == -EACCES) {
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 0242243681b6b6f9..77428d9e73c4f1b6 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -147,6 +147,7 @@ bool is_event_supported(u8 type, u64 config)
 
 	evsel = evsel__new(&attr);
 	if (evsel) {
+		evsel->support_probe = true;
 		ret = evsel__open(evsel, NULL, tmap) >= 0;
 
 		if (!ret) {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type()
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (7 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:19   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for Arnaldo Carvalho de Melo
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

What hide_core_pmu_type() does is take a set of bits out of each of the
'|'-separated alternatives in a value, wildcards included, keeping the
rest of the expression: extract that as mask_bits() and implement
hide_core_pmu_type() in terms of it, a prep for using the same thing to
ignore, when comparing expectations, the sample id bit perf ends up
asking for and the read_format group bit that a PMU count can remove.

No change in behaviour.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 70ff73585cb3ffa9..151b884670952e9f 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -64,24 +64,31 @@ PERF_TYPE_HW_CACHE = 3
 # 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):
+def mask_bits(value, bits):
     """
-    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.
+    Take the bits in 'bits' out of each of the '|'-separated alternatives in
+    'value', keeping the rest of the expression, wildcards included.
     """
-    hidden = []
+    masked = []
 
     for alternative in value.split('|'):
         try:
-            alternative = str(int(alternative, 0) & 0xffffffff)
+            alternative = str(int(alternative, 0) & ~bits)
         except ValueError:
             pass
-        hidden.append(alternative)
+        masked.append(alternative)
 
-    return '|'.join(hidden)
+    return '|'.join(masked)
+
+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.
+    """
+    return mask_bits(value, 0xffffffff00000000)
 
 class Fail(Exception):
     def __init__(self, test, msg):
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (8 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type() Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:24   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries Arnaldo Carvalho de Melo
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

evlist__config() asks for a way to tell the samples apart,
PERF_SAMPLE_IDENTIFIER where available, PERF_SAMPLE_ID otherwise:

  bool sample_id = opts->sample_id;
  ...
  if (opts->full_auxtrace || opts->sample_identifier) {
        ...
        use_sample_identifier = perf_can_sample_identifier();
        sample_id = true;
  } else if (evlist__nr_entries(evlist) > 1) {
        ...
        use_sample_identifier = perf_can_sample_identifier();
        sample_id = true;
  }

so the id is asked for whenever the evlist has more than one event, and
also when asked to outright, with --sample-identifier or by an auxtrace
session, or by record__config() for a pipe run, through opts->sample_id,
none of which depends on the sample types: 'perf record
--sample-identifier -e cycles' and the arm SPE expectations in this very
directory ask for it with a single event.

Which one it asks for depends on the composition of the evlist: with
more than one core PMU a request for 'cycles' becomes one event per core
PMU, the extra dummy tracking events then make the sample types differ and
perf goes for IDENTIFIER, while the expectations were written on machines
with a single core PMU, where perf asks for ID:

  expected sample_type=263, got 65799    # 263 | (1 << 16)
  expected sample_type=343, got 65815    # (343 & ~(1 << 6)) | (1 << 16), group-sampling

Both bits mean the same thing here, how the samples get associated with the
event that produced them, so when the expectation asks for one of them, ask
for "an id of some sort" on both sides, and when the expectation doesn't
ask for any, take the bits out of what got recorded: whether they are there
and which one it is are consequences of what was asked for and of how the
request got expanded, none of that is what these expectations are about.

Since Test.compare() looks at both directions, expectations matching the
recorded events and recorded events matching the expectations, keep track
of which side is the one perf recorded to take the bits out of the right
side.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 151b884670952e9f..e911684428b1b74b 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -59,6 +59,8 @@ def core_pmu_available():
 
 PERF_TYPE_HARDWARE = 0
 PERF_TYPE_HW_CACHE = 3
+PERF_SAMPLE_ID = 1 << 6
+PERF_SAMPLE_IDENTIFIER = 1 << 16
 
 # 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.
@@ -90,6 +92,49 @@ def hide_core_pmu_type(value):
     """
     return mask_bits(value, 0xffffffff00000000)
 
+# The two sample id bits mean the same thing here: how the samples of the
+# events in the evlist get associated with the event that produced them.
+SAMPLE_ID_BITS = PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER
+
+# No real sample_type bit is this high, so it can stand for "either of the
+# sample id bits" when comparing the two sides.
+SAMPLE_ID_EITHER = 1 << 40
+
+def hide_sample_id(expected, recorded):
+    """
+    perf asks for a way to tell the samples apart, PERF_SAMPLE_IDENTIFIER where
+    available, PERF_SAMPLE_ID otherwise, see evlist__config(): whenever the
+    evlist has more than one event, and when asked to outright, with
+    --sample-identifier, by an auxtrace session, or by record__config() for a
+    pipe run, none of which depends on the sample types, as those only choose
+    between the two bits:
+    with more than one core PMU the dummy tracking events make the sample types
+    differ, so IDENTIFIER is asked for even for events whose expectations ask
+    for ID, written on a machine with a single core PMU.
+
+    So treat the two as the same thing: when the expectation asks for either
+    of them, ask for "an id of some sort" on both sides, and when the
+    expectation doesn't ask for any, take the bits out of what got recorded.
+    """
+    try:
+        asked = int(expected, 0) & SAMPLE_ID_BITS
+    except ValueError:
+        # The expectation is a wildcard or a set of alternatives.
+        return expected, recorded
+
+    try:
+        recorded_id_bits = int(recorded, 0) & SAMPLE_ID_BITS
+    except ValueError:
+        # The recorded side is a wildcard or a set of alternatives.
+        return expected, mask_bits(recorded, SAMPLE_ID_BITS)
+
+    if not asked:
+        return expected, mask_bits(recorded, SAMPLE_ID_BITS)
+
+    return (str(int(expected, 0) & ~SAMPLE_ID_BITS | SAMPLE_ID_EITHER),
+            str(int(recorded, 0) & ~SAMPLE_ID_BITS |
+                (SAMPLE_ID_EITHER if recorded_id_bits else 0)))
+
 class Fail(Exception):
     def __init__(self, test, msg):
         self.msg = msg
@@ -182,13 +227,33 @@ class Event(dict):
 
         return value
 
+    def hide_event_count_dependent_terms(self, term, expected, recorded):
+        if term == 'sample_type':
+            return hide_sample_id(expected, recorded)
+
+        return expected, recorded
+
+    def recorded_side(self, other):
+        """
+        Which one is what perf recorded? Both directions get compared, see
+        Test.compare(), so this can't be taken from the arguments' order.
+        """
+        if getattr(other, 'recorded', False):
+            return other
+
+        return self if getattr(self, 'recorded', False) else None
+
     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
-            exp = self.hide_core_pmu_type(t, self[t], other)
-            got = self.hide_core_pmu_type(t, other[t], other)
+            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)
+            got = self.hide_core_pmu_type(t, (recorded or self)[t], expected)
+            if recorded is not None:
+                exp, got = self.hide_event_count_dependent_terms(t, exp, got)
             if not data_equal(exp, got):
                 return False
         return True
@@ -202,10 +267,14 @@ class Event(dict):
         for t in Event.terms:
             if t not in self or t not in other:
                 continue
-            exp = self.hide_core_pmu_type(t, self[t], other)
-            got = self.hide_core_pmu_type(t, other[t], other)
+            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)
+            got = self.hide_core_pmu_type(t, (recorded or self)[t], expected)
+            if recorded is not None:
+                exp, got = self.hide_event_count_dependent_terms(t, exp, got)
             if not data_equal(exp, got):
-                log.warning("expected %s=%s, got %s", t, self[t], other[t])
+                log.warning("expected %s=%s, got %s", t, exp, got)
 
 def parse_version(version):
     if not version:
@@ -487,6 +556,8 @@ class Test(object):
             log.debug("  loading result events")
             for f in glob.glob(tempdir + '/event*'):
                 self.load_events(f, self.result)
+            for event in self.result.values():
+                event.recorded = True
 
             # resolve group_fd to event names
             self.resolve_groups(self.expect)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (9 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:21   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs Arnaldo Carvalho de Melo
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

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.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index e911684428b1b74b..222ce6f598831e3f 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -61,6 +61,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.
@@ -243,11 +258,26 @@ 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
+
+        return (getattr(self, 'tracking_moved_to_dummy', False) or
+                getattr(other, 'tracking_moved_to_dummy', 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)
@@ -267,6 +297,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)
@@ -478,6 +511,27 @@ 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
+
+        for name, event in list(self.result.items()):
+            if is_dummy_tracking_event(event):
+                log.debug("      dropping dummy tracking event [%s]", name)
+                del self.result[name]
+
+        # The tracking terms got carried by the dropped dummies, flag the
+        # remaining events so that the match leaves those terms out.
+        for event in self.result.values():
+            event.tracking_moved_to_dummy = True
+
     def compare(self, expect, result, strict_groups=True):
         match = {}
 
@@ -559,6 +613,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (10 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:21   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove Arnaldo Carvalho de Melo
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

On a hybrid machine the default stat metrics are opened on each core PMU,
and the events of the other one have their own encodings: on an i7-14700K
cpu_atom (type 10) opens UOPS_RETIRED.ALL, config 194, which the
expectations, written for cpu_core (type 4), don't name:

  expected type=4, got 10
  expected config=209|391, got 194

The test already masks the core PMU type out of 'config' for the legacy
events, but raw events keep their type and encoding, so leave out the
events of the core PMUs no expectation names, like the dummy tracking
event.  Core PMUs are found as perf finds them: 'cpu' or 'cpum_cf', or a
PMU with a 'cpus' file in sysfs.  Nothing is left out unless an
expectation names a core PMU, so single core PMU machines and the tests
with only legacy expectations are not affected.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 222ce6f598831e3f..65b1e655a2bcb505 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -532,6 +532,44 @@ class Test(object):
         for event in self.result.values():
             event.tracking_moved_to_dummy = True
 
+    def drop_other_core_pmu_events(self, devices='/sys/bus/event_source/devices'):
+        """
+        Leave out the events of the other core PMUs, which have their own
+        encodings, when the expectations name one of the core PMUs.
+        """
+        asked = set()
+        for event in self.expect.values():
+            types = event.get('type', '').split('|')
+            if '*' in types:
+                return
+            asked.update(types)
+
+        try:
+            names = os.listdir(devices)
+        except OSError:
+            return
+
+        core = set()
+        for name in names:
+            path = os.path.join(devices, name)
+            has_cpus = os.path.exists(os.path.join(path, 'cpus'))
+            if name not in ('cpu', 'cpum_cf') and not has_cpus:
+                continue
+            try:
+                with open(os.path.join(path, 'type'), encoding='utf-8') as f:
+                    core.add(f.read().strip())
+            except OSError:
+                pass
+
+        if not core & asked:
+            return
+
+        others = core - asked
+        for name, event in list(self.result.items()):
+            if event.get('type') in others:
+                log.debug("      dropping other core PMU event [%s]", name)
+                del self.result[name]
+
     def compare(self, expect, result, strict_groups=True):
         match = {}
 
@@ -614,6 +652,7 @@ class Test(object):
                 event.recorded = True
 
             self.drop_dummy_tracking_events()
+            self.drop_other_core_pmu_events()
 
             # resolve group_fd to event names
             self.resolve_groups(self.expect)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (11 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:20   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped Arnaldo Carvalho de Melo
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

read_format's PERF_FORMAT_GROUP bit asks for the group members to be read
along with the leader, which aside from this bit is verified with the
fd/group_fd based group checking in Test.compare(), and with more than
one core PMU a group whose members are pinned to one of them, say
'default_core', leaves the clones for the other core PMUs without any
members:

  $ perf stat -e '{cycles,default_core/legacy-hardware-config=3/}:S'
  ... the A53 leader keeps its cohort, both A73 cycles clones are alone,
      read_format 20 instead of 28

so that bit isn't there: whether it is there is a consequence of how the
evlist got expanded to more than one core PMU, not of the groupness the
expectations say about, which is verified structurally, so take it out on
both sides.

That leaves the A73 leader clones, now with:

  expected read_format=28|31, got 20

matching the base event expectations again.

With a single core PMU there is nothing to expand, so there is nothing to
explain away: count the core PMUs when the script starts, from what perf
makes of 'cycles', see parse_events_multi_pmu_add(), and take the bit out
only when there is more than one, leaving the machines with a single core
PMU verifying what the expectations say about it.  That count is 2 on the
Amlogic A311D, where its A53 and A73 PMUs both answer for 'cycles', and 1
on a machine with a single core PMU.  Where there is no binding to ask,
as when attr.py runs without setup_python.sh, the count isn't available
and the bit is taken out as well.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 65b1e655a2bcb505..3bf07d4df95987d8 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -115,6 +115,50 @@ SAMPLE_ID_BITS = PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER
 # sample id bits" when comparing the two sides.
 SAMPLE_ID_EITHER = 1 << 40
 
+PERF_FORMAT_GROUP = 1 << 3
+
+def core_pmu_count():
+    """
+    How many core PMUs does perf see?  An event with no PMU named in it
+    becomes one event per core PMU, see parse_events_multi_pmu_add(), and
+    that is the expansion that leaves the clones for the other core PMUs
+    without any group members and that makes the kernel qualify 'config'
+    with the type of the PMU it is for, so this is the number of events perf
+    makes of 'cycles'.  None when there is no binding to ask, as in
+    core_pmu_available().
+    """
+    try:
+        import perf
+        parse_events = perf.parse_events
+    except (ImportError, AttributeError):
+        return None
+
+    try:
+        return len(parse_events('cycles'))
+    except OSError:
+        # No PMU that can count it, see core_pmu_available().
+        return None
+
+SINGLE_CORE_PMU = core_pmu_count() == 1
+
+def hide_group_format(expected, recorded):
+    """
+    read_format's PERF_FORMAT_GROUP bit asks for the group members to be read
+    along with the leader, which aside from this bit is verified with the
+    fd/group_fd based group checking, and with more than one core PMU a group
+    whose members are pinned to one of them, say 'default_core', leaves the
+    clones for the other core PMUs without any members, so that bit isn't
+    there: whether it is there is a consequence of how the evlist got
+    expanded to more than one core PMU, not of what the expectations are
+    about, so take it out on both sides.  On a machine with a single core PMU
+    there is nothing to expand, so the expectations keep verifying it.
+    """
+    if SINGLE_CORE_PMU:
+        return expected, recorded
+
+    return (mask_bits(expected, PERF_FORMAT_GROUP),
+            mask_bits(recorded, PERF_FORMAT_GROUP))
+
 def hide_sample_id(expected, recorded):
     """
     perf asks for a way to tell the samples apart, PERF_SAMPLE_IDENTIFIER where
@@ -245,6 +289,8 @@ class Event(dict):
     def hide_event_count_dependent_terms(self, term, expected, recorded):
         if term == 'sample_type':
             return hide_sample_id(expected, recorded)
+        if term == 'read_format':
+            return hide_group_format(expected, recorded)
 
         return expected, recorded
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (12 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:22   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag Arnaldo Carvalho de Melo
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Per skip the default output shows a self contained one liner:

  unsupp  'test-record-branch-filter-any' exit 255, expected 1, likely a missing capability
  skipped [kernel newer than 6.12] 'test-record-group-sampling'
  skipped [arch = !aarch64, machine = aarch64] 'test-record-graph-default'

The extra notes explaining the reason, with a copy pasteable command to
observe the failure, come in the very same stream when the verbosity is
bumped, at -vvv, nothing is written to a separate place for later
consultation, it is just the logging ladder:

  detail: '/root/bin/perf record --no-bpf-event -j any kill' exited with
  255, the expectations want the workload's 1 exit, so there are no attrs
  recorded to verify; this is normally a capability problem, run
  '/root/bin/perf record --no-bpf-event -j any kill' to see why

Note that 'perf test -v/-vv/-vvv attribute' appends a single -v to the
shell test entry (shell_test__run()), so to see the notes one runs the
python module directly, f.i.:

  python3 tests/shell/lib/attr.py -vvv -d tests/shell/attr -p ./perf

The skip functions now return the reason fragment when they want to skip
and None when the test can go on.

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

diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
index 3bf07d4df95987d8..ee78c912d94693dd 100644
--- a/tools/perf/tests/shell/lib/attr.py
+++ b/tools/perf/tests/shell/lib/attr.py
@@ -209,10 +209,13 @@ class Notest(Exception):
         return '[%s] \'%s\'' % (self.arch, self.test.path)
 
 class Unsup(Exception):
-    def __init__(self, test):
+    def __init__(self, test, ret, expected):
         self.test = test
+        self.ret = ret
+        self.expected = expected
     def getMsg(self):
-        return '\'%s\'' % self.test.path
+        return '\'%s\' exit %d, expected %s, likely a missing capability' % (
+            self.test.path, self.ret, self.expected)
 
 class Event(dict):
     terms = [
@@ -425,13 +428,17 @@ class Test(object):
 
     def skip_test_kernel_since(self):
         if not self.kernel_since:
-            return False
-        return not self.kernel_since <= parse_version(platform.release())
+            return None
+        if self.kernel_since <= parse_version(platform.release()):
+            return None
+        return "kernel older than %d.%d" % tuple(self.kernel_since)
 
     def skip_test_kernel_until(self):
         if not self.kernel_until:
-            return False
-        return not parse_version(platform.release()) < self.kernel_until
+            return None
+        if parse_version(platform.release()) < self.kernel_until:
+            return None
+        return "kernel newer than %d.%d" % tuple(self.kernel_until)
 
     def skip_test_auxv(self):
         def new_auxv(a, pattern):
@@ -447,18 +454,20 @@ class Test(object):
             return (items[0], value)
 
         if not self.auxv:
-            return False
+            return None
         auxv = subprocess.check_output("LD_SHOW_AUXV=1 sleep 0", shell=True) \
                .decode(sys.stdout.encoding)
         pattern = re.compile(r"[: ]+")
         auxv = dict([new_auxv(a, pattern) for a in auxv.splitlines()])
-        return not eval(self.auxv)
+        if eval(self.auxv):
+            return None
+
+        return "the auxv condition doesn't hold here"
 
     def skip_test_arch(self, myarch):
         # If architecture not set always run test
         if self.arch == '':
-            # log.warning("test for arch %s is ok" % myarch)
-            return False
+            return None
 
         # Allow multiple values in assignment separated by ','
         arch_list = self.arch.split(',')
@@ -468,24 +477,23 @@ class Test(object):
             arch_list[0] = arch_list[0][1:]
             log.warning("excluded architecture list %s", arch_list)
             for arch_item in arch_list:
-                # log.warning("test for %s arch is %s" % (arch_item, myarch))
                 if arch_item == myarch:
-                    return True
-            return False
+                    return "arch = %s, machine = %s" % (self.arch, myarch)
+            return None
 
         for arch_item in arch_list:
-            # log.warning("test for architecture '%s' current '%s'" % (arch_item, myarch))
             if arch_item == myarch:
-                return False
-        return True
+                return None
+
+        return "arch = %s, machine = %s" % (self.arch, myarch)
 
     def skip_test_requires(self):
         for requirement in filter(None, map(str.strip, self.requires.split(','))):
             if requirement == 'core-pmu' and not core_pmu_available():
                 log.warning("test limitation 'requires %s'", requirement)
-                return True
+                return "needs a core PMU, none here"
 
-        return False
+        return None
 
     def restore_sample_rate(self, value=10000):
         try:
@@ -529,20 +537,36 @@ class Test(object):
     def run_cmd(self, tempdir):
         _junk1, _junk2, _junk3, _junk4, myarch = (os.uname())
 
-        if self.skip_test_arch(myarch):
-            raise Notest(self, myarch)
-
-        if self.skip_test_auxv():
-            raise Notest(self, "auxv skip")
-
-        if self.skip_test_kernel_since():
-            raise Notest(self, "old kernel skip")
-
-        if self.skip_test_kernel_until():
-            raise Notest(self, "new kernel skip")
-
-        if self.skip_test_requires():
-            raise Notest(self, "requirements skip")
+        arch = self.skip_test_arch(myarch)
+        if arch:
+            log.debug("  detail: the test file has arch = %s, the machine reports %s",
+                     self.arch, myarch)
+            raise Notest(self, arch)
+
+        auxv = self.skip_test_auxv()
+        if auxv:
+            log.debug("  detail: the auxv condition %s doesn't hold, the bits come "
+                     "from the LD_SHOW_AUXV=1 output", self.auxv)
+            raise Notest(self, auxv)
+
+        kernel = self.skip_test_kernel_since()
+        if kernel:
+            log.debug("  detail: the test file's kernel_since = %d.%d, this kernel is %s",
+                     self.kernel_since[0], self.kernel_since[1], platform.release())
+            raise Notest(self, kernel)
+
+        kernel = self.skip_test_kernel_until()
+        if kernel:
+            log.debug("  detail: the test file's kernel_until = %d.%d, this kernel is %s",
+                     self.kernel_until[0], self.kernel_until[1], platform.release())
+            raise Notest(self, kernel)
+
+        requirement = self.skip_test_requires()
+        if requirement:
+            log.debug("  detail: requires core-pmu, nothing countable here, check "
+                     "kernel.perf_event_paranoid and the PMU nodes under "
+                     "/sys/bus/event_source/")
+            raise Notest(self, requirement)
 
         self.restore_sample_rate()
         cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir,
@@ -552,10 +576,16 @@ class Test(object):
         log.info("  '%s' ret '%s', expected '%s'", cmd, str(ret), str(self.ret))
 
         if not data_equal(str(ret), str(self.ret)):
+            plain_cmd = "%s %s %s" % (self.perf, self.command,
+                                      self.args.split('>/dev/null')[0].rstrip())
             if self.test_ret:
                 raise Fail(self, "Perf exit code failure")
             else:
-                raise Unsup(self)
+                log.debug("  detail: '%s' exited with %d, the expectations want the "
+                         "workload's %s exit, so there are no attrs recorded to "
+                         "verify; this is normally a capability problem, run '%s' "
+                         "to see why", plain_cmd, ret, self.ret, plain_cmd)
+                raise Unsup(self, ret, str(self.ret))
 
     def drop_dummy_tracking_events(self):
         """
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (13 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:23   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer Arnaldo Carvalho de Melo
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Each perf instance maps one ring buffer per CPU, all charged to the same
per user budget set in kernel.perf_event_mlock_kb, so suites that map
them need to be told apart from the ones that don't, to later have them
take turns using that budget while the other tests keep running in
parallel.

Add the flag to 'struct test_suite' and a DEFINE_SUITE_NEEDS_MLOCK()
variant for the suites using the DEFINE_SUITE() form.

Shell tests run perf, that maps ring buffers, so they are taken to need
room in that budget by default; the ones that don't map them waive it in
the requisites, a comma separated list in the parens ending their
description line, e.g.:

  # perf stat tests (no_mlock)

"exclusive" is the catch-all that keeps a test from running alongside
any other, so it runs alone, in sequence, and implies every other
requisite, "no_mlock" waives the default, and more keywords are to come,
each stating what a test needs, till the catch-all isn't needed anymore.

'perf test requisites' lists what each test needs, reusing the listing
routine and taking the same test name fragments and numbers as 'list'.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/Documentation/perf-test.txt | 28 +++++++++++++--
 tools/perf/tests/builtin-test.c        | 44 +++++++++++++++++++++---
 tools/perf/tests/tests-scripts.c       | 47 ++++++++++++++++++++++----
 tools/perf/tests/tests.h               | 17 ++++++++++
 4 files changed, 122 insertions(+), 14 deletions(-)

diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
index 859df74e62efc4b1..d3a7f7efaec705ed 100644
--- a/tools/perf/Documentation/perf-test.txt
+++ b/tools/perf/Documentation/perf-test.txt
@@ -8,7 +8,7 @@ perf-test - Runs sanity tests.
 SYNOPSIS
 --------
 [verse]
-'perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]'
+'perf test [<options>] [{list|requisites} [<test-name-fragment>]|[<test-name-fragments>|<test-numbers>]]'
 
 DESCRIPTION
 -----------
@@ -18,9 +18,31 @@ also will look for a directory with more tests in the form of scripts.
 To get a list of available tests use 'perf test list', specifying a test name
 fragment will show all tests that have it.
 
+To get a list of what the tests need, i.e. of their requisites (see below), use
+'perf test requisites', that takes the same test name fragments and numbers as
+'list'.
+
 To run just specific tests, inform test name fragments or the numbers obtained
 from 'perf test list'.
 
+REQUISITES
+----------
+Shell tests run perf, that maps ring buffers, so they are taken to need room
+in the per user budget set in kernel.perf_event_mlock_kb, shared by the tests
+running at the same time, that take turns using it.  Tests state what they
+need, or waive, in a comma separated list in the parens ending their
+description line, e.g.:
+
+	# perf stat tests (no_mlock)
+
+"exclusive" is a catch-all: the test can't run alongside any other one, so
+it runs alone, in sequence, and implies every other requisite.  "no_mlock"
+waives the default, for tests that don't map ring buffers.  More keywords
+are to come, each stating what a test needs, till the catch-all isn't needed
+anymore.
+
+Use 'perf test requisites' to list what each test needs.
+
 OPTIONS
 -------
 -s::
@@ -36,8 +58,8 @@ OPTIONS
 
 -S::
 --sequential::
-	Run all tests one after the other. By default "exclusive"
-	tests are run sequentially, but other tests are run in
+	Run all tests one after the other. By default the tests asking to be
+	"exclusive" (see REQUISITES) run sequentially, but other tests run in
 	parallel to speed execution.
 
 -r::
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 98134b1c74cf80f4..acffa0d0d33d6d30 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -56,6 +56,8 @@ static struct strbuf junit_xml_buf = STRBUF_INIT;
 static bool dont_fork;
 /* Fork the tests in parallel and wait for their completion. */
 static bool sequential;
+/* List what each test needs, set by the "requisites" subcommand. */
+static bool list_requisites;
 /* Number of times each test is run. */
 static unsigned int runs_per_test = 1;
 /* Number of lines to include in failure snippet. */
@@ -283,6 +285,30 @@ static const char *test_description(const struct test_suite *t, int test_case)
 	return t->desc;
 }
 
+static void requisites__fprintf(FILE *fp, const struct test_suite *t, int test_case)
+{
+	bool needs_mlock = t->needs_mlock, exclusive;
+
+	if (test_case < 0)
+		test_case = test_suite__num_test_cases(t) == 1 ? 0 : -1;
+
+	if (test_case >= 0) {
+		exclusive = t->test_cases[test_case].exclusive;
+		/* The catch-all implies every other requisite. */
+		needs_mlock = needs_mlock || exclusive;
+	} else {
+		exclusive = false;
+	}
+
+	if (!exclusive && !needs_mlock)
+		return;
+
+	fprintf(fp, " (");
+	if (exclusive)
+		fprintf(fp, "exclusive, ");
+	fprintf(fp, "mlock)");
+}
+
 static test_fnptr test_function(const struct test_suite *t, int test_case)
 {
 	if (test_case <= 0)
@@ -1450,14 +1476,20 @@ static int perf_test__list(FILE *fp, struct test_suite **suites, int argc, const
 		if (!perf_test__matches(test_description(*t, -1), curr_suite, argc, argv))
 			continue;
 
-		fprintf(fp, "%3d: %s\n", curr_suite + 1, test_description(*t, -1));
+		fprintf(fp, "%3d: %s", curr_suite + 1, test_description(*t, -1));
+		if (list_requisites)
+			requisites__fprintf(fp, *t, /*test_case=*/-1);
+		fprintf(fp, "\n");
 
 		if (test_suite__num_test_cases(*t) <= 1)
 			continue;
 
 		test_suite__for_each_test_case(*t, curr_test_case) {
-			fprintf(fp, "%3d.%1d: %s\n", curr_suite + 1, curr_test_case + 1,
+			fprintf(fp, "%3d.%1d: %s", curr_suite + 1, curr_test_case + 1,
 				test_description(*t, curr_test_case));
+			if (list_requisites)
+				requisites__fprintf(fp, *t, curr_test_case);
+			fprintf(fp, "\n");
 		}
 	}
 	return 0;
@@ -1747,7 +1779,8 @@ static struct test_suite **build_suites(void)
 int cmd_test(int argc, const char **argv)
 {
 	const char *test_usage[] = {
-	"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
+	"perf test [<options>] [{list|requisites} [<test-name-fragment>]|"
+	"[<test-name-fragments>|<test-numbers>]]",
 	NULL,
 	};
 	const char *skip = NULL;
@@ -1776,7 +1809,7 @@ int cmd_test(int argc, const char **argv)
 			  "Generate junit XML output, default test.xml", "test.xml"),
 	OPT_END()
 	};
-	const char * const test_subcommands[] = { "list", NULL };
+	const char * const test_subcommands[] = { "list", "requisites", NULL };
 	struct intlist *skiplist = NULL;
 	struct test_suite **suites;
 	int ret;
@@ -1808,10 +1841,11 @@ int cmd_test(int argc, const char **argv)
 	setvbuf(stdout, NULL, _IONBF, 0);
 
 	argc = parse_options_subcommand(argc, argv, test_options, test_subcommands, test_usage, 0);
-	if (argc >= 1 && !strcmp(argv[0], "list")) {
+	if (argc >= 1 && (!strcmp(argv[0], "list") || !strcmp(argv[0], "requisites"))) {
 		suites = build_suites();
 		if (!suites)
 			return errno ? -errno : -ENOMEM;
+		list_requisites = !strcmp(argv[0], "requisites");
 		ret = perf_test__list(stdout, suites, argc - 1, argv + 1);
 		free(suites);
 		return ret;
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index 0370bc7013737e31..03430cdb9a5cc14c 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -179,6 +179,42 @@ static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)
 	return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL;
 }
 
+/*
+ * The requisites are a comma separated list in the parens ending the
+ * description line, e.g. "perf stat tests (no_mlock)": "exclusive" is the
+ * catch-all, implying every other requisite, "no_mlock" waives the default of
+ * needing room in the per user budget set in kernel.perf_event_mlock_kb.
+ */
+static void shell_test__requisites(char *desc, bool *exclusive, bool *needs_mlock)
+{
+	char *lparen, *rparen, *requisites;
+
+	/* Only a list closing the description line is taken for requisites. */
+	rparen = strrchr(desc, ')');
+	if (rparen == NULL || rparen[1] != '\0')
+		return;
+
+	lparen = strrchr(desc, '(');
+	if (lparen == NULL || lparen > rparen)
+		return;
+
+	requisites = lparen + 1;
+	*exclusive = strstr(requisites, "exclusive") != NULL;
+	if (*exclusive) {
+		/* The catch-all implies every other requisite. */
+		*needs_mlock = true;
+	} else if (strstr(requisites, "no_mlock")) {
+		*needs_mlock = false;
+	} else if (!strstr(requisites, "mlock")) {
+		/* Unknown requisite: leave the parens in the description. */
+		return;
+	}
+
+	*lparen = '\0';
+	if (lparen > desc && isspace(lparen[-1]))
+		lparen[-1] = '\0';
+}
+
 static void append_script(int dir_fd, const char *name, char *desc,
 			  struct test_suite ***result,
 			  size_t *result_sz)
@@ -187,7 +223,8 @@ static void append_script(int dir_fd, const char *name, char *desc,
 	struct test_suite *test_suite, **result_tmp;
 	struct test_case *tests;
 	ssize_t len;
-	char *exclusive;
+	/* Shell tests run perf, that maps ring buffers, so they need the budget. */
+	bool exclusive = false, needs_mlock = true;
 
 	snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);
 	len = readlink(link, filename, sizeof(filename) - 1);
@@ -204,11 +241,8 @@ static void append_script(int dir_fd, const char *name, char *desc,
 		return;
 	}
 	tests[0].name = strdup_check(name);
-	exclusive = strstr(desc, " (exclusive)");
-	if (exclusive != NULL) {
-		tests[0].exclusive = true;
-		exclusive[0] = '\0';
-	}
+	shell_test__requisites(desc, &exclusive, &needs_mlock);
+	tests[0].exclusive = exclusive;
 	tests[0].desc = strdup_check(desc);
 	tests[0].run_case = shell_test__run;
 	test_suite = zalloc(sizeof(*test_suite));
@@ -220,6 +254,7 @@ static void append_script(int dir_fd, const char *name, char *desc,
 	test_suite->desc = desc;
 	test_suite->test_cases = tests;
 	test_suite->priv = strdup_check(filename);
+	test_suite->needs_mlock = needs_mlock;
 	/* Realloc is good enough, though we could realloc by chunks, not that
 	 * anyone will ever measure performance here */
 	result_tmp = realloc(*result, (*result_sz + 1) * sizeof(*result_tmp));
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index f379620069d34f3a..5982c4e62cd37206 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -46,6 +46,12 @@ struct test_suite {
 	struct test_case *test_cases;
 	void *priv;
 	int (*setup)(struct test_suite *suite);
+	/*
+	 * Set for the suites mapping perf ring buffers, that need room in the
+	 * per user budget set in kernel.perf_event_mlock_kb, see
+	 * shell_test__requisites() for the shell suites.
+	 */
+	bool needs_mlock;
 };
 
 #define DECLARE_SUITE(name) \
@@ -103,6 +109,17 @@ struct test_suite {
 		.test_cases = tests__##_name,		\
 	}
 
+#define DEFINE_SUITE_NEEDS_MLOCK(description, _name)	\
+	struct test_case tests__##_name[] = {           \
+		TEST_CASE(description, _name),		\
+		{	.name = NULL, }			\
+	};						\
+	struct test_suite suite__##_name = {		\
+		.desc = description,			\
+		.test_cases = tests__##_name,		\
+		.needs_mlock = true,			\
+	}
+
 /* Tests */
 DECLARE_SUITE(vmlinux_matches_kallsyms);
 DECLARE_SUITE(openat_syscall_event);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (14 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:27   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests Arnaldo Carvalho de Melo
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

A mutex held by a process that can die before unlocking it, like the
tests forked by 'perf test' that will take turns using the per user
budget set in kernel.perf_event_mlock_kb, has to be taken over by the
next locker instead of staying locked forever.

Robust mutexes, on top of the kernel's robust futexes, do that: when an
owner dies while holding it the kernel marks the futex word owner-died
and the next pthread_mutex_lock() returns EOWNERDEAD, then mutex_lock()
makes it consistent and takes it over.

Add mutex_init_pshared_robust(), returning false when robust mutexes
aren't available: from libcs without the API, gated by a feature check,
to musl and glibc refusing them at runtime, when get_robust_list(2) or
set_robust_list(2) aren't usable.  glibc only sets a flag in
pthread_mutexattr_setrobust(), refusing the mutex itself in
pthread_mutex_init() with ENOTSUP instead.  Neither is an error, so
nothing is printed about them.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/build/Makefile.feature                  |  1 +
 tools/build/feature/Makefile                  |  4 +
 tools/build/feature/test-all.c                |  5 ++
 .../build/feature/test-pthread-mutex-robust.c | 19 ++++
 tools/perf/Makefile.config                    |  4 +
 tools/perf/util/mutex.c                       | 86 ++++++++++++++++---
 tools/perf/util/mutex.h                       |  5 ++
 7 files changed, 112 insertions(+), 12 deletions(-)
 create mode 100644 tools/build/feature/test-pthread-mutex-robust.c

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index dbef5b6c4e43c857..4d37d11f674c5bd5 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -85,6 +85,7 @@ FEATURE_TESTS_BASIC :=                  \
         libcpupower                     \
         pthread-attr-setaffinity-np     \
         pthread-barrier     		\
+        pthread-mutex-robust		\
         reallocarray                    \
         stackprotector-all              \
         timerfd                         \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 62f9cbf3dee97936..10340a5372242e05 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -41,6 +41,7 @@ FILES=                                          \
          $(foreach arch,$(LIBUNWIND_ARCHS),test-libunwind-$(arch).bin test-libunwind-debug-frame-$(arch).bin) \
          test-pthread-attr-setaffinity-np.bin   \
          test-pthread-barrier.bin		\
+         test-pthread-mutex-robust.bin		\
          test-stackprotector-all.bin            \
          test-timerfd.bin                       \
          test-babeltrace2-ctf-writer.bin        \
@@ -128,6 +129,9 @@ $(OUTPUT)test-pthread-attr-setaffinity-np.bin:
 $(OUTPUT)test-pthread-barrier.bin:
 	$(BUILD) -lpthread
 
+$(OUTPUT)test-pthread-mutex-robust.bin:
+	$(BUILD) -lpthread
+
 $(OUTPUT)test-stackprotector-all.bin:
 	$(BUILD) -fstack-protector-all
 
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c
index 0ee16eccd9e05788..4a6890290f7462a7 100644
--- a/tools/build/feature/test-all.c
+++ b/tools/build/feature/test-all.c
@@ -90,6 +90,10 @@
 # include "test-pthread-barrier.c"
 #undef main
 
+#define main main_test_pthread_mutex_robust
+# include "test-pthread-mutex-robust.c"
+#undef main
+
 #define main main_test_scandirat
 # include "test-scandirat.c"
 #undef main
@@ -167,6 +171,7 @@ int main(int argc, char *argv[])
 	main_test_zlib();
 	main_test_pthread_attr_setaffinity_np();
 	main_test_pthread_barrier();
+	main_test_pthread_mutex_robust();
 	main_test_lzma();
 	main_test_bpf();
 	main_test_scandirat();
diff --git a/tools/build/feature/test-pthread-mutex-robust.c b/tools/build/feature/test-pthread-mutex-robust.c
new file mode 100644
index 0000000000000000..33241fbde3eab87f
--- /dev/null
+++ b/tools/build/feature/test-pthread-mutex-robust.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdint.h>
+#include <pthread.h>
+
+int main(void)
+{
+	pthread_mutexattr_t attr;
+	pthread_mutex_t mtx;
+
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+	pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
+	pthread_mutex_init(&mtx, &attr);
+	pthread_mutex_consistent(&mtx);
+	pthread_mutex_destroy(&mtx);
+	pthread_mutexattr_destroy(&attr);
+
+	return 0;
+}
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index cafc7be79643b26c..b4764f69f47f4d8c 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -417,6 +417,10 @@ ifeq ($(feature-pthread-barrier), 1)
   CFLAGS += -DHAVE_PTHREAD_BARRIER
 endif
 
+ifeq ($(feature-pthread-mutex-robust), 1)
+  CFLAGS += -DHAVE_PTHREAD_MUTEX_ROBUST
+endif
+
 ifndef NO_BIONIC
   $(call feature_check,bionic)
   ifeq ($(feature-bionic), 1)
diff --git a/tools/perf/util/mutex.c b/tools/perf/util/mutex.c
index 7aa1f3f55a7d4089..712fbf839d59085f 100644
--- a/tools/perf/util/mutex.c
+++ b/tools/perf/util/mutex.c
@@ -17,37 +17,84 @@ static void check_err(const char *fn, int err)
 
 #define CHECK_ERR(err) check_err(__func__, err)
 
-static void __mutex_init(struct mutex *mtx, bool pshared, bool recursive)
+static bool __mutex_init(struct mutex *mtx, bool pshared, bool recursive, bool robust)
 {
 	pthread_mutexattr_t attr;
+	bool ok = false;
+	int err;
 
-	CHECK_ERR(pthread_mutexattr_init(&attr));
+	err = pthread_mutexattr_init(&attr);
+	if (err)
+		goto out;
 
 #ifndef NDEBUG
 	/* In normal builds enable error checking, such as recursive usage. */
-	CHECK_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK));
+	err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+	if (err)
+		goto out_destroy;
 #endif
-	if (recursive)
-		CHECK_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
-	if (pshared)
-		CHECK_ERR(pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
-	CHECK_ERR(pthread_mutex_init(&mtx->lock, &attr));
-	CHECK_ERR(pthread_mutexattr_destroy(&attr));
+	if (recursive) {
+		err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+		if (err)
+			goto out_destroy;
+	}
+	if (pshared) {
+		err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+		if (err)
+			goto out_destroy;
+	}
+	if (robust) {
+#ifdef HAVE_PTHREAD_MUTEX_ROBUST
+		err = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
+		if (err)
+			goto out_unavailable;
+#else
+		goto out_unavailable;
+#endif
+	}
+
+	err = pthread_mutex_init(&mtx->lock, &attr);
+	if (!err)
+		ok = true;
+	else if (robust && pshared)
+		/*
+		 * glibc refuses a robust process shared mutex with ENOTSUP when
+		 * set_robust_list(2) isn't usable, i.e. when the machine provides
+		 * no robust mutexes for the process shared case: an unavailable
+		 * API, not a bad request.
+		 */
+		err = 0;
+out_destroy:
+	pthread_mutexattr_destroy(&attr);
+out:
+	if (err)
+		CHECK_ERR(err);
+
+	return ok;
+
+out_unavailable:
+	pthread_mutexattr_destroy(&attr);
+	return false;
 }
 
 void mutex_init(struct mutex *mtx)
 {
-	__mutex_init(mtx, /*pshared=*/false, /*recursive=*/false);
+	__mutex_init(mtx, /*pshared=*/false, /*recursive=*/false, /*robust=*/false);
 }
 
 void mutex_init_pshared(struct mutex *mtx)
 {
-	__mutex_init(mtx, /*pshared=*/true, /*recursive=*/false);
+	__mutex_init(mtx, /*pshared=*/true, /*recursive=*/false, /*robust=*/false);
 }
 
 void mutex_init_recursive(struct mutex *mtx)
 {
-	__mutex_init(mtx, /*pshared=*/false, /*recursive=*/true);
+	__mutex_init(mtx, /*pshared=*/false, /*recursive=*/true, /*robust=*/false);
+}
+
+bool mutex_init_pshared_robust(struct mutex *mtx)
+{
+	return __mutex_init(mtx, /*pshared=*/true, /*recursive=*/false, /*robust=*/true);
 }
 
 void mutex_destroy(struct mutex *mtx)
@@ -58,7 +105,22 @@ void mutex_destroy(struct mutex *mtx)
 void mutex_lock(struct mutex *mtx)
 	NO_THREAD_SAFETY_ANALYSIS
 {
+#ifdef HAVE_PTHREAD_MUTEX_ROBUST
+	int err = pthread_mutex_lock(&mtx->lock);
+
+	/*
+	 * A robust mutex whose owner died while holding it comes back as
+	 * EOWNERDEAD, marked by the kernel: take it over.
+	 */
+	if (err == EOWNERDEAD) {
+		pr_debug("mutex: taking over one left by a dead owner\n");
+		CHECK_ERR(pthread_mutex_consistent(&mtx->lock));
+	} else {
+		CHECK_ERR(err);
+	}
+#else
 	CHECK_ERR(pthread_mutex_lock(&mtx->lock));
+#endif
 }
 
 void mutex_unlock(struct mutex *mtx)
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index 5554937675886ef3..fcac92d307fb7dad 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -142,6 +142,11 @@ void mutex_init(struct mutex *mtx);
 void mutex_init_pshared(struct mutex *mtx);
 /* Initializes a mutex that may be recursively held on the same thread. */
 void mutex_init_recursive(struct mutex *mtx);
+/*
+ * As mutex_init_pshared(), plus the robust attribute: a process dying while
+ * holding it doesn't leave it locked.  Returns false when not available.
+ */
+bool mutex_init_pshared_robust(struct mutex *mtx);
 void mutex_destroy(struct mutex *mtx);
 
 void mutex_lock(struct mutex *mtx) EXCLUSIVE_LOCK_FUNCTION(*mtx);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (15 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:26   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers Arnaldo Carvalho de Melo
  2026-10-13 20:14 ` [PATCH v2 19/19] perf tests: Mark the shell tests not " Arnaldo Carvalho de Melo
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

The mmap size used when none is asked for is derived from
kernel.perf_event_mlock_kb itself (see perf_event_mlock_kb_in_pages()),
that after rounding down to a power of two number of pages ends up being
about all of a single CPU's share of that per user budget, so with 'perf
test' starting all the selected tests at once, any further mapping done
while another test still has its ring buffers mapped fails with:

  Permission error mapping pages.
  Consider increasing /proc/sys/kernel/perf_event_mlock_kb,
  or try again with a smaller value of -m/--mmap_pages.

that then shows up as a failure or, worse, as an "unsupp" skip in tests
like the attr ones, i.e. as a false pass.

Instead of making those tests exclusive, which would serialize them
against every other test, keep them running in parallel with the tests
that don't map ring buffers and have them take turns using the budget,
via a robust, process-shared mutex (see mutex_init_pshared_robust()) in
a MAP_SHARED|MAP_ANONYMOUS area that is inherited by the forked test
processes: a test killed by a signal doesn't leave the budget locked,
the kernel marks the mutex owner-died and the next locker takes it over.

Perf's struct mutex is used, so the lock discipline is checked in clang
builds via -Wthread-safety; as the thread safety analysis can't verify
conditionally held locks, the tests that map ring buffers run through a
helper taking the mutex unconditionally around the test function call.

Robust mutexes aren't always available, at build time or, as glibc and
musl can do when get_robust_list(2)/set_robust_list(2) aren't usable, at
run time: in that case the budget is not shared and the tests run as
before, failing or skipping on starvation instead of hanging on a lock
left behind by a test killed by a signal.

The area is shared by the tests of one 'perf test' invocation only: a
second invocation running at the same time maps its own, so the two
don't take turns with each other.

The sysctl is left alone.  Growing it, to leave room for whatever else
the user has running, would need the original value shared between the
invocations, so that the last one out puts it back, and it only helps a
user who can write the sysctl but lacks CAP_IPC_LOCK, the one capability
that lifts the limit.

None of this is done when just one of the selected tests maps ring
buffers, as there is nothing to share the budget with in that case.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/builtin-test.c | 94 ++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 1 deletion(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index acffa0d0d33d6d30..8d6e652d3af25afe 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -32,6 +32,9 @@
 #include <subcmd/parse-options.h>
 #include <subcmd/run-command.h>
 
+#include <signal.h>
+#include <sys/mman.h>
+
 #include "builtin.h"
 #include "color.h"
 #include "config.h"
@@ -42,6 +45,7 @@
 #include "symbol.h"
 #include "tests-scripts.h"
 #include "tests.h"
+#include "util/mutex.h"
 #include "util/rlimit.h"
 #include "util/strbuf.h"
 #include "util/term.h"
@@ -62,6 +66,17 @@ static bool list_requisites;
 static unsigned int runs_per_test = 1;
 /* Number of lines to include in failure snippet. */
 static unsigned int failure_snippet_lines = 10;
+/*
+ * The tests mapping ring buffers take turns using their share of
+ * kernel.perf_event_mlock_kb via this mutex, shared with the forked test
+ * processes and robust so that a test killed while holding it doesn't leave
+ * the budget locked, see mutex_init_pshared_robust().
+ */
+struct mlock_budget {
+	struct mutex lock;
+};
+
+static struct mlock_budget *mlock_budget;
 const char *dso_to_test;
 const char *test_objdump_path = "objdump";
 static const char *workload_control;
@@ -378,12 +393,30 @@ static void child_test_sig_handler(int sig)
 	siglongjmp(run_test_jmp_buf, sig);
 }
 
+static int run_test_case(struct child_test *child)
+{
+	return test_function(child->test, child->test_case_num)(child->test, child->test_case_num);
+}
+
+/* The thread safety analysis can't express a lock held conditionally. */
+static int run_test_case_mlocked(struct child_test *child)
+{
+	int err;
+
+	mutex_lock(&mlock_budget->lock);
+	err = run_test_case(child);
+	mutex_unlock(&mlock_budget->lock);
+
+	return err;
+}
+
 static int run_test_child(struct child_process *process)
 {
 	const int signals[] = {
 		SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM,
 	};
 	struct child_test *child = container_of(process, struct child_test, process);
+	bool needs_mlock = mlock_budget && child->test->needs_mlock;
 	int err;
 
 	close_parent_fds();
@@ -400,7 +433,12 @@ static int run_test_child(struct child_process *process)
 
 	pr_debug("---- start ----\n");
 	pr_debug("test child forked, pid %d\n", getpid());
-	err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num);
+
+	if (needs_mlock)
+		err = run_test_case_mlocked(child);
+	else
+		err = run_test_case(child);
+
 	pr_debug("---- end(%d) ----\n", err);
 
 	check_leaks();
@@ -1776,6 +1814,59 @@ static struct test_suite **build_suites(void)
 #undef for_each_suite
 }
 
+static bool perf_test__shares_mlock_budget(struct test_suite **suites, int argc,
+					   const char *argv[], struct intlist *skiplist)
+{
+	int sharing = 0;
+
+	/* Nothing to share the budget with if just one test maps ring buffers. */
+	for (int curr_suite = 0; suites[curr_suite]; curr_suite++) {
+		struct test_suite *t = suites[curr_suite];
+		int curr_test_case;
+		bool suite_matched;
+
+		if (!t->needs_mlock || intlist__find(skiplist, curr_suite + 1))
+			continue;
+
+		suite_matched = perf_test__matches(test_description(t, /*test_case=*/-1),
+						   curr_suite, argc, argv);
+		test_suite__for_each_test_case(t, curr_test_case) {
+			if (test_exclusive(t, curr_test_case))
+				continue;
+
+			if (suite_matched ||
+			    perf_test__matches(test_description(t, curr_test_case),
+					       curr_suite, argc, argv))
+				sharing += runs_per_test;
+		}
+		if (sharing > 1)
+			return true;
+	}
+	return false;
+}
+
+static void perf_test__setup_mlock_budget(struct test_suite **suites, int argc,
+					  const char *argv[], struct intlist *skiplist)
+{
+	/* Nothing runs at the same time, nothing to share the budget with. */
+	if (sequential || !perf_test__shares_mlock_budget(suites, argc, argv, skiplist))
+		return;
+
+	mlock_budget = mmap(NULL, sizeof(*mlock_budget), PROT_READ | PROT_WRITE,
+			    MAP_SHARED | MAP_ANONYMOUS, /*fd=*/-1, /*offset=*/0);
+	if (mlock_budget == MAP_FAILED) {
+		mlock_budget = NULL;
+		pr_err("Failed to map the shared area used to take turns using the perf mlock budget\n");
+		return;
+	}
+
+	if (!mutex_init_pshared_robust(&mlock_budget->lock)) {
+		munmap(mlock_budget, sizeof(*mlock_budget));
+		mlock_budget = NULL;
+		pr_debug("Robust mutexes aren't available, not taking turns using the perf mlock budget\n");
+	}
+}
+
 int cmd_test(int argc, const char **argv)
 {
 	const char *test_usage[] = {
@@ -1884,6 +1975,7 @@ int cmd_test(int argc, const char **argv)
 		intlist__delete(skiplist);
 		return err ? -err : -ENOMEM;
 	}
+	perf_test__setup_mlock_budget(suites, argc, argv, skiplist);
 	ret = __cmd_test(suites, argc, argv, skiplist);
 	free(suites);
 	intlist__delete(skiplist);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (16 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:25   ` sashiko-bot
  2026-10-13 20:14 ` [PATCH v2 19/19] perf tests: Mark the shell tests not " Arnaldo Carvalho de Melo
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

The C tests that run 'perf record' in the test process itself map ring
buffers and so need room in the per user budget set in
kernel.perf_event_mlock_kb: mark them with the new 'needs_mlock' suite
flag, see the previous changes, so that they take turns using that
budget while the tests that don't map them keep running in parallel.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/backward-ring-buffer.c     | 2 +-
 tools/perf/tests/code-reading.c             | 2 +-
 tools/perf/tests/keep-tracking.c            | 2 +-
 tools/perf/tests/mmap-basic.c               | 1 +
 tools/perf/tests/openat-syscall-tp-fields.c | 1 +
 tools/perf/tests/perf-record.c              | 1 +
 tools/perf/tests/perf-time-to-tsc.c         | 1 +
 tools/perf/tests/sw-clock.c                 | 2 +-
 tools/perf/tests/task-exit.c                | 1 +
 9 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/backward-ring-buffer.c
index 2735cc26d7eec3fb..9314696858ff7c84 100644
--- a/tools/perf/tests/backward-ring-buffer.c
+++ b/tools/perf/tests/backward-ring-buffer.c
@@ -167,4 +167,4 @@ static int test__backward_ring_buffer(struct test_suite *test __maybe_unused, in
 	return ret;
 }
 
-DEFINE_SUITE("Read backward ring buffer", backward_ring_buffer);
+DEFINE_SUITE_NEEDS_MLOCK("Read backward ring buffer", backward_ring_buffer);
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
index c50b526f44fb523f..130cd6021f930597 100644
--- a/tools/perf/tests/code-reading.c
+++ b/tools/perf/tests/code-reading.c
@@ -885,4 +885,4 @@ static int test__code_reading(struct test_suite *test __maybe_unused, int subtes
 	};
 }
 
-DEFINE_SUITE("Object code reading", code_reading);
+DEFINE_SUITE_NEEDS_MLOCK("Object code reading", code_reading);
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c
index b760041bed307859..6d8d8cd57200645d 100644
--- a/tools/perf/tests/keep-tracking.c
+++ b/tools/perf/tests/keep-tracking.c
@@ -161,4 +161,4 @@ static int test__keep_tracking(struct test_suite *test __maybe_unused, int subte
 	return err;
 }
 
-DEFINE_SUITE("Use a dummy software event to keep tracking", keep_tracking);
+DEFINE_SUITE_NEEDS_MLOCK("Use a dummy software event to keep tracking", keep_tracking);
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 5cec7644952c7dba..02cfe8533ac05036 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -448,4 +448,5 @@ static struct test_case tests__basic_mmap[] = {
 struct test_suite suite__basic_mmap = {
 	.desc = "mmap interface tests",
 	.test_cases = tests__basic_mmap,
+	.needs_mlock = true,
 };
diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c
index 5365889d326f8bc7..1ea336fbc7e7c030 100644
--- a/tools/perf/tests/openat-syscall-tp-fields.c
+++ b/tools/perf/tests/openat-syscall-tp-fields.c
@@ -160,4 +160,5 @@ static struct test_case tests__syscall_openat_tp_fields[] = {
 struct test_suite suite__syscall_openat_tp_fields = {
 	.desc = "syscalls:sys_enter_openat event fields",
 	.test_cases = tests__syscall_openat_tp_fields,
+	.needs_mlock = true,
 };
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c
index 0cac6ae1a1fc05ed..370dbde08eac71c3 100644
--- a/tools/perf/tests/perf-record.c
+++ b/tools/perf/tests/perf-record.c
@@ -372,4 +372,5 @@ static struct test_case tests__PERF_RECORD[] = {
 struct test_suite suite__PERF_RECORD = {
 	.desc = "PERF_RECORD_* events & perf_sample fields",
 	.test_cases = tests__PERF_RECORD,
+	.needs_mlock = true,
 };
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c
index f8f71fdd32b1bfed..0cefa2df2db728f8 100644
--- a/tools/perf/tests/perf-time-to-tsc.c
+++ b/tools/perf/tests/perf-time-to-tsc.c
@@ -218,4 +218,5 @@ static struct test_case time_to_tsc_tests[] = {
 struct test_suite suite__perf_time_to_tsc = {
 	.desc = "Convert perf time to TSC",
 	.test_cases = time_to_tsc_tests,
+	.needs_mlock = true,
 };
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c
index d181858816352c67..1b62dae55f076d30 100644
--- a/tools/perf/tests/sw-clock.c
+++ b/tools/perf/tests/sw-clock.c
@@ -149,4 +149,4 @@ static int test__sw_clock_freq(struct test_suite *test __maybe_unused, int subte
 	return ret;
 }
 
-DEFINE_SUITE("Software clock events period values", sw_clock_freq);
+DEFINE_SUITE_NEEDS_MLOCK("Software clock events period values", sw_clock_freq);
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c
index 95393edbfe363e5e..9fac426ac2f9eab3 100644
--- a/tools/perf/tests/task-exit.c
+++ b/tools/perf/tests/task-exit.c
@@ -158,4 +158,5 @@ struct test_case tests__task_exit[] = {
 struct test_suite suite__task_exit = {
 	.desc = "Number of exit events of a simple workload",
 	.test_cases = tests__task_exit,
+	.needs_mlock = true,
 };
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v2 19/19] perf tests: Mark the shell tests not mapping perf ring buffers
  2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
                   ` (17 preceding siblings ...)
  2026-10-13 20:14 ` [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers Arnaldo Carvalho de Melo
@ 2026-10-13 20:14 ` Arnaldo Carvalho de Melo
  2026-10-13 20:27   ` sashiko-bot
  18 siblings, 1 reply; 39+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-10-13 20:14 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
	Arnaldo Carvalho de Melo

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Shell tests run perf, that maps ring buffers, so they take a turn using
the per user budget in kernel.perf_event_mlock_kb by default, see the
previous changes; the ones that don't map them, like the ones running
just 'perf stat', say " (no_mlock)" in their description line and thus
keep running in parallel with the ones taking turns.

The 'perf sched stats' tests are among them, they synthesize the samples
from /proc/schedstat without mapping a ring buffer, and the tests already
marked " (exclusive)" don't need it, exclusive implies every other
requisite and keeps them running alone.

Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/shell/drm_pmu.sh                       | 2 +-
 tools/perf/tests/shell/ftrace.sh                        | 2 +-
 tools/perf/tests/shell/kallsyms.sh                      | 2 +-
 tools/perf/tests/shell/list.sh                          | 2 +-
 tools/perf/tests/shell/perf_sched_stats.sh              | 2 +-
 tools/perf/tests/shell/probe_vfs_getname.sh             | 2 +-
 tools/perf/tests/shell/python-use.sh                    | 2 +-
 tools/perf/tests/shell/schedstat_snapshots.sh           | 2 +-
 tools/perf/tests/shell/stat+csv_output.sh               | 2 +-
 tools/perf/tests/shell/stat+csv_summary.sh              | 2 +-
 tools/perf/tests/shell/stat+event_uniquifying.sh        | 2 +-
 tools/perf/tests/shell/stat+json_output.sh              | 2 +-
 tools/perf/tests/shell/stat+shadow_stat.sh              | 2 +-
 tools/perf/tests/shell/stat+std_output.sh               | 2 +-
 tools/perf/tests/shell/stat.sh                          | 2 +-
 tools/perf/tests/shell/stat_all_metricgroups.sh         | 2 +-
 tools/perf/tests/shell/stat_all_metrics.sh              | 2 +-
 tools/perf/tests/shell/stat_all_pfm.sh                  | 2 +-
 tools/perf/tests/shell/stat_bpf_counters_cgrp.sh        | 2 +-
 tools/perf/tests/shell/stat_metrics_cgrp.sh             | 2 +-
 tools/perf/tests/shell/stat_metrics_values.sh           | 2 +-
 tools/perf/tests/shell/test_ilist_python.sh             | 2 +-
 tools/perf/tests/shell/test_stat_cpi_python.sh          | 2 +-
 tools/perf/tests/shell/test_test_junit_output.sh        | 2 +-
 tools/perf/tests/shell/test_uprobe_from_different_cu.sh | 2 +-
 25 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/shell/drm_pmu.sh b/tools/perf/tests/shell/drm_pmu.sh
index e629fe0e8463d6cc..c827e613fc66ee0f 100755
--- a/tools/perf/tests/shell/drm_pmu.sh
+++ b/tools/perf/tests/shell/drm_pmu.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# DRM PMU
+# DRM PMU (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh
index 9f6e590f6437b2d9..84d6126bf94d1e61 100755
--- a/tools/perf/tests/shell/ftrace.sh
+++ b/tools/perf/tests/shell/ftrace.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf ftrace tests
+# perf ftrace tests (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/kallsyms.sh b/tools/perf/tests/shell/kallsyms.sh
index d0eb99753f477f57..e3674cc52b30983f 100755
--- a/tools/perf/tests/shell/kallsyms.sh
+++ b/tools/perf/tests/shell/kallsyms.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf kallsyms tests
+# perf kallsyms tests (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 err=0
diff --git a/tools/perf/tests/shell/list.sh b/tools/perf/tests/shell/list.sh
index 14a03e0290ed1b4d..d4a0b04154d636f0 100755
--- a/tools/perf/tests/shell/list.sh
+++ b/tools/perf/tests/shell/list.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf list tests
+# perf list tests (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/perf_sched_stats.sh b/tools/perf/tests/shell/perf_sched_stats.sh
index f13eb0a75b76ef5a..4ede9d701630c719 100755
--- a/tools/perf/tests/shell/perf_sched_stats.sh
+++ b/tools/perf/tests/shell/perf_sched_stats.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# perf sched stats tests
+# perf sched stats tests (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh
index c40e5f3f0825b051..43bdd693076b408c 100755
--- a/tools/perf/tests/shell/probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/probe_vfs_getname.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Add vfs_getname probe to get syscall args filenames
+# Add vfs_getname probe to get syscall args filenames (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
 
diff --git a/tools/perf/tests/shell/python-use.sh b/tools/perf/tests/shell/python-use.sh
index 2d6017d5dd9d9eab..ece4d306869ae48e 100755
--- a/tools/perf/tests/shell/python-use.sh
+++ b/tools/perf/tests/shell/python-use.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# 'import perf' in python
+# 'import perf' in python (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 # Just test if we can load the python binding.
 set -e
diff --git a/tools/perf/tests/shell/schedstat_snapshots.sh b/tools/perf/tests/shell/schedstat_snapshots.sh
index a4bdf2673d43ccc0..2050478e3f1a682f 100755
--- a/tools/perf/tests/shell/schedstat_snapshots.sh
+++ b/tools/perf/tests/shell/schedstat_snapshots.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Validate CPU and domain pairing in perf sched stats snapshots
+# Validate CPU and domain pairing in perf sched stats snapshots (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh
index a5b34c42e8d72aff..8beb6c674d055a52 100755
--- a/tools/perf/tests/shell/stat+csv_output.sh
+++ b/tools/perf/tests/shell/stat+csv_output.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat CSV output linter
+# perf stat CSV output linter (no_mlock)
 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 # Tests various perf stat CSV output commands for the
 # correct number of fields and the CSV separator set to ','.
diff --git a/tools/perf/tests/shell/stat+csv_summary.sh b/tools/perf/tests/shell/stat+csv_summary.sh
index 9a4353db3825ede9..6d8beb88fb6f300a 100755
--- a/tools/perf/tests/shell/stat+csv_summary.sh
+++ b/tools/perf/tests/shell/stat+csv_summary.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat csv summary test
+# perf stat csv summary test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat+event_uniquifying.sh b/tools/perf/tests/shell/stat+event_uniquifying.sh
index b5dec6b6da369382..f5c6fa30d8db38d1 100755
--- a/tools/perf/tests/shell/stat+event_uniquifying.sh
+++ b/tools/perf/tests/shell/stat+event_uniquifying.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat events uniquifying
+# perf stat events uniquifying (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/shell/stat+json_output.sh
index 4969ad70b67b6196..cbf7a4ffd7e8ab4f 100755
--- a/tools/perf/tests/shell/stat+json_output.sh
+++ b/tools/perf/tests/shell/stat+json_output.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat JSON output linter
+# perf stat JSON output linter (no_mlock)
 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 # Checks various perf stat JSON output commands for the
 # correct number of fields.
diff --git a/tools/perf/tests/shell/stat+shadow_stat.sh b/tools/perf/tests/shell/stat+shadow_stat.sh
index cabbbf17c662df23..7a58e1507ce27957 100755
--- a/tools/perf/tests/shell/stat+shadow_stat.sh
+++ b/tools/perf/tests/shell/stat+shadow_stat.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat metrics (shadow stat) test
+# perf stat metrics (shadow stat) test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh
index 64c2b81f02deb18b..b915656a20f69d68 100755
--- a/tools/perf/tests/shell/stat+std_output.sh
+++ b/tools/perf/tests/shell/stat+std_output.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat STD output linter
+# perf stat STD output linter (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 # Tests various perf stat STD output commands for
 # default event and metricgroup
diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh
index 1f5d4cc05807a247..ac9e031831daca9c 100755
--- a/tools/perf/tests/shell/stat.sh
+++ b/tools/perf/tests/shell/stat.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat tests
+# perf stat tests (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat_all_metricgroups.sh b/tools/perf/tests/shell/stat_all_metricgroups.sh
index 81bc7070b5ab0d5c..074a437da0ae3dce 100755
--- a/tools/perf/tests/shell/stat_all_metricgroups.sh
+++ b/tools/perf/tests/shell/stat_all_metricgroups.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf all metricgroups test
+# perf all metricgroups test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 ParanoidAndNotRoot()
diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh
index df5fa27b21ae28d8..9d0d4b8a217e6730 100755
--- a/tools/perf/tests/shell/stat_all_metrics.sh
+++ b/tools/perf/tests/shell/stat_all_metrics.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf all metrics test
+# perf all metrics test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 ParanoidAndNotRoot()
diff --git a/tools/perf/tests/shell/stat_all_pfm.sh b/tools/perf/tests/shell/stat_all_pfm.sh
index c08c186af2c42e84..78d8006fb729c8f4 100755
--- a/tools/perf/tests/shell/stat_all_pfm.sh
+++ b/tools/perf/tests/shell/stat_all_pfm.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf all libpfm4 events test
+# perf all libpfm4 events test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 if perf version --build-options | grep HAVE_LIBPFM | grep -q OFF
diff --git a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh
index febe3af0644157f3..d1b4632c17a3e8e1 100755
--- a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh
+++ b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat --bpf-counters --for-each-cgroup test
+# perf stat --bpf-counters --for-each-cgroup test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh
index 0728f48f21fa971d..75b8e01d11cc7ff3 100755
--- a/tools/perf/tests/shell/stat_metrics_cgrp.sh
+++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf stat metrics --for-each-cgroup test
+# perf stat metrics --for-each-cgroup test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tests/shell/stat_metrics_values.sh
index 0982f6b9737a780f..3e073a0e5b9f9b99 100755
--- a/tools/perf/tests/shell/stat_metrics_values.sh
+++ b/tools/perf/tests/shell/stat_metrics_values.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf metrics value validation
+# perf metrics value validation (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 shelldir=$(dirname "$0")
diff --git a/tools/perf/tests/shell/test_ilist_python.sh b/tools/perf/tests/shell/test_ilist_python.sh
index 83048474710d2857..a235539c01767ee5 100755
--- a/tools/perf/tests/shell/test_ilist_python.sh
+++ b/tools/perf/tests/shell/test_ilist_python.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# perf script ilist and perf list --tui test
+# perf script ilist and perf list --tui test (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
diff --git a/tools/perf/tests/shell/test_stat_cpi_python.sh b/tools/perf/tests/shell/test_stat_cpi_python.sh
index 6cb376c92e2f13a7..d1571a20f4683d73 100755
--- a/tools/perf/tests/shell/test_stat_cpi_python.sh
+++ b/tools/perf/tests/shell/test_stat_cpi_python.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 # SPDX-License-Identifier: GPL-2.0
-# stat-cpi python test
+# stat-cpi python test (no_mlock)
 
 set -e
 
diff --git a/tools/perf/tests/shell/test_test_junit_output.sh b/tools/perf/tests/shell/test_test_junit_output.sh
index 38a29e646906e3aa..2a3e8a00c48b7d39 100755
--- a/tools/perf/tests/shell/test_test_junit_output.sh
+++ b/tools/perf/tests/shell/test_test_junit_output.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 # SPDX-License-Identifier: GPL-2.0
-# perf test junit XML output validation
+# perf test junit XML output validation (no_mlock)
 
 set -e
 
diff --git a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh
index 33bf36c4037e45ae..0a02ff9b86d6f326 100755
--- a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh
+++ b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# test perf probe of function from different CU
+# test perf probe of function from different CU (no_mlock)
 # SPDX-License-Identifier: GPL-2.0
 
 set -e
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations
  2026-10-13 20:14 ` [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations Arnaldo Carvalho de Melo
@ 2026-10-13 20:18   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:18 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> When matching the recorded events back against the expectations,
> attr.py requires every event opened in a group to match an expectation
> that declares the same group.  The perf stat default metrics open the
> events of each metric in groups, but the test expectations only verify
> the event attributes, so test-stat-default fails with:
> 
>   FAILED '.../test-stat-default' - group failure
> 
> Only enforce the group of a recorded event when the expectation it
> matches declares one.  Expectations that ask for a group are still
> verified in both directions.
> 
> Fixes: c7adeb0974f18da4 ("perf jevents: Add set of common metrics based on default ones")
> Fixes: 2dfc0cab3da4a205 ("perf stat: Add detail -d,-dd,-ddd metrics")
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=5


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type()
  2026-10-13 20:14 ` [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type() Arnaldo Carvalho de Melo
@ 2026-10-13 20:19   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:19 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> What hide_core_pmu_type() does is take a set of bits out of each of the
> '|'-separated alternatives in a value, wildcards included, keeping the
> rest of the expression: extract that as mask_bits() and implement
> hide_core_pmu_type() in terms of it, a prep for using the same thing to
> ignore, when comparing expectations, the sample id bit perf ends up
> asking for and the read_format group bit that a PMU count can remove.
> 
> No change in behaviour.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=9


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove
  2026-10-13 20:14 ` [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove Arnaldo Carvalho de Melo
@ 2026-10-13 20:20   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:20 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> read_format's PERF_FORMAT_GROUP bit asks for the group members to be read
> along with the leader, which aside from this bit is verified with the
> fd/group_fd based group checking in Test.compare(), and with more than
> one core PMU a group whose members are pinned to one of them, say
> 'default_core', leaves the clones for the other core PMUs without any
> members:
> 
>   $ perf stat -e '{cycles,default_core/legacy-hardware-config=3/}:S'
>   ... the A53 leader keeps its cohort, both A73 cycles clones are alone,
>       read_format 20 instead of 28
> 
> so that bit isn't there: whether it is there is a consequence of how the
> evlist got expanded to more than one core PMU, not of the groupness the
> [ ... ]
> as when attr.py runs without setup_python.sh, the count isn't available
> and the bit is taken out as well.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=13


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation
  2026-10-13 20:13 ` [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation Arnaldo Carvalho de Melo
@ 2026-10-13 20:21   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:21 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Since commit bdd051e24914 ("perf record: Split --data-mmap option"),
> record_opts__parse_callchain() sets record_data_mmap instead of
> record->sample_address for the DWARF unwind mode, so -d/--data and its
> implied PERF_SAMPLE_DATA_SRC are not enabled implicitly anymore.
> 
> As a result, --call-graph dwarf no longer sets PERF_SAMPLE_ADDR and
> PERF_SAMPLE_DATA_SRC, making the test fail:
> 
>   expected sample_type=45359, got 12583
> 
> where 45359 (0xb12f) is:
> 
>   IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC
> 
> and 12583 (0x3127) is:
> 
>   IP|TID|TIME|CALLCHAIN|PERIOD|REGS_USER|STACK_USER
> 
> i.e. the values differ exactly by PERF_SAMPLE_ADDR (0x8) and
> PERF_SAMPLE_DATA_SRC (0x8000).  The mmap_data=1 expectation is still
> correct as DWARF now enables record_data_mmap.
> 
> This went unnoticed until tests/attr.sh stopped ignoring attr.py's
> return value.
> 
> Fixes: bdd051e249141c79 ("perf record: Split --data-mmap option")
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=1


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs
  2026-10-13 20:14 ` [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs Arnaldo Carvalho de Melo
@ 2026-10-13 20:21   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:21 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> On a hybrid machine the default stat metrics are opened on each core PMU,
> and the events of the other one have their own encodings: on an i7-14700K
> cpu_atom (type 10) opens UOPS_RETIRED.ALL, config 194, which the
> expectations, written for cpu_core (type 4), don't name:
> 
>   expected type=4, got 10
>   expected config=209|391, got 194
> 
> The test already masks the core PMU type out of 'config' for the legacy
> events, but raw events keep their type and encoding, so leave out the
> events of the core PMUs no expectation names, like the dummy tracking
> event.  Core PMUs are found as perf finds them: 'cpu' or 'cpum_cf', or a
> PMU with a 'cpus' file in sysfs.  Nothing is left out unless an
> expectation names a core PMU, so single core PMU machines and the tests
> with only legacy expectations are not affected.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=12


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries
  2026-10-13 20:14 ` [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries Arnaldo Carvalho de Melo
@ 2026-10-13 20:21   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:21 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> 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.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=11


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped
  2026-10-13 20:14 ` [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped Arnaldo Carvalho de Melo
@ 2026-10-13 20:22   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:22 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] perf test: TypeError and IndexError if kernel_since or kernel_until has one component
--

commit 21b41941c9b2a0fd4a3bf8744960357d34dbae9e
Author: Arnaldo Carvalho de Melo <acme@redhat.com>

perf test attr: State, spelled out in a line, why a test is skipped

This patch updates the test skipping logic in the perf test attribute scripts
to return descriptive strings instead of boolean values. This enables the
test suite to explicitly log why a test is skipped instead of just reporting
a generic skip.

> diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
> index 3bf07d4df9598..ee78c912d9469 100644
> --- a/tools/perf/tests/shell/lib/attr.py
> +++ b/tools/perf/tests/shell/lib/attr.py
> @@ -425,13 +428,17 @@ class Test(object):
>  
>      def skip_test_kernel_since(self):
>          if not self.kernel_since:
> -            return False
> -        return not self.kernel_since <= parse_version(platform.release())
> +            return None
> +        if self.kernel_since <= parse_version(platform.release()):
> +            return None
> +        return "kernel older than %d.%d" % tuple(self.kernel_since)

[Severity: Medium]
Does this code raise a TypeError if a test specifies a single integer for
kernel_since (for example, kernel_since = 6)?

Prior to this patch, Python's native list comparison safely handled 1-element
vs 2-element list comparisons without crashing. It looks like formatting a
variable-length tuple with a fixed 2-element format string %d.%d will cause
a TypeError for 1-element lists.

>  
>      def skip_test_kernel_until(self):
>          if not self.kernel_until:
> -            return False
> -        return not parse_version(platform.release()) < self.kernel_until
> +            return None
> +        if parse_version(platform.release()) < self.kernel_until:
> +            return None
> +        return "kernel newer than %d.%d" % tuple(self.kernel_until)

[Severity: Medium]
Will this formatting also trigger a TypeError if kernel_until contains only
one component?

[ ... ]

> @@ -529,20 +537,36 @@ class Test(object):
>      def run_cmd(self, tempdir):
>          _junk1, _junk2, _junk3, _junk4, myarch = (os.uname())
>  
[ ... ]
> +        kernel = self.skip_test_kernel_since()
> +        if kernel:
> +            log.debug("  detail: the test file's kernel_since = %d.%d, this kernel is %s",
> +                     self.kernel_since[0], self.kernel_since[1], platform.release())

[Severity: Medium]
Could directly accessing index 1 via self.kernel_since[1] cause an IndexError
when the list only contains a major version?

> +            raise Notest(self, kernel)
> +
> +        kernel = self.skip_test_kernel_until()
> +        if kernel:
> +            log.debug("  detail: the test file's kernel_until = %d.%d, this kernel is %s",
> +                     self.kernel_until[0], self.kernel_until[1], platform.release())

[Severity: Medium]
Similarly, does accessing self.kernel_until[1] raise an IndexError if the test
specifies only a single-component version?

> +            raise Notest(self, kernel)

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=14

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests
  2026-10-13 20:13 ` [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests Arnaldo Carvalho de Melo
@ 2026-10-13 20:22   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:22 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Since commit b1c5efbfd92e ("perf parse-events: Remove hard coded legacy
> hardware and cache parsing"), event names are resolved with sysfs/JSON
> priority over the legacy encodings, so bare cache-misses and
> instructions now become the core PMU sysfs/JSON events (on x86 type 4,
> configs 0x412e and 0xc0) rather than the legacy
> PERF_TYPE_HARDWARE/config=3 and config=1 events the tests expect, e.g.:
> 
>   expected config=3, got 0
>   expected disabled=0, got 1
>   expected mmap=0, got 1
>   expected comm=0, got 1
>   expected freq=0, got 1
>   expected enable_on_exec=0, got 1
> [ ... ]
> return value.
> 
> Fixes: b1c5efbfd92eb84d ("perf parse-events: Remove hard coded legacy hardware and cache parsing")
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=3


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open
  2026-10-13 20:14 ` [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open Arnaldo Carvalho de Melo
@ 2026-10-13 20:23   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:23 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Opening a group with perf record on a machine with perf_event_paranoid >
> 1 and no CAP_PERFMON fails the first perf_event_open() with EACCES, after
> which evsel__open_cpu() clears evsel->supported; evsel__fallback() then
> adds exclude_kernel and record__open() retries, and the retry succeeds
> with supported left false.  get_group_fd() treats unsupported events as
> ungroupable, so the retry opens the group members without their leader:
> 
>   sys_perf_event_open: pid 12345 cpu 0 group_fd -1
>   FAILED '.../test-record-group-sampling1' - group failure
> 
> which fails the group subtests of "perf test 82", the Perf attribute
> expectations test, when it is run as a regular user, while it passes as
> root or with CAP_PERFMON, where the first open doesn't fail.
> 
> commit 2cc7aa995ce9 ("perf stat: Refactor retry/skip/fatal error handling")
> dropped the pos->supported = true that record__open() did after a
> successful open and moved the resets into the stat code only, while
> saying "When an event is tried the supported is set to true again".  Do
> that in evsel__open_cpu(), which every open entry point funnels through,
> so that a retry describes its own try instead of the previous failure.
> 
> Fixes: 2cc7aa995ce9ecd8 ("perf stat: Refactor retry/skip/fatal error handling")
> Cc: Ian Rogers <irogers@google.com>
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=6


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag
  2026-10-13 20:14 ` [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag Arnaldo Carvalho de Melo
@ 2026-10-13 20:23   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:23 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Each perf instance maps one ring buffer per CPU, all charged to the same
> per user budget set in kernel.perf_event_mlock_kb, so suites that map
> them need to be told apart from the ones that don't, to later have them
> take turns using that budget while the other tests keep running in
> parallel.
> 
> Add the flag to 'struct test_suite' and a DEFINE_SUITE_NEEDS_MLOCK()
> variant for the suites using the DEFINE_SUITE() form.
> 
> Shell tests run perf, that maps ring buffers, so they are taken to need
> room in that budget by default; the ones that don't map them waive it in
> the requisites, a comma separated list in the parens ending their
> description line, e.g.:
> 
>   # perf stat tests (no_mlock)
> 
> "exclusive" is the catch-all that keeps a test from running alongside
> any other, so it runs alone, in sequence, and implies every other
> requisite, "no_mlock" waives the default, and more keywords are to come,
> each stating what a test needs, till the catch-all isn't needed anymore.
> 
> 'perf test requisites' lists what each test needs, reusing the listing
> routine and taking the same test name fragments and numbers as 'list'.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=15


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for
  2026-10-13 20:14 ` [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for Arnaldo Carvalho de Melo
@ 2026-10-13 20:24   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:24 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> evlist__config() asks for a way to tell the samples apart,
> PERF_SAMPLE_IDENTIFIER where available, PERF_SAMPLE_ID otherwise:
> 
>   bool sample_id = opts->sample_id;
>   ...
>   if (opts->full_auxtrace || opts->sample_identifier) {
>         ...
>         use_sample_identifier = perf_can_sample_identifier();
>         sample_id = true;
>   } else if (evlist__nr_entries(evlist) > 1) {
>         ...
>         use_sample_identifier = perf_can_sample_identifier();
>         sample_id = true;
> [ ... ]
> of which side is the one perf recorded to take the bits out of the right
> side.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=10


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers
  2026-10-13 20:14 ` [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers Arnaldo Carvalho de Melo
@ 2026-10-13 20:25   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:25 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> The C tests that run 'perf record' in the test process itself map ring
> buffers and so need room in the per user budget set in
> kernel.perf_event_mlock_kb: mark them with the new 'needs_mlock' suite
> flag, see the previous changes, so that they take turns using that
> budget while the tests that don't map them keep running in parallel.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=18


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config'
  2026-10-13 20:14 ` [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config' Arnaldo Carvalho de Melo
@ 2026-10-13 20:25   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:25 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] perf: KeyError crash in Event.equal() on missing event terms
--

commit 2d6e742f00c1817f447e64e8fed117f4dbcf95c2
Author: Arnaldo Carvalho de Melo <acme@redhat.com>

perf test attr: Ignore the core PMU type perf puts in 'config'

Since commit 55bcf6ef314a in v5.13, and arm_pmu opted in with commit
5c816728651a 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'. This patch
takes those bits out when comparing the 'config' of legacy type events so
expectations written for the single core PMU case continue matching on
heterogeneous machines.

> diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
> index f254d03c1b253..70ff73585cb3f 100644
> --- a/tools/perf/tests/shell/lib/attr.py
> +++ b/tools/perf/tests/shell/lib/attr.py

[ ... ]

> @@ -130,12 +156,33 @@ class Event(dict):
>          self.add(base)
>          self.add(data)
>  

[ ... ]

>      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:

[Severity: Medium]
In Event.equal(), log.debug() accesses self[t] and other[t] before the
boundary check executes:

    log.debug("      [%s] %s %s", t, self[t], other[t])
    if t not in self or t not in other:

Because the Event class inherits from the standard Python dict and does not
provide default values for missing keys, will accessing a non-existent key
here immediately raise an unhandled KeyError instead of gracefully returning
False?

>                  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
>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=7

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR
  2026-10-13 20:14 ` [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR Arnaldo Carvalho de Melo
@ 2026-10-13 20:26   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:26 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Events opened just to ask if they are supported, i.e. the ones in
> is_event_supported() and is_libpfm_event_supported(), go through the
> regular evsel__open() path and thus end up in the event files written to
> $PERF_TEST_ATTR, with everything but type, config and disabled zeroed:
> 
>   [event-0-38654705664-3]
>   fd=3
>   cpu=-1
>   pid=0
>   type=0
>   size=0
>   config=38654705664
>   sample_type=0
> [ ... ]
> 
> Reported-by: Namhyung Kim <namhyung@kernel.org>
> Link: https://lore.kernel.org/linux-perf-users/asWQvXRdqp_RK85c@google.com/
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=8


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests
  2026-10-13 20:14 ` [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests Arnaldo Carvalho de Melo
@ 2026-10-13 20:26   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:26 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> The mmap size used when none is asked for is derived from
> kernel.perf_event_mlock_kb itself (see perf_event_mlock_kb_in_pages()),
> that after rounding down to a power of two number of pages ends up being
> about all of a single CPU's share of that per user budget, so with 'perf
> test' starting all the selected tests at once, any further mapping done
> while another test still has its ring buffers mapped fails with:
> 
>   Permission error mapping pages.
>   Consider increasing /proc/sys/kernel/perf_event_mlock_kb,
>   or try again with a smaller value of -m/--mmap_pages.
> 
> that then shows up as a failure or, worse, as an "unsupp" skip in tests
> like the attr ones, i.e. as a false pass.
> [ ... ]
> None of this is done when just one of the selected tests maps ring
> buffers, as there is nothing to share the budget with in that case.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=17


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 19/19] perf tests: Mark the shell tests not mapping perf ring buffers
  2026-10-13 20:14 ` [PATCH v2 19/19] perf tests: Mark the shell tests not " Arnaldo Carvalho de Melo
@ 2026-10-13 20:27   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:27 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Shell tests run perf, that maps ring buffers, so they take a turn using
> the per user budget in kernel.perf_event_mlock_kb by default, see the
> previous changes; the ones that don't map them, like the ones running
> just 'perf stat', say " (no_mlock)" in their description line and thus
> keep running in parallel with the ones taking turns.
> 
> The 'perf sched stats' tests are among them, they synthesize the samples
> from /proc/schedstat without mapping a ring buffer, and the tests already
> marked " (exclusive)" don't need it, exclusive implies every other
> requisite and keeps them running alone.
> 
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=19


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer
  2026-10-13 20:14 ` [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer Arnaldo Carvalho de Melo
@ 2026-10-13 20:27   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:27 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] perf/mutex: mutex_trylock and cond_wait break robust mutexes
--

commit be4b6030ad2e50ab9f547f2302595658b05224cb
Author: Arnaldo Carvalho de Melo <acme@redhat.com>

perf mutex: Add a robust, process-shared mutex initializer

A mutex held by a process that can die before unlocking it, like the
tests forked by 'perf test' that will take turns using the per user
budget set in kernel.perf_event_mlock_kb, has to be taken over by the
next locker instead of staying locked forever.

Robust mutexes, on top of the kernel's robust futexes, do that: when an
owner dies while holding it the kernel marks the futex word owner-died
and the next pthread_mutex_lock() returns EOWNERDEAD, then mutex_lock()
makes it consistent and takes it over.

> diff --git a/tools/perf/util/mutex.c b/tools/perf/util/mutex.c
> index 7aa1f3f55a7d4..712fbf839d590 100644
> --- a/tools/perf/util/mutex.c
> +++ b/tools/perf/util/mutex.c

[ ... ]

> @@ -58,7 +105,22 @@ void mutex_destroy(struct mutex *mtx)
>  void mutex_lock(struct mutex *mtx)
>  	NO_THREAD_SAFETY_ANALYSIS
>  {
> +#ifdef HAVE_PTHREAD_MUTEX_ROBUST
> +	int err = pthread_mutex_lock(&mtx->lock);
> +
> +	/*
> +	 * A robust mutex whose owner died while holding it comes back as
> +	 * EOWNERDEAD, marked by the kernel: take it over.
> +	 */
> +	if (err == EOWNERDEAD) {
> +		pr_debug("mutex: taking over one left by a dead owner\n");
> +		CHECK_ERR(pthread_mutex_consistent(&mtx->lock));
> +	} else {
> +		CHECK_ERR(err);
> +	}
> +#else
>  	CHECK_ERR(pthread_mutex_lock(&mtx->lock));
> +#endif
>  }

[Severity: Medium]
Since this patch introduces robust mutexes and adds EOWNERDEAD handling to
mutex_lock(), does it leave mutex_trylock() and cond_wait() broken when used
with them?

If pthread_mutex_trylock() returns EOWNERDEAD, the lock is acquired by the
caller, but mutex_trylock() currently treats it as an error:

tools/perf/util/mutex.c:mutex_trylock() {
        ...
        if (ret == 0)
                return true; /* Lock acquired. */

        if (ret == EBUSY)
                return false; /* Lock busy. */

        /* Print error. */
        CHECK_ERR(ret);
        return false;
}

This will silently leak the lock and cause the caller to incorrectly assume
the lock is not held.

Similarly, if pthread_cond_wait() returns EOWNERDEAD:

tools/perf/util/mutex.c:cond_wait() {
        CHECK_ERR(pthread_cond_wait(&cnd->cond, &mtx->lock));
}

The mutex is re-acquired but inconsistent. Failing to call
pthread_mutex_consistent() before the caller unlocks it will permanently
break the mutex.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=16

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU
  2026-10-13 20:13 ` [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU Arnaldo Carvalho de Melo
@ 2026-10-13 20:28   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:28 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] perf python: memory leak of parse_events_error in pyrf__parse_events()
--

commit f47ff7c9fc4b5bf17c150eab6410f6c98f01cb7c
Author: Arnaldo Carvalho de Melo <acme@redhat.com>

perf test attr: Allow tests to require a core PMU

On machines without a usable core PMU, like an aarch64 box with no DT
node for the ARM PMU, the expectations that pin the legacy hardware
encoding can't be checked: without a core PMU at all 'cycles' doesn't
even resolve, and with one that can't be opened 'perf record' takes the
software fallback and records attributes they can never match.

> diff --git a/tools/perf/tests/shell/lib/attr.py b/tools/perf/tests/shell/lib/attr.py
> index 68f06df9565a0..b55c9b881e598 100644
> --- a/tools/perf/tests/shell/lib/attr.py
> +++ b/tools/perf/tests/shell/lib/attr.py

[ ... ]

> @@ -28,6 +29,34 @@ def data_equal(a, b):
>  
>      return False
>  
> +def core_pmu_available():
> +    """
> +    Is there a core PMU on this machine? Without one hardware events aren't
> +    supported and get replaced by the software fallback, so there's no way to
> +    check the expectations that pin their legacy encodings.
> +    """
> +    try:
> +        import perf
> +        parse_events = perf.parse_events
> +    except (ImportError, AttributeError):
> +        # Nothing to ask about it, let the expectations speak.
> +        return True
> +
> +    try:
> +        evlist = parse_events('cycles')
> +    except OSError:
> +        # No PMU that can count it, the same as the ENOENT below.
> +        return False
> +

[Severity: Medium]
Does this expected failure trigger a memory leak of the parse_events_error
structure in the underlying C extension?

When parse_events() fails or generates warnings, it dynamically allocates
memory for error strings. However, looking at the python bindings in
tools/perf/util/python.c:

tools/perf/util/python.c:pyrf__parse_events() {
	...
	parse_events_error__init(&err);
	perf_evlist__set_maps(evlist__core(evlist), cpus, threads);
	if (parse_events(evlist, input, &err)) {
		parse_events_error__print(&err, input);
		PyErr_SetFromErrno(PyExc_OSError);
		evlist__put(evlist);
		return NULL;
	}
	result = pyrf_evlist__from_evlist(evlist);
	evlist__put(evlist);
	return result;
}

The err structure is dynamically populated but parse_events_error__exit(&err)
is never called on either the success or error paths, leaking memory whenever
errors or warnings are generated.

Could pyrf__parse_events() be updated to clean up the error structure?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=2

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations
  2026-10-13 20:13 ` [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations Arnaldo Carvalho de Melo
@ 2026-10-13 20:37   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-10-13 20:37 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users

> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Since commit c7adeb0974f1 ("perf jevents: Add set of common metrics based
> on default ones") and commit 2dfc0cab3da4 ("perf stat: Add detail -d,-dd,-ddd
> metrics") the perf stat default metrics open their events in groups, so
> group leaders and members have read_format=15 and members are enabled
> without enable_on_exec, while standalone events keep read_format=3 and
> are disabled until exec.
> 
> Also, since commit b1c5efbfd92e ("perf parse-events: Remove hard coded
> legacy hardware and cache parsing") bare hardware events resolve to the
> core PMU sysfs/JSON events (type 4), so add the raw encodings seen on
> Intel and AMD for instructions, branch-misses, cpu-cycles and
> stalled-cycles-frontend/backend to test-stat-default and
> test-stat-detailed-1/2/3.
> 
> Add base-stat-group, that accepts both the standalone and the group
> variants of read_format, disabled and enable_on_exec, and point the four
> default/detailed expectations at it.  test-stat-basic, test-stat-C0 and
> test-stat-no-inherit keep base-stat, whose events aren't opened in a
> group: for those, a change that groups or enables them differently is
> still something to catch.  test-stat-group1 does open its events in a
> group, but pins read_format, disabled and enable_on_exec itself, the very
> terms base-stat-group loosens, so it doesn't need it either.
> 
> Fixes: b1c5efbfd92eb84d ("perf parse-events: Remove hard coded legacy hardware and cache parsing")
> Fixes: c7adeb0974f18da4 ("perf jevents: Add set of common metrics based on default ones")
> Fixes: 2dfc0cab3da4a205 ("perf stat: Add detail -d,-dd,-ddd metrics")
> Assisted-by: LLM
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013201415.36428-1-acme@kernel.org?part=4


^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2026-10-13 20:37 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 20:13 [PATCH v2 0/19] perf test attr: Report the failures again and cope with more than one core PMU Arnaldo Carvalho de Melo
2026-10-13 20:13 ` [PATCH v2 01/19] perf test attr: Fix record dwarf sample_type expectation Arnaldo Carvalho de Melo
2026-10-13 20:21   ` sashiko-bot
2026-10-13 20:13 ` [PATCH v2 02/19] perf test attr: Allow tests to require a core PMU Arnaldo Carvalho de Melo
2026-10-13 20:28   ` sashiko-bot
2026-10-13 20:13 ` [PATCH v2 03/19] perf test attr: Fix legacy event encodings in group tests Arnaldo Carvalho de Melo
2026-10-13 20:22   ` sashiko-bot
2026-10-13 20:13 ` [PATCH v2 04/19] perf test attr: Fix default stat metrics expectations Arnaldo Carvalho de Melo
2026-10-13 20:37   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 05/19] perf test attr: Relax group checking for ungrouped expectations Arnaldo Carvalho de Melo
2026-10-13 20:18   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 06/19] perf evsel: Mark the event as supported when retrying an open Arnaldo Carvalho de Melo
2026-10-13 20:23   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 07/19] perf test attr: Ignore the core PMU type perf puts in 'config' Arnaldo Carvalho de Melo
2026-10-13 20:25   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 08/19] perf evsel: Don't dump probe events to PERF_TEST_ATTR Arnaldo Carvalho de Melo
2026-10-13 20:26   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 09/19] perf test attr: Introduce mask_bits(), generalizing hide_core_pmu_type() Arnaldo Carvalho de Melo
2026-10-13 20:19   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 10/19] perf test attr: Accept either sample id bit perf asks for Arnaldo Carvalho de Melo
2026-10-13 20:24   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 11/19] perf test attr: Ignore the dummy tracking event and the terms it carries Arnaldo Carvalho de Melo
2026-10-13 20:21   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 12/19] perf test attr: Leave out the events of the other core PMUs Arnaldo Carvalho de Melo
2026-10-13 20:21   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 13/19] perf test attr: Ignore the group read_format bit the PMU count can remove Arnaldo Carvalho de Melo
2026-10-13 20:20   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 14/19] perf test attr: State, spelled out in a line, why a test is skipped Arnaldo Carvalho de Melo
2026-10-13 20:22   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 15/19] perf tests: Introduce a 'needs_mlock' suite flag Arnaldo Carvalho de Melo
2026-10-13 20:23   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 16/19] perf mutex: Add a robust, process-shared mutex initializer Arnaldo Carvalho de Melo
2026-10-13 20:27   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 17/19] perf test: Take turns using the perf mlock budget in the mmaping tests Arnaldo Carvalho de Melo
2026-10-13 20:26   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 18/19] perf tests: Mark the C tests mapping perf ring buffers Arnaldo Carvalho de Melo
2026-10-13 20:25   ` sashiko-bot
2026-10-13 20:14 ` [PATCH v2 19/19] perf tests: Mark the shell tests not " Arnaldo Carvalho de Melo
2026-10-13 20:27   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).