]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf tests: Add test for uncore event sorting
authorIan Rogers <irogers@google.com>
Mon, 18 May 2026 06:29:03 +0000 (23:29 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 20 May 2026 21:23:55 +0000 (18:23 -0300)
Add a test for uncore event sorting matching multiple PMUs.

Uncore PMUs may have a common prefix, like the PMUs
uncore_imc_free_running_0 and uncore_imc_free_running_1 have a prefix of
uncore_imc_free_running.

Parsing an event group like "{data_read,data_write}" for those PMUs
should result with two groups:

  "{uncore_imc_free_running_0/data_read/,uncore_imc_free_running_0/data_write/},
   {uncore_imc_free_running_1/data_read/,uncore_imc_free_running_1/data_write/}"

which means the evsels need resorting as when initially parsed the
evsels are ordered with mixed PMUs:

  "{uncore_imc_free_running_0/data_read/,uncore_imc_free_running_1/data_read/,
  uncore_imc_free_running_0/data_write/,uncore_imc_free_running_1/data_write/}".

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Zide Chen <zide.chen@intel.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Dmitrii Dolgov <9erthalion6@gmail.com>
Cc: German Gomez <german.gomez@arm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/tests/Build
tools/perf/tests/builtin-test.c
tools/perf/tests/tests.h
tools/perf/tests/uncore-event-sorting.c [new file with mode: 0644]

index c2a67ce459417e0f4e4624e91bb8196052899f53..66944a4f49682b8096efb96fa25148cf285f7f10 100644 (file)
@@ -3,6 +3,7 @@
 perf-test-y += builtin-test.o
 perf-test-y += tests-scripts.o
 perf-test-y += parse-events.o
+perf-test-y += uncore-event-sorting.o
 perf-test-y += dso-data.o
 perf-test-y += vmlinux-kallsyms.o
 perf-test-y += openat-syscall.o
