perf tests: Add test for uncore event sorting

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>
This commit is contained in:
Ian Rogers
2026-05-17 23:29:03 -07:00
committed by Arnaldo Carvalho de Melo
parent 98e68cb778
commit 9f116f4b81
4 changed files with 179 additions and 0 deletions

View 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

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

View 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);

View File

@@ -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);