From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>,
Kan Liang <kan.liang@linux.intel.com>,
linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org,
Ian Rogers <irogers@google.com>
Subject: [PATCH 09/10] perf tests pmu-events: Test metricgroup descriptions and concurrent lookups
Date: Fri, 13 Nov 2026 13:08:21 -0800 [thread overview]
Message-ID: <20261113210822.3112700-10-irogers@google.com> (raw)
In-Reply-To: <20261113210822.3112700-1-irogers@google.com>
Add metricgroups.json to arch/test/test_soc/cpu/ with prefix-overlapping
keys ("group1" and "group1A"), regenerate empty-pmu-events.c, and add a
"metricgroups" subtest to "PMU JSON event tests".
The new subtest verifies exact and prefix-overlapping metricgroup binary
searches via describe_metricgroup(), validates metricgroup descriptions
across all core and system metrics, and launches 8 concurrent worker
threads scanning core/sys events and metrics to stress-test lock-free
block decompression and shared dictionary initialization.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../arch/test/test_soc/cpu/metricgroups.json | 4 +
tools/perf/pmu-events/empty-pmu-events.c | 84 +++----
tools/perf/tests/pmu-events.c | 209 +++++++++++++++++-
3 files changed, 259 insertions(+), 38 deletions(-)
create mode 100644 tools/perf/pmu-events/arch/test/test_soc/cpu/metricgroups.json
diff --git a/tools/perf/pmu-events/arch/test/test_soc/cpu/metricgroups.json b/tools/perf/pmu-events/arch/test/test_soc/cpu/metricgroups.json
new file mode 100644
index 000000000000..2bf7e4fbff3c
--- /dev/null
+++ b/tools/perf/pmu-events/arch/test/test_soc/cpu/metricgroups.json
@@ -0,0 +1,4 @@
+{
+ "group1": "Test metricgroup 1",
+ "group1A": "Test metricgroup 1A"
+}
diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 46a32a4cb9d6..f4009e5c51ee 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -2605,42 +2605,50 @@ static const char big_c_string[] =
/* offset=128790 */
"M3\000\0001 / M3\000\000\000\000\000\000\000\000000"
/* offset=128811 */
+"Test metricgroup 1\000"
+/* offset=128830 */
+"Test metricgroup 1A\000"
+/* offset=128850 */
"backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001"
-/* offset=128985 */
+/* offset=129024 */
"branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011"
-/* offset=129165 */
+/* offset=129204 */
"branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001"
-/* offset=129269 */
+/* offset=129308 */
"cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000"
-/* offset=129341 */
+/* offset=129380 */
"cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switches@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Context switches per CPU second\000\0001cs/sec\000\000\000\000011"
-/* offset=129574 */
+/* offset=129613 */
"cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011"
-/* offset=129750 */
+/* offset=129789 */
"dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000"
-/* offset=129815 */
+/* offset=129854 */
"dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001"
-/* offset=129921 */
+/* offset=129960 */
"frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001"
-/* offset=130100 */
+/* offset=130139 */
+"group1\000"
+/* offset=130146 */
+"group1A\000"
+/* offset=130154 */
"icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000"
-/* offset=130168 */
+/* offset=130222 */
"insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001"
-/* offset=130281 */
+/* offset=130335 */
"itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001"
-/* offset=130387 */
+/* offset=130441 */
"l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001"
-/* offset=130535 */
+/* offset=130589 */
"l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001"
-/* offset=130651 */
+/* offset=130705 */
"l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001"
-/* offset=130766 */
+/* offset=130820 */
"llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001"
-/* offset=130867 */
+/* offset=130921 */
"migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011"
-/* offset=131127 */
+/* offset=131181 */
"page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011"
-/* offset=131358 */
+/* offset=131412 */
"stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001"
;
@@ -5197,37 +5205,37 @@ static const struct compact_pmu_event pmu_metrics__common_default_core[] = {
/* CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #targ... */
{ 127820 },
/* backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if ... */
- { 128811 },
+ { 128850 },
/* branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-c... */
- { 128985 },
+ { 129024 },
/* branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > ... */
- { 129165 },
+ { 129204 },
/* cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switc... */
- { 129341 },
+ { 129380 },
/* cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu... */
- { 129574 },
+ { 129613 },
/* dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_r... */
- { 129815 },
+ { 129854 },
/* frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles i... */
- { 129921 },
+ { 129960 },
/* insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\0... */
- { 130168 },
+ { 130222 },
/* itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_r... */
- { 130281 },
+ { 130335 },
/* l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcac... */
- { 130387 },
+ { 130441 },
/* l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\0... */
- { 130535 },
+ { 130589 },
/* l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\0... */
- { 130651 },
+ { 130705 },
/* llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate ... */
- { 130766 },
+ { 130820 },
/* migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-mig... */
- { 130867 },
+ { 130921 },
/* page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faul... */
- { 131127 },
+ { 131181 },
/* stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, s... */
- { 131358 },
+ { 131412 },
};
@@ -5338,11 +5346,11 @@ static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] =
/* M3\000\0001 / M3\000\000\000\000\000\000\000\000000 */
{ 128790 },
/* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\... */
- { 129269 },
+ { 129308 },
/* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\0... */
- { 129750 },
+ { 129789 },
/* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\00... */
- { 130100 },
+ { 130154 },
};
@@ -6034,6 +6042,8 @@ int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data)
}
static const uint32_t metricgroups[][2] = {
+ { 130139, 128811 }, /* group1\000 => Test metricgroup 1\000 */
+ { 130146, 128830 }, /* group1A\000 => Test metricgroup 1A\000 */
};
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index e6e4f4cf1fa2..a06525cafb48 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -5,7 +5,12 @@
#include "pmus.h"
#include "tests.h"
#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <linux/kernel.h>
#include <linux/zalloc.h>
#include "debug.h"
@@ -1094,6 +1099,202 @@ static int test__parsing_threshold(struct test_suite *test __maybe_unused,
return pmu_for_each_sys_metric(test__parsing_threshold_callback, NULL);
}
+static int check_event_strings_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *data __maybe_unused)
+{
+ if (!pe->pmu || !pe->name || (!pe->event && !pe->desc))
+ return TEST_FAIL;
+ return 0;
+}
+
+static int check_find_btb_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *data)
+{
+ int *matched = data;
+
+ if (!pe->event || strcmp(pe->event, "event=0x8a"))
+ return TEST_FAIL;
+ (*matched)++;
+ return 0;
+}
+
+static int check_find_ipc_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *data)
+{
+ int *matched = data;
+
+ if (!pm->metric_group || strcmp(pm->metric_group, "group1"))
+ return TEST_FAIL;
+ (*matched)++;
+ return 0;
+}
+
+static int check_metricgroup_strings_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *data __maybe_unused)
+{
+ char *dup, *group, *saveptr = NULL;
+
+ if (!pm->pmu || !pm->metric_expr)
+ return TEST_FAIL;
+ if (!pm->metric_group)
+ return 0;
+
+ dup = strdup(pm->metric_group);
+ if (!dup)
+ return -ENOMEM;
+
+ for (group = strtok_r(dup, ";", &saveptr); group;
+ group = strtok_r(NULL, ";", &saveptr)) {
+ const char *desc = describe_metricgroup(group);
+
+ if (desc && desc[0] == '\0') {
+ free(dup);
+ return TEST_FAIL;
+ }
+ if (!strcmp(group, "group1") &&
+ (!desc || strcmp(desc, "Test metricgroup 1"))) {
+ free(dup);
+ return TEST_FAIL;
+ }
+ }
+ free(dup);
+ return 0;
+}
+
+struct concurrent_lookup_gate {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ bool start;
+};
+
+struct concurrent_lookup_worker {
+ struct concurrent_lookup_gate *gate;
+ int err;
+};
+
+static int run_lookup_checks(void)
+{
+ const struct pmu_events_table *etest;
+ const struct pmu_metrics_table *mtest;
+ const char *desc;
+ int matched, err;
+
+ desc = describe_metricgroup("group1");
+ if (!desc || strcmp(desc, "Test metricgroup 1")) {
+ pr_err("Unexpected group1 description: %s\n", desc ?: "(null)");
+ return TEST_FAIL;
+ }
+
+ desc = describe_metricgroup("group1A");
+ if (!desc || strcmp(desc, "Test metricgroup 1A")) {
+ pr_err("Unexpected group1A description: %s\n", desc ?: "(null)");
+ return TEST_FAIL;
+ }
+
+ if (describe_metricgroup("group1_nonexistent") || describe_metricgroup("")) {
+ pr_err("Unexpected match for nonexistent metricgroup\n");
+ return TEST_FAIL;
+ }
+
+ if (describe_metricgroup("BadSpec") && !describe_metricgroup("Bad")) {
+ pr_err("Prefix metricgroup 'Bad' missing when 'BadSpec' is present\n");
+ return TEST_FAIL;
+ }
+
+ etest = find_core_events_table("testarch", "testcpu");
+ mtest = find_core_metrics_table("testarch", "testcpu");
+ if (!etest || !mtest)
+ return TEST_FAIL;
+
+ matched = 0;
+ err = pmu_events_table__find_event(etest, NULL, "bp_l1_btb_correct",
+ check_find_btb_cb, &matched);
+ if (err || matched != 1)
+ return TEST_FAIL;
+
+ matched = 0;
+ err = pmu_metrics_table__find_metric(mtest, NULL, "IPC",
+ check_find_ipc_cb, &matched);
+ if (err || matched != 1)
+ return TEST_FAIL;
+
+ err = pmu_for_each_core_event(check_event_strings_cb, NULL);
+ if (err)
+ return err;
+
+ err = pmu_for_each_sys_event(check_event_strings_cb, NULL);
+ if (err)
+ return err;
+
+ err = pmu_for_each_core_metric(check_metricgroup_strings_cb, NULL);
+ if (err)
+ return err;
+
+ return pmu_for_each_sys_metric(check_metricgroup_strings_cb, NULL);
+}
+
+static void *concurrent_lookup_thread(void *arg)
+{
+ struct concurrent_lookup_worker *worker = arg;
+
+ pthread_mutex_lock(&worker->gate->mutex);
+ while (!worker->gate->start)
+ pthread_cond_wait(&worker->gate->cond, &worker->gate->mutex);
+ pthread_mutex_unlock(&worker->gate->mutex);
+
+ worker->err = run_lookup_checks();
+ return NULL;
+}
+
+/*
+ * Verify metricgroup binary search and release 8 worker threads simultaneously
+ * to stress-test lock-free on-demand decompression of ~8 KiB string blocks in
+ * pmu_events__get_string() (big_c_string_cache[]) and shared dictionary setup.
+ */
+static int test__metricgroups(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct concurrent_lookup_gate gate = {
+ .mutex = PTHREAD_MUTEX_INITIALIZER,
+ .cond = PTHREAD_COND_INITIALIZER,
+ .start = false,
+ };
+ struct concurrent_lookup_worker workers[8];
+ pthread_t threads[ARRAY_SIZE(workers)];
+ size_t created = 0;
+ int ret = TEST_OK;
+
+ for (size_t i = 0; i < ARRAY_SIZE(workers); i++) {
+ workers[i].gate = &gate;
+ workers[i].err = 0;
+ if (pthread_create(&threads[i], NULL, concurrent_lookup_thread,
+ &workers[i]) != 0) {
+ ret = TEST_FAIL;
+ break;
+ }
+ created++;
+ }
+
+ pthread_mutex_lock(&gate.mutex);
+ gate.start = true;
+ pthread_cond_broadcast(&gate.cond);
+ pthread_mutex_unlock(&gate.mutex);
+
+ for (size_t i = 0; i < created; i++) {
+ pthread_join(threads[i], NULL);
+ if (workers[i].err && ret == TEST_OK)
+ ret = workers[i].err;
+ }
+
+ pthread_cond_destroy(&gate.cond);
+ pthread_mutex_destroy(&gate.mutex);
+ return ret;
+}
+
struct populate_cb_data {
struct test_case *test_cases;
size_t curr;
@@ -1145,7 +1346,7 @@ static struct test_case pmu_events_tests[];
static int setup_pmu_events_suite(struct test_suite *suite)
{
size_t num_tables = 0;
- size_t num_fixed_tests = 4;
+ size_t num_fixed_tests = 5;
size_t tests_per_table = 2;
size_t total_tests;
struct test_case *test_cases;
@@ -1171,6 +1372,11 @@ static int setup_pmu_events_suite(struct test_suite *suite)
.desc = "PMU event table sanity",
.run_case = test__pmu_event_table,
};
+ test_cases[curr++] = (struct test_case){
+ .name = "metricgroups",
+ .desc = "PMU metricgroup descriptions and concurrent lookups",
+ .run_case = test__metricgroups,
+ };
test_cases[curr++] = (struct test_case){
.name = "aliases",
.desc = "PMU event map aliases",
@@ -1208,6 +1414,7 @@ static int setup_pmu_events_suite(struct test_suite *suite)
static struct test_case pmu_events_tests[] = {
TEST_CASE("PMU event table sanity", pmu_event_table),
+ TEST_CASE("PMU metricgroup descriptions and concurrent lookups", metricgroups),
TEST_CASE("PMU event map aliases", aliases),
TEST_CASE_REASON("Parsing of PMU event table metrics", parsing,
"some metrics failed"),
--
2.56.1.703.gf848ed807c-goog
next prev parent reply other threads:[~2026-11-13 21:08 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 21:08 [PATCH 00/10] perf jevents: Block-indexed string compression and default JEVENTS_ARCH=all Ian Rogers
2026-11-13 21:08 ` [PATCH 01/10] perf jevents: Store raw bytes in BigCString instead of C-escaped strings Ian Rogers
2026-11-13 21:08 ` [PATCH 02/10] perf jevents: Sort BigCString entries alphabetically Ian Rogers
2026-11-13 21:08 ` [PATCH 03/10] perf jevents: Store PMU names as string literals in struct pmu_table_entry Ian Rogers
2026-11-13 21:08 ` [PATCH 04/10] perf jevents: Encapsulate big_c_string lookups behind pmu_events__get_string() Ian Rogers
2026-11-13 21:08 ` [PATCH 05/10] perf jevents: Add block-indexed string packing and --compress=zlib Ian Rogers
2026-11-13 21:08 ` [PATCH 06/10] perf jevents: Add --compress=lzma block compression backend Ian Rogers
2026-11-13 21:08 ` [PATCH 07/10] perf jevents: Add --compress=zstd " Ian Rogers
2026-11-13 21:08 ` [PATCH 08/10] perf build: Enable jevents string compression when available Ian Rogers
2026-11-13 21:08 ` Ian Rogers [this message]
2026-11-13 21:08 ` [PATCH 10/10] perf build: Default JEVENTS_ARCH to all Ian Rogers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113210822.3112700-10-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®