index 06507066213b10bd783d1a5580c23aa770262631..f2c135891477c6d6e6e800859dda3e616a171d8b 100644 (file)
@@ -71,6 +71,7 @@ static struct test_suite *generic_tests[] = {
        &suite__basic_mmap,
        &suite__mem,
        &suite__parse_events,
+       &suite__uncore_event_sorting,
        &suite__expr,
        &suite__PERF_RECORD,
        &suite__pmu,
index f5f1238d1f7f23b5ad532874544392e205769591..ee00518bf36f24e4d44bfaad0b49938620f1dc93 100644 (file)
@@ -177,6 +177,7 @@ DECLARE_SUITE(sigtrap);
 DECLARE_SUITE(event_groups);
 DECLARE_SUITE(symbols);
 DECLARE_SUITE(util);
+DECLARE_SUITE(uncore_event_sorting);
 DECLARE_SUITE(subcmd_help);
 DECLARE_SUITE(kallsyms_split);
 
diff --git a/tools/perf/tests/uncore-event-sorting.c b/tools/perf/tests/uncore-event-sorting.c
new file mode 100644 (file)
index 0000000..7d2fc30
--- /dev/null
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include <string.h>
+
+#include "debug.h"
+#include "evlist.h"
+#include "parse-events.h"
+#include "pmu.h"
+#include "pmus.h"
+#include "tests.h"
+
+struct match_state {
+       char *event1;
+       char *event2;
+};
+
+static char *clean_event_name(struct pmu_event_info *info)
+{
+       const char *name = info->name;
+       const char *pmu_name = info->pmu->name;
+       size_t pmu_len = strlen(pmu_name);
+       char *res;
+       size_t len;
+
+       if (!strncmp(name, pmu_name, pmu_len) && name[pmu_len] == '/')
+               name += pmu_len + 1;
+
+       res = strdup(name);
+       if (!res)
+               return NULL;
+
+       len = strlen(res);
+       if (len > 0 && res[len - 1] == '/')
+               res[len - 1] = '\0';
+
+       return res;
+}
+
+static int event_cb(void *state, struct pmu_event_info *info)
+{
+       struct match_state *m = state;
+       char *clean_name;
+
+       if (m->event1 && m->event2)
+               return 1;
+
+       clean_name = clean_event_name(info);
+       if (!clean_name)
+               return 0;
+
+       if (!m->event1) {
+               m->event1 = clean_name;
+       } else {
+               if (strcmp(m->event1, clean_name)) {
+                       m->event2 = clean_name;
+                       return 1;
+               }
+               free(clean_name);
+       }
+       return 0;
+}
+
+#define CHECK_COND(cond, text)                                 \
+do {                                                           \
+       if (!(cond)) {                                          \
+               pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
+               ret = TEST_FAIL;                                \
+               goto out_err;                                   \
+       }                                                       \
+} while (0)
+
+#define CHECK_EQUAL(val, expected, text)                       \
+do {                                                           \
+       if ((val) != (expected)) {                              \
+               pr_debug("FAILED %s:%d %s (%d != %d)\n",        \
+                        __FILE__, __LINE__, text, (val), (expected)); \
+               ret = TEST_FAIL;                                \
+               goto out_err;                                   \
+       }                                                       \
+} while (0)
+
+static int test__uncore_event_sorting(struct test_suite *test __maybe_unused,
+                                     int subtest __maybe_unused)
+{
+       struct evlist *evlist = NULL;
+       struct parse_events_error err;
+       struct evsel *evsel;
+       struct perf_pmu *pmu = NULL;
+       char *pmu_prefix = NULL;
+       struct match_state m = { NULL, NULL };
+       char buf[1024];
+       int ret;
+
+       parse_events_error__init(&err);
+
+       while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+               size_t len;
+               struct perf_pmu *sibling;
+
+               if (pmu->is_core)
+                       continue;
+
+               len = pmu_name_len_no_suffix(pmu->name);
+               if (len == strlen(pmu->name))
+                       continue;
+
+               sibling = pmu;
+               while ((sibling = perf_pmus__scan(sibling)) != NULL) {
+                       if (sibling->is_core)
+                               continue;
+                       if (pmu_name_len_no_suffix(sibling->name) == len &&
+                           !strncmp(pmu->name, sibling->name, len))
+                               break;
+               }
+
+               if (!sibling)
+                       continue;
+
+               m.event1 = m.event2 = NULL;
+               perf_pmu__for_each_event(pmu, false, &m, event_cb);
+
+               if (m.event1 && m.event2) {
+                       pmu_prefix = strndup(pmu->name, len);
+                       break;
+               }
+               zfree(&m.event1);
+       }
+
+       if (!pmu_prefix) {
+               pr_debug("No suitable uncore PMU found\n");
+               ret = TEST_SKIP;
+               goto out_err;
+       }
+
+       evlist = evlist__new();
+       if (!evlist) {
+               ret = TEST_FAIL;
+               goto out_err;
+       }
+
+       snprintf(buf, sizeof(buf), "{%s/%s/,%s/%s/}", pmu_prefix, m.event1, pmu_prefix, m.event2);
+       pr_debug("Parsing: %s\n", buf);
+
+       ret = parse_events(evlist, buf, &err);
+       if (ret) {
+               pr_debug("parse_events failed\n");
+               ret = TEST_FAIL;
+               goto out_err;
+       }
+
+       CHECK_COND(evlist->core.nr_entries >= 4, "Number of events is >= 4");
+       CHECK_EQUAL(evlist->core.nr_entries % 2, 0, "Number of events is a multiple of 2");
+
+       evlist__for_each_entry(evlist, evsel) {
+               struct evsel *next;
+
+               if (!evsel__is_group_leader(evsel))
+                       continue;
+
+               next = evsel__next(evsel);
+               CHECK_EQUAL(evsel->core.nr_members, 2, "Group size is 2");
+               CHECK_COND(evsel->pmu == next->pmu, "PMU match");
+               CHECK_COND(strstr(evsel__name(evsel), m.event1) != NULL, "First event name");
+               CHECK_COND(strstr(evsel__name(next), m.event2) != NULL, "Second event name");
+       }
+       ret = TEST_OK;
+
+out_err:
+       evlist__delete(evlist);
+       parse_events_error__exit(&err);
+       zfree(&pmu_prefix);
+       zfree(&m.event1);
+       zfree(&m.event2);
+       return ret;
+}
+
+DEFINE_SUITE("Uncore event sorting", uncore_event_sorting);