// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) #include #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);