From 5a52817e388bc2beaceff7b2059988987491cb59 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:17 -0700 Subject: perf test: Test more sysfs events Parse events for all PMUs, and not just cpu, in test "Parsing of all PMU events from sysfs". Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-11-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 129 ++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 58 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 8068cfd89b84..3721a2182f45 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -7,6 +7,7 @@ #include "debug.h" #include "pmu.h" #include "pmu-hybrid.h" +#include "pmus.h" #include #include #include "fncache.h" @@ -558,7 +559,8 @@ static int test__checkevent_pmu_events(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || + strcmp(evsel->pmu_name, "cpu")); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -590,7 +592,8 @@ static int test__checkevent_pmu_events_mix(struct evlist *evlist) /* cpu/pmu-event/u*/ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || + strcmp(evsel->pmu_name, "cpu")); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -2225,74 +2228,84 @@ static int test_pmu(void) static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - struct stat st; - char path[PATH_MAX]; - struct dirent *ent; - DIR *dir; - int ret; - - if (!test_pmu()) - return TEST_SKIP; + struct perf_pmu *pmu; + int ret = TEST_OK; - snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/", - sysfs__mountpoint()); + if (list_empty(&pmus)) + perf_pmu__scan(NULL); - ret = stat(path, &st); - if (ret) { - pr_debug("omitting PMU cpu events tests: %s\n", path); - return TEST_OK; - } + perf_pmus__for_each_pmu(pmu) { + struct stat st; + char path[PATH_MAX]; + struct dirent *ent; + DIR *dir; + int err; - dir = opendir(path); - if (!dir) { - pr_debug("can't open pmu event dir: %s\n", path); - return TEST_FAIL; - } + snprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/events/", + sysfs__mountpoint(), pmu->name); - ret = TEST_OK; - while ((ent = readdir(dir))) { - struct evlist_test e = { .name = NULL, }; - char name[2 * NAME_MAX + 1 + 12 + 3]; - int test_ret; + err = stat(path, &st); + if (err) { + pr_debug("skipping PMU %s events tests: %s\n", pmu->name, path); + continue; + } - /* Names containing . are special and cannot be used directly */ - if (strchr(ent->d_name, '.')) + dir = opendir(path); + if (!dir) { + pr_debug("can't open pmu event dir: %s\n", path); + ret = combine_test_results(ret, TEST_SKIP); continue; + } - snprintf(name, sizeof(name), "cpu/event=%s/u", ent->d_name); + while ((ent = readdir(dir))) { + struct evlist_test e = { .name = NULL, }; + char name[2 * NAME_MAX + 1 + 12 + 3]; + int test_ret; - e.name = name; - e.check = test__checkevent_pmu_events; + /* Names containing . are special and cannot be used directly */ + if (strchr(ent->d_name, '.')) + continue; - test_ret = test_event(&e); - if (test_ret != TEST_OK) { - pr_debug("Test PMU event failed for '%s'", name); - ret = combine_test_results(ret, test_ret); - } - /* - * Names containing '-' are recognized as prefixes and suffixes - * due to '-' being a legacy PMU separator. This fails when the - * prefix or suffix collides with an existing legacy token. For - * example, branch-brs has a prefix (branch) that collides with - * a PE_NAME_CACHE_TYPE token causing a parse error as a suffix - * isn't expected after this. As event names in the config - * slashes are allowed a '-' in the name we check this works - * above. - */ - if (strchr(ent->d_name, '-')) - continue; + snprintf(name, sizeof(name), "%s/event=%s/u", pmu->name, ent->d_name); - snprintf(name, sizeof(name), "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); - e.name = name; - e.check = test__checkevent_pmu_events_mix; - test_ret = test_event(&e); - if (test_ret != TEST_OK) { - pr_debug("Test PMU event failed for '%s'", name); - ret = combine_test_results(ret, test_ret); + e.name = name; + e.check = test__checkevent_pmu_events; + + test_ret = test_event(&e); + if (test_ret != TEST_OK) { + pr_debug("Test PMU event failed for '%s'", name); + ret = combine_test_results(ret, test_ret); + } + + if (!is_pmu_core(pmu->name)) + continue; + + /* + * Names containing '-' are recognized as prefixes and suffixes + * due to '-' being a legacy PMU separator. This fails when the + * prefix or suffix collides with an existing legacy token. For + * example, branch-brs has a prefix (branch) that collides with + * a PE_NAME_CACHE_TYPE token causing a parse error as a suffix + * isn't expected after this. As event names in the config + * slashes are allowed a '-' in the name we check this works + * above. + */ + if (strchr(ent->d_name, '-')) + continue; + + snprintf(name, sizeof(name), "%s:u,%s/event=%s/u", + ent->d_name, pmu->name, ent->d_name); + e.name = name; + e.check = test__checkevent_pmu_events_mix; + test_ret = test_event(&e); + if (test_ret != TEST_OK) { + pr_debug("Test PMU event failed for '%s'", name); + ret = combine_test_results(ret, test_ret); + } } - } - closedir(dir); + closedir(dir); + } return ret; } -- cgit v1.2.3 From 8f8c106886983f7963df76d33bf9e42df8ec3e8a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:18 -0700 Subject: perf test: Use valid for PMU tests Rather than skip all tests in test__events_pmu if PMU cpu isn't present, use the per-test valid test. This allows the running of software PMU tests on hybrid and arm systems. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-12-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 3721a2182f45..c06fa7653ac2 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1432,6 +1432,11 @@ static int test__checkevent_config_cache(struct evlist *evlist) return TEST_OK; } +static bool test__pmu_cpu_valid(void) +{ + return !!perf_pmu__find("cpu"); +} + static bool test__intel_pt_valid(void) { return !!perf_pmu__find("intel_pt"); @@ -1981,21 +1986,25 @@ static const struct evlist_test test__events[] = { static const struct evlist_test test__events_pmu[] = { { .name = "cpu/config=10,config1,config2=3,period=1000/u", + .valid = test__pmu_cpu_valid, .check = test__checkevent_pmu, /* 0 */ }, { .name = "cpu/config=1,name=krava/u,cpu/config=2/u", + .valid = test__pmu_cpu_valid, .check = test__checkevent_pmu_name, /* 1 */ }, { .name = "cpu/config=1,call-graph=fp,time,period=100000/,cpu/config=2,call-graph=no,time=0,period=2000/", + .valid = test__pmu_cpu_valid, .check = test__checkevent_pmu_partial_time_callgraph, /* 2 */ }, { .name = "cpu/name='COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks',period=0x1,event=0x2/ukp", + .valid = test__pmu_cpu_valid, .check = test__checkevent_complex_name, /* 3 */ }, @@ -2211,21 +2220,6 @@ static int test__terms2(struct test_suite *test __maybe_unused, int subtest __ma return test_terms(test__terms, ARRAY_SIZE(test__terms)); } -static int test_pmu(void) -{ - struct stat st; - char path[PATH_MAX]; - int ret; - - snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/format/", - sysfs__mountpoint()); - - ret = stat(path, &st); - if (ret) - pr_debug("omitting PMU cpu tests\n"); - return !ret; -} - static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { struct perf_pmu *pmu; @@ -2311,9 +2305,6 @@ static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest static int test__pmu_events2(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - if (!test_pmu()) - return TEST_SKIP; - return test_events(test__events_pmu, ARRAY_SIZE(test__events_pmu)); } -- cgit v1.2.3 From 9854934b055cfc37b3acdff5323dd2060745a774 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:19 -0700 Subject: perf test: Mask configs with extended types then test Add helper to test the config of an evsel. Dependent on the type of the evsel, mask the config so that high-bits containing the extended PMU type are ignored. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-13-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 197 ++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 109 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c06fa7653ac2..4b00cb4aa73a 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -21,6 +21,21 @@ #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) +static bool test_config(const struct evsel *evsel, __u64 expected_config) +{ + __u32 type = evsel->core.attr.type; + __u64 config = evsel->core.attr.config; + + if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) { + /* + * HARDWARE and HW_CACHE events encode the PMU's extended type + * in the top 32-bits. Mask in order to ignore. + */ + config &= PERF_HW_EVENT_MASK; + } + return config == expected_config; +} + #ifdef HAVE_LIBTRACEEVENT #if defined(__s390x__) @@ -87,7 +102,7 @@ static int test__checkevent_raw(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; } @@ -97,7 +112,7 @@ static int test__checkevent_numeric(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); return TEST_OK; } @@ -107,8 +122,7 @@ static int test__checkevent_symbolic_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); return TEST_OK; } @@ -118,8 +132,7 @@ static int test__checkevent_symbolic_name_config(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); /* * The period value gets configured within evlist__config, * while this test executes only parse events method. @@ -139,8 +152,7 @@ static int test__checkevent_symbolic_alias(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_SW_PAGE_FAULTS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_PAGE_FAULTS)); return TEST_OK; } @@ -150,7 +162,7 @@ static int test__checkevent_genhw(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1 << 16)); return TEST_OK; } @@ -160,7 +172,7 @@ static int test__checkevent_breakpoint(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 == @@ -174,7 +186,7 @@ static int test__checkevent_breakpoint_x(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_X == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->core.attr.bp_len); @@ -188,7 +200,7 @@ static int test__checkevent_breakpoint_r(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_R == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", @@ -203,7 +215,7 @@ static int test__checkevent_breakpoint_w(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", @@ -218,7 +230,7 @@ static int test__checkevent_breakpoint_rw(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", @@ -447,7 +459,7 @@ static int test__checkevent_pmu(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 10 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 10)); TEST_ASSERT_VAL("wrong config1", 1 == evsel->core.attr.config1); TEST_ASSERT_VAL("wrong config2", 3 == evsel->core.attr.config2); TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3); @@ -469,7 +481,7 @@ static int test__checkevent_list(struct evlist *evlist) /* r1 */ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); TEST_ASSERT_VAL("wrong config1", 0 == evsel->core.attr.config1); TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3); @@ -492,7 +504,7 @@ static int test__checkevent_list(struct evlist *evlist) /* 1:1:hp */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -509,14 +521,14 @@ static int test__checkevent_pmu_name(struct evlist *evlist) /* cpu/config=1,name=krava/u */ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "krava")); /* cpu/config=2/u" */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 2 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 2)); TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "cpu/config=2/u")); @@ -530,7 +542,7 @@ static int test__checkevent_pmu_partial_time_callgraph(struct evlist *evlist) /* cpu/config=1,call-graph=fp,time,period=100000/ */ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); /* * The period, time and callgraph value gets configured within evlist__config, * while this test executes only parse events method. @@ -542,7 +554,7 @@ static int test__checkevent_pmu_partial_time_callgraph(struct evlist *evlist) /* cpu/config=2,call-graph=no,time=0,period=2000/ */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 2 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 2)); /* * The period, time and callgraph value gets configured within evlist__config, * while this test executes only parse events method. @@ -696,8 +708,7 @@ static int test__group1(struct evlist *evlist) /* instructions:k */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -712,8 +723,7 @@ static int test__group1(struct evlist *evlist) /* cycles:upp */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -738,8 +748,7 @@ static int test__group2(struct evlist *evlist) /* faults + :ku modifier */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_SW_PAGE_FAULTS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_PAGE_FAULTS)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -754,8 +763,7 @@ static int test__group2(struct evlist *evlist) /* cache-references + :u modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_REFERENCES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_REFERENCES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -769,8 +777,7 @@ static int test__group2(struct evlist *evlist) /* cycles:k */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -813,8 +820,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) /* group1 cycles:kppp */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -830,8 +836,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) /* group2 cycles + G modifier */ evsel = leader = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -848,7 +853,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) /* group2 1:3 + G modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 3 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 3)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -862,8 +867,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) /* instructions:u */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -887,8 +891,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) /* cycles:u + p */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -905,8 +908,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) /* instructions:kp + p */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -931,8 +933,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) /* cycles + G */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -948,8 +949,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) /* instructions + G */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -963,8 +963,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) /* cycles:G */ evsel = leader = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -980,8 +979,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) /* instructions:G */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -994,8 +992,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) /* cycles */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1017,8 +1014,7 @@ static int test__group_gh1(struct evlist *evlist) /* cycles + :H group modifier */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1033,8 +1029,7 @@ static int test__group_gh1(struct evlist *evlist) /* cache-misses:G + :H group modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1057,8 +1052,7 @@ static int test__group_gh2(struct evlist *evlist) /* cycles + :G group modifier */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1073,8 +1067,7 @@ static int test__group_gh2(struct evlist *evlist) /* cache-misses:H + :G group modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1097,8 +1090,7 @@ static int test__group_gh3(struct evlist *evlist) /* cycles:G + :u group modifier */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1113,8 +1105,7 @@ static int test__group_gh3(struct evlist *evlist) /* cache-misses:H + :u group modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1137,8 +1128,7 @@ static int test__group_gh4(struct evlist *evlist) /* cycles:G + :uG group modifier */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1153,8 +1143,7 @@ static int test__group_gh4(struct evlist *evlist) /* cache-misses:H + :uG group modifier */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1176,8 +1165,7 @@ static int test__leader_sample1(struct evlist *evlist) /* cycles - sampling group leader */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1191,8 +1179,7 @@ static int test__leader_sample1(struct evlist *evlist) /* cache-misses - not sampling */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1205,8 +1192,7 @@ static int test__leader_sample1(struct evlist *evlist) /* branch-misses - not sampling */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); @@ -1229,8 +1215,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) /* instructions - sampling group leader */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1244,8 +1229,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) /* branch-misses - not sampling */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); @@ -1281,8 +1265,7 @@ static int test__pinned_group(struct evlist *evlist) /* cycles - group leader */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned); @@ -1290,14 +1273,12 @@ static int test__pinned_group(struct evlist *evlist) /* cache-misses - can not be pinned, but will go on with the leader */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); /* branch-misses - ditto */ evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); return TEST_OK; @@ -1325,8 +1306,7 @@ static int test__exclusive_group(struct evlist *evlist) /* cycles - group leader */ evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); TEST_ASSERT_VAL("wrong exclusive", evsel->core.attr.exclusive); @@ -1334,14 +1314,12 @@ static int test__exclusive_group(struct evlist *evlist) /* cache-misses - can not be pinned, but will go on with the leader */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); /* branch-misses - ditto */ evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); return TEST_OK; @@ -1352,7 +1330,7 @@ static int test__checkevent_breakpoint_len(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 == @@ -1367,7 +1345,7 @@ static int test__checkevent_breakpoint_len_w(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W == evsel->core.attr.bp_type); TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 == @@ -1395,8 +1373,7 @@ static int test__checkevent_precise_max_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_SW_TASK_CLOCK == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_TASK_CLOCK)); return TEST_OK; } @@ -1454,7 +1431,9 @@ static int test__checkevent_complex_name(struct evlist *evlist) { struct evsel *evsel = evlist__first(evlist); - TEST_ASSERT_VAL("wrong complex name parsing", evsel__name_is(evsel, "COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks")); + TEST_ASSERT_VAL("wrong complex name parsing", + evsel__name_is(evsel, + "COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks")); return TEST_OK; } @@ -1464,7 +1443,7 @@ static int test__checkevent_raw_pmu(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; } @@ -1473,7 +1452,7 @@ static int test__sym_event_slash(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE); - TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); return TEST_OK; } @@ -1483,7 +1462,7 @@ static int test__sym_event_dc(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE); - TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); return TEST_OK; } @@ -1550,7 +1529,7 @@ static int test__hybrid_hw_event_with_pmu(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x3c == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); return TEST_OK; } @@ -1561,12 +1540,12 @@ static int test__hybrid_hw_group_event(struct evlist *evlist) evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x3c == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0xc0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); return TEST_OK; } @@ -1582,7 +1561,7 @@ static int test__hybrid_sw_hw_group_event(struct evlist *evlist) evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x3c == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); return TEST_OK; } @@ -1594,7 +1573,7 @@ static int test__hybrid_hw_sw_group_event(struct evlist *evlist) evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x3c == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); evsel = evsel__next(evsel); @@ -1610,14 +1589,14 @@ static int test__hybrid_group_modifier1(struct evlist *evlist) evsel = leader = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x3c == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0xc0 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); @@ -1631,17 +1610,17 @@ static int test__hybrid_raw1(struct evlist *evlist) if (!perf_pmu__hybrid_mounted("cpu_atom")) { TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; } TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); /* The type of second event is randome value */ evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; } @@ -1651,7 +1630,7 @@ static int test__hybrid_raw2(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); return TEST_OK; } -- cgit v1.2.3 From 4a7c4eafb74860fd7cb1eecbd2606cdd26809d0a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:20 -0700 Subject: perf test: Test more with config_cache test__checkevent_config_cache checks the parsing of "L1-dcache-misses/name=cachepmu/". Don't just check that the name is set correctly, also validate the rest of the perf_event_attr for L1-dcache-misses. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-14-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 4b00cb4aa73a..3f75f0315db8 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1406,7 +1406,7 @@ static int test__checkevent_config_cache(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong name setting", evsel__name_is(evsel, "cachepmu")); - return TEST_OK; + return test__checkevent_genhw(evlist); } static bool test__pmu_cpu_valid(void) -- cgit v1.2.3 From a8af6e48c6220992430cd55713679f49c23ac6d2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:21 -0700 Subject: perf test: Roundtrip name, don't assume 1 event per name Opening hardware names and a legacy cache event on a hybrid PMU opens it on each PMU. Parsing and checking indexes fails, as the parsed index is double the expected. Avoid checking the index by just comparing the names immediately after the parse. This change removes hard coded hybrid logic and removes assumptions about the expansion of an event. On hybrid the PMUs may or may not support an event and so using a distance isn't a consistent solution. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-15-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/evsel-roundtrip-name.c | 119 +++++++++++++------------------- 1 file changed, 49 insertions(+), 70 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index e94fed901992..15ff86f9da0b 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -4,114 +4,93 @@ #include "parse-events.h" #include "tests.h" #include "debug.h" -#include "pmu.h" -#include "pmu-hybrid.h" -#include #include static int perf_evsel__roundtrip_cache_name_test(void) { - char name[128]; - int type, op, err = 0, ret = 0, i, idx; - struct evsel *evsel; - struct evlist *evlist = evlist__new(); + int ret = TEST_OK; - if (evlist == NULL) - return -ENOMEM; - - for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { - for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { + for (int type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { + for (int op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { /* skip invalid cache type */ if (!evsel__is_cache_op_valid(type, op)) continue; - for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { - __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name)); - err = parse_event(evlist, name); - if (err) - ret = err; - } - } - } - - idx = 0; - evsel = evlist__first(evlist); + for (int res = 0; res < PERF_COUNT_HW_CACHE_RESULT_MAX; res++) { + char name[128]; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + int err; - for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { - for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { - /* skip invalid cache type */ - if (!evsel__is_cache_op_valid(type, op)) - continue; + if (evlist == NULL) { + pr_debug("Failed to alloc evlist"); + return TEST_FAIL; + } + __evsel__hw_cache_type_op_res_name(type, op, res, + name, sizeof(name)); - for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { - __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name)); - if (evsel->core.idx != idx) + err = parse_event(evlist, name); + if (err) { + pr_debug("Failure to parse cache event '%s' possibly as PMUs don't support it", + name); + evlist__delete(evlist); continue; - - ++idx; - - if (strcmp(evsel__name(evsel), name)) { - pr_debug("%s != %s\n", evsel__name(evsel), name); - ret = -1; } - - evsel = evsel__next(evsel); + evlist__for_each_entry(evlist, evsel) { + if (strcmp(evsel__name(evsel), name)) { + pr_debug("%s != %s\n", evsel__name(evsel), name); + ret = TEST_FAIL; + } + } + evlist__delete(evlist); } } } - - evlist__delete(evlist); return ret; } -static int __perf_evsel__name_array_test(const char *const names[], int nr_names, - int distance) +static int perf_evsel__name_array_test(const char *const names[], int nr_names) { - int i, err; - struct evsel *evsel; - struct evlist *evlist = evlist__new(); + int ret = TEST_OK; - if (evlist == NULL) - return -ENOMEM; + for (int i = 0; i < nr_names; ++i) { + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + int err; - for (i = 0; i < nr_names; ++i) { + if (evlist == NULL) { + pr_debug("Failed to alloc evlist"); + return TEST_FAIL; + } err = parse_event(evlist, names[i]); if (err) { pr_debug("failed to parse event '%s', err %d\n", names[i], err); - goto out_delete_evlist; + evlist__delete(evlist); + ret = TEST_FAIL; + continue; } - } - - err = 0; - evlist__for_each_entry(evlist, evsel) { - if (strcmp(evsel__name(evsel), names[evsel->core.idx / distance])) { - --err; - pr_debug("%s != %s\n", evsel__name(evsel), names[evsel->core.idx / distance]); + evlist__for_each_entry(evlist, evsel) { + if (strcmp(evsel__name(evsel), names[i])) { + pr_debug("%s != %s\n", evsel__name(evsel), names[i]); + ret = TEST_FAIL; + } } + evlist__delete(evlist); } - -out_delete_evlist: - evlist__delete(evlist); - return err; + return ret; } -#define perf_evsel__name_array_test(names, distance) \ - __perf_evsel__name_array_test(names, ARRAY_SIZE(names), distance) - static int test__perf_evsel__roundtrip_name_test(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - int err = 0, ret = 0; - - if (perf_pmu__has_hybrid() && perf_pmu__hybrid_mounted("cpu_atom")) - return perf_evsel__name_array_test(evsel__hw_names, 2); + int err = 0, ret = TEST_OK; - err = perf_evsel__name_array_test(evsel__hw_names, 1); + err = perf_evsel__name_array_test(evsel__hw_names, PERF_COUNT_HW_MAX); if (err) ret = err; - err = __perf_evsel__name_array_test(evsel__sw_names, PERF_COUNT_SW_DUMMY + 1, 1); + err = perf_evsel__name_array_test(evsel__sw_names, PERF_COUNT_SW_DUMMY + 1); if (err) ret = err; -- cgit v1.2.3 From 70c90e4a6b2fbe775b662eafefae51f64d627790 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:25 -0700 Subject: perf parse-events: Avoid scanning PMUs before parsing The event parser needs to handle two special cases: 1) legacy events like L1-dcache-load-miss. These event names don't appear in JSON or sysfs, and lookup tables are used for the config value. 2) raw events where 'r0xead' is the same as 'read' unless the PMU has an event called 'read' in which case the event has priority. The previous parser to handle these cases would scan all PMUs for components of event names. These components would then be used to classify in the lexer whether the token should be part of a legacy event, a raw event or an event. The grammar would handle legacy event tokens or recombining the tokens back into a regular event name. The code wasn't PMU specific and had issues around events like AMD's branch-brs that would fail to parse as it expects brs to be a suffix on a legacy event style name: $ perf stat -e branch-brs true event syntax error: 'branch-brs' \___ parser error This change removes processing all PMUs by using the lexer in the form of a regular expression matcher. The lexer will return the token for the longest matched sequence of characters, and in the event of a tie the first. The legacy events are a fixed number of regular expressions, and by matching these before a name token its possible to generate an accurate legacy event token with everything else matching as a name. Because of the lexer change the handling of hyphens in the grammar can be removed as hyphens just become a part of the name. To handle raw events and terms the parser is changed to defer trying to evaluate whether something is a raw event until the PMU is known in the grammar. Once the PMU is known, the events of the PMU can be scanned for the 'read' style problem. A new term type is added for these raw terms, used to enable deferring the evaluation. While this change is large, it has stats of: 170 insertions(+), 436 deletions(-) the bulk of the change is deleting the old approach. It isn't possible to break apart the code added due to the dependencies on how the parts of the parsing work. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-19-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 24 +-- tools/perf/tests/pmu-events.c | 9 -- tools/perf/util/parse-events.c | 329 +++++++++++++--------------------------- tools/perf/util/parse-events.h | 16 +- tools/perf/util/parse-events.l | 85 ++--------- tools/perf/util/parse-events.y | 143 ++++++----------- 6 files changed, 170 insertions(+), 436 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 3f75f0315db8..5ba90b32c524 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -676,11 +676,11 @@ static int test__checkterms_simple(struct list_head *terms) */ term = list_entry(term->list.next, struct parse_events_term, list); TEST_ASSERT_VAL("wrong type term", - term->type_term == PARSE_EVENTS__TERM_TYPE_USER); + term->type_term == PARSE_EVENTS__TERM_TYPE_RAW); TEST_ASSERT_VAL("wrong type val", - term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); - TEST_ASSERT_VAL("wrong val", term->val.num == 1); - TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "read")); + term->type_val == PARSE_EVENTS__TERM_TYPE_STR); + TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "read")); + TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw")); /* * r0xead @@ -690,11 +690,11 @@ static int test__checkterms_simple(struct list_head *terms) */ term = list_entry(term->list.next, struct parse_events_term, list); TEST_ASSERT_VAL("wrong type term", - term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); + term->type_term == PARSE_EVENTS__TERM_TYPE_RAW); TEST_ASSERT_VAL("wrong type val", - term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); - TEST_ASSERT_VAL("wrong val", term->val.num == 0xead); - TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "config")); + term->type_val == PARSE_EVENTS__TERM_TYPE_STR); + TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "r0xead")); + TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw")); return TEST_OK; } @@ -2104,7 +2104,6 @@ static int test_event_fake_pmu(const char *str) return -ENOMEM; parse_events_error__init(&err); - perf_pmu__test_parse_init(); ret = __parse_events(evlist, str, &err, &perf_pmu__fake, /*warn_if_reordered=*/true); if (ret) { pr_debug("failed to parse event '%s', err %d, str '%s'\n", @@ -2158,13 +2157,6 @@ static int test_term(const struct terms_test *t) INIT_LIST_HEAD(&terms); - /* - * The perf_pmu__test_parse_init prepares perf_pmu_events_list - * which gets freed in parse_events_terms. - */ - if (perf_pmu__test_parse_init()) - return -1; - ret = parse_events_terms(&terms, t->str); if (ret) { pr_debug("failed to parse terms '%s', err %d\n", diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 1dff863b9711..a2cde61b1c77 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -776,15 +776,6 @@ static int check_parse_id(const char *id, struct parse_events_error *error, for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@')) *cur = '/'; - if (fake_pmu) { - /* - * Every call to __parse_events will try to initialize the PMU - * state from sysfs and then clean it up at the end. Reset the - * PMU events to the test state so that we don't pick up - * erroneous prefixes and suffixes. - */ - perf_pmu__test_parse_init(); - } ret = __parse_events(evlist, dup, error, fake_pmu, /*warn_if_reordered=*/true); free(dup); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9cb5f040a74c..b5d95fce520c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -34,11 +34,6 @@ #define MAX_NAME_LEN 100 -struct perf_pmu_event_symbol { - char *symbol; - enum perf_pmu_event_symbol_type type; -}; - #ifdef PARSER_DEBUG extern int parse_events_debug; #endif @@ -49,15 +44,6 @@ static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state, const char *str, char *pmu_name, struct list_head *list); -static struct perf_pmu_event_symbol *perf_pmu_events_list; -/* - * The variable indicates the number of supported pmu event symbols. - * 0 means not initialized and ready to init - * -1 means failed to init, don't try anymore - * >0 is the number of supported pmu event symbols - */ -static int perf_pmu_events_list_num; - struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { [PERF_COUNT_HW_CPU_CYCLES] = { .symbol = "cpu-cycles", @@ -236,6 +222,57 @@ static char *get_config_name(struct list_head *head_terms) return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_NAME); } +/** + * fix_raw - For each raw term see if there is an event (aka alias) in pmu that + * matches the raw's string value. If the string value matches an + * event then change the term to be an event, if not then change it to + * be a config term. For example, "read" may be an event of the PMU or + * a raw hex encoding of 0xead. The fix-up is done late so the PMU of + * the event can be determined and we don't need to scan all PMUs + * ahead-of-time. + * @config_terms: the list of terms that may contain a raw term. + * @pmu: the PMU to scan for events from. + */ +static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu) +{ + struct parse_events_term *term; + + list_for_each_entry(term, config_terms, list) { + struct perf_pmu_alias *alias; + bool matched = false; + + if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW) + continue; + + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcmp(alias->name, term->val.str)) { + free(term->config); + term->config = term->val.str; + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; + term->type_term = PARSE_EVENTS__TERM_TYPE_USER; + term->val.num = 1; + term->no_value = true; + matched = true; + break; + } + } + if (!matched) { + u64 num; + + free(term->config); + term->config = strdup("config"); + errno = 0; + num = strtoull(term->val.str + 1, NULL, 16); + assert(errno == 0); + free(term->val.str); + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; + term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG; + term->val.num = num; + term->no_value = false; + } + } +} + static struct evsel * __add_event(struct list_head *list, int *idx, struct perf_event_attr *attr, @@ -329,18 +366,27 @@ static int add_event_tool(struct list_head *list, int *idx, return 0; } -static int parse_aliases(char *str, const char *const names[][EVSEL__MAX_ALIASES], int size) +/** + * parse_aliases - search names for entries beginning or equalling str ignoring + * case. If mutliple entries in names match str then the longest + * is chosen. + * @str: The needle to look for. + * @names: The haystack to search. + * @size: The size of the haystack. + * @longest: Out argument giving the length of the matching entry. + */ +static int parse_aliases(const char *str, const char *const names[][EVSEL__MAX_ALIASES], int size, + int *longest) { - int i, j; - int n, longest = -1; + *longest = -1; + for (int i = 0; i < size; i++) { + for (int j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) { + int n = strlen(names[i][j]); - for (i = 0; i < size; i++) { - for (j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) { - n = strlen(names[i][j]); - if (n > longest && !strncasecmp(str, names[i][j], n)) - longest = n; + if (n > *longest && !strncasecmp(str, names[i][j], n)) + *longest = n; } - if (longest > 0) + if (*longest > 0) return i; } @@ -358,52 +404,58 @@ static int config_attr(struct perf_event_attr *attr, struct parse_events_error *err, config_term_func_t config_term); -int parse_events_add_cache(struct list_head *list, int *idx, - char *type, char *op_result1, char *op_result2, +int parse_events_add_cache(struct list_head *list, int *idx, const char *name, struct parse_events_error *err, struct list_head *head_config, struct parse_events_state *parse_state) { struct perf_event_attr attr; LIST_HEAD(config_terms); - char name[MAX_NAME_LEN]; const char *config_name, *metric_id; int cache_type = -1, cache_op = -1, cache_result = -1; - char *op_result[2] = { op_result1, op_result2 }; - int i, n, ret; + int ret, len; + const char *name_end = &name[strlen(name) + 1]; bool hybrid; + const char *str = name; /* - * No fallback - if we cannot get a clear cache type - * then bail out: + * Search str for the legacy cache event name composed of 1, 2 or 3 + * hyphen separated sections. The first section is the cache type while + * the others are the optional op and optional result. To make life hard + * the names in the table also contain hyphens and the longest name + * should always be selected. */ - cache_type = parse_aliases(type, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX); + cache_type = parse_aliases(str, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX, &len); if (cache_type == -1) return -EINVAL; + str += len + 1; config_name = get_config_name(head_config); - n = snprintf(name, MAX_NAME_LEN, "%s", type); - - for (i = 0; (i < 2) && (op_result[i]); i++) { - char *str = op_result[i]; - - n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str); - - if (cache_op == -1) { + if (str < name_end) { + cache_op = parse_aliases(str, evsel__hw_cache_op, + PERF_COUNT_HW_CACHE_OP_MAX, &len); + if (cache_op >= 0) { + if (!evsel__is_cache_op_valid(cache_type, cache_op)) + return -EINVAL; + str += len + 1; + } else { + cache_result = parse_aliases(str, evsel__hw_cache_result, + PERF_COUNT_HW_CACHE_RESULT_MAX, &len); + if (cache_result >= 0) + str += len + 1; + } + } + if (str < name_end) { + if (cache_op < 0) { cache_op = parse_aliases(str, evsel__hw_cache_op, - PERF_COUNT_HW_CACHE_OP_MAX); + PERF_COUNT_HW_CACHE_OP_MAX, &len); if (cache_op >= 0) { if (!evsel__is_cache_op_valid(cache_type, cache_op)) return -EINVAL; - continue; } - } - - if (cache_result == -1) { + } else if (cache_result < 0) { cache_result = parse_aliases(str, evsel__hw_cache_result, - PERF_COUNT_HW_CACHE_RESULT_MAX); - if (cache_result >= 0) - continue; + PERF_COUNT_HW_CACHE_RESULT_MAX, &len); } } @@ -969,6 +1021,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output", [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size", [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", + [PARSE_EVENTS__TERM_TYPE_RAW] = "raw", }; static bool config_term_shrinked; @@ -1090,6 +1143,9 @@ do { \ case PARSE_EVENTS__TERM_TYPE_METRIC_ID: CHECK_TYPE_VAL(STR); break; + case PARSE_EVENTS__TERM_TYPE_RAW: + CHECK_TYPE_VAL(STR); + break; case PARSE_EVENTS__TERM_TYPE_MAX_STACK: CHECK_TYPE_VAL(NUM); break; @@ -1486,6 +1542,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, parse_events_error__handle(err, 0, err_str, NULL); return -EINVAL; } + if (head_config) + fix_raw(head_config, pmu); if (pmu->default_config) { memcpy(&attr, pmu->default_config, @@ -1870,180 +1928,6 @@ int parse_events_name(struct list_head *list, const char *name) return 0; } -static int -comp_pmu(const void *p1, const void *p2) -{ - struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; - struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; - - return strcasecmp(pmu1->symbol, pmu2->symbol); -} - -static void perf_pmu__parse_cleanup(void) -{ - if (perf_pmu_events_list_num > 0) { - struct perf_pmu_event_symbol *p; - int i; - - for (i = 0; i < perf_pmu_events_list_num; i++) { - p = perf_pmu_events_list + i; - zfree(&p->symbol); - } - zfree(&perf_pmu_events_list); - perf_pmu_events_list_num = 0; - } -} - -#define SET_SYMBOL(str, stype) \ -do { \ - p->symbol = str; \ - if (!p->symbol) \ - goto err; \ - p->type = stype; \ -} while (0) - -/* - * Read the pmu events list from sysfs - * Save it into perf_pmu_events_list - */ -static void perf_pmu__parse_init(void) -{ - - struct perf_pmu *pmu = NULL; - struct perf_pmu_alias *alias; - int len = 0; - - pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - list_for_each_entry(alias, &pmu->aliases, list) { - char *tmp = strchr(alias->name, '-'); - - if (tmp) { - char *tmp2 = NULL; - - tmp2 = strchr(tmp + 1, '-'); - len++; - if (tmp2) - len++; - } - - len++; - } - } - - if (len == 0) { - perf_pmu_events_list_num = -1; - return; - } - perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); - if (!perf_pmu_events_list) - return; - perf_pmu_events_list_num = len; - - len = 0; - pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - list_for_each_entry(alias, &pmu->aliases, list) { - struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; - char *tmp = strchr(alias->name, '-'); - char *tmp2 = NULL; - - if (tmp) - tmp2 = strchr(tmp + 1, '-'); - if (tmp2) { - SET_SYMBOL(strndup(alias->name, tmp - alias->name), - PMU_EVENT_SYMBOL_PREFIX); - p++; - tmp++; - SET_SYMBOL(strndup(tmp, tmp2 - tmp), PMU_EVENT_SYMBOL_SUFFIX); - p++; - SET_SYMBOL(strdup(++tmp2), PMU_EVENT_SYMBOL_SUFFIX2); - len += 3; - } else if (tmp) { - SET_SYMBOL(strndup(alias->name, tmp - alias->name), - PMU_EVENT_SYMBOL_PREFIX); - p++; - SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); - len += 2; - } else { - SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); - len++; - } - } - } - qsort(perf_pmu_events_list, len, - sizeof(struct perf_pmu_event_symbol), comp_pmu); - - return; -err: - perf_pmu__parse_cleanup(); -} - -/* - * This function injects special term in - * perf_pmu_events_list so the test code - * can check on this functionality. - */ -int perf_pmu__test_parse_init(void) -{ - struct perf_pmu_event_symbol *list, *tmp, symbols[] = { - {(char *)"read", PMU_EVENT_SYMBOL}, - {(char *)"event", PMU_EVENT_SYMBOL_PREFIX}, - {(char *)"two", PMU_EVENT_SYMBOL_SUFFIX}, - {(char *)"hyphen", PMU_EVENT_SYMBOL_SUFFIX}, - {(char *)"hyph", PMU_EVENT_SYMBOL_SUFFIX2}, - }; - unsigned long i, j; - - tmp = list = malloc(sizeof(*list) * ARRAY_SIZE(symbols)); - if (!list) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(symbols); i++, tmp++) { - tmp->type = symbols[i].type; - tmp->symbol = strdup(symbols[i].symbol); - if (!tmp->symbol) - goto err_free; - } - - perf_pmu_events_list = list; - perf_pmu_events_list_num = ARRAY_SIZE(symbols); - - qsort(perf_pmu_events_list, ARRAY_SIZE(symbols), - sizeof(struct perf_pmu_event_symbol), comp_pmu); - return 0; - -err_free: - for (j = 0, tmp = list; j < i; j++, tmp++) - zfree(&tmp->symbol); - free(list); - return -ENOMEM; -} - -enum perf_pmu_event_symbol_type -perf_pmu__parse_check(const char *name) -{ - struct perf_pmu_event_symbol p, *r; - - /* scan kernel pmu events from sysfs if needed */ - if (perf_pmu_events_list_num == 0) - perf_pmu__parse_init(); - /* - * name "cpu" could be prefix of cpu-cycles or cpu// events. - * cpu-cycles has been handled by hardcode. - * So it must be cpu// events, not kernel pmu event. - */ - if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu")) - return PMU_EVENT_SYMBOL_ERR; - - p.symbol = strdup(name); - r = bsearch(&p, perf_pmu_events_list, - (size_t) perf_pmu_events_list_num, - sizeof(struct perf_pmu_event_symbol), comp_pmu); - zfree(&p.symbol); - return r ? r->type : PMU_EVENT_SYMBOL_ERR; -} - static int parse_events__scanner(const char *str, struct parse_events_state *parse_state) { @@ -2081,7 +1965,6 @@ int parse_events_terms(struct list_head *terms, const char *str) int ret; ret = parse_events__scanner(str, &parse_state); - perf_pmu__parse_cleanup(); if (!ret) { list_splice(parse_state.terms, terms); @@ -2106,7 +1989,6 @@ static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state, int ret; ret = parse_events__scanner(str, &ps); - perf_pmu__parse_cleanup(); if (!ret) { if (!list_empty(&ps.list)) { @@ -2269,7 +2151,6 @@ int __parse_events(struct evlist *evlist, const char *str, int ret; ret = parse_events__scanner(str, &parse_state); - perf_pmu__parse_cleanup(); if (!ret && list_empty(&parse_state.list)) { WARN_ONCE(true, "WARNING: event parser found nothing\n"); diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 86ad4438a2aa..f638542c8638 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -41,14 +41,6 @@ int parse_events_terms(struct list_head *terms, const char *str); int parse_filter(const struct option *opt, const char *str, int unset); int exclude_perf(const struct option *opt, const char *arg, int unset); -enum perf_pmu_event_symbol_type { - PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */ - PMU_EVENT_SYMBOL, /* normal style PMU event */ - PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ - PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ - PMU_EVENT_SYMBOL_SUFFIX2, /* suffix of pre-suf2 style event */ -}; - enum { PARSE_EVENTS__TERM_TYPE_NUM, PARSE_EVENTS__TERM_TYPE_STR, @@ -78,6 +70,7 @@ enum { PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE, PARSE_EVENTS__TERM_TYPE_METRIC_ID, + PARSE_EVENTS__TERM_TYPE_RAW, __PARSE_EVENTS__TERM_TYPE_NR, }; @@ -174,8 +167,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, int parse_events_add_tool(struct parse_events_state *parse_state, struct list_head *list, int tool_event); -int parse_events_add_cache(struct list_head *list, int *idx, - char *type, char *op_result1, char *op_result2, +int parse_events_add_cache(struct list_head *list, int *idx, const char *name, struct parse_events_error *error, struct list_head *head_config, struct parse_events_state *parse_state); @@ -198,8 +190,6 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, int parse_events_copy_term_list(struct list_head *old, struct list_head **new); -enum perf_pmu_event_symbol_type -perf_pmu__parse_check(const char *name); void parse_events__set_leader(char *name, struct list_head *list); void parse_events_update_lists(struct list_head *list_event, struct list_head *list_all); @@ -241,8 +231,6 @@ static inline bool is_sdt_event(char *str __maybe_unused) } #endif /* HAVE_LIBELF_SUPPORT */ -int perf_pmu__test_parse_init(void); - struct evsel *parse_events__add_event_hybrid(struct list_head *list, int *idx, struct perf_event_attr *attr, const char *name, diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 51fe0a9fb3de..4b35c099189a 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -63,17 +63,6 @@ static int str(yyscan_t scanner, int token) return token; } -static int raw(yyscan_t scanner) -{ - YYSTYPE *yylval = parse_events_get_lval(scanner); - char *text = parse_events_get_text(scanner); - - if (perf_pmu__parse_check(text) == PMU_EVENT_SYMBOL) - return str(scanner, PE_NAME); - - return __value(yylval, text + 1, 16, PE_RAW); -} - static bool isbpf_suffix(char *text) { int len = strlen(text); @@ -131,35 +120,6 @@ do { \ yyless(0); \ } while (0) -static int pmu_str_check(yyscan_t scanner, struct parse_events_state *parse_state) -{ - YYSTYPE *yylval = parse_events_get_lval(scanner); - char *text = parse_events_get_text(scanner); - - yylval->str = strdup(text); - - /* - * If we're not testing then parse check determines the PMU event type - * which if it isn't a PMU returns PE_NAME. When testing the result of - * parse check can't be trusted so we return PE_PMU_EVENT_FAKE unless - * an '!' is present in which case the text can't be a PMU name. - */ - switch (perf_pmu__parse_check(text)) { - case PMU_EVENT_SYMBOL_PREFIX: - return PE_PMU_EVENT_PRE; - case PMU_EVENT_SYMBOL_SUFFIX: - return PE_PMU_EVENT_SUF; - case PMU_EVENT_SYMBOL_SUFFIX2: - return PE_PMU_EVENT_SUF2; - case PMU_EVENT_SYMBOL: - return parse_state->fake_pmu - ? PE_PMU_EVENT_FAKE : PE_KERNEL_PMU_EVENT; - default: - return parse_state->fake_pmu && !strchr(text,'!') - ? PE_PMU_EVENT_FAKE : PE_NAME; - } -} - static int sym(yyscan_t scanner, int type, int config) { YYSTYPE *yylval = parse_events_get_lval(scanner); @@ -211,13 +171,15 @@ bpf_source [^,{}]+\.c[a-zA-Z0-9._]* num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ -name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!]* +name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!\-]* name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\'] name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]* drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)? /* If you add a modifier you need to update check_modifier() */ modifier_event [ukhpPGHSDIWeb]+ modifier_bp [rwx]{1,3} +lc_type (L1-dcache|l1-d|l1d|L1-data|L1-icache|l1-i|l1i|L1-instruction|LLC|L2|dTLB|d-tlb|Data-TLB|iTLB|i-tlb|Instruction-TLB|branch|branches|bpu|btb|bpc|node) +lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss) %% @@ -303,8 +265,8 @@ percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); } aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); } aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); } metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); } -r{num_raw_hex} { return raw(yyscanner); } -r0x{num_raw_hex} { return raw(yyscanner); } +r{num_raw_hex} { return str(yyscanner, PE_RAW); } +r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } , { return ','; } "/" { BEGIN(INITIAL); return '/'; } {name_minus} { return str(yyscanner, PE_NAME); } @@ -359,47 +321,20 @@ system_time { return tool(yyscanner, PERF_TOOL_SYSTEM_TIME); } bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); } cgroup-switches { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); } - /* - * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. - * Because the prefix cycles is mixed up with cpu-cycles. - * loads and stores are mixed up with cache event - */ -cycles-ct | -cycles-t | -mem-loads | -mem-loads-aux | -mem-stores | -topdown-[a-z-]+ | -tx-capacity-[a-z-]+ | -el-capacity-[a-z-]+ { return str(yyscanner, PE_KERNEL_PMU_EVENT); } - -L1-dcache|l1-d|l1d|L1-data | -L1-icache|l1-i|l1i|L1-instruction | -LLC|L2 | -dTLB|d-tlb|Data-TLB | -iTLB|i-tlb|Instruction-TLB | -branch|branches|bpu|btb|bpc | -node { return str(yyscanner, PE_NAME_CACHE_TYPE); } - -load|loads|read | -store|stores|write | -prefetch|prefetches | -speculative-read|speculative-load | -refs|Reference|ops|access | -misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } - +{lc_type} { return str(yyscanner, PE_LEGACY_CACHE); } +{lc_type}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); } +{lc_type}-{lc_op_result}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); } mem: { BEGIN(mem); return PE_PREFIX_MEM; } -r{num_raw_hex} { return raw(yyscanner); } +r{num_raw_hex} { return str(yyscanner, PE_RAW); } {num_dec} { return value(yyscanner, 10); } {num_hex} { return value(yyscanner, 16); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } {bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); } {bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); } -{name} { return pmu_str_check(yyscanner, _parse_state); } +{name} { return str(yyscanner, PE_NAME); } {name_tag} { return str(yyscanner, PE_NAME); } "/" { BEGIN(config); return '/'; } -- { return '-'; } , { BEGIN(event); return ','; } : { return ':'; } "{" { BEGIN(event); return '{'; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4488443e506e..e7072b5601c5 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -8,6 +8,7 @@ #define YYDEBUG 1 +#include #include #include #include @@ -52,36 +53,35 @@ static void free_list_evsel(struct list_head* list_evsel) %} %token PE_START_EVENTS PE_START_TERMS -%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM +%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM %token PE_VALUE_SYM_TOOL %token PE_EVENT_NAME -%token PE_NAME +%token PE_RAW PE_NAME %token PE_BPF_OBJECT PE_BPF_SOURCE %token PE_MODIFIER_EVENT PE_MODIFIER_BP -%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT +%token PE_LEGACY_CACHE %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP %token PE_ERROR -%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE +%token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %token PE_ARRAY_ALL PE_ARRAY_RANGE %token PE_DRV_CFG_TERM %type PE_VALUE %type PE_VALUE_SYM_HW %type PE_VALUE_SYM_SW %type PE_VALUE_SYM_TOOL -%type PE_RAW %type PE_TERM %type value_sym +%type PE_RAW %type PE_NAME %type PE_BPF_OBJECT %type PE_BPF_SOURCE -%type PE_NAME_CACHE_TYPE -%type PE_NAME_CACHE_OP_RESULT +%type PE_LEGACY_CACHE %type PE_MODIFIER_EVENT %type PE_MODIFIER_BP %type PE_EVENT_NAME -%type PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE +%type PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %type PE_DRV_CFG_TERM -%type event_pmu_name +%type name_or_raw %destructor { free ($$); } %type event_term %destructor { parse_events_term__delete ($$); } @@ -273,11 +273,8 @@ event_def: event_pmu | event_legacy_raw sep_dc | event_bpf_file -event_pmu_name: -PE_NAME | PE_PMU_EVENT_PRE - event_pmu: -event_pmu_name opt_pmu_config +PE_NAME opt_pmu_config { struct parse_events_state *parse_state = _parse_state; struct parse_events_error *error = parse_state->error; @@ -303,10 +300,12 @@ event_pmu_name opt_pmu_config list = alloc_list(); if (!list) CLEANUP_YYABORT; + /* Attempt to add to list assuming $1 is a PMU name. */ if (parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false)) { struct perf_pmu *pmu = NULL; int ok = 0; + /* Failure to add, try wildcard expansion of $1 as a PMU name. */ if (asprintf(&pattern, "%s*", $1) < 0) CLEANUP_YYABORT; @@ -329,6 +328,12 @@ event_pmu_name opt_pmu_config } } + if (!ok) { + /* Failure to add, assume $1 is an event name. */ + zfree(&list); + ok = !parse_events_multi_pmu_add(_parse_state, $1, $2, &list); + $2 = NULL; + } if (!ok) CLEANUP_YYABORT; } @@ -352,41 +357,27 @@ PE_KERNEL_PMU_EVENT sep_dc $$ = list; } | -PE_KERNEL_PMU_EVENT opt_pmu_config +PE_NAME sep_dc { struct list_head *list; int err; - /* frees $2 */ - err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list); + err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list); free($1); if (err < 0) YYABORT; $$ = list; } | -PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF '-' PE_PMU_EVENT_SUF2 sep_dc -{ - struct list_head *list; - char pmu_name[128]; - snprintf(pmu_name, sizeof(pmu_name), "%s-%s-%s", $1, $3, $5); - free($1); - free($3); - free($5); - if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0) - YYABORT; - $$ = list; -} -| -PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc +PE_KERNEL_PMU_EVENT opt_pmu_config { struct list_head *list; - char pmu_name[128]; + int err; - snprintf(pmu_name, sizeof(pmu_name), "%s-%s", $1, $3); + /* frees $2 */ + err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list); free($1); - free($3); - if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0) + if (err < 0) YYABORT; $$ = list; } @@ -476,7 +467,7 @@ PE_VALUE_SYM_TOOL sep_slash_slash_dc } event_legacy_cache: -PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config +PE_LEGACY_CACHE opt_event_config { struct parse_events_state *parse_state = _parse_state; struct parse_events_error *error = parse_state->error; @@ -485,51 +476,8 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_e list = alloc_list(); ABORT_ON(!list); - err = parse_events_add_cache(list, &parse_state->idx, $1, $3, $5, error, $6, - parse_state); - parse_events_terms__delete($6); - free($1); - free($3); - free($5); - if (err) { - free_list_evsel(list); - YYABORT; - } - $$ = list; -} -| -PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config -{ - struct parse_events_state *parse_state = _parse_state; - struct parse_events_error *error = parse_state->error; - struct list_head *list; - int err; + err = parse_events_add_cache(list, &parse_state->idx, $1, error, $2, parse_state); - list = alloc_list(); - ABORT_ON(!list); - err = parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4, - parse_state); - parse_events_terms__delete($4); - free($1); - free($3); - if (err) { - free_list_evsel(list); - YYABORT; - } - $$ = list; -} -| -PE_NAME_CACHE_TYPE opt_event_config -{ - struct parse_events_state *parse_state = _parse_state; - struct parse_events_error *error = parse_state->error; - struct list_head *list; - int err; - - list = alloc_list(); - ABORT_ON(!list); - err = parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2, - parse_state); parse_events_terms__delete($2); free($1); if (err) { @@ -633,17 +581,6 @@ tracepoint_name opt_event_config } tracepoint_name: -PE_NAME '-' PE_NAME ':' PE_NAME -{ - struct tracepoint_name tracepoint; - - ABORT_ON(asprintf(&tracepoint.sys, "%s-%s", $1, $3) < 0); - tracepoint.event = $5; - free($1); - free($3); - $$ = tracepoint; -} -| PE_NAME ':' PE_NAME { struct tracepoint_name tracepoint = {$1, $3}; @@ -673,10 +610,15 @@ PE_RAW opt_event_config { struct list_head *list; int err; + u64 num; list = alloc_list(); ABORT_ON(!list); - err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, $1, $2); + errno = 0; + num = strtoull($1 + 1, NULL, 16); + ABORT_ON(errno); + free($1); + err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2); parse_events_terms__delete($2); if (err) { free(list); @@ -781,17 +723,22 @@ event_term $$ = head; } +name_or_raw: PE_RAW | PE_NAME + event_term: PE_RAW { struct parse_events_term *term; - ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_CONFIG, - NULL, $1, false, &@1, NULL)); + if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW, + strdup("raw"), $1, &@1, &@1)) { + free($1); + YYABORT; + } $$ = term; } | -PE_NAME '=' PE_NAME +name_or_raw '=' PE_NAME { struct parse_events_term *term; @@ -804,7 +751,7 @@ PE_NAME '=' PE_NAME $$ = term; } | -PE_NAME '=' PE_VALUE +name_or_raw '=' PE_VALUE { struct parse_events_term *term; @@ -816,7 +763,7 @@ PE_NAME '=' PE_VALUE $$ = term; } | -PE_NAME '=' PE_VALUE_SYM_HW +name_or_raw '=' PE_VALUE_SYM_HW { struct parse_events_term *term; int config = $3 & 255; @@ -876,7 +823,7 @@ PE_TERM $$ = term; } | -PE_NAME array '=' PE_NAME +name_or_raw array '=' PE_NAME { struct parse_events_term *term; @@ -891,7 +838,7 @@ PE_NAME array '=' PE_NAME $$ = term; } | -PE_NAME array '=' PE_VALUE +name_or_raw array '=' PE_VALUE { struct parse_events_term *term; -- cgit v1.2.3 From ae4aa00a1a9358e0007f6edc71b018a0b0d21190 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:27 -0700 Subject: perf test: Move x86 hybrid tests to arch/x86 The tests use x86 hybrid specific PMUs. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-21-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/include/arch-tests.h | 1 + tools/perf/arch/x86/tests/Build | 1 + tools/perf/arch/x86/tests/arch-tests.c | 10 ++ tools/perf/arch/x86/tests/hybrid.c | 277 +++++++++++++++++++++++++++++++ tools/perf/tests/parse-events.c | 181 -------------------- 5 files changed, 289 insertions(+), 181 deletions(-) create mode 100644 tools/perf/arch/x86/tests/hybrid.c (limited to 'tools/perf/tests') diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 902e9ea9b99e..33d39c1d3e64 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -11,6 +11,7 @@ int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest); int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest); int test__bp_modify(struct test_suite *test, int subtest); int test__x86_sample_parsing(struct test_suite *test, int subtest); +int test__hybrid(struct test_suite *test, int subtest); extern struct test_suite *arch_tests[]; diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 6f4e8636c3bf..08cc8b9c931e 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build @@ -3,5 +3,6 @@ perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-y += arch-tests.o perf-y += sample-parsing.o +perf-y += hybrid.o perf-$(CONFIG_AUXTRACE) += insn-x86.o intel-pt-test.o perf-$(CONFIG_X86_64) += bp-modify.o diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index aae6ea0fe52b..147ad0638bbb 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -22,6 +22,15 @@ struct test_suite suite__intel_pt = { DEFINE_SUITE("x86 bp modify", bp_modify); #endif DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing); +static struct test_case hybrid_tests[] = { + TEST_CASE_REASON("x86 hybrid event parsing", hybrid, "not hybrid"), + { .name = NULL, } +}; + +struct test_suite suite__hybrid = { + .desc = "x86 hybrid", + .test_cases = hybrid_tests, +}; struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT @@ -35,5 +44,6 @@ struct test_suite *arch_tests[] = { &suite__bp_modify, #endif &suite__x86_sample_parsing, + &suite__hybrid, NULL, }; diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c new file mode 100644 index 000000000000..0f99cfd116ee --- /dev/null +++ b/tools/perf/arch/x86/tests/hybrid.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "arch-tests.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "pmu-hybrid.h" +#include "tests/tests.h" + +static bool test_config(const struct evsel *evsel, __u64 expected_config) +{ + return (evsel->core.attr.config & PERF_HW_EVENT_MASK) == expected_config; +} + +static int test__hybrid_hw_event_with_pmu(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); + return TEST_OK; +} + +static int test__hybrid_hw_group_event(struct evlist *evlist) +{ + struct evsel *evsel, *leader; + + evsel = leader = evlist__first(evlist); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + return TEST_OK; +} + +static int test__hybrid_sw_hw_group_event(struct evlist *evlist) +{ + struct evsel *evsel, *leader; + + evsel = leader = evlist__first(evlist); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + return TEST_OK; +} + +static int test__hybrid_hw_sw_group_event(struct evlist *evlist) +{ + struct evsel *evsel, *leader; + + evsel = leader = evlist__first(evlist); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + return TEST_OK; +} + +static int test__hybrid_group_modifier1(struct evlist *evlist) +{ + struct evsel *evsel, *leader; + + evsel = leader = evlist__first(evlist); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + return TEST_OK; +} + +static int test__hybrid_raw1(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + if (!perf_pmu__hybrid_mounted("cpu_atom")) { + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); + return TEST_OK; + } + + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); + + /* The type of second event is randome value */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); + return TEST_OK; +} + +static int test__hybrid_raw2(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); + return TEST_OK; +} + +static int test__hybrid_cache_event(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", 0x2 == (evsel->core.attr.config & 0xffffffff)); + return TEST_OK; +} + +static int test__checkevent_pmu(struct evlist *evlist) +{ + + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", 10 == evsel->core.attr.config); + TEST_ASSERT_VAL("wrong config1", 1 == evsel->core.attr.config1); + TEST_ASSERT_VAL("wrong config2", 3 == evsel->core.attr.config2); + TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3); + /* + * The period value gets configured within evlist__config, + * while this test executes only parse events method. + */ + TEST_ASSERT_VAL("wrong period", 0 == evsel->core.attr.sample_period); + + return TEST_OK; +} + +struct evlist_test { + const char *name; + bool (*valid)(void); + int (*check)(struct evlist *evlist); +}; + +static const struct evlist_test test__hybrid_events[] = { + { + .name = "cpu_core/cpu-cycles/", + .check = test__hybrid_hw_event_with_pmu, + /* 0 */ + }, + { + .name = "{cpu_core/cpu-cycles/,cpu_core/instructions/}", + .check = test__hybrid_hw_group_event, + /* 1 */ + }, + { + .name = "{cpu-clock,cpu_core/cpu-cycles/}", + .check = test__hybrid_sw_hw_group_event, + /* 2 */ + }, + { + .name = "{cpu_core/cpu-cycles/,cpu-clock}", + .check = test__hybrid_hw_sw_group_event, + /* 3 */ + }, + { + .name = "{cpu_core/cpu-cycles/k,cpu_core/instructions/u}", + .check = test__hybrid_group_modifier1, + /* 4 */ + }, + { + .name = "r1a", + .check = test__hybrid_raw1, + /* 5 */ + }, + { + .name = "cpu_core/r1a/", + .check = test__hybrid_raw2, + /* 6 */ + }, + { + .name = "cpu_core/config=10,config1,config2=3,period=1000/u", + .check = test__checkevent_pmu, + /* 7 */ + }, + { + .name = "cpu_core/LLC-loads/", + .check = test__hybrid_cache_event, + /* 8 */ + }, +}; + +static int test_event(const struct evlist_test *e) +{ + struct parse_events_error err; + struct evlist *evlist; + int ret; + + if (e->valid && !e->valid()) { + pr_debug("... SKIP\n"); + return TEST_OK; + } + + evlist = evlist__new(); + if (evlist == NULL) { + pr_err("Failed allocation"); + return TEST_FAIL; + } + parse_events_error__init(&err); + ret = parse_events(evlist, e->name, &err); + if (ret) { + pr_debug("failed to parse event '%s', err %d, str '%s'\n", + e->name, ret, err.str); + parse_events_error__print(&err, e->name); + ret = TEST_FAIL; + if (strstr(err.str, "can't access trace events")) + ret = TEST_SKIP; + } else { + ret = e->check(evlist); + } + parse_events_error__exit(&err); + evlist__delete(evlist); + + return ret; +} + +static int combine_test_results(int existing, int latest) +{ + if (existing == TEST_FAIL) + return TEST_FAIL; + if (existing == TEST_SKIP) + return latest == TEST_OK ? TEST_SKIP : latest; + return latest; +} + +static int test_events(const struct evlist_test *events, int cnt) +{ + int ret = TEST_OK; + + for (int i = 0; i < cnt; i++) { + const struct evlist_test *e = &events[i]; + int test_ret; + + pr_debug("running test %d '%s'\n", i, e->name); + test_ret = test_event(e); + if (test_ret != TEST_OK) { + pr_debug("Event test failure: test %d '%s'", i, e->name); + ret = combine_test_results(ret, test_ret); + } + } + + return ret; +} + +int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + if (!perf_pmu__has_hybrid()) + return TEST_SKIP; + + return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events)); +} diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 5ba90b32c524..43c0778983e5 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -6,7 +6,6 @@ #include "tests.h" #include "debug.h" #include "pmu.h" -#include "pmu-hybrid.h" #include "pmus.h" #include #include @@ -1523,127 +1522,6 @@ static int test__all_tracepoints(struct evlist *evlist) } #endif /* HAVE_LIBTRACEVENT */ -static int test__hybrid_hw_event_with_pmu(struct evlist *evlist) -{ - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); - return TEST_OK; -} - -static int test__hybrid_hw_group_event(struct evlist *evlist) -{ - struct evsel *evsel, *leader; - - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - return TEST_OK; -} - -static int test__hybrid_sw_hw_group_event(struct evlist *evlist) -{ - struct evsel *evsel, *leader; - - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - return TEST_OK; -} - -static int test__hybrid_hw_sw_group_event(struct evlist *evlist) -{ - struct evsel *evsel, *leader; - - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - return TEST_OK; -} - -static int test__hybrid_group_modifier1(struct evlist *evlist) -{ - struct evsel *evsel, *leader; - - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x3c)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0xc0)); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - return TEST_OK; -} - -static int test__hybrid_raw1(struct evlist *evlist) -{ - struct evsel *evsel = evlist__first(evlist); - - if (!perf_pmu__hybrid_mounted("cpu_atom")) { - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); - return TEST_OK; - } - - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); - - /* The type of second event is randome value */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); - return TEST_OK; -} - -static int test__hybrid_raw2(struct evlist *evlist) -{ - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); - return TEST_OK; -} - -static int test__hybrid_cache_event(struct evlist *evlist) -{ - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", 0x2 == (evsel->core.attr.config & 0xffffffff)); - return TEST_OK; -} - struct evlist_test { const char *name; bool (*valid)(void); @@ -2011,54 +1889,6 @@ static const struct terms_test test__terms[] = { }, }; -static const struct evlist_test test__hybrid_events[] = { - { - .name = "cpu_core/cpu-cycles/", - .check = test__hybrid_hw_event_with_pmu, - /* 0 */ - }, - { - .name = "{cpu_core/cpu-cycles/,cpu_core/instructions/}", - .check = test__hybrid_hw_group_event, - /* 1 */ - }, - { - .name = "{cpu-clock,cpu_core/cpu-cycles/}", - .check = test__hybrid_sw_hw_group_event, - /* 2 */ - }, - { - .name = "{cpu_core/cpu-cycles/,cpu-clock}", - .check = test__hybrid_hw_sw_group_event, - /* 3 */ - }, - { - .name = "{cpu_core/cpu-cycles/k,cpu_core/instructions/u}", - .check = test__hybrid_group_modifier1, - /* 4 */ - }, - { - .name = "r1a", - .check = test__hybrid_raw1, - /* 5 */ - }, - { - .name = "cpu_core/r1a/", - .check = test__hybrid_raw2, - /* 6 */ - }, - { - .name = "cpu_core/config=10,config1,config2=3,period=1000/u", - .check = test__checkevent_pmu, - /* 7 */ - }, - { - .name = "cpu_core/LLC-loads/", - .check = test__hybrid_cache_event, - /* 8 */ - }, -}; - static int test_event(const struct evlist_test *e) { struct parse_events_error err; @@ -2337,14 +2167,6 @@ static bool test_alias(char **event, char **alias) return false; } -static int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - if (!perf_pmu__has_hybrid()) - return TEST_SKIP; - - return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events)); -} - static int test__checkevent_pmu_events_alias(struct evlist *evlist) { struct evsel *evsel1 = evlist__first(evlist); @@ -2408,9 +2230,6 @@ static struct test_case tests__parse_events[] = { TEST_CASE_REASON("Test event parsing", events2, "permissions"), - TEST_CASE_REASON("Test parsing of \"hybrid\" CPU events", - hybrid, - "not hybrid"), TEST_CASE_REASON("Parsing of all PMU events from sysfs", pmu_events, "permissions"), -- cgit v1.2.3 From 6fd1e5191591f9d55afe4d23fa35af2a5cf8f81f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:30 -0700 Subject: perf parse-events: Support PMUs for legacy cache events Allow a legacy cache event to be both, for example, "L1-dcache-load-miss" and "cpu/L1-dcache-load-miss/" by introducing a new legacy cache term type. The term type is processed in config_term_pmu, setting both the type in perf_event_attr and the config. The code to determine the config is factored out of parse_events_add_cache and shared. If the PMU doesn't support legacy events, currently just core/hybrid PMUs do, then the term is treated like a PE_NAME term - as before. If only terms are being parsed, such as for perf_pmu__new_alias, then the PE_LEGACY_CACHE token is always parsed as PE_NAME. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-24-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 18 +++++++++++ tools/perf/util/parse-events.c | 70 ++++++++++++++++++++++++++++------------- tools/perf/util/parse-events.h | 3 ++ tools/perf/util/parse-events.l | 9 +++++- tools/perf/util/parse-events.y | 14 ++++++++- tools/perf/util/pmu.c | 5 +++ tools/perf/util/pmu.h | 1 + 7 files changed, 96 insertions(+), 24 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 43c0778983e5..c3afd0b129bb 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1875,6 +1875,24 @@ static const struct evlist_test test__events_pmu[] = { .check = test__checkevent_raw_pmu, /* 5 */ }, + { + .name = "cpu/L1-dcache-load-miss/", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_genhw, + /* 6 */ + }, + { + .name = "cpu/L1-dcache-load-miss/kp", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_genhw_modifier, + /* 7 */ + }, + { + .name = "cpu/L1-dcache-misses,name=cachepmu/", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_config_cache, + /* 8 */ + }, }; struct terms_test { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b5d95fce520c..f692dd953593 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -404,33 +404,27 @@ static int config_attr(struct perf_event_attr *attr, struct parse_events_error *err, config_term_func_t config_term); -int parse_events_add_cache(struct list_head *list, int *idx, const char *name, - struct parse_events_error *err, - struct list_head *head_config, - struct parse_events_state *parse_state) +/** + * parse_events__decode_legacy_cache - Search name for the legacy cache event + * name composed of 1, 2 or 3 hyphen + * separated sections. The first section is + * the cache type while the others are the + * optional op and optional result. To make + * life hard the names in the table also + * contain hyphens and the longest name + * should always be selected. + */ +static int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config) { - struct perf_event_attr attr; - LIST_HEAD(config_terms); - const char *config_name, *metric_id; - int cache_type = -1, cache_op = -1, cache_result = -1; - int ret, len; + int len, cache_type = -1, cache_op = -1, cache_result = -1; const char *name_end = &name[strlen(name) + 1]; - bool hybrid; const char *str = name; - /* - * Search str for the legacy cache event name composed of 1, 2 or 3 - * hyphen separated sections. The first section is the cache type while - * the others are the optional op and optional result. To make life hard - * the names in the table also contain hyphens and the longest name - * should always be selected. - */ cache_type = parse_aliases(str, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX, &len); if (cache_type == -1) return -EINVAL; str += len + 1; - config_name = get_config_name(head_config); if (str < name_end) { cache_op = parse_aliases(str, evsel__hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX, &len); @@ -471,9 +465,28 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, if (cache_result == -1) cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; + *config = ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT) | + cache_type | (cache_op << 8) | (cache_result << 16); + return 0; +} + +int parse_events_add_cache(struct list_head *list, int *idx, const char *name, + struct parse_events_error *err, + struct list_head *head_config, + struct parse_events_state *parse_state) +{ + struct perf_event_attr attr; + LIST_HEAD(config_terms); + const char *config_name, *metric_id; + int ret; + bool hybrid; + + memset(&attr, 0, sizeof(attr)); - attr.config = cache_type | (cache_op << 8) | (cache_result << 16); attr.type = PERF_TYPE_HW_CACHE; + ret = parse_events__decode_legacy_cache(name, /*pmu_type=*/0, &attr.config); + if (ret) + return ret; if (head_config) { if (config_attr(&attr, head_config, err, @@ -484,6 +497,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, return -ENOMEM; } + config_name = get_config_name(head_config); metric_id = get_config_metric_id(head_config); ret = parse_events__add_cache_hybrid(list, idx, &attr, config_name ? : name, @@ -1022,6 +1036,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size", [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", [PARSE_EVENTS__TERM_TYPE_RAW] = "raw", + [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache", }; static bool config_term_shrinked; @@ -1199,15 +1214,25 @@ static int config_term_pmu(struct perf_event_attr *attr, struct parse_events_term *term, struct parse_events_error *err) { + if (term->type_term == PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE) { + const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type); + + if (perf_pmu__supports_legacy_cache(pmu)) { + attr->type = PERF_TYPE_HW_CACHE; + return parse_events__decode_legacy_cache(term->config, pmu->type, + &attr->config); + } else + term->type_term = PARSE_EVENTS__TERM_TYPE_USER; + } if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER || - term->type_term == PARSE_EVENTS__TERM_TYPE_DRV_CFG) + term->type_term == PARSE_EVENTS__TERM_TYPE_DRV_CFG) { /* * Always succeed for sysfs terms, as we dont know * at this point what type they need to have. */ return 0; - else - return config_term_common(attr, term, err); + } + return config_term_common(attr, term, err); } #ifdef HAVE_LIBTRACEEVENT @@ -2147,6 +2172,7 @@ int __parse_events(struct evlist *evlist, const char *str, .evlist = evlist, .stoken = PE_START_EVENTS, .fake_pmu = fake_pmu, + .match_legacy_cache_terms = true, }; int ret; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f638542c8638..5acb62c2e00a 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -71,6 +71,7 @@ enum { PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE, PARSE_EVENTS__TERM_TYPE_METRIC_ID, PARSE_EVENTS__TERM_TYPE_RAW, + PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, __PARSE_EVENTS__TERM_TYPE_NR, }; @@ -122,6 +123,8 @@ struct parse_events_state { int stoken; struct perf_pmu *fake_pmu; char *hybrid_pmu_name; + /* Should PE_LEGACY_NAME tokens be generated for config terms? */ + bool match_legacy_cache_terms; bool wild_card_pmus; }; diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 4b35c099189a..abe0ce681d29 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -63,6 +63,11 @@ static int str(yyscan_t scanner, int token) return token; } +static int lc_str(yyscan_t scanner, const struct parse_events_state *state) +{ + return str(scanner, state->match_legacy_cache_terms ? PE_LEGACY_CACHE : PE_NAME); +} + static bool isbpf_suffix(char *text) { int len = strlen(text); @@ -185,7 +190,6 @@ lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative %{ struct parse_events_state *_parse_state = parse_events_get_extra(yyscanner); - { int start_token = _parse_state->stoken; @@ -269,6 +273,9 @@ r{num_raw_hex} { return str(yyscanner, PE_RAW); } r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } , { return ','; } "/" { BEGIN(INITIAL); return '/'; } +{lc_type} { return lc_str(yyscanner, _parse_state); } +{lc_type}-{lc_op_result} { return lc_str(yyscanner, _parse_state); } +{lc_type}-{lc_op_result}-{lc_op_result} { return lc_str(yyscanner, _parse_state); } {name_minus} { return str(yyscanner, PE_NAME); } \[all\] { return PE_ARRAY_ALL; } "[" { BEGIN(array); return '['; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index e7072b5601c5..f84fa1b132b3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -723,7 +723,7 @@ event_term $$ = head; } -name_or_raw: PE_RAW | PE_NAME +name_or_raw: PE_RAW | PE_NAME | PE_LEGACY_CACHE event_term: PE_RAW @@ -775,6 +775,18 @@ name_or_raw '=' PE_VALUE_SYM_HW $$ = term; } | +PE_LEGACY_CACHE +{ + struct parse_events_term *term; + + if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, + $1, 1, true, &@1, NULL)) { + free($1); + YYABORT; + } + $$ = term; +} +| PE_NAME { struct parse_events_term *term; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index cb33d869f1ed..63071d876190 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1650,6 +1650,11 @@ bool is_pmu_core(const char *name) return !strcmp(name, "cpu") || is_arm_pmu_core(name); } +bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu) +{ + return is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name); +} + static bool pmu_alias_is_duplicate(struct sevent *alias_a, struct sevent *alias_b) { diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index b9a02dedd473..05702bc4bcf8 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -220,6 +220,7 @@ void perf_pmu__del_formats(struct list_head *formats); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); bool is_pmu_core(const char *name); +bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); void print_pmu_events(const struct print_callbacks *print_cb, void *print_state); bool pmu_have_event(const char *pname, const char *name); -- cgit v1.2.3 From 411ad22ecf0281d666a82aa7f4de90c70365da7d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:36 -0700 Subject: perf parse-events: Add pmu filter To support the cputype argument added to "perf stat" for hybrid it is necessary to filter events during wildcard matching. Add a scanner argument for the filter and checking it when wildcard matching. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-30-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 13 +++++++++-- tools/perf/builtin-stat.c | 10 +++++--- tools/perf/builtin-top.c | 5 +++- tools/perf/builtin-trace.c | 5 +++- tools/perf/tests/parse-events.c | 3 ++- tools/perf/tests/pmu-events.c | 3 ++- tools/perf/util/evlist.h | 1 - tools/perf/util/metricgroup.c | 4 ++-- tools/perf/util/parse-events.c | 51 ++++++++++++++++++++++++++++++----------- tools/perf/util/parse-events.h | 21 +++++++++++++---- tools/perf/util/parse-events.y | 6 +++-- 11 files changed, 90 insertions(+), 32 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index efa03e4ac2c9..ec0f2d5f189f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3335,6 +3335,14 @@ const char record_callchain_help[] = CALLCHAIN_RECORD_HELP static bool dry_run; +static struct parse_events_option_args parse_events_option_args = { + .evlistp = &record.evlist, +}; + +static struct parse_events_option_args switch_output_parse_events_option_args = { + .evlistp = &record.sb_evlist, +}; + /* * XXX Will stay a global variable till we fix builtin-script.c to stop messing * with it and switch to use the library functions in perf_evlist that came @@ -3343,7 +3351,7 @@ static bool dry_run; * using pipes, etc. */ static struct option __record_options[] = { - OPT_CALLBACK('e', "event", &record.evlist, "event", + OPT_CALLBACK('e', "event", &parse_events_option_args, "event", "event selector. use 'perf list' to list available events", parse_events_option), OPT_CALLBACK(0, "filter", &record.evlist, "filter", @@ -3496,7 +3504,8 @@ static struct option __record_options[] = { &record.switch_output.set, "signal or size[BKMG] or time[smhd]", "Switch output when receiving SIGUSR2 (signal) or cross a size or time threshold", "signal"), - OPT_CALLBACK_SET(0, "switch-output-event", &record.sb_evlist, &record.switch_output_event_set, "switch output event", + OPT_CALLBACK_SET(0, "switch-output-event", &switch_output_parse_events_option_args, + &record.switch_output_event_set, "switch output event", "switch output event selector. use 'perf list' to list available events", parse_events_option_new_evlist), OPT_INTEGER(0, "switch-max-files", &record.switch_output.num_files, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index b9ad32f21e57..de2e915427c9 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -101,6 +101,10 @@ static void print_counters(struct timespec *ts, int argc, const char **argv); static struct evlist *evsel_list; +static struct parse_events_option_args parse_events_option_args = { + .evlistp = &evsel_list, +}; + static bool all_counters_use_bpf = true; static struct target target = { @@ -1096,8 +1100,8 @@ static int parse_hybrid_type(const struct option *opt, return -1; } - evlist->hybrid_pmu_name = perf_pmu__hybrid_type_to_pmu(str); - if (!evlist->hybrid_pmu_name) { + parse_events_option_args.pmu_filter = perf_pmu__hybrid_type_to_pmu(str); + if (!parse_events_option_args.pmu_filter) { fprintf(stderr, "--cputype %s is not supported!\n", str); return -1; } @@ -1108,7 +1112,7 @@ static int parse_hybrid_type(const struct option *opt, static struct option stat_options[] = { OPT_BOOLEAN('T', "transaction", &transaction_run, "hardware transaction statistics"), - OPT_CALLBACK('e', "event", &evsel_list, "event", + OPT_CALLBACK('e', "event", &parse_events_option_args, "event", "event selector. use 'perf list' to list available events", parse_events_option), OPT_CALLBACK(0, "filter", &evsel_list, "filter", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index eb5740154bc0..48ee49e95c5e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1440,12 +1440,15 @@ int cmd_top(int argc, const char **argv) .max_stack = sysctl__max_stack(), .nr_threads_synthesize = UINT_MAX, }; + struct parse_events_option_args parse_events_option_args = { + .evlistp = &top.evlist, + }; bool branch_call_mode = false; struct record_opts *opts = &top.record_opts; struct target *target = &opts->target; const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; const struct option options[] = { - OPT_CALLBACK('e', "event", &top.evlist, "event", + OPT_CALLBACK('e', "event", &parse_events_option_args, "event", "event selector. use 'perf list' to list available events", parse_events_option), OPT_U64('c', "count", &opts->user_interval, "event period to sample"), diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8ee3a45c3c54..b49d3abb1203 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -4591,8 +4591,11 @@ do_concat: err = 0; if (lists[0]) { + struct parse_events_option_args parse_events_option_args = { + .evlistp = &trace->evlist, + }; struct option o = { - .value = &trace->evlist, + .value = &parse_events_option_args, }; err = parse_events_option(&o, lists[0], 0); } diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c3afd0b129bb..0d0c869d2d09 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1952,7 +1952,8 @@ static int test_event_fake_pmu(const char *str) return -ENOMEM; parse_events_error__init(&err); - ret = __parse_events(evlist, str, &err, &perf_pmu__fake, /*warn_if_reordered=*/true); + ret = __parse_events(evlist, str, /*pmu_filter=*/NULL, &err, + &perf_pmu__fake, /*warn_if_reordered=*/true); if (ret) { pr_debug("failed to parse event '%s', err %d, str '%s'\n", str, ret, err.str); diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index a2cde61b1c77..734004f1a37d 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -776,7 +776,8 @@ static int check_parse_id(const char *id, struct parse_events_error *error, for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@')) *cur = '/'; - ret = __parse_events(evlist, dup, error, fake_pmu, /*warn_if_reordered=*/true); + ret = __parse_events(evlist, dup, /*pmu_filter=*/NULL, error, fake_pmu, + /*warn_if_reordered=*/true); free(dup); evlist__delete(evlist); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 46cf402add93..e7e5540cc970 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -67,7 +67,6 @@ struct evlist { struct evsel *selected; struct events_stats stats; struct perf_env *env; - const char *hybrid_pmu_name; void (*trace_event_sample_raw)(struct evlist *evlist, union perf_event *event, struct perf_sample *sample); diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 4e7d41d285b4..908124dab122 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -1441,8 +1441,8 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, } pr_debug("Parsing metric events '%s'\n", events.buf); parse_events_error__init(&parse_error); - ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu, - /*warn_if_reordered=*/false); + ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL, + &parse_error, fake_pmu, /*warn_if_reordered=*/false); if (ret) { parse_events_error__print(&parse_error, events.buf); goto err_out; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1d8c3cf9c185..d9d964bbc0e2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -465,8 +465,24 @@ int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *con return 0; } +/** + * parse_events__filter_pmu - returns false if a wildcard PMU should be + * considered, true if it should be filtered. + */ +bool parse_events__filter_pmu(const struct parse_events_state *parse_state, + const struct perf_pmu *pmu) +{ + if (parse_state->pmu_filter == NULL) + return false; + + if (pmu->name == NULL) + return true; + + return strcmp(parse_state->pmu_filter, pmu->name) != 0; +} + int parse_events_add_cache(struct list_head *list, int *idx, const char *name, - struct parse_events_error *err, + struct parse_events_state *parse_state, struct list_head *head_config) { struct perf_pmu *pmu = NULL; @@ -483,6 +499,9 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, if (!perf_pmu__supports_legacy_cache(pmu)) continue; + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + memset(&attr, 0, sizeof(attr)); attr.type = PERF_TYPE_HW_CACHE; @@ -493,8 +512,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, found_supported = true; if (head_config) { - if (config_attr(&attr, head_config, err, - config_term_common)) + if (config_attr(&attr, head_config, parse_state->error, config_term_common)) return -EINVAL; if (get_config_terms(head_config, &config_terms)) @@ -1494,6 +1512,9 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, if (!perf_pmu__supports_wildcard_numeric(pmu)) continue; + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + found_supported = true; ret = __parse_events_add_numeric(parse_state, list, pmu, pmu->type, config, head_config); @@ -1682,6 +1703,9 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, while ((pmu = perf_pmu__scan(pmu)) != NULL) { struct perf_pmu_alias *alias; + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + list_for_each_entry(alias, &pmu->aliases, list) { if (!strcasecmp(alias->name, str)) { parse_events_copy_term_list(head, &orig_head); @@ -2121,7 +2145,7 @@ static bool parse_events__sort_events_and_fix_groups(struct list_head *list) return idx_changed || num_leaders != orig_num_leaders; } -int __parse_events(struct evlist *evlist, const char *str, +int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filter, struct parse_events_error *err, struct perf_pmu *fake_pmu, bool warn_if_reordered) { @@ -2132,6 +2156,7 @@ int __parse_events(struct evlist *evlist, const char *str, .evlist = evlist, .stoken = PE_START_EVENTS, .fake_pmu = fake_pmu, + .pmu_filter = pmu_filter, .match_legacy_cache_terms = true, }; int ret; @@ -2313,12 +2338,13 @@ void parse_events_error__print(struct parse_events_error *err, int parse_events_option(const struct option *opt, const char *str, int unset __maybe_unused) { - struct evlist *evlist = *(struct evlist **)opt->value; + struct parse_events_option_args *args = opt->value; struct parse_events_error err; int ret; parse_events_error__init(&err); - ret = parse_events(evlist, str, &err); + ret = __parse_events(*args->evlistp, str, args->pmu_filter, &err, + /*fake_pmu=*/NULL, /*warn_if_reordered=*/true); if (ret) { parse_events_error__print(&err, str); @@ -2331,22 +2357,21 @@ int parse_events_option(const struct option *opt, const char *str, int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset) { - struct evlist **evlistp = opt->value; + struct parse_events_option_args *args = opt->value; int ret; - if (*evlistp == NULL) { - *evlistp = evlist__new(); + if (*args->evlistp == NULL) { + *args->evlistp = evlist__new(); - if (*evlistp == NULL) { + if (*args->evlistp == NULL) { fprintf(stderr, "Not enough memory to create evlist\n"); return -1; } } - ret = parse_events_option(opt, str, unset); if (ret) { - evlist__delete(*evlistp); - *evlistp = NULL; + evlist__delete(*args->evlistp); + *args->evlistp = NULL; } return ret; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 77b8f7efdb94..d4cbda6e946a 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -22,17 +22,24 @@ bool is_event_supported(u8 type, u64 config); const char *event_type(int type); +/* Arguments encoded in opt->value. */ +struct parse_events_option_args { + struct evlist **evlistp; + const char *pmu_filter; +}; int parse_events_option(const struct option *opt, const char *str, int unset); int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset); -__attribute__((nonnull(1, 2, 3))) -int __parse_events(struct evlist *evlist, const char *str, struct parse_events_error *error, - struct perf_pmu *fake_pmu, bool warn_if_reordered); +__attribute__((nonnull(1, 2, 4))) +int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filter, + struct parse_events_error *error, struct perf_pmu *fake_pmu, + bool warn_if_reordered); __attribute__((nonnull(1, 2, 3))) static inline int parse_events(struct evlist *evlist, const char *str, struct parse_events_error *err) { - return __parse_events(evlist, str, err, /*fake_pmu=*/NULL, /*warn_if_reordered=*/true); + return __parse_events(evlist, str, /*pmu_filter=*/NULL, err, /*fake_pmu=*/NULL, + /*warn_if_reordered=*/true); } int parse_event(struct evlist *evlist, const char *str); @@ -122,11 +129,15 @@ struct parse_events_state { struct list_head *terms; int stoken; struct perf_pmu *fake_pmu; + /* If non-null, when wildcard matching only match the given PMU. */ + const char *pmu_filter; /* Should PE_LEGACY_NAME tokens be generated for config terms? */ bool match_legacy_cache_terms; bool wild_card_pmus; }; +bool parse_events__filter_pmu(const struct parse_events_state *parse_state, + const struct perf_pmu *pmu); void parse_events__shrink_config_terms(void); int parse_events__is_hardcoded_term(struct parse_events_term *term); int parse_events_term__num(struct parse_events_term **term, @@ -171,7 +182,7 @@ int parse_events_add_tool(struct parse_events_state *parse_state, struct list_head *list, int tool_event); int parse_events_add_cache(struct list_head *list, int *idx, const char *name, - struct parse_events_error *error, + struct parse_events_state *parse_state, struct list_head *head_config); int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config); int parse_events_add_breakpoint(struct list_head *list, int *idx, diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index e709508b1d6e..c95877cbd6cf 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -312,6 +312,9 @@ PE_NAME opt_pmu_config while ((pmu = perf_pmu__scan(pmu)) != NULL) { char *name = pmu->name; + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + if (!strncmp(name, "uncore_", 7) && strncmp($1, "uncore_", 7)) name += 7; @@ -473,13 +476,12 @@ event_legacy_cache: PE_LEGACY_CACHE opt_event_config { struct parse_events_state *parse_state = _parse_state; - struct parse_events_error *error = parse_state->error; struct list_head *list; int err; list = alloc_list(); ABORT_ON(!list); - err = parse_events_add_cache(list, &parse_state->idx, $1, error, $2); + err = parse_events_add_cache(list, &parse_state->idx, $1, parse_state, $2); parse_events_terms__delete($2); free($1); -- cgit v1.2.3 From aefde50a446b56f592a589e12e89935bea3b85f9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:39 -0700 Subject: perf test: Fix parse-events tests for >1 core PMU Remove assumptions of just 1 core PMU. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-33-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 177 ++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 72 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 0d0c869d2d09..71c77d9d2744 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -35,6 +35,11 @@ static bool test_config(const struct evsel *evsel, __u64 expected_config) return config == expected_config; } +static bool test_perf_config(const struct perf_evsel *evsel, __u64 expected_config) +{ + return (evsel->attr.config & PERF_HW_EVENT_MASK) == expected_config; +} + #ifdef HAVE_LIBTRACEEVENT #if defined(__s390x__) @@ -97,11 +102,27 @@ static int test__checkevent_tracepoint_multi(struct evlist *evlist) static int test__checkevent_raw(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct perf_evsel *evsel; + bool raw_type_match = false; - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a)); + TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); + + perf_evlist__for_each_evsel(&evlist->core, evsel) { + struct perf_pmu *pmu; + bool type_matched = false; + + TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a)); + perf_pmus__for_each_pmu(pmu) { + if (pmu->type == evsel->attr.type) { + TEST_ASSERT_VAL("PMU type expected once", !type_matched); + type_matched = true; + if (pmu->type == PERF_TYPE_RAW) + raw_type_match = true; + } + } + TEST_ASSERT_VAL("No PMU found for type", type_matched); + } + TEST_ASSERT_VAL("Raw PMU not matched", raw_type_match); return TEST_OK; } @@ -117,31 +138,35 @@ static int test__checkevent_numeric(struct evlist *evlist) static int test__checkevent_symbolic_name(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct perf_evsel *evsel; - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); + + perf_evlist__for_each_evsel(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", + test_perf_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + } return TEST_OK; } static int test__checkevent_symbolic_name_config(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct perf_evsel *evsel; - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - /* - * The period value gets configured within evlist__config, - * while this test executes only parse events method. - */ - TEST_ASSERT_VAL("wrong period", - 0 == evsel->core.attr.sample_period); - TEST_ASSERT_VAL("wrong config1", - 0 == evsel->core.attr.config1); - TEST_ASSERT_VAL("wrong config2", - 1 == evsel->core.attr.config2); + TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); + + perf_evlist__for_each_evsel(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + /* + * The period value gets configured within evlist__config, + * while this test executes only parse events method. + */ + TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period); + TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); + TEST_ASSERT_VAL("wrong config2", 1 == evsel->attr.config2); + } return TEST_OK; } @@ -157,11 +182,14 @@ static int test__checkevent_symbolic_alias(struct evlist *evlist) static int test__checkevent_genhw(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct perf_evsel *evsel; - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 1 << 16)); + TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); + + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 1 << 16)); + } return TEST_OK; } @@ -253,17 +281,15 @@ static int test__checkevent_tracepoint_modifier(struct evlist *evlist) static int test__checkevent_tracepoint_multi_modifier(struct evlist *evlist) { - struct evsel *evsel; + struct perf_evsel *evsel; TEST_ASSERT_VAL("wrong number of entries", evlist->core.nr_entries > 1); - evlist__for_each_entry(evlist, evsel) { - TEST_ASSERT_VAL("wrong exclude_user", - !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", - evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); } return test__checkevent_tracepoint_multi(evlist); @@ -272,25 +298,27 @@ test__checkevent_tracepoint_multi_modifier(struct evlist *evlist) static int test__checkevent_raw_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + struct perf_evsel *evsel; + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + } return test__checkevent_raw(evlist); } static int test__checkevent_numeric_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + struct perf_evsel *evsel; + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + } return test__checkevent_numeric(evlist); } @@ -308,21 +336,23 @@ static int test__checkevent_symbolic_name_modifier(struct evlist *evlist) static int test__checkevent_exclude_host_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + struct perf_evsel *evsel; + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); + } return test__checkevent_symbolic_name(evlist); } static int test__checkevent_exclude_guest_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + struct perf_evsel *evsel; + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); + } return test__checkevent_symbolic_name(evlist); } @@ -340,13 +370,14 @@ static int test__checkevent_symbolic_alias_modifier(struct evlist *evlist) static int test__checkevent_genhw_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + struct perf_evsel *evsel; + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + } return test__checkevent_genhw(evlist); } @@ -476,21 +507,23 @@ static int test__checkevent_list(struct evlist *evlist) { struct evsel *evsel = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", 3 <= evlist->core.nr_entries); /* r1 */ - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); - TEST_ASSERT_VAL("wrong config1", 0 == evsel->core.attr.config1); - TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); - TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT != evsel->core.attr.type); + while (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) { + TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); + TEST_ASSERT_VAL("wrong config1", 0 == evsel->core.attr.config1); + TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); + TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + evsel = evsel__next(evsel); + } /* syscalls:sys_enter_openat:k */ - evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type); TEST_ASSERT_VAL("wrong sample_type", PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type); @@ -1930,7 +1963,7 @@ static int test_event(const struct evlist_test *e) e->name, ret, err.str); parse_events_error__print(&err, e->name); ret = TEST_FAIL; - if (strstr(err.str, "can't access trace events")) + if (err.str && strstr(err.str, "can't access trace events")) ret = TEST_SKIP; } else { ret = e->check(evlist); -- cgit v1.2.3 From 5ea8f2ccffb23983f02012a2731464586b10fbf3 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:40 -0700 Subject: perf parse-events: Support hardware events as terms An event like "cpu/instructions/" typically parses due to there being a sysfs event called instructions. On hybrid recursive parsing means that the hardware event is encoded in the attribute, with the PMU being placed in the high bits of the config: ''' $ perf stat -vv -e 'cpu_core/cycles/' true ... ------------------------------------------------------------ perf_event_attr: size 136 config 0x400000000 sample_type IDENTIFIER read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING disabled 1 inherit 1 enable_on_exec 1 exclude_guest 1 ------------------------------------------------------------ ''' Make this behavior the default by adding a new term type and token for hardware events. The token gathers both the numeric config and the parsed name, so that if the token appears like "cycles/name=cycles/" then the token can be handled like a name. The numeric value isn't sufficient to distinguish say "cpu-cycles" from "cycles". Extend the parse-events test so that all current non-PMU hardware parsing tests, also test with the PMU cpu - more than half the change. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-34-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 126 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/parse-events.c | 37 ++++-------- tools/perf/util/parse-events.h | 3 +- tools/perf/util/parse-events.l | 20 +++++++ tools/perf/util/parse-events.y | 34 +++++++++-- 5 files changed, 187 insertions(+), 33 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 71c77d9d2744..9ca8e19bda00 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1926,6 +1926,132 @@ static const struct evlist_test test__events_pmu[] = { .check = test__checkevent_config_cache, /* 8 */ }, + { + .name = "cpu/instructions/", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_symbolic_name, + /* 9 */ + }, + { + .name = "cpu/cycles,period=100000,config2/", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_symbolic_name_config, + /* 0 */ + }, + { + .name = "cpu/instructions/h", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_symbolic_name_modifier, + /* 1 */ + }, + { + .name = "cpu/instructions/G", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_exclude_host_modifier, + /* 2 */ + }, + { + .name = "cpu/instructions/H", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_exclude_guest_modifier, + /* 3 */ + }, + { + .name = "{cpu/instructions/k,cpu/cycles/upp}", + .valid = test__pmu_cpu_valid, + .check = test__group1, + /* 4 */ + }, + { + .name = "{cpu/cycles/u,cpu/instructions/kp}:p", + .valid = test__pmu_cpu_valid, + .check = test__group4, + /* 5 */ + }, + { + .name = "{cpu/cycles/,cpu/cache-misses/G}:H", + .valid = test__pmu_cpu_valid, + .check = test__group_gh1, + /* 6 */ + }, + { + .name = "{cpu/cycles/,cpu/cache-misses/H}:G", + .valid = test__pmu_cpu_valid, + .check = test__group_gh2, + /* 7 */ + }, + { + .name = "{cpu/cycles/G,cpu/cache-misses/H}:u", + .valid = test__pmu_cpu_valid, + .check = test__group_gh3, + /* 8 */ + }, + { + .name = "{cpu/cycles/G,cpu/cache-misses/H}:uG", + .valid = test__pmu_cpu_valid, + .check = test__group_gh4, + /* 9 */ + }, + { + .name = "{cpu/cycles/,cpu/cache-misses/,cpu/branch-misses/}:S", + .valid = test__pmu_cpu_valid, + .check = test__leader_sample1, + /* 0 */ + }, + { + .name = "{cpu/instructions/,cpu/branch-misses/}:Su", + .valid = test__pmu_cpu_valid, + .check = test__leader_sample2, + /* 1 */ + }, + { + .name = "cpu/instructions/uDp", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_pinned_modifier, + /* 2 */ + }, + { + .name = "{cpu/cycles/,cpu/cache-misses/,cpu/branch-misses/}:D", + .valid = test__pmu_cpu_valid, + .check = test__pinned_group, + /* 3 */ + }, + { + .name = "cpu/instructions/I", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_exclude_idle_modifier, + /* 4 */ + }, + { + .name = "cpu/instructions/kIG", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_exclude_idle_modifier_1, + /* 5 */ + }, + { + .name = "cpu/cycles/u", + .valid = test__pmu_cpu_valid, + .check = test__sym_event_slash, + /* 6 */ + }, + { + .name = "cpu/cycles/k", + .valid = test__pmu_cpu_valid, + .check = test__sym_event_dc, + /* 7 */ + }, + { + .name = "cpu/instructions/uep", + .valid = test__pmu_cpu_valid, + .check = test__checkevent_exclusive_modifier, + /* 8 */ + }, + { + .name = "{cpu/cycles/,cpu/cache-misses/,cpu/branch-misses/}:e", + .valid = test__pmu_cpu_valid, + .check = test__exclusive_group, + /* 9 */ + }, }; struct terms_test { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d9d964bbc0e2..dea27bc0b376 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1052,6 +1052,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", [PARSE_EVENTS__TERM_TYPE_RAW] = "raw", [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache", + [PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware", }; static bool config_term_shrinked; @@ -1239,6 +1240,17 @@ static int config_term_pmu(struct perf_event_attr *attr, } else term->type_term = PARSE_EVENTS__TERM_TYPE_USER; } + if (term->type_term == PARSE_EVENTS__TERM_TYPE_HARDWARE) { + const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type); + + if (!pmu) { + pr_debug("Failed to find PMU for type %d", attr->type); + return -EINVAL; + } + attr->type = PERF_TYPE_HARDWARE; + attr->config = ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT) | term->val.num; + return 0; + } if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER || term->type_term == PARSE_EVENTS__TERM_TYPE_DRV_CFG) { /* @@ -2569,31 +2581,6 @@ int parse_events_term__str(struct parse_events_term **term, return new_term(term, &temp, str, 0); } -int parse_events_term__sym_hw(struct parse_events_term **term, - char *config, unsigned idx) -{ - struct event_symbol *sym; - char *str; - struct parse_events_term temp = { - .type_val = PARSE_EVENTS__TERM_TYPE_STR, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - .config = config, - }; - - if (!temp.config) { - temp.config = strdup("event"); - if (!temp.config) - return -ENOMEM; - } - BUG_ON(idx >= PERF_COUNT_HW_MAX); - sym = &event_symbols_hw[idx]; - - str = strdup(sym->symbol); - if (!str) - return -ENOMEM; - return new_term(term, &temp, str, 0); -} - int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term) { diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index d4cbda6e946a..7fe80b416143 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -79,6 +79,7 @@ enum { PARSE_EVENTS__TERM_TYPE_METRIC_ID, PARSE_EVENTS__TERM_TYPE_RAW, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, + PARSE_EVENTS__TERM_TYPE_HARDWARE, __PARSE_EVENTS__TERM_TYPE_NR, }; @@ -147,8 +148,6 @@ int parse_events_term__num(struct parse_events_term **term, int parse_events_term__str(struct parse_events_term **term, int type_term, char *config, char *str, void *loc_term, void *loc_val); -int parse_events_term__sym_hw(struct parse_events_term **term, - char *config, unsigned idx); int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term); void parse_events_term__delete(struct parse_events_term *term); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index abe0ce681d29..6deb70c25984 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -149,6 +149,16 @@ static int term(yyscan_t scanner, int type) return PE_TERM; } +static int hw_term(yyscan_t scanner, int config) +{ + YYSTYPE *yylval = parse_events_get_lval(scanner); + char *text = parse_events_get_text(scanner); + + yylval->hardware_term.str = strdup(text); + yylval->hardware_term.num = PERF_TYPE_HARDWARE + config; + return PE_TERM_HW; +} + #define YY_USER_ACTION \ do { \ yylloc->last_column = yylloc->first_column; \ @@ -269,6 +279,16 @@ percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); } aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); } aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); } metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); } +cpu-cycles|cycles { return hw_term(yyscanner, PERF_COUNT_HW_CPU_CYCLES); } +stalled-cycles-frontend|idle-cycles-frontend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } +stalled-cycles-backend|idle-cycles-backend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } +instructions { return hw_term(yyscanner, PERF_COUNT_HW_INSTRUCTIONS); } +cache-references { return hw_term(yyscanner, PERF_COUNT_HW_CACHE_REFERENCES); } +cache-misses { return hw_term(yyscanner, PERF_COUNT_HW_CACHE_MISSES); } +branch-instructions|branches { return hw_term(yyscanner, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } +branch-misses { return hw_term(yyscanner, PERF_COUNT_HW_BRANCH_MISSES); } +bus-cycles { return hw_term(yyscanner, PERF_COUNT_HW_BUS_CYCLES); } +ref-cycles { return hw_term(yyscanner, PERF_COUNT_HW_REF_CPU_CYCLES); } r{num_raw_hex} { return str(yyscanner, PE_RAW); } r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } , { return ','; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index c95877cbd6cf..819a5123fd77 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -65,6 +65,7 @@ static void free_list_evsel(struct list_head* list_evsel) %token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %token PE_ARRAY_ALL PE_ARRAY_RANGE %token PE_DRV_CFG_TERM +%token PE_TERM_HW %type PE_VALUE %type PE_VALUE_SYM_HW %type PE_VALUE_SYM_SW @@ -112,6 +113,8 @@ static void free_list_evsel(struct list_head* list_evsel) %type array_term %type array_terms %destructor { free ($$.ranges); } +%type PE_TERM_HW +%destructor { free ($$.str); } %union { @@ -125,6 +128,10 @@ static void free_list_evsel(struct list_head* list_evsel) char *event; } tracepoint_name; struct parse_events_array array; + struct hardware_term { + char *str; + u64 num; + } hardware_term; } %% @@ -770,13 +777,14 @@ name_or_raw '=' PE_VALUE $$ = term; } | -name_or_raw '=' PE_VALUE_SYM_HW +name_or_raw '=' PE_TERM_HW { struct parse_events_term *term; - int config = $3 & 255; - if (parse_events_term__sym_hw(&term, $1, config)) { + if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3.str, &@1, &@3)) { free($1); + free($3.str); YYABORT; } $$ = term; @@ -806,12 +814,15 @@ PE_NAME $$ = term; } | -PE_VALUE_SYM_HW +PE_TERM_HW { struct parse_events_term *term; - int config = $1 & 255; - ABORT_ON(parse_events_term__sym_hw(&term, NULL, config)); + if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_HARDWARE, + $1.str, $1.num & 255, false, &@1, NULL)) { + free($1.str); + YYABORT; + } $$ = term; } | @@ -826,6 +837,17 @@ PE_TERM '=' PE_NAME $$ = term; } | +PE_TERM '=' PE_TERM_HW +{ + struct parse_events_term *term; + + if (parse_events_term__str(&term, (int)$1, NULL, $3.str, &@1, &@3)) { + free($3.str); + YYABORT; + } + $$ = term; +} +| PE_TERM '=' PE_VALUE { struct parse_events_term *term; -- cgit v1.2.3 From e831f3ccf9920fa099d4ebb9d57214cc7ecd2e70 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:41 -0700 Subject: perf parse-events: Avoid error when assigning a term Avoid the parser error: ''' $ perf stat -e 'cycles/name=name/' true event syntax error: 'cycles/name=name/' \___ parser error ''' by turning the term back to a string if it is on the right. Add PMU and generic parsing tests. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-35-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 21 +++++++++++++++++++++ tools/perf/util/parse-events.c | 9 +++++++++ tools/perf/util/parse-events.h | 3 +++ tools/perf/util/parse-events.y | 8 ++++++++ 4 files changed, 41 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 9ca8e19bda00..eb893cc15878 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1499,6 +1499,16 @@ static int test__sym_event_dc(struct evlist *evlist) return TEST_OK; } +static int test__term_equal_term(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "name") == 0); + return TEST_OK; +} + #ifdef HAVE_LIBTRACEEVENT static int count_tracepoints(void) { @@ -1871,6 +1881,11 @@ static const struct evlist_test test__events[] = { .check = test__exclusive_group, /* 7 */ }, + { + .name = "cycles/name=name/", + .check = test__term_equal_term, + /* 8 */ + }, }; static const struct evlist_test test__events_pmu[] = { @@ -2052,6 +2067,12 @@ static const struct evlist_test test__events_pmu[] = { .check = test__exclusive_group, /* 9 */ }, + { + .name = "cpu/cycles,name=name/", + .valid = test__pmu_cpu_valid, + .check = test__term_equal_term, + /* 0 */ + }, }; struct terms_test { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index dea27bc0b376..5d5d77fa398b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2581,6 +2581,15 @@ int parse_events_term__str(struct parse_events_term **term, return new_term(term, &temp, str, 0); } +int parse_events_term__term(struct parse_events_term **term, + int term_lhs, int term_rhs, + void *loc_term, void *loc_val) +{ + return parse_events_term__str(term, term_lhs, NULL, + strdup(config_term_names[term_rhs]), + loc_term, loc_val); +} + int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term) { diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 7fe80b416143..2a8cafe0ee8f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -148,6 +148,9 @@ int parse_events_term__num(struct parse_events_term **term, int parse_events_term__str(struct parse_events_term **term, int type_term, char *config, char *str, void *loc_term, void *loc_val); +int parse_events_term__term(struct parse_events_term **term, + int term_lhs, int term_rhs, + void *loc_term, void *loc_val); int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term); void parse_events_term__delete(struct parse_events_term *term); diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 819a5123fd77..0aaebc57748e 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -848,6 +848,14 @@ PE_TERM '=' PE_TERM_HW $$ = term; } | +PE_TERM '=' PE_TERM +{ + struct parse_events_term *term; + + ABORT_ON(parse_events_term__term(&term, (int)$1, (int)$3, &@1, &@3)); + $$ = term; +} +| PE_TERM '=' PE_VALUE { struct parse_events_term *term; -- cgit v1.2.3 From 2aadca4b35427a7c65acc6aa415b38758128b22c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 May 2023 15:38:42 -0700 Subject: perf parse-events: Avoid error when assigning a legacy cache term Avoid the parser error: ''' $ perf stat -e 'cycles/name=l1d/' true event syntax error: 'cycles/name=l1d/' \___ parser error ''' by combining the name and legacy cache cases in the parser. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230502223851.2234828-36-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 21 +++++++++++++++++++++ tools/perf/util/parse-events.y | 10 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index eb893cc15878..72a10bed84fd 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1509,6 +1509,16 @@ static int test__term_equal_term(struct evlist *evlist) return TEST_OK; } +static int test__term_equal_legacy(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "l1d") == 0); + return TEST_OK; +} + #ifdef HAVE_LIBTRACEEVENT static int count_tracepoints(void) { @@ -1886,6 +1896,11 @@ static const struct evlist_test test__events[] = { .check = test__term_equal_term, /* 8 */ }, + { + .name = "cycles/name=l1d/", + .check = test__term_equal_legacy, + /* 9 */ + }, }; static const struct evlist_test test__events_pmu[] = { @@ -2073,6 +2088,12 @@ static const struct evlist_test test__events_pmu[] = { .check = test__term_equal_term, /* 0 */ }, + { + .name = "cpu/cycles,name=l1d/", + .valid = test__pmu_cpu_valid, + .check = test__term_equal_legacy, + /* 1 */ + }, }; struct terms_test { diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0aaebc57748e..f4ee03b5976b 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -82,7 +82,7 @@ static void free_list_evsel(struct list_head* list_evsel) %type PE_EVENT_NAME %type PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %type PE_DRV_CFG_TERM -%type name_or_raw +%type name_or_raw name_or_legacy %destructor { free ($$); } %type event_term %destructor { parse_events_term__delete ($$); } @@ -739,6 +739,8 @@ event_term name_or_raw: PE_RAW | PE_NAME | PE_LEGACY_CACHE +name_or_legacy: PE_NAME | PE_LEGACY_CACHE + event_term: PE_RAW { @@ -752,7 +754,7 @@ PE_RAW $$ = term; } | -name_or_raw '=' PE_NAME +name_or_raw '=' name_or_legacy { struct parse_events_term *term; @@ -826,7 +828,7 @@ PE_TERM_HW $$ = term; } | -PE_TERM '=' PE_NAME +PE_TERM '=' name_or_legacy { struct parse_events_term *term; @@ -872,7 +874,7 @@ PE_TERM $$ = term; } | -name_or_raw array '=' PE_NAME +name_or_raw array '=' name_or_legacy { struct parse_events_term *term; -- cgit v1.2.3 From c0d68601cbcefaf69018b3e7aff2687316950ace Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 12 May 2023 23:34:47 -0700 Subject: perf test: Add cputype testing to perf stat Check a bogus PMU fails and that a known PMU succeeds. Limit to PMUs known cpu, cpu_atom and armv8_pmuv3_0 ones. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Caleb Biggers Cc: Edward Baker Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Samantha Alt Cc: Stephane Eranian Cc: Sumanth Korikkar Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Tiezhu Yang Cc: Weilin Wang Cc: Xing Zhengjun Cc: Yang Jihong Link: https://lore.kernel.org/r/20230513063447.464691-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat.sh | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh index b154fbb15d54..3f1e67795490 100755 --- a/tools/perf/tests/shell/stat.sh +++ b/tools/perf/tests/shell/stat.sh @@ -103,10 +103,54 @@ test_topdown_weak_groups() { echo "Topdown weak groups test [Success]" } +test_cputype() { + # Test --cputype argument. + echo "cputype test" + + # Bogus PMU should fail. + if perf stat --cputype="123" -e instructions true > /dev/null 2>&1 + then + echo "cputype test [Bogus PMU didn't fail]" + err=1 + return + fi + + # Find a known PMU for cputype. + pmu="" + for i in cpu cpu_atom armv8_pmuv3_0 + do + if test -d "/sys/devices/$i" + then + pmu="$i" + break + fi + if perf stat -e "$i/instructions/" true > /dev/null 2>&1 + then + pmu="$i" + break + fi + done + if test "x$pmu" = "x" + then + echo "cputype test [Skipped known PMU not found]" + return + fi + + # Test running with cputype produces output. + if ! perf stat --cputype="$pmu" -e instructions true 2>&1 | grep -E -q "instructions" + then + echo "cputype test [Failed count missed with given filter]" + err=1 + return + fi + echo "cputype test [Success]" +} + test_default_stat test_stat_record_report test_stat_record_script test_stat_repeat_weak_groups test_topdown_groups test_topdown_weak_groups +test_cputype exit $err -- cgit v1.2.3 From 68d12418261090b4f5b8d1b2067d15062e858e01 Mon Sep 17 00:00:00 2001 From: Anup Sharma Date: Fri, 19 May 2023 13:11:24 +0530 Subject: perf test: Add test validating JSON generated by 'perf data convert --to-json' This commit adds support for testing the JSON output generated by the 'perf data' command's conversion to JSON functionality. The test script now includes a step to ensure that the resulting JSON file contains valid data. Changes: V1 -> V2: Added a check for the existence of the result output file. Replaced the usage of jq with json.load for validating the JSON format. Checks using ShellCheck and checkpatch, addressing and resolving warnings. Removed the unnecessary root permission check. Modified the 'perf record' command to avoid requiring root permissions. Committer testing: $ perf test to-json 115: 'perf data convert --to-json' command test : Ok $ perf test -v to-json Couldn't bump rlimit(MEMLOCK), failures may take place when creating BPF maps, etc 115: 'perf data convert --to-json' command test : --- start --- test child forked, pid 1746867 Testing Perf Data Convertion Command to JSON Perf Data Converter Command to JSON [SUCCESS] Validating Perf Data Converted JSON file The file contains valid JSON format [SUCCESS] test child finished with 0 ---- end ---- 'perf data convert --to-json' command test: Ok $ Signed-off-by: Anup Sharma Acked-by: Ian Rogers Link: https://lore.kernel.org/r/ZGcoJBAGlknjsA/n@yoga Tested-by: Arnaldo Carvalho de Melo Cc: Mark Rutland Cc: Anup Sharma Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Alexander Shishkin Cc: Ingo Molnar Cc: linux-kernel@vger.kernel.org Cc: linux-perf-users@vger.kernel.org [ Fixup indentation to use consistently tabs, not a mixture of spaces and tabs, have 'if ... ; then' on the same line ] Signed-off-by: Arnaldo Carvalho de Melo --- .../tests/shell/test_perf_data_converter_json.sh | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 tools/perf/tests/shell/test_perf_data_converter_json.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_perf_data_converter_json.sh b/tools/perf/tests/shell/test_perf_data_converter_json.sh new file mode 100755 index 000000000000..72ac6c83231c --- /dev/null +++ b/tools/perf/tests/shell/test_perf_data_converter_json.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# 'perf data convert --to-json' command test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +err=0 + +if [ "$PYTHON" = "" ] ; then + if which python3 > /dev/null ; then + PYTHON=python3 + elif which python > /dev/null ; then + PYTHON=python + else + echo Skipping test, python not detected please set environment variable PYTHON. + exit 2 + fi +fi + +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) +result=$(mktemp /tmp/__perf_test.output.json.XXXXX) + +cleanup() +{ + rm -f "${perfdata}" + rm -f "${result}" + trap - exit term int +} + +trap_cleanup() +{ + cleanup + exit ${err} +} +trap trap_cleanup exit term int + +test_json_converter_command() +{ + echo "Testing Perf Data Convertion Command to JSON" + perf record -o "$perfdata" -F 99 -g -- perf test -w noploop > /dev/null 2>&1 + perf data convert --to-json "$result" --force -i "$perfdata" >/dev/null 2>&1 + if [ $(cat "${result}" | wc -l) -gt "0" ] ; then + echo "Perf Data Converter Command to JSON [SUCCESS]" + else + echo "Perf Data Converter Command to JSON [FAILED]" + err=1 + exit + fi +} + +validate_json_format() +{ + echo "Validating Perf Data Converted JSON file" + if [ -f "$result" ] ; then + if $PYTHON -c "import json; json.load(open('$result'))" >/dev/null 2>&1 ; then + echo "The file contains valid JSON format [SUCCESS]" + else + echo "The file does not contain valid JSON format [FAILED]" + err=1 + exit + fi + else + echo "File not found [FAILED]" + err=2 + exit + fi +} + +test_json_converter_command +validate_json_format + +exit ${err} -- cgit v1.2.3 From bfce728db31790420758de3173c3e7185ba57cb1 Mon Sep 17 00:00:00 2001 From: K Prateek Nayak Date: Wed, 17 May 2023 22:57:45 +0530 Subject: pert tests: Add tests for new "perf stat --per-cache" aggregation option Add tests for the new "--per-cache" option in 'perf stat' for CSV and JSON generation as well as for the JSON linting. Suggested-by: Gautham Shenoy Signed-off-by: K Prateek Nayak Acked-by: Ian Rogers Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Stephane Eranian Cc: Wen Pu Link: https://lore.kernel.org/r/20230517172745.5833-6-kprateek.nayak@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/perf_json_output_lint.py | 4 +++- tools/perf/tests/shell/stat+csv_output.sh | 14 ++++++++++++++ tools/perf/tests/shell/stat+json_output.sh | 13 +++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index 61f3059ca54b..4acaaed5560d 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -14,6 +14,7 @@ ap.add_argument('--system-wide', action='store_true') ap.add_argument('--event', action='store_true') ap.add_argument('--per-core', action='store_true') ap.add_argument('--per-thread', action='store_true') +ap.add_argument('--per-cache', action='store_true') ap.add_argument('--per-die', action='store_true') ap.add_argument('--per-node', action='store_true') ap.add_argument('--per-socket', action='store_true') @@ -47,6 +48,7 @@ def check_json_output(expected_items): 'counter-value': lambda x: is_counter_value(x), 'cgroup': lambda x: True, 'cpu': lambda x: isint(x), + 'cache': lambda x: True, 'die': lambda x: True, 'event': lambda x: True, 'event-runtime': lambda x: isfloat(x), @@ -83,7 +85,7 @@ try: expected_items = 7 elif args.interval or args.per_thread or args.system_wide_no_aggr: expected_items = 8 - elif args.per_core or args.per_socket or args.per_node or args.per_die: + elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cache_instance: expected_items = 9 else: # If no option is specified, don't check the number of items. diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh index fb78b6251a4e..a1969f236a0a 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -40,6 +40,7 @@ function commachecker() ;; "--per-socket") exp=8 ;; "--per-node") exp=8 ;; "--per-die") exp=8 + ;; "--per-cache") exp=8 esac while read line @@ -145,6 +146,18 @@ check_per_thread() echo "[Success]" } +check_per_cache_instance() +{ + echo -n "Checking CSV output: per cache instance " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat -x$csv_sep --per-cache -a true 2>&1 | commachecker --per-cache + echo "[Success]" +} + check_per_die() { echo -n "Checking CSV output: per die " @@ -222,6 +235,7 @@ if [ $skip_test -ne 1 ] then check_system_wide_no_aggr check_per_core + check_per_cache_instance check_per_die check_per_socket else diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/shell/stat+json_output.sh index f3e4967cc72e..c282afa6217c 100755 --- a/tools/perf/tests/shell/stat+json_output.sh +++ b/tools/perf/tests/shell/stat+json_output.sh @@ -120,6 +120,18 @@ check_per_thread() echo "[Success]" } +check_per_cache_instance() +{ + echo -n "Checking json output: per cache_instance " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoia and not root" + return + fi + perf stat -j --per-cache -a true 2>&1 | $PYTHON $pythonchecker --per-cache + echo "[Success]" +} + check_per_die() { echo -n "Checking json output: per die " @@ -197,6 +209,7 @@ if [ $skip_test -ne 1 ] then check_system_wide_no_aggr check_per_core + check_per_cache_instance check_per_die check_per_socket else -- cgit v1.2.3 From 237d41d4a2d7d45e41f5450636d1cf689cba0e8a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 26 May 2023 14:53:36 -0700 Subject: perf cpumap: Add intersect function The merge function gives the union of two cpu maps. Add an intersect function which is necessary, for example, when intersecting a PMUs supported CPUs with user requested. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230526215410.2435674-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/cpumap.c | 35 ++++++++++++++++++++++++++++++ tools/lib/perf/include/perf/cpumap.h | 2 ++ tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/cpumap.c | 41 ++++++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + 5 files changed, 80 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 1229b18bcdb1..d4f3a1a12522 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -402,3 +402,38 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, perf_cpu_map__put(orig); return merged; } + +struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig, + struct perf_cpu_map *other) +{ + struct perf_cpu *tmp_cpus; + int tmp_len; + int i, j, k; + struct perf_cpu_map *merged = NULL; + + if (perf_cpu_map__is_subset(other, orig)) + return perf_cpu_map__get(orig); + if (perf_cpu_map__is_subset(orig, other)) + return perf_cpu_map__get(other); + + tmp_len = max(orig->nr, other->nr); + tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); + if (!tmp_cpus) + return NULL; + + i = j = k = 0; + while (i < orig->nr && j < other->nr) { + if (orig->map[i].cpu < other->map[j].cpu) + i++; + else if (orig->map[i].cpu > other->map[j].cpu) + j++; + else { + j++; + tmp_cpus[k++] = orig->map[i++]; + } + } + if (k) + merged = cpu_map__trim_new(k, tmp_cpus); + free(tmp_cpus); + return merged; +} diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h index 8724dde79342..b4c9a827a88a 100644 --- a/tools/lib/perf/include/perf/cpumap.h +++ b/tools/lib/perf/include/perf/cpumap.h @@ -25,6 +25,8 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file); LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, struct perf_cpu_map *other); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig, + struct perf_cpu_map *other); LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map); LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index b89d69afcef0..eef400025fca 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -97,6 +97,7 @@ static struct test_suite *generic_tests[] = { &suite__backward_ring_buffer, &suite__cpu_map_print, &suite__cpu_map_merge, + &suite__cpu_map_intersect, &suite__sdt_event, &suite__is_printable_array, &suite__bitmap_print, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index b1a924314e09..92232978fe5e 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -171,6 +171,47 @@ static int test__cpu_map_merge(struct test_suite *test __maybe_unused, int subte return 0; } +static int __test__cpu_map_intersect(const char *lhs, const char *rhs, int nr, const char *expected) +{ + struct perf_cpu_map *a = perf_cpu_map__new(lhs); + struct perf_cpu_map *b = perf_cpu_map__new(rhs); + struct perf_cpu_map *c = perf_cpu_map__intersect(a, b); + char buf[100]; + + TEST_ASSERT_EQUAL("failed to intersect map: bad nr", perf_cpu_map__nr(c), nr); + cpu_map__snprint(c, buf, sizeof(buf)); + TEST_ASSERT_VAL("failed to intersect map: bad result", !strcmp(buf, expected)); + perf_cpu_map__put(a); + perf_cpu_map__put(b); + perf_cpu_map__put(c); + return 0; +} + +static int test__cpu_map_intersect(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int ret; + + ret = __test__cpu_map_intersect("4,2,1", "4,5,7", 1, "4"); + if (ret) + return ret; + ret = __test__cpu_map_intersect("1-8", "6-9", 3, "6-8"); + if (ret) + return ret; + ret = __test__cpu_map_intersect("1-8,12-20", "6-9,15", 4, "6-8,15"); + if (ret) + return ret; + ret = __test__cpu_map_intersect("4,2,1", "1", 1, "1"); + if (ret) + return ret; + ret = __test__cpu_map_intersect("1", "4,2,1", 1, "1"); + if (ret) + return ret; + ret = __test__cpu_map_intersect("1", "1", 1, "1"); + return ret; +} + DEFINE_SUITE("Synthesize cpu map", cpu_map_synthesize); DEFINE_SUITE("Print cpu map", cpu_map_print); DEFINE_SUITE("Merge cpu map", cpu_map_merge); +DEFINE_SUITE("Intersect cpu map", cpu_map_intersect); diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 9a0f3904e53d..b4e54f08bc39 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -127,6 +127,7 @@ DECLARE_SUITE(event_times); DECLARE_SUITE(backward_ring_buffer); DECLARE_SUITE(cpu_map_print); DECLARE_SUITE(cpu_map_merge); +DECLARE_SUITE(cpu_map_intersect); DECLARE_SUITE(sdt_event); DECLARE_SUITE(is_printable_array); DECLARE_SUITE(bitmap_print); -- cgit v1.2.3 From 5cebb33fd929dc67812072741408dfcd1a7db4a7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 26 May 2023 14:53:37 -0700 Subject: perf tests: Organize cpu_map tests into a single suite Go from 4 suites to a single suite with 4 test cases. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230526215410.2435674-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 5 +---- tools/perf/tests/cpumap.c | 16 ++++++++++++---- tools/perf/tests/tests.h | 5 +---- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index eef400025fca..aa44fdc84763 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -88,16 +88,13 @@ static struct test_suite *generic_tests[] = { &suite__bpf, &suite__thread_map_synthesize, &suite__thread_map_remove, - &suite__cpu_map_synthesize, + &suite__cpu_map, &suite__synthesize_stat_config, &suite__synthesize_stat, &suite__synthesize_stat_round, &suite__event_update, &suite__event_times, &suite__backward_ring_buffer, - &suite__cpu_map_print, - &suite__cpu_map_merge, - &suite__cpu_map_intersect, &suite__sdt_event, &suite__is_printable_array, &suite__bitmap_print, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 92232978fe5e..83805690c209 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -211,7 +211,15 @@ static int test__cpu_map_intersect(struct test_suite *test __maybe_unused, return ret; } -DEFINE_SUITE("Synthesize cpu map", cpu_map_synthesize); -DEFINE_SUITE("Print cpu map", cpu_map_print); -DEFINE_SUITE("Merge cpu map", cpu_map_merge); -DEFINE_SUITE("Intersect cpu map", cpu_map_intersect); +static struct test_case tests__cpu_map[] = { + TEST_CASE("Synthesize cpu map", cpu_map_synthesize), + TEST_CASE("Print cpu map", cpu_map_print), + TEST_CASE("Merge cpu map", cpu_map_merge), + TEST_CASE("Intersect cpu map", cpu_map_intersect), + { .name = NULL, } +}; + +struct test_suite suite__cpu_map = { + .desc = "CPU map", + .test_cases = tests__cpu_map, +}; diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index b4e54f08bc39..f424c0b7f43f 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -118,16 +118,13 @@ DECLARE_SUITE(bpf); DECLARE_SUITE(session_topology); DECLARE_SUITE(thread_map_synthesize); DECLARE_SUITE(thread_map_remove); -DECLARE_SUITE(cpu_map_synthesize); +DECLARE_SUITE(cpu_map); DECLARE_SUITE(synthesize_stat_config); DECLARE_SUITE(synthesize_stat); DECLARE_SUITE(synthesize_stat_round); DECLARE_SUITE(event_update); DECLARE_SUITE(event_times); DECLARE_SUITE(backward_ring_buffer); -DECLARE_SUITE(cpu_map_print); -DECLARE_SUITE(cpu_map_merge); -DECLARE_SUITE(cpu_map_intersect); DECLARE_SUITE(sdt_event); DECLARE_SUITE(is_printable_array); DECLARE_SUITE(bitmap_print); -- cgit v1.2.3 From 540c910c65a94fb4622b9dd03d71adc0e82d94e9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 24 May 2023 14:06:00 -0700 Subject: perf test: Fix perf stat JSON output test The recent --per-cache option test caused a problem. According to the option name, I think it should check args.per_cache instead of args.per_cache_instance. $ sudo ./perf test -v 99 99: perf stat JSON output linter : --- start --- test child forked, pid 3086101 Checking json output: no args [Success] Checking json output: system wide [Success] Checking json output: interval [Success] Checking json output: event [Success] Checking json output: per thread [Success] Checking json output: per node [Success] Checking json output: system wide no aggregation [Success] Checking json output: per core [Success] Checking json output: per cache_instance Test failed for input: ... Traceback (most recent call last): File "linux/tools/perf/tests/shell/lib/perf_json_output_lint.py", line 88, in elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cache_instance: AttributeError: 'Namespace' object has no attribute 'per_cache_instance' test child finished with -1 ---- end ---- perf stat JSON output linter: FAILED! Fixes: bfce728db3179042 ("pert tests: Add tests for new "perf stat --per-cache" aggregation option") Signed-off-by: Namhyung Kim Tested-by: K Prateek Nayak Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230524210600.3095830-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/perf_json_output_lint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index 4acaaed5560d..b81582a89d36 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -85,7 +85,7 @@ try: expected_items = 7 elif args.interval or args.per_thread or args.system_wide_no_aggr: expected_items = 8 - elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cache_instance: + elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cache: expected_items = 9 else: # If no option is specified, don't check the number of items. -- cgit v1.2.3 From caa90a7bd3bef1814e680da9e2538c1a813aa8a9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 26 May 2023 22:55:17 -0700 Subject: perf test python: Put perf python at start of sys.path This avoids picking up a system installed version of the perf python module. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230527055517.2711487-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/python-use.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c index 6b990ee38575..0ebc22ac8d5b 100644 --- a/tools/perf/tests/python-use.c +++ b/tools/perf/tests/python-use.c @@ -14,7 +14,7 @@ static int test__python_use(struct test_suite *test __maybe_unused, int subtest char *cmd; int ret; - if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s", + if (asprintf(&cmd, "echo \"import sys ; sys.path.insert(0, '%s'); import perf\" | %s %s", PYTHONPATH, PYTHON, verbose > 0 ? "" : "2> /dev/null") < 0) return -1; -- cgit v1.2.3 From 74c075cab1e793fe8418f15eb6e6c88d2197ce1d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 27 May 2023 00:21:38 -0700 Subject: perf cpumap: Add equal function Equality is a useful property to compare after merging and intersecting maps. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230527072210.2900565-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/cpumap.c | 21 ++++++++++++++++++++ tools/lib/perf/include/perf/cpumap.h | 2 ++ tools/perf/tests/cpumap.c | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index ec3f4ac8b1e2..98d7cb24a158 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -335,6 +335,27 @@ bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu) return perf_cpu_map__idx(cpus, cpu) != -1; } +bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs) +{ + int nr; + + if (lhs == rhs) + return true; + + if (!lhs || !rhs) + return false; + + nr = __perf_cpu_map__nr(lhs); + if (nr != __perf_cpu_map__nr(rhs)) + return false; + + for (int idx = 0; idx < nr; idx++) { + if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu) + return false; + } + return true; +} + struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map) { struct perf_cpu result = { diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h index b4c9a827a88a..cedfc26d944e 100644 --- a/tools/lib/perf/include/perf/cpumap.h +++ b/tools/lib/perf/include/perf/cpumap.h @@ -33,6 +33,8 @@ LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); LIBPERF_API bool perf_cpu_map__empty(const struct perf_cpu_map *map); LIBPERF_API struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map); LIBPERF_API bool perf_cpu_map__has(const struct perf_cpu_map *map, struct perf_cpu cpu); +LIBPERF_API bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, + const struct perf_cpu_map *rhs); #define perf_cpu_map__for_each_cpu(cpu, idx, cpus) \ for ((idx) = 0, (cpu) = perf_cpu_map__cpu(cpus, idx); \ diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 83805690c209..7730fc2ab40b 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -211,11 +211,48 @@ static int test__cpu_map_intersect(struct test_suite *test __maybe_unused, return ret; } +static int test__cpu_map_equal(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + struct perf_cpu_map *any = perf_cpu_map__dummy_new(); + struct perf_cpu_map *one = perf_cpu_map__new("1"); + struct perf_cpu_map *two = perf_cpu_map__new("2"); + struct perf_cpu_map *empty = perf_cpu_map__intersect(one, two); + struct perf_cpu_map *pair = perf_cpu_map__new("1-2"); + struct perf_cpu_map *tmp; + struct perf_cpu_map *maps[] = {empty, any, one, two, pair}; + + for (size_t i = 0; i < ARRAY_SIZE(maps); i++) { + /* Maps equal themself. */ + TEST_ASSERT_VAL("equal", perf_cpu_map__equal(maps[i], maps[i])); + for (size_t j = 0; j < ARRAY_SIZE(maps); j++) { + /* Maps dont't equal each other. */ + if (i == j) + continue; + TEST_ASSERT_VAL("not equal", !perf_cpu_map__equal(maps[i], maps[j])); + } + } + + /* Maps equal made maps. */ + tmp = perf_cpu_map__merge(perf_cpu_map__get(one), two); + TEST_ASSERT_VAL("pair", perf_cpu_map__equal(pair, tmp)); + perf_cpu_map__put(tmp); + + tmp = perf_cpu_map__intersect(pair, one); + TEST_ASSERT_VAL("one", perf_cpu_map__equal(one, tmp)); + perf_cpu_map__put(tmp); + + for (size_t i = 0; i < ARRAY_SIZE(maps); i++) + perf_cpu_map__put(maps[i]); + + return TEST_OK; +} + static struct test_case tests__cpu_map[] = { TEST_CASE("Synthesize cpu map", cpu_map_synthesize), TEST_CASE("Print cpu map", cpu_map_print), TEST_CASE("Merge cpu map", cpu_map_merge), TEST_CASE("Intersect cpu map", cpu_map_intersect), + TEST_CASE("Equal cpu map", cpu_map_equal), { .name = NULL, } }; -- cgit v1.2.3 From f24ebe8053514936d4e8cffb707af3a275fa32e5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 27 May 2023 00:22:01 -0700 Subject: perf pmus: Prefer perf_pmu__scan over perf_pmus__for_each_pmu perf_pmus__for_each_pmu doesn't lazily initialize pmus making its use error prone. Just use perf_pmu__scan as this only impacts non-performance critical tests. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230527072210.2900565-26-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/bench/pmu-scan.c | 6 ++---- tools/perf/tests/event_groups.c | 7 ++----- tools/perf/tests/parse-events.c | 11 ++++------- tools/perf/util/pmus.h | 2 -- 4 files changed, 8 insertions(+), 18 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c index f0f007843bb8..f4a6c37cbe27 100644 --- a/tools/perf/bench/pmu-scan.c +++ b/tools/perf/bench/pmu-scan.c @@ -40,13 +40,11 @@ static struct pmu_scan_result *results; static int save_result(void) { - struct perf_pmu *pmu; + struct perf_pmu *pmu = NULL; struct list_head *list; struct pmu_scan_result *r; - perf_pmu__scan(NULL); - - perf_pmus__for_each_pmu(pmu) { + while ((pmu = perf_pmu__scan(pmu)) != NULL) { r = realloc(results, (nr_pmus + 1) * sizeof(*r)); if (r == NULL) return -ENOMEM; diff --git a/tools/perf/tests/event_groups.c b/tools/perf/tests/event_groups.c index 029442b4e9c6..3d9a2b524bba 100644 --- a/tools/perf/tests/event_groups.c +++ b/tools/perf/tests/event_groups.c @@ -50,13 +50,10 @@ static int event_open(int type, unsigned long config, int group_fd) static int setup_uncore_event(void) { - struct perf_pmu *pmu; + struct perf_pmu *pmu = NULL; int i, fd; - if (list_empty(&pmus)) - perf_pmu__scan(NULL); - - perf_pmus__for_each_pmu(pmu) { + while ((pmu = perf_pmu__scan(pmu)) != NULL) { for (i = 0; i < NR_UNCORE_PMUS; i++) { if (!strcmp(uncore_pmus[i].name, pmu->name)) { pr_debug("Using %s for uncore pmu event\n", pmu->name); diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 72a10bed84fd..277607ede060 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -108,11 +108,11 @@ static int test__checkevent_raw(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); perf_evlist__for_each_evsel(&evlist->core, evsel) { - struct perf_pmu *pmu; + struct perf_pmu *pmu = NULL; bool type_matched = false; TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a)); - perf_pmus__for_each_pmu(pmu) { + while ((pmu = perf_pmu__scan(pmu)) != NULL) { if (pmu->type == evsel->attr.type) { TEST_ASSERT_VAL("PMU type expected once", !type_matched); type_matched = true; @@ -2243,13 +2243,10 @@ static int test__terms2(struct test_suite *test __maybe_unused, int subtest __ma static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - struct perf_pmu *pmu; + struct perf_pmu *pmu = NULL; int ret = TEST_OK; - if (list_empty(&pmus)) - perf_pmu__scan(NULL); - - perf_pmus__for_each_pmu(pmu) { + while ((pmu = perf_pmu__scan(pmu)) != NULL) { struct stat st; char path[PATH_MAX]; struct dirent *ent; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index d475e2960c10..257de10788e8 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -5,8 +5,6 @@ extern struct list_head pmus; struct perf_pmu; -#define perf_pmus__for_each_pmu(pmu) list_for_each_entry(pmu, &pmus, list) - const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); #endif /* __PMUS_H */ -- cgit v1.2.3 From 1eaf496ed386934f1c2439a120fe84a05194f91a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 27 May 2023 00:22:03 -0700 Subject: perf pmu: Separate pmu and pmus Separate and hide the pmus list in pmus.[ch]. Move pmus functionality out of pmu.[ch] into pmus.[ch] renaming pmus functions which were prefixed perf_pmu__ to perf_pmus__. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230527072210.2900565-28-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/auxtrace.c | 7 +- tools/perf/arch/arm/util/cs-etm.c | 4 +- tools/perf/arch/arm64/util/pmu.c | 3 +- tools/perf/arch/x86/tests/hybrid.c | 5 +- tools/perf/arch/x86/util/auxtrace.c | 5 +- tools/perf/arch/x86/util/evlist.c | 5 +- tools/perf/arch/x86/util/evsel.c | 7 +- tools/perf/arch/x86/util/intel-bts.c | 4 +- tools/perf/arch/x86/util/intel-pt.c | 4 +- tools/perf/arch/x86/util/mem-events.c | 9 +- tools/perf/arch/x86/util/perf_regs.c | 5 +- tools/perf/arch/x86/util/topdown.c | 5 +- tools/perf/bench/pmu-scan.c | 10 +- tools/perf/builtin-c2c.c | 4 +- tools/perf/builtin-list.c | 4 +- tools/perf/builtin-mem.c | 4 +- tools/perf/builtin-record.c | 6 +- tools/perf/builtin-stat.c | 4 +- tools/perf/tests/attr.c | 4 +- tools/perf/tests/event_groups.c | 2 +- tools/perf/tests/parse-events.c | 8 +- tools/perf/tests/parse-metric.c | 4 +- tools/perf/tests/pmu-events.c | 3 +- tools/perf/tests/switch-tracking.c | 4 +- tools/perf/tests/topology.c | 4 +- tools/perf/util/cputopo.c | 7 +- tools/perf/util/env.c | 5 +- tools/perf/util/evsel.c | 3 +- tools/perf/util/header.c | 15 +- tools/perf/util/mem-events.c | 11 +- tools/perf/util/metricgroup.c | 5 +- tools/perf/util/parse-events.c | 15 +- tools/perf/util/parse-events.y | 3 +- tools/perf/util/pfm.c | 6 +- tools/perf/util/pmu.c | 411 +--------------------------------- tools/perf/util/pmu.h | 13 +- tools/perf/util/pmus.c | 396 +++++++++++++++++++++++++++++++- tools/perf/util/pmus.h | 14 +- tools/perf/util/print-events.c | 5 +- tools/perf/util/python.c | 3 +- tools/perf/util/stat-display.c | 3 +- 41 files changed, 533 insertions(+), 506 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c index adec6c9ee11d..3b8eca0ffb17 100644 --- a/tools/perf/arch/arm/util/auxtrace.c +++ b/tools/perf/arch/arm/util/auxtrace.c @@ -14,6 +14,7 @@ #include "../../../util/debug.h" #include "../../../util/evlist.h" #include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "cs-etm.h" #include "arm-spe.h" #include "hisi-ptt.h" @@ -40,7 +41,7 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) return NULL; } - arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name); + arm_spe_pmus[*nr_spes] = perf_pmus__find(arm_spe_pmu_name); if (arm_spe_pmus[*nr_spes]) { pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n", __func__, __LINE__, *nr_spes, @@ -87,7 +88,7 @@ static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err) rewinddir(dir); while ((dent = readdir(dir))) { if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) { - hisi_ptt_pmus[idx] = perf_pmu__find(dent->d_name); + hisi_ptt_pmus[idx] = perf_pmus__find(dent->d_name); if (hisi_ptt_pmus[idx]) idx++; } @@ -131,7 +132,7 @@ struct auxtrace_record if (!evlist) return NULL; - cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); + cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME); arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err); diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 9ca040bfb1aa..7c51fa182b51 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -25,7 +25,7 @@ #include "../../../util/evsel.h" #include "../../../util/perf_api_probe.h" #include "../../../util/evsel_config.h" -#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/cs-etm.h" #include // page_size #include "../../../util/session.h" @@ -881,7 +881,7 @@ struct auxtrace_record *cs_etm_record_init(int *err) struct perf_pmu *cs_etm_pmu; struct cs_etm_recording *ptr; - cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); + cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME); if (!cs_etm_pmu) { *err = -EINVAL; diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index ef1ed645097c..2504d43a39a7 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -3,6 +3,7 @@ #include #include "../../../util/cpumap.h" #include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include #include @@ -10,7 +11,7 @@ static struct perf_pmu *pmu__find_core_pmu(void) { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!is_pmu_core(pmu->name)) continue; diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c index 944bd1b4bab6..e466735d68d5 100644 --- a/tools/perf/arch/x86/tests/hybrid.c +++ b/tools/perf/arch/x86/tests/hybrid.c @@ -4,6 +4,7 @@ #include "evlist.h" #include "evsel.h" #include "pmu.h" +#include "pmus.h" #include "tests/tests.h" static bool test_config(const struct evsel *evsel, __u64 expected_config) @@ -113,7 +114,7 @@ static int test__hybrid_raw1(struct evlist *evlist) struct perf_evsel *evsel; perf_evlist__for_each_evsel(&evlist->core, evsel) { - struct perf_pmu *pmu = perf_pmu__find_by_type(evsel->attr.type); + struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->attr.type); TEST_ASSERT_VAL("missing pmu", pmu); TEST_ASSERT_VAL("unexpected pmu", !strncmp(pmu->name, "cpu_", 4)); @@ -280,7 +281,7 @@ static int test_events(const struct evlist_test *events, int cnt) int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return TEST_SKIP; return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events)); diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util/auxtrace.c index 330d03216b0e..354780ff1605 100644 --- a/tools/perf/arch/x86/util/auxtrace.c +++ b/tools/perf/arch/x86/util/auxtrace.c @@ -10,6 +10,7 @@ #include "../../../util/header.h" #include "../../../util/debug.h" #include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/auxtrace.h" #include "../../../util/intel-pt.h" #include "../../../util/intel-bts.h" @@ -25,8 +26,8 @@ struct auxtrace_record *auxtrace_record__init_intel(struct evlist *evlist, bool found_pt = false; bool found_bts = false; - intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME); - intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME); + intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME); + intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME); evlist__for_each_entry(evlist, evsel) { if (intel_pt_pmu && evsel->core.attr.type == intel_pt_pmu->type) diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 03f7eb4cf0a4..03240c640c7f 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include "util/pmu.h" +#include "util/pmus.h" #include "util/evlist.h" #include "util/parse-events.h" #include "util/event.h" @@ -17,7 +18,7 @@ static int ___evlist__add_default_attrs(struct evlist *evlist, for (i = 0; i < nr_attrs; i++) event_attr_init(attrs + i); - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return evlist__add_attrs(evlist, attrs, nr_attrs); for (i = 0; i < nr_attrs; i++) { @@ -32,7 +33,7 @@ static int ___evlist__add_default_attrs(struct evlist *evlist, continue; } - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { struct perf_cpu_map *cpus; struct evsel *evsel; diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 153cdca94cd4..25da46c8cca9 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -4,6 +4,7 @@ #include "util/evsel.h" #include "util/env.h" #include "util/pmu.h" +#include "util/pmus.h" #include "linux/string.h" #include "evsel.h" #include "util/debug.h" @@ -30,7 +31,7 @@ bool evsel__sys_has_perf_metrics(const struct evsel *evsel) * should be good enough to detect the perf metrics feature. */ if ((evsel->core.attr.type == PERF_TYPE_RAW) && - pmu_have_event(pmu_name, "slots")) + perf_pmus__have_event(pmu_name, "slots")) return true; return false; @@ -98,8 +99,8 @@ void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr) if (!evsel_pmu) return; - ibs_fetch_pmu = perf_pmu__find("ibs_fetch"); - ibs_op_pmu = perf_pmu__find("ibs_op"); + ibs_fetch_pmu = perf_pmus__find("ibs_fetch"); + ibs_op_pmu = perf_pmus__find("ibs_op"); if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) { if (attr->config & IBS_FETCH_L3MISSONLY) { diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 439c2956f3e7..d2c8cac11470 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -17,7 +17,7 @@ #include "../../../util/evlist.h" #include "../../../util/mmap.h" #include "../../../util/session.h" -#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/debug.h" #include "../../../util/record.h" #include "../../../util/tsc.h" @@ -416,7 +416,7 @@ out_err: struct auxtrace_record *intel_bts_recording_init(int *err) { - struct perf_pmu *intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME); + struct perf_pmu *intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME); struct intel_bts_recording *btsr; if (!intel_bts_pmu) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 17336da08b58..74b70fd379df 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -23,7 +23,7 @@ #include "../../../util/mmap.h" #include #include "../../../util/parse-events.h" -#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/debug.h" #include "../../../util/auxtrace.h" #include "../../../util/perf_api_probe.h" @@ -1185,7 +1185,7 @@ static u64 intel_pt_reference(struct auxtrace_record *itr __maybe_unused) struct auxtrace_record *intel_pt_recording_init(int *err) { - struct perf_pmu *intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME); + struct perf_pmu *intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME); struct intel_pt_recording *ptr; if (!intel_pt_pmu) diff --git a/tools/perf/arch/x86/util/mem-events.c b/tools/perf/arch/x86/util/mem-events.c index 02d65e446f46..32879d12a8d5 100644 --- a/tools/perf/arch/x86/util/mem-events.c +++ b/tools/perf/arch/x86/util/mem-events.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "util/pmu.h" +#include "util/pmus.h" #include "util/env.h" #include "map_symbol.h" #include "mem-events.h" @@ -55,12 +56,12 @@ struct perf_mem_event *perf_mem_events__ptr(int i) bool is_mem_loads_aux_event(struct evsel *leader) { - struct perf_pmu *pmu = perf_pmu__find("cpu"); + struct perf_pmu *pmu = perf_pmus__find("cpu"); if (!pmu) - pmu = perf_pmu__find("cpu_core"); + pmu = perf_pmus__find("cpu_core"); - if (pmu && !pmu_have_event(pmu->name, "mem-loads-aux")) + if (pmu && !perf_pmu__have_event(pmu, "mem-loads-aux")) return false; return leader->core.attr.config == MEM_LOADS_AUX; @@ -82,7 +83,7 @@ char *perf_mem_events__name(int i, char *pmu_name) pmu_name = (char *)"cpu"; } - if (pmu_have_event(pmu_name, "mem-loads-aux")) { + if (perf_pmus__have_event(pmu_name, "mem-loads-aux")) { scnprintf(mem_loads_name, sizeof(mem_loads_name), MEM_LOADS_AUX_NAME, pmu_name, pmu_name, perf_mem_events__loads_ldlat); diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c index 26abc159fc0e..befa7f3659b9 100644 --- a/tools/perf/arch/x86/util/perf_regs.c +++ b/tools/perf/arch/x86/util/perf_regs.c @@ -10,6 +10,7 @@ #include "../../../util/debug.h" #include "../../../util/event.h" #include "../../../util/pmu.h" +#include "../../../util/pmus.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG(AX, PERF_REG_X86_AX), @@ -291,7 +292,7 @@ uint64_t arch__intr_reg_mask(void) */ attr.sample_period = 1; - if (perf_pmu__has_hybrid()) { + if (perf_pmus__has_hybrid()) { struct perf_pmu *pmu = NULL; __u64 type = PERF_TYPE_RAW; @@ -299,7 +300,7 @@ uint64_t arch__intr_reg_mask(void) * The same register set is supported among different hybrid PMUs. * Only check the first available one. */ - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (pmu->is_core) { type = pmu->type; break; diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c index 9ad5e5c7bd27..3f9a267d4501 100644 --- a/tools/perf/arch/x86/util/topdown.c +++ b/tools/perf/arch/x86/util/topdown.c @@ -2,6 +2,7 @@ #include "api/fs/fs.h" #include "util/evsel.h" #include "util/pmu.h" +#include "util/pmus.h" #include "util/topdown.h" #include "topdown.h" #include "evsel.h" @@ -22,8 +23,8 @@ bool topdown_sys_has_perf_metrics(void) * The slots event is only available when the core PMU * supports the perf metrics feature. */ - pmu = perf_pmu__find_by_type(PERF_TYPE_RAW); - if (pmu && pmu_have_event(pmu->name, "slots")) + pmu = perf_pmus__find_by_type(PERF_TYPE_RAW); + if (pmu && perf_pmu__have_event(pmu, "slots")) has_perf_metrics = true; cached = true; diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c index f4a6c37cbe27..51cae2d03353 100644 --- a/tools/perf/bench/pmu-scan.c +++ b/tools/perf/bench/pmu-scan.c @@ -44,7 +44,7 @@ static int save_result(void) struct list_head *list; struct pmu_scan_result *r; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { r = realloc(results, (nr_pmus + 1) * sizeof(*r)); if (r == NULL) return -ENOMEM; @@ -68,7 +68,7 @@ static int save_result(void) nr_pmus++; } - perf_pmu__destroy(); + perf_pmus__destroy(); return 0; } @@ -81,7 +81,7 @@ static int check_result(void) for (int i = 0; i < nr_pmus; i++) { r = &results[i]; - pmu = perf_pmu__find(r->name); + pmu = perf_pmus__find(r->name); if (pmu == NULL) { pr_err("Cannot find PMU %s\n", r->name); return -1; @@ -144,7 +144,7 @@ static int run_pmu_scan(void) for (i = 0; i < iterations; i++) { gettimeofday(&start, NULL); - perf_pmu__scan(NULL); + perf_pmus__scan(NULL); gettimeofday(&end, NULL); timersub(&end, &start, &diff); @@ -152,7 +152,7 @@ static int run_pmu_scan(void) update_stats(&stats, runtime_us); ret = check_result(); - perf_pmu__destroy(); + perf_pmus__destroy(); if (ret < 0) break; } diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 2757ccc19c5e..05dfd98af170 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -41,7 +41,7 @@ #include "symbol.h" #include "ui/ui.h" #include "ui/progress.h" -#include "pmu.h" +#include "pmus.h" #include "string2.h" #include "util/util.h" @@ -3259,7 +3259,7 @@ static int perf_c2c__record(int argc, const char **argv) PARSE_OPT_KEEP_UNKNOWN); /* Max number of arguments multiplied by number of PMUs that can support them. */ - rec_argc = argc + 11 * perf_pmu__num_mem_pmus(); + rec_argc = argc + 11 * perf_pmus__num_mem_pmus(); rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (!rec_argv) diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index e8520a027b45..03b5d26b2489 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -527,7 +527,7 @@ int cmd_list(int argc, const char **argv) strcmp(argv[i], "hwcache") == 0) print_hwcache_events(&print_cb, ps); else if (strcmp(argv[i], "pmu") == 0) - print_pmu_events(&print_cb, ps); + perf_pmus__print_pmu_events(&print_cb, ps); else if (strcmp(argv[i], "sdt") == 0) print_sdt_events(&print_cb, ps); else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) { @@ -567,7 +567,7 @@ int cmd_list(int argc, const char **argv) event_symbols_sw, PERF_COUNT_SW_MAX); print_tool_events(&print_cb, ps); print_hwcache_events(&print_cb, ps); - print_pmu_events(&print_cb, ps); + perf_pmus__print_pmu_events(&print_cb, ps); print_tracepoint_events(&print_cb, ps); print_sdt_events(&print_cb, ps); default_ps.metrics = true; diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index f4f1ff76d49d..960bfd4b732a 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -17,7 +17,7 @@ #include "util/dso.h" #include "util/map.h" #include "util/symbol.h" -#include "util/pmu.h" +#include "util/pmus.h" #include "util/sample.h" #include "util/string2.h" #include "util/util.h" @@ -93,7 +93,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) PARSE_OPT_KEEP_UNKNOWN); /* Max number of arguments multiplied by number of PMUs that can support them. */ - rec_argc = argc + 9 * perf_pmu__num_mem_pmus(); + rec_argc = argc + 9 * perf_pmus__num_mem_pmus(); if (mem->cpu_list) rec_argc += 2; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2abcad2998f6..4b9212f75493 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -48,6 +48,8 @@ #include "util/bpf-event.h" #include "util/util.h" #include "util/pfm.h" +#include "util/pmu.h" +#include "util/pmus.h" #include "util/clockid.h" #include "util/off_cpu.h" #include "util/bpf-filter.h" @@ -1292,7 +1294,7 @@ static int record__open(struct record *rec) * of waiting or event synthesis. */ if (opts->target.initial_delay || target__has_cpu(&opts->target) || - perf_pmu__has_hybrid()) { + perf_pmus__has_hybrid()) { pos = evlist__get_tracking_event(evlist); if (!evsel__is_dummy_event(pos)) { /* Set up dummy event. */ @@ -2191,7 +2193,7 @@ static void record__uniquify_name(struct record *rec) char *new_name; int ret; - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return; evlist__for_each_entry(evlist, pos) { diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 62bbeea93bf3..c87c6897edc9 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -2140,11 +2140,11 @@ static int add_default_attributes(void) if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0) return -1; - if (pmu_have_event("cpu", "stalled-cycles-frontend")) { + if (perf_pmus__have_event("cpu", "stalled-cycles-frontend")) { if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0) return -1; } - if (pmu_have_event("cpu", "stalled-cycles-backend")) { + if (perf_pmus__have_event("cpu", "stalled-cycles-backend")) { if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0) return -1; } diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 56fba08a3037..674876e6c8e6 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -34,7 +34,7 @@ #include "event.h" #include "util.h" #include "tests.h" -#include "pmu.h" +#include "pmus.h" #define ENV "PERF_TEST_ATTR" @@ -185,7 +185,7 @@ static int test__attr(struct test_suite *test __maybe_unused, int subtest __mayb char path_dir[PATH_MAX]; char *exec_path; - if (perf_pmu__has_hybrid()) + if (perf_pmus__has_hybrid()) return TEST_SKIP; /* First try development tree tests. */ diff --git a/tools/perf/tests/event_groups.c b/tools/perf/tests/event_groups.c index 3d9a2b524bba..ccd9d8b2903f 100644 --- a/tools/perf/tests/event_groups.c +++ b/tools/perf/tests/event_groups.c @@ -53,7 +53,7 @@ static int setup_uncore_event(void) struct perf_pmu *pmu = NULL; int i, fd; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { for (i = 0; i < NR_UNCORE_PMUS; i++) { if (!strcmp(uncore_pmus[i].name, pmu->name)) { pr_debug("Using %s for uncore pmu event\n", pmu->name); diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 277607ede060..9d05bc551791 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -112,7 +112,7 @@ static int test__checkevent_raw(struct evlist *evlist) bool type_matched = false; TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a)); - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (pmu->type == evsel->attr.type) { TEST_ASSERT_VAL("PMU type expected once", !type_matched); type_matched = true; @@ -1443,12 +1443,12 @@ static int test__checkevent_config_cache(struct evlist *evlist) static bool test__pmu_cpu_valid(void) { - return !!perf_pmu__find("cpu"); + return !!perf_pmus__find("cpu"); } static bool test__intel_pt_valid(void) { - return !!perf_pmu__find("intel_pt"); + return !!perf_pmus__find("intel_pt"); } static int test__intel_pt(struct evlist *evlist) @@ -2246,7 +2246,7 @@ static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest struct perf_pmu *pmu = NULL; int ret = TEST_OK; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { struct stat st; char path[PATH_MAX]; struct dirent *ent; diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c index c05148ea400c..1d6493a5a956 100644 --- a/tools/perf/tests/parse-metric.c +++ b/tools/perf/tests/parse-metric.c @@ -11,7 +11,7 @@ #include "debug.h" #include "expr.h" #include "stat.h" -#include "pmu.h" +#include "pmus.h" struct value { const char *event; @@ -303,7 +303,7 @@ static int test__parse_metric(struct test_suite *test __maybe_unused, int subtes TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0); TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0); - if (!perf_pmu__has_hybrid()) { + if (!perf_pmus__has_hybrid()) { TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0); TEST_ASSERT_VAL("test metric group", test_metric_group() == 0); } diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 734004f1a37d..64ecb7845af4 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -2,6 +2,7 @@ #include "math.h" #include "parse-events.h" #include "pmu.h" +#include "pmus.h" #include "tests.h" #include #include @@ -708,7 +709,7 @@ static int test__aliases(struct test_suite *test __maybe_unused, struct perf_pmu *pmu = NULL; unsigned long i; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { int count = 0; if (!is_pmu_core(pmu->name)) diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index b3bd14b025a8..cff6ab87b2f6 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -20,7 +20,7 @@ #include "tests.h" #include "util/mmap.h" #include "util/sample.h" -#include "pmu.h" +#include "pmus.h" static int spin_sleep(void) { @@ -375,7 +375,7 @@ static int test__switch_tracking(struct test_suite *test __maybe_unused, int sub cpu_clocks_evsel = evlist__last(evlist); /* Second event */ - if (perf_pmu__has_hybrid()) { + if (perf_pmus__has_hybrid()) { cycles = "cpu_core/cycles/u"; err = parse_event(evlist, cycles); if (err) { diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index c4630cfc80ea..49e80d15420b 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -8,7 +8,7 @@ #include "session.h" #include "evlist.h" #include "debug.h" -#include "pmu.h" +#include "pmus.h" #include #define TEMPL "/tmp/perf-test-XXXXXX" @@ -41,7 +41,7 @@ static int session_write_header(char *path) session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); - if (!perf_pmu__has_hybrid()) { + if (!perf_pmus__has_hybrid()) { session->evlist = evlist__new_default(); TEST_ASSERT_VAL("can't get evlist", session->evlist); } else { diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c index a5c259bd5cc0..4578c26747e1 100644 --- a/tools/perf/util/cputopo.c +++ b/tools/perf/util/cputopo.c @@ -13,6 +13,7 @@ #include "debug.h" #include "env.h" #include "pmu.h" +#include "pmus.h" #define PACKAGE_CPUS_FMT \ "%s/devices/system/cpu/cpu%d/topology/package_cpus_list" @@ -473,10 +474,10 @@ struct hybrid_topology *hybrid_topology__new(void) struct hybrid_topology *tp = NULL; u32 nr = 0, i = 0; - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (pmu->is_core) nr++; } @@ -488,7 +489,7 @@ struct hybrid_topology *hybrid_topology__new(void) return NULL; tp->nr = nr; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (!pmu->is_core) continue; diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 4a4fdad820d6..9eabf3ec56e9 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -10,6 +10,7 @@ #include #include #include +#include "pmus.h" #include "strbuf.h" struct perf_env perf_env; @@ -323,7 +324,7 @@ int perf_env__read_pmu_mappings(struct perf_env *env) u32 pmu_num = 0; struct strbuf sb; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->name) continue; pmu_num++; @@ -337,7 +338,7 @@ int perf_env__read_pmu_mappings(struct perf_env *env) if (strbuf_init(&sb, 128 * pmu_num) < 0) return -ENOMEM; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->name) continue; if (strbuf_addf(&sb, "%u:%s", pmu->type, pmu->name) < 0) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1c6e22e3f345..b4237fc713d5 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -48,6 +48,7 @@ #include "util/hashmap.h" #include "off_cpu.h" #include "pmu.h" +#include "pmus.h" #include "../perf-sys.h" #include "util/parse-branch-options.h" #include "util/bpf-filter.h" @@ -3139,7 +3140,7 @@ void evsel__zero_per_pkg(struct evsel *evsel) */ bool evsel__is_hybrid(const struct evsel *evsel) { - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return false; return evsel->core.is_pmu_core; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 37fa66b1ca77..e6d8ecd7a08e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -37,6 +37,7 @@ #include "debug.h" #include "cpumap.h" #include "pmu.h" +#include "pmus.h" #include "vdso.h" #include "strbuf.h" #include "build-id.h" @@ -744,7 +745,7 @@ static int write_pmu_mappings(struct feat_fd *ff, * Do a first pass to count number of pmu to avoid lseek so this * works in pipe mode as well. */ - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->name) continue; pmu_num++; @@ -754,7 +755,7 @@ static int write_pmu_mappings(struct feat_fd *ff, if (ret < 0) return ret; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->name) continue; @@ -1566,7 +1567,7 @@ static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, static int write_cpu_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { - struct perf_pmu *cpu_pmu = perf_pmu__find("cpu"); + struct perf_pmu *cpu_pmu = perf_pmus__find("cpu"); int ret; if (!cpu_pmu) @@ -1586,7 +1587,7 @@ static int write_pmu_caps(struct feat_fd *ff, int nr_pmu = 0; int ret; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->name || !strcmp(pmu->name, "cpu") || perf_pmu__caps_parse(pmu) <= 0) continue; @@ -1604,9 +1605,9 @@ static int write_pmu_caps(struct feat_fd *ff, * Write hybrid pmu caps first to maintain compatibility with * older perf tool. */ - if (perf_pmu__has_hybrid()) { + if (perf_pmus__has_hybrid()) { pmu = NULL; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->is_core) continue; @@ -1617,7 +1618,7 @@ static int write_pmu_caps(struct feat_fd *ff, } pmu = NULL; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (pmu->is_core || !pmu->nr_caps) continue; diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index c9e422a38258..08ac3ea2e366 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -13,6 +13,7 @@ #include "debug.h" #include "symbol.h" #include "pmu.h" +#include "pmus.h" unsigned int perf_mem_events__loads_ldlat = 30; @@ -128,14 +129,14 @@ int perf_mem_events__init(void) if (!e->tag) continue; - if (!perf_pmu__has_hybrid()) { + if (!perf_pmus__has_hybrid()) { scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, "cpu"); e->supported = perf_mem_event__supported(mnt, sysfs_name); } else { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (!pmu->is_core) continue; @@ -175,7 +176,7 @@ static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e, char sysfs_name[100]; struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (!pmu->is_core) continue; @@ -201,7 +202,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, if (!e->record) continue; - if (!perf_pmu__has_hybrid()) { + if (!perf_pmus__has_hybrid()) { if (!e->supported) { pr_err("failed: event '%s' not supported\n", perf_mem_events__name(j, NULL)); @@ -216,7 +217,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, return -1; } - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (!pmu->is_core) continue; rec_argv[i++] = "-e"; diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 3f04a686d1cd..092ed6386a39 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -11,6 +11,7 @@ #include "evsel.h" #include "strbuf.h" #include "pmu.h" +#include "pmus.h" #include "print-events.h" #include "smt.h" #include "expr.h" @@ -273,7 +274,7 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids, const char *metric_id; struct evsel *ev; size_t ids_size, matched_events, i; - bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__has_hybrid() || !is_pmu_hybrid(pmu); + bool all_pmus = !strcmp(pmu, "all") || !perf_pmus__has_hybrid() || !is_pmu_hybrid(pmu); *out_metric_events = NULL; ids_size = hashmap__size(ids); @@ -488,7 +489,7 @@ static int metricgroup__sys_event_iter(const struct pmu_metric *pm, if (!pm->metric_expr || !pm->compat) return 0; - while ((pmu = perf_pmu__scan(pmu))) { + while ((pmu = perf_pmus__scan(pmu))) { if (!pmu->id || strcmp(pmu->id, pm->compat)) continue; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1a0be395c887..be544f948be2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -21,6 +21,7 @@ #include "parse-events-bison.h" #include "parse-events-flex.h" #include "pmu.h" +#include "pmus.h" #include "asm/bug.h" #include "util/parse-branch-options.h" #include "util/evsel_config.h" @@ -452,7 +453,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, const char *config_name = get_config_name(head_config); const char *metric_id = get_config_metric_id(head_config); - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { LIST_HEAD(config_terms); struct perf_event_attr attr; int ret; @@ -1193,7 +1194,7 @@ static int config_term_pmu(struct perf_event_attr *attr, struct parse_events_error *err) { if (term->type_term == PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE) { - const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type); + const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); if (perf_pmu__supports_legacy_cache(pmu)) { attr->type = PERF_TYPE_HW_CACHE; @@ -1203,7 +1204,7 @@ static int config_term_pmu(struct perf_event_attr *attr, term->type_term = PARSE_EVENTS__TERM_TYPE_USER; } if (term->type_term == PARSE_EVENTS__TERM_TYPE_HARDWARE) { - const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type); + const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); if (!pmu) { pr_debug("Failed to find PMU for type %d", attr->type); @@ -1480,7 +1481,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL, type, config, head_config); - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { int ret; if (!perf_pmu__supports_wildcard_numeric(pmu)) @@ -1529,7 +1530,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, struct parse_events_error *err = parse_state->error; LIST_HEAD(config_terms); - pmu = parse_state->fake_pmu ?: perf_pmu__find(name); + pmu = parse_state->fake_pmu ?: perf_pmus__find(name); if (verbose > 1 && !(pmu && pmu->selectable)) { fprintf(stderr, "Attempting to add event pmu '%s' with '", @@ -1674,7 +1675,7 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, INIT_LIST_HEAD(list); - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { struct perf_pmu_alias *alias; bool auto_merge_stats; @@ -2410,7 +2411,7 @@ static int set_filter(struct evsel *evsel, const void *arg) return 0; } - while ((pmu = perf_pmu__scan(pmu)) != NULL) + while ((pmu = perf_pmus__scan(pmu)) != NULL) if (pmu->type == evsel->core.attr.type) { found = true; break; diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4e1f5de35be8..abd6ab460e12 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -15,6 +15,7 @@ #include #include #include "pmu.h" +#include "pmus.h" #include "evsel.h" #include "parse-events.h" #include "parse-events-bison.h" @@ -316,7 +317,7 @@ PE_NAME opt_pmu_config if (asprintf(&pattern, "%s*", $1) < 0) CLEANUP_YYABORT; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { char *name = pmu->name; if (parse_events__filter_pmu(parse_state, pmu)) diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index 6c11914c179f..076aecc22c16 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -10,7 +10,7 @@ #include "util/evlist.h" #include "util/evsel.h" #include "util/parse-events.h" -#include "util/pmu.h" +#include "util/pmus.h" #include "util/pfm.h" #include "util/strbuf.h" @@ -49,7 +49,7 @@ int parse_libpfm_events_option(const struct option *opt, const char *str, /* * force loading of the PMU list */ - perf_pmu__scan(NULL); + perf_pmus__scan(NULL); for (q = p; strsep(&p, ",{}"); q = p) { sep = p ? str + (p - p_orig - 1) : ""; @@ -86,7 +86,7 @@ int parse_libpfm_events_option(const struct option *opt, const char *str, goto error; } - pmu = perf_pmu__find_by_type((unsigned int)attr.type); + pmu = perf_pmus__find_by_type((unsigned int)attr.type); evsel = parse_events__add_event(evlist->core.nr_entries, &attr, q, /*metric_id=*/NULL, pmu); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 21ee23b78f5a..05056305fb58 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -4,20 +4,15 @@ #include #include #include -#include #include -#include #include #include #include #include #include -#include #include #include #include -#include -#include #include #include #include "debug.h" @@ -59,8 +54,6 @@ struct perf_pmu_format { struct list_head list; }; -static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name); - /* * Parse & process all the sysfs attributes located under * the directory specified in 'dir' parameter. @@ -554,31 +547,6 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias, return 0; } -/* Add all pmus in sysfs to pmu list: */ -static void pmu_read_sysfs(void) -{ - int fd; - DIR *dir; - struct dirent *dent; - - fd = perf_pmu__event_source_devices_fd(); - if (fd < 0) - return; - - dir = fdopendir(fd); - if (!dir) - return; - - while ((dent = readdir(dir))) { - if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) - continue; - /* add to static LIST_HEAD(pmus): */ - perf_pmu__find2(fd, dent->d_name); - } - - closedir(dir); -} - /* * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64) * may have a "cpus" file. @@ -894,7 +862,7 @@ static int pmu_max_precise(int dirfd, struct perf_pmu *pmu) return max_precise; } -static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name) +struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name) { struct perf_pmu *pmu; LIST_HEAD(format); @@ -951,7 +919,7 @@ static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name) INIT_LIST_HEAD(&pmu->caps); list_splice(&format, &pmu->format); list_splice(&aliases, &pmu->aliases); - list_add_tail(&pmu->list, &pmus); + list_add_tail(&pmu->list, pmus); pmu->default_config = perf_pmu__get_default_config(pmu); @@ -979,61 +947,6 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) } } -static struct perf_pmu *pmu_find(const char *name) -{ - struct perf_pmu *pmu; - - list_for_each_entry(pmu, &pmus, list) { - if (!strcmp(pmu->name, name) || - (pmu->alias_name && !strcmp(pmu->alias_name, name))) - return pmu; - } - - return NULL; -} - -struct perf_pmu *perf_pmu__find_by_type(unsigned int type) -{ - struct perf_pmu *pmu; - - list_for_each_entry(pmu, &pmus, list) - if (pmu->type == type) - return pmu; - - return NULL; -} - -struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) -{ - /* - * pmu iterator: If pmu is NULL, we start at the begin, - * otherwise return the next pmu. Returns NULL on end. - */ - if (!pmu) { - pmu_read_sysfs(); - pmu = list_prepare_entry(pmu, &pmus, list); - } - list_for_each_entry_continue(pmu, &pmus, list) - return pmu; - return NULL; -} - -struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) -{ - struct perf_pmu *pmu = NULL; - - if (evsel->pmu) - return evsel->pmu; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (pmu->type == evsel->core.attr.type) - break; - } - - ((struct evsel *)evsel)->pmu = pmu; - return pmu; -} - bool evsel__is_aux_event(const struct evsel *evsel) { struct perf_pmu *pmu = evsel__find_pmu(evsel); @@ -1070,43 +983,6 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, evsel->core.attr.config |= field_prep(bits, val); } -struct perf_pmu *perf_pmu__find(const char *name) -{ - struct perf_pmu *pmu; - int dirfd; - - /* - * Once PMU is loaded it stays in the list, - * so we keep us from multiple reading/parsing - * the pmu format definitions. - */ - pmu = pmu_find(name); - if (pmu) - return pmu; - - dirfd = perf_pmu__event_source_devices_fd(); - pmu = pmu_lookup(dirfd, name); - close(dirfd); - - return pmu; -} - -static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) -{ - struct perf_pmu *pmu; - - /* - * Once PMU is loaded it stays in the list, - * so we keep us from multiple reading/parsing - * the pmu format definitions. - */ - pmu = pmu_find(name); - if (pmu) - return pmu; - - return pmu_lookup(dirfd, name); -} - static struct perf_pmu_format * pmu_find_format(struct list_head *formats, const char *name) { @@ -1536,99 +1412,6 @@ void perf_pmu__del_formats(struct list_head *formats) } } -static int sub_non_neg(int a, int b) -{ - if (b > a) - return 0; - return a - b; -} - -static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, - const struct perf_pmu_alias *alias) -{ - struct parse_events_term *term; - int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); - - list_for_each_entry(term, &alias->terms, list) { - if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) - used += snprintf(buf + used, sub_non_neg(len, used), - ",%s=%s", term->config, - term->val.str); - } - - if (sub_non_neg(len, used) > 0) { - buf[used] = '/'; - used++; - } - if (sub_non_neg(len, used) > 0) { - buf[used] = '\0'; - used++; - } else - buf[len - 1] = '\0'; - - return buf; -} - -/** Struct for ordering events as output in perf list. */ -struct sevent { - /** PMU for event. */ - const struct perf_pmu *pmu; - /** - * Optional event for name, desc, etc. If not present then this is a - * selectable PMU and the event name is shown as "//". - */ - const struct perf_pmu_alias *event; - /** Is the PMU for the CPU? */ - bool is_cpu; -}; - -static int cmp_sevent(const void *a, const void *b) -{ - const struct sevent *as = a; - const struct sevent *bs = b; - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *a_desc = NULL, *a_topic = ""; - const char *b_name = "//", *b_desc = NULL, *b_topic = ""; - int ret; - - if (as->event) { - a_name = as->event->name; - a_desc = as->event->desc; - a_topic = as->event->topic ?: ""; - a_pmu_name = as->event->pmu_name; - } - if (bs->event) { - b_name = bs->event->name; - b_desc = bs->event->desc; - b_topic = bs->event->topic ?: ""; - b_pmu_name = bs->event->pmu_name; - } - /* Put extra events last. */ - if (!!a_desc != !!b_desc) - return !!a_desc - !!b_desc; - - /* Order by topics. */ - ret = strcmp(a_topic, b_topic); - if (ret) - return ret; - - /* Order CPU core events to be first */ - if (as->is_cpu != bs->is_cpu) - return as->is_cpu ? -1 : 1; - - /* Order by PMU name. */ - if (as->pmu != bs->pmu) { - a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); - ret = strcmp(a_pmu_name, b_pmu_name); - if (ret) - return ret; - } - - /* Order by event name. */ - return strcmp(a_name, b_name); -} - bool is_pmu_core(const char *name) { return !strcmp(name, "cpu") || is_sysfs_pmu_core(name); @@ -1654,167 +1437,18 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) return !is_pmu_hybrid(pmu->name); } -static bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) +bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) { return pmu->is_core; } -int perf_pmu__num_mem_pmus(void) -{ - struct perf_pmu *pmu = NULL; - int count = 0; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (perf_pmu__is_mem_pmu(pmu)) - count++; - } - return count; -} - -static bool pmu_alias_is_duplicate(struct sevent *alias_a, - struct sevent *alias_b) -{ - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *b_name = "//"; - - - if (alias_a->event) { - a_name = alias_a->event->name; - a_pmu_name = alias_a->event->pmu_name; - } - if (alias_b->event) { - b_name = alias_b->event->name; - b_pmu_name = alias_b->event->pmu_name; - } - - /* Different names -> never duplicates */ - if (strcmp(a_name, b_name)) - return false; - - /* Don't remove duplicates for different PMUs */ - a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: ""); - return strcmp(a_pmu_name, b_pmu_name) == 0; -} - -void print_pmu_events(const struct print_callbacks *print_cb, void *print_state) -{ - struct perf_pmu *pmu; - struct perf_pmu_alias *event; - char buf[1024]; - int printed = 0; - int len, j; - struct sevent *aliases; - - pmu = NULL; - len = 0; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - list_for_each_entry(event, &pmu->aliases, list) - len++; - if (pmu->selectable) - len++; - } - aliases = zalloc(sizeof(struct sevent) * len); - if (!aliases) { - pr_err("FATAL: not enough memory to print PMU events\n"); - return; - } - pmu = NULL; - j = 0; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - bool is_cpu = pmu->is_core; - - list_for_each_entry(event, &pmu->aliases, list) { - aliases[j].event = event; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } - if (pmu->selectable) { - aliases[j].event = NULL; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } - } - len = j; - qsort(aliases, len, sizeof(struct sevent), cmp_sevent); - for (j = 0; j < len; j++) { - const char *name, *alias = NULL, *scale_unit = NULL, - *desc = NULL, *long_desc = NULL, - *encoding_desc = NULL, *topic = NULL, - *pmu_name = NULL; - bool deprecated = false; - size_t buf_used; - - /* Skip duplicates */ - if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) - continue; - - if (!aliases[j].event) { - /* A selectable event. */ - pmu_name = aliases[j].pmu->name; - buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1; - name = buf; - } else { - if (aliases[j].event->desc) { - name = aliases[j].event->name; - buf_used = 0; - } else { - name = format_alias(buf, sizeof(buf), aliases[j].pmu, - aliases[j].event); - if (aliases[j].is_cpu) { - alias = name; - name = aliases[j].event->name; - } - buf_used = strlen(buf) + 1; - } - pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: ""); - if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) { - scale_unit = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%G%s", aliases[j].event->scale, - aliases[j].event->unit) + 1; - } - desc = aliases[j].event->desc; - long_desc = aliases[j].event->long_desc; - topic = aliases[j].event->topic; - encoding_desc = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%s/%s/", pmu_name, aliases[j].event->str) + 1; - deprecated = aliases[j].event->deprecated; - } - print_cb->print_event(print_state, - pmu_name, - topic, - name, - alias, - scale_unit, - deprecated, - "Kernel PMU event", - desc, - long_desc, - encoding_desc); - } - if (printed && pager_in_use()) - printf("\n"); - - zfree(&aliases); - return; -} - -bool pmu_have_event(const char *pname, const char *name) +bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) { - struct perf_pmu *pmu; struct perf_pmu_alias *alias; - pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (strcmp(pname, pmu->name)) - continue; - list_for_each_entry(alias, &pmu->aliases, list) - if (!strcmp(alias->name, name)) - return true; + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcmp(alias->name, name)) + return true; } return false; } @@ -2020,24 +1654,6 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, name ?: "N/A", buf, config); } -bool perf_pmu__has_hybrid(void) -{ - static bool hybrid_scanned, has_hybrid; - - if (!hybrid_scanned) { - struct perf_pmu *pmu = NULL; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (pmu->is_core && is_pmu_hybrid(pmu->name)) { - has_hybrid = true; - break; - } - } - hybrid_scanned = true; - } - return has_hybrid; -} - int perf_pmu__match(char *pattern, char *name, char *tok) { if (!name) @@ -2105,7 +1721,7 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, return openat(dirfd, path, flags); } -static void perf_pmu__delete(struct perf_pmu *pmu) +void perf_pmu__delete(struct perf_pmu *pmu) { perf_pmu__del_formats(&pmu->format); perf_pmu__del_aliases(pmu); @@ -2118,14 +1734,3 @@ static void perf_pmu__delete(struct perf_pmu *pmu) zfree(&pmu->alias_name); free(pmu); } - -void perf_pmu__destroy(void) -{ - struct perf_pmu *pmu, *tmp; - - list_for_each_entry_safe(pmu, tmp, &pmus, list) { - list_del(&pmu->list); - - perf_pmu__delete(pmu); - } -} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index cb51ad6e40fa..f1f3e8a2e00e 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -198,8 +198,6 @@ struct perf_pmu_alias { char *pmu_name; }; -struct perf_pmu *perf_pmu__find(const char *name); -struct perf_pmu *perf_pmu__find_by_type(unsigned int type); void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu); int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms, @@ -222,16 +220,13 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to); int perf_pmu__format_parse(int dirfd, struct list_head *head); void perf_pmu__del_formats(struct list_head *formats); -struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); - bool is_pmu_core(const char *name); bool is_pmu_hybrid(const char *name); bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu); bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); -int perf_pmu__num_mem_pmus(void); -void print_pmu_events(const struct print_callbacks *print_cb, void *print_state); -bool pmu_have_event(const char *pname, const char *name); +bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu); +bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name); FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name); FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name); @@ -261,7 +256,6 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, const char *name); void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu); -bool perf_pmu__has_hybrid(void); int perf_pmu__match(char *pattern, char *name, char *tok); char *pmu_find_real_name(const char *name); @@ -273,6 +267,7 @@ int perf_pmu__pathname_scnprintf(char *buf, size_t size, int perf_pmu__event_source_devices_fd(void); int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags); -void perf_pmu__destroy(void); +struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name); +void perf_pmu__delete(struct perf_pmu *pmu); #endif /* __PMU_H */ diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 140e11f00b29..58ff7937e9b7 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -1,16 +1,136 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include +#include +#include +#include #include +#include +#include "debug.h" +#include "evsel.h" #include "pmus.h" #include "pmu.h" +#include "print-events.h" -LIST_HEAD(pmus); +static LIST_HEAD(pmus); + +void perf_pmus__destroy(void) +{ + struct perf_pmu *pmu, *tmp; + + list_for_each_entry_safe(pmu, tmp, &pmus, list) { + list_del(&pmu->list); + + perf_pmu__delete(pmu); + } +} + +static struct perf_pmu *pmu_find(const char *name) +{ + struct perf_pmu *pmu; + + list_for_each_entry(pmu, &pmus, list) { + if (!strcmp(pmu->name, name) || + (pmu->alias_name && !strcmp(pmu->alias_name, name))) + return pmu; + } + + return NULL; +} + +struct perf_pmu *perf_pmus__find(const char *name) +{ + struct perf_pmu *pmu; + int dirfd; + + /* + * Once PMU is loaded it stays in the list, + * so we keep us from multiple reading/parsing + * the pmu format definitions. + */ + pmu = pmu_find(name); + if (pmu) + return pmu; + + dirfd = perf_pmu__event_source_devices_fd(); + pmu = perf_pmu__lookup(&pmus, dirfd, name); + close(dirfd); + + return pmu; +} + +static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) +{ + struct perf_pmu *pmu; + + /* + * Once PMU is loaded it stays in the list, + * so we keep us from multiple reading/parsing + * the pmu format definitions. + */ + pmu = pmu_find(name); + if (pmu) + return pmu; + + return perf_pmu__lookup(&pmus, dirfd, name); +} + +/* Add all pmus in sysfs to pmu list: */ +static void pmu_read_sysfs(void) +{ + int fd; + DIR *dir; + struct dirent *dent; + + fd = perf_pmu__event_source_devices_fd(); + if (fd < 0) + return; + + dir = fdopendir(fd); + if (!dir) + return; + + while ((dent = readdir(dir))) { + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) + continue; + /* add to static LIST_HEAD(pmus): */ + perf_pmu__find2(fd, dent->d_name); + } + + closedir(dir); +} + +struct perf_pmu *perf_pmus__find_by_type(unsigned int type) +{ + struct perf_pmu *pmu; + + list_for_each_entry(pmu, &pmus, list) + if (pmu->type == type) + return pmu; + + return NULL; +} + +struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu) +{ + /* + * pmu iterator: If pmu is NULL, we start at the begin, + * otherwise return the next pmu. Returns NULL on end. + */ + if (!pmu) { + pmu_read_sysfs(); + pmu = list_prepare_entry(pmu, &pmus, list); + } + list_for_each_entry_continue(pmu, &pmus, list) + return pmu; + return NULL; +} const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (!strcmp(pmu->name, str)) return pmu; /* Ignore "uncore_" prefix. */ @@ -26,3 +146,275 @@ const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) } return NULL; } + +int perf_pmus__num_mem_pmus(void) +{ + struct perf_pmu *pmu = NULL; + int count = 0; + + while ((pmu = perf_pmus__scan(pmu)) != NULL) { + if (perf_pmu__is_mem_pmu(pmu)) + count++; + } + return count; +} + +/** Struct for ordering events as output in perf list. */ +struct sevent { + /** PMU for event. */ + const struct perf_pmu *pmu; + /** + * Optional event for name, desc, etc. If not present then this is a + * selectable PMU and the event name is shown as "//". + */ + const struct perf_pmu_alias *event; + /** Is the PMU for the CPU? */ + bool is_cpu; +}; + +static int cmp_sevent(const void *a, const void *b) +{ + const struct sevent *as = a; + const struct sevent *bs = b; + const char *a_pmu_name = NULL, *b_pmu_name = NULL; + const char *a_name = "//", *a_desc = NULL, *a_topic = ""; + const char *b_name = "//", *b_desc = NULL, *b_topic = ""; + int ret; + + if (as->event) { + a_name = as->event->name; + a_desc = as->event->desc; + a_topic = as->event->topic ?: ""; + a_pmu_name = as->event->pmu_name; + } + if (bs->event) { + b_name = bs->event->name; + b_desc = bs->event->desc; + b_topic = bs->event->topic ?: ""; + b_pmu_name = bs->event->pmu_name; + } + /* Put extra events last. */ + if (!!a_desc != !!b_desc) + return !!a_desc - !!b_desc; + + /* Order by topics. */ + ret = strcmp(a_topic, b_topic); + if (ret) + return ret; + + /* Order CPU core events to be first */ + if (as->is_cpu != bs->is_cpu) + return as->is_cpu ? -1 : 1; + + /* Order by PMU name. */ + if (as->pmu != bs->pmu) { + a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); + b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); + ret = strcmp(a_pmu_name, b_pmu_name); + if (ret) + return ret; + } + + /* Order by event name. */ + return strcmp(a_name, b_name); +} + +static bool pmu_alias_is_duplicate(struct sevent *alias_a, + struct sevent *alias_b) +{ + const char *a_pmu_name = NULL, *b_pmu_name = NULL; + const char *a_name = "//", *b_name = "//"; + + + if (alias_a->event) { + a_name = alias_a->event->name; + a_pmu_name = alias_a->event->pmu_name; + } + if (alias_b->event) { + b_name = alias_b->event->name; + b_pmu_name = alias_b->event->pmu_name; + } + + /* Different names -> never duplicates */ + if (strcmp(a_name, b_name)) + return false; + + /* Don't remove duplicates for different PMUs */ + a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); + b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: ""); + return strcmp(a_pmu_name, b_pmu_name) == 0; +} + +static int sub_non_neg(int a, int b) +{ + if (b > a) + return 0; + return a - b; +} + +static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, + const struct perf_pmu_alias *alias) +{ + struct parse_events_term *term; + int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); + + list_for_each_entry(term, &alias->terms, list) { + if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) + used += snprintf(buf + used, sub_non_neg(len, used), + ",%s=%s", term->config, + term->val.str); + } + + if (sub_non_neg(len, used) > 0) { + buf[used] = '/'; + used++; + } + if (sub_non_neg(len, used) > 0) { + buf[used] = '\0'; + used++; + } else + buf[len - 1] = '\0'; + + return buf; +} + +void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state) +{ + struct perf_pmu *pmu; + struct perf_pmu_alias *event; + char buf[1024]; + int printed = 0; + int len, j; + struct sevent *aliases; + + pmu = NULL; + len = 0; + while ((pmu = perf_pmus__scan(pmu)) != NULL) { + list_for_each_entry(event, &pmu->aliases, list) + len++; + if (pmu->selectable) + len++; + } + aliases = zalloc(sizeof(struct sevent) * len); + if (!aliases) { + pr_err("FATAL: not enough memory to print PMU events\n"); + return; + } + pmu = NULL; + j = 0; + while ((pmu = perf_pmus__scan(pmu)) != NULL) { + bool is_cpu = pmu->is_core; + + list_for_each_entry(event, &pmu->aliases, list) { + aliases[j].event = event; + aliases[j].pmu = pmu; + aliases[j].is_cpu = is_cpu; + j++; + } + if (pmu->selectable) { + aliases[j].event = NULL; + aliases[j].pmu = pmu; + aliases[j].is_cpu = is_cpu; + j++; + } + } + len = j; + qsort(aliases, len, sizeof(struct sevent), cmp_sevent); + for (j = 0; j < len; j++) { + const char *name, *alias = NULL, *scale_unit = NULL, + *desc = NULL, *long_desc = NULL, + *encoding_desc = NULL, *topic = NULL, + *pmu_name = NULL; + bool deprecated = false; + size_t buf_used; + + /* Skip duplicates */ + if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) + continue; + + if (!aliases[j].event) { + /* A selectable event. */ + pmu_name = aliases[j].pmu->name; + buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1; + name = buf; + } else { + if (aliases[j].event->desc) { + name = aliases[j].event->name; + buf_used = 0; + } else { + name = format_alias(buf, sizeof(buf), aliases[j].pmu, + aliases[j].event); + if (aliases[j].is_cpu) { + alias = name; + name = aliases[j].event->name; + } + buf_used = strlen(buf) + 1; + } + pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: ""); + if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) { + scale_unit = buf + buf_used; + buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, + "%G%s", aliases[j].event->scale, + aliases[j].event->unit) + 1; + } + desc = aliases[j].event->desc; + long_desc = aliases[j].event->long_desc; + topic = aliases[j].event->topic; + encoding_desc = buf + buf_used; + buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, + "%s/%s/", pmu_name, aliases[j].event->str) + 1; + deprecated = aliases[j].event->deprecated; + } + print_cb->print_event(print_state, + pmu_name, + topic, + name, + alias, + scale_unit, + deprecated, + "Kernel PMU event", + desc, + long_desc, + encoding_desc); + } + if (printed && pager_in_use()) + printf("\n"); + + zfree(&aliases); +} + +bool perf_pmus__have_event(const char *pname, const char *name) +{ + struct perf_pmu *pmu = perf_pmus__find(pname); + + return pmu && perf_pmu__have_event(pmu, name); +} + +bool perf_pmus__has_hybrid(void) +{ + static bool hybrid_scanned, has_hybrid; + + if (!hybrid_scanned) { + struct perf_pmu *pmu = NULL; + + while ((pmu = perf_pmus__scan(pmu)) != NULL) { + if (pmu->is_core && is_pmu_hybrid(pmu->name)) { + has_hybrid = true; + break; + } + } + hybrid_scanned = true; + } + return has_hybrid; +} + +struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) +{ + struct perf_pmu *pmu = evsel->pmu; + + if (!pmu) { + pmu = perf_pmus__find_by_type(evsel->core.attr.type); + ((struct evsel *)evsel)->pmu = pmu; + } + return pmu; +} diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 257de10788e8..2a771d9f8da7 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -2,9 +2,21 @@ #ifndef __PMUS_H #define __PMUS_H -extern struct list_head pmus; struct perf_pmu; +struct print_callbacks; + +void perf_pmus__destroy(void); + +struct perf_pmu *perf_pmus__find(const char *name); +struct perf_pmu *perf_pmus__find_by_type(unsigned int type); + +struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); +int perf_pmus__num_mem_pmus(void); +void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state); +bool perf_pmus__have_event(const char *pname, const char *name); +bool perf_pmus__has_hybrid(void); + #endif /* __PMUS_H */ diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 8d823bc906e6..9cee7bb7a561 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -20,6 +20,7 @@ #include "metricgroup.h" #include "parse-events.h" #include "pmu.h" +#include "pmus.h" #include "print-events.h" #include "probe-file.h" #include "string2.h" @@ -271,7 +272,7 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta struct perf_pmu *pmu = NULL; const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE]; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan(pmu)) != NULL) { /* * Skip uncore PMUs for performance. PERF_TYPE_HW_CACHE type * attributes can accept software PMUs in the extended type, so @@ -404,7 +405,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state) print_hwcache_events(print_cb, print_state); - print_pmu_events(print_cb, print_state); + perf_pmus__print_pmu_events(print_cb, print_state); print_cb->print_event(print_state, /*topic=*/NULL, diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index b27b27086422..7173f6fcdc11 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -22,6 +22,7 @@ #include "util/bpf-filter.h" #include "util/env.h" #include "util/pmu.h" +#include "util/pmus.h" #include #include "util.h" @@ -102,7 +103,7 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, return EOF; } -bool perf_pmu__has_hybrid(void) +bool perf_pmus__has_hybrid(void) { return false; } diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index a3e184e0b5ba..7ca69151136b 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -20,6 +20,7 @@ #include "util.h" #include "iostat.h" #include "pmu.h" +#include "pmus.h" #define CNTR_NOT_SUPPORTED "" #define CNTR_NOT_COUNTED "" @@ -695,7 +696,7 @@ static bool evlist__has_hybrid(struct evlist *evlist) { struct evsel *evsel; - if (!perf_pmu__has_hybrid()) + if (!perf_pmus__has_hybrid()) return false; evlist__for_each_entry(evlist, evsel) { -- cgit v1.2.3 From 9d6a1df9b2eef52ad03a594b1237a16dbbe34e83 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 27 May 2023 00:22:05 -0700 Subject: perf pmus: Allow just core PMU scanning Scanning all PMUs is expensive as all PMUs sysfs entries are loaded, benchmarking shows more than 4x the cost: ``` $ perf bench internals pmu-scan -i 1000 Computing performance of sysfs PMU event scan for 1000 times Average core PMU scanning took: 989.231 usec (+- 1.535 usec) Average PMU scanning took: 4309.425 usec (+- 74.322 usec) ``` Add new perf_pmus__scan_core routine that scans just core PMUs. Replace perf_pmus__scan calls with perf_pmus__scan_core when non-core PMUs are being ignored. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230527072210.2900565-30-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/util/pmu.c | 5 +--- tools/perf/arch/x86/util/evlist.c | 5 +--- tools/perf/arch/x86/util/perf_regs.c | 8 +++--- tools/perf/bench/pmu-scan.c | 50 +++++++++++++++++++++--------------- tools/perf/tests/pmu-events.c | 5 +--- tools/perf/util/cputopo.c | 12 +++------ tools/perf/util/header.c | 5 +--- tools/perf/util/mem-events.c | 14 +++------- tools/perf/util/parse-events.c | 13 +++------- tools/perf/util/pmu.c | 10 -------- tools/perf/util/pmu.h | 2 -- tools/perf/util/pmus.c | 30 ++++++++++++++++------ tools/perf/util/pmus.h | 1 + tools/perf/util/print-events.c | 11 ++++---- 14 files changed, 75 insertions(+), 96 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index 2504d43a39a7..561de0cb6b95 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -11,10 +11,7 @@ static struct perf_pmu *pmu__find_core_pmu(void) { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmus__scan(pmu))) { - if (!is_pmu_core(pmu->name)) - continue; - + while ((pmu = perf_pmus__scan_core(pmu))) { /* * The cpumap should cover all CPUs. Otherwise, some CPUs may * not support some events or have different event IDs. diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 03240c640c7f..8a6a0b98b976 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -33,13 +33,10 @@ static int ___evlist__add_default_attrs(struct evlist *evlist, continue; } - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { struct perf_cpu_map *cpus; struct evsel *evsel; - if (!pmu->is_core) - continue; - evsel = evsel__new(attrs + i); if (evsel == NULL) goto out_delete_partial_list; diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c index befa7f3659b9..116384f19baf 100644 --- a/tools/perf/arch/x86/util/perf_regs.c +++ b/tools/perf/arch/x86/util/perf_regs.c @@ -300,11 +300,9 @@ uint64_t arch__intr_reg_mask(void) * The same register set is supported among different hybrid PMUs. * Only check the first available one. */ - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (pmu->is_core) { - type = pmu->type; - break; - } + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + type = pmu->type; + break; } attr.config |= type << PERF_PMU_TYPE_SHIFT; } diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c index 51cae2d03353..c7d207f8e13c 100644 --- a/tools/perf/bench/pmu-scan.c +++ b/tools/perf/bench/pmu-scan.c @@ -22,6 +22,7 @@ struct pmu_scan_result { int nr_aliases; int nr_formats; int nr_caps; + bool is_core; }; static const struct option options[] = { @@ -53,6 +54,7 @@ static int save_result(void) r = results + nr_pmus; r->name = strdup(pmu->name); + r->is_core = pmu->is_core; r->nr_caps = pmu->nr_caps; r->nr_aliases = 0; @@ -72,7 +74,7 @@ static int save_result(void) return 0; } -static int check_result(void) +static int check_result(bool core_only) { struct pmu_scan_result *r; struct perf_pmu *pmu; @@ -81,6 +83,9 @@ static int check_result(void) for (int i = 0; i < nr_pmus; i++) { r = &results[i]; + if (core_only && !r->is_core) + continue; + pmu = perf_pmus__find(r->name); if (pmu == NULL) { pr_err("Cannot find PMU %s\n", r->name); @@ -130,7 +135,6 @@ static int run_pmu_scan(void) struct timeval start, end, diff; double time_average, time_stddev; u64 runtime_us; - unsigned int i; int ret; init_stats(&stats); @@ -142,26 +146,30 @@ static int run_pmu_scan(void) return -1; } - for (i = 0; i < iterations; i++) { - gettimeofday(&start, NULL); - perf_pmus__scan(NULL); - gettimeofday(&end, NULL); - - timersub(&end, &start, &diff); - runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; - update_stats(&stats, runtime_us); - - ret = check_result(); - perf_pmus__destroy(); - if (ret < 0) - break; + for (int j = 0; j < 2; j++) { + bool core_only = (j == 0); + + for (unsigned int i = 0; i < iterations; i++) { + gettimeofday(&start, NULL); + if (core_only) + perf_pmus__scan_core(NULL); + else + perf_pmus__scan(NULL); + gettimeofday(&end, NULL); + timersub(&end, &start, &diff); + runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; + update_stats(&stats, runtime_us); + + ret = check_result(core_only); + perf_pmus__destroy(); + if (ret < 0) + break; + } + time_average = avg_stats(&stats); + time_stddev = stddev_stats(&stats); + pr_info(" Average%s PMU scanning took: %.3f usec (+- %.3f usec)\n", + core_only ? " core" : "", time_average, time_stddev); } - - time_average = avg_stats(&stats); - time_stddev = stddev_stats(&stats); - pr_info(" Average PMU scanning took: %.3f usec (+- %.3f usec)\n", - time_average, time_stddev); - delete_result(); return 0; } diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 64ecb7845af4..64383fc34ef1 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -709,12 +709,9 @@ static int test__aliases(struct test_suite *test __maybe_unused, struct perf_pmu *pmu = NULL; unsigned long i; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { int count = 0; - if (!is_pmu_core(pmu->name)) - continue; - if (list_empty(&pmu->format)) { pr_debug2("skipping testing core PMU %s\n", pmu->name); continue; diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c index 4578c26747e1..729142ec9a9a 100644 --- a/tools/perf/util/cputopo.c +++ b/tools/perf/util/cputopo.c @@ -477,10 +477,9 @@ struct hybrid_topology *hybrid_topology__new(void) if (!perf_pmus__has_hybrid()) return NULL; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (pmu->is_core) - nr++; - } + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) + nr++; + if (nr == 0) return NULL; @@ -489,10 +488,7 @@ struct hybrid_topology *hybrid_topology__new(void) return NULL; tp->nr = nr; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!pmu->is_core) - continue; - + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { if (load_hybrid_node(&tp->nodes[i], pmu)) { hybrid_topology__delete(tp); return NULL; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e6d8ecd7a08e..2dde3ca20de5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1607,10 +1607,7 @@ static int write_pmu_caps(struct feat_fd *ff, */ if (perf_pmus__has_hybrid()) { pmu = NULL; - while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->is_core) - continue; - + while ((pmu = perf_pmus__scan_core(pmu))) { ret = __write_pmu_caps(ff, pmu, true); if (ret < 0) return ret; diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index 08ac3ea2e366..c5596230a308 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -136,10 +136,7 @@ int perf_mem_events__init(void) } else { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!pmu->is_core) - continue; - + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, pmu->name); e->supported |= perf_mem_event__supported(mnt, sysfs_name); @@ -176,10 +173,7 @@ static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e, char sysfs_name[100]; struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!pmu->is_core) - continue; - + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, pmu->name); if (!perf_mem_event__supported(mnt, sysfs_name)) { @@ -217,9 +211,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, return -1; } - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!pmu->is_core) - continue; + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { rec_argv[i++] = "-e"; s = perf_mem_events__name(j, pmu->name); if (s) { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index be544f948be2..e0c3f2037477 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -453,15 +453,12 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, const char *config_name = get_config_name(head_config); const char *metric_id = get_config_metric_id(head_config); - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + /* Legacy cache events are only supported by core PMUs. */ + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { LIST_HEAD(config_terms); struct perf_event_attr attr; int ret; - /* Skip unsupported PMUs. */ - if (!perf_pmu__supports_legacy_cache(pmu)) - continue; - if (parse_events__filter_pmu(parse_state, pmu)) continue; @@ -1481,12 +1478,10 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL, type, config, head_config); - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + /* Wildcards on numeric values are only supported by core PMUs. */ + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { int ret; - if (!perf_pmu__supports_wildcard_numeric(pmu)) - continue; - if (parse_events__filter_pmu(parse_state, pmu)) continue; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 05056305fb58..7102084dd3aa 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1427,21 +1427,11 @@ bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu) return pmu->is_core; } -bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu) -{ - return pmu->is_core; -} - bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) { return !is_pmu_hybrid(pmu->name); } -bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) -{ - return pmu->is_core; -} - bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) { struct perf_pmu_alias *alias; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index f1f3e8a2e00e..02fec0a7d4c8 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -223,9 +223,7 @@ void perf_pmu__del_formats(struct list_head *formats); bool is_pmu_core(const char *name); bool is_pmu_hybrid(const char *name); bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); -bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu); bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); -bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu); bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name); FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name); diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 4ef4fecd335f..de7fc36519c9 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -87,7 +87,7 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) } /* Add all pmus in sysfs to pmu list: */ -static void pmu_read_sysfs(void) +static void pmu_read_sysfs(bool core_only) { int fd; DIR *dir; @@ -104,6 +104,8 @@ static void pmu_read_sysfs(void) while ((dent = readdir(dir))) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; + if (core_only && !is_pmu_core(dent->d_name)) + continue; /* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */ perf_pmu__find2(fd, dent->d_name); } @@ -135,7 +137,7 @@ struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu) bool use_core_pmus = !pmu || pmu->is_core; if (!pmu) { - pmu_read_sysfs(); + pmu_read_sysfs(/*core_only=*/false); pmu = list_prepare_entry(pmu, &core_pmus, list); } if (use_core_pmus) { @@ -150,6 +152,18 @@ struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu) return NULL; } +struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu) +{ + if (!pmu) { + pmu_read_sysfs(/*core_only=*/true); + pmu = list_prepare_entry(pmu, &core_pmus, list); + } + list_for_each_entry_continue(pmu, &core_pmus, list) + return pmu; + + return NULL; +} + const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) { struct perf_pmu *pmu = NULL; @@ -176,10 +190,10 @@ int perf_pmus__num_mem_pmus(void) struct perf_pmu *pmu = NULL; int count = 0; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (perf_pmu__is_mem_pmu(pmu)) - count++; - } + /* All core PMUs are for mem events. */ + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) + count++; + return count; } @@ -421,8 +435,8 @@ bool perf_pmus__has_hybrid(void) if (!hybrid_scanned) { struct perf_pmu *pmu = NULL; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (pmu->is_core && is_pmu_hybrid(pmu->name)) { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + if (is_pmu_hybrid(pmu->name)) { has_hybrid = true; break; } diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 2a771d9f8da7..9de0222ed52b 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -11,6 +11,7 @@ struct perf_pmu *perf_pmus__find(const char *name); struct perf_pmu *perf_pmus__find_by_type(unsigned int type); struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); +struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 9cee7bb7a561..7a5f87392720 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -272,12 +272,11 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta struct perf_pmu *pmu = NULL; const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE]; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - /* - * Skip uncore PMUs for performance. PERF_TYPE_HW_CACHE type - * attributes can accept software PMUs in the extended type, so - * also skip. - */ + /* + * Only print core PMUs, skipping uncore for performance and + * PERF_TYPE_SOFTWARE that can succeed in opening legacy cache evenst. + */ + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { if (pmu->is_uncore || pmu->type == PERF_TYPE_SOFTWARE) continue; -- cgit v1.2.3 From 94f9eb95d954bee0149fd1ce84c239c9e09ae9d8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 27 May 2023 00:22:09 -0700 Subject: perf pmus: Remove perf_pmus__has_hybrid perf_pmus__has_hybrid was used to detect when there was >1 core PMU, this can be achieved with perf_pmus__num_core_pmus that doesn't depend upon is_pmu_hybrid and PMU name comparisons. When modifying the function calls take the opportunity to improve comments, enable/simplify tests that were previously failing for hybrid but now pass and to simplify generic code. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Athira Rajeev Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Huacai Chen Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kang Minchul Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Mike Leach Cc: Ming Wang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Sandipan Das Cc: Sean Christopherson Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Xing Zhengjun Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230527072210.2900565-34-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/hybrid.c | 2 +- tools/perf/arch/x86/util/evlist.c | 2 +- tools/perf/arch/x86/util/perf_regs.c | 2 +- tools/perf/builtin-record.c | 4 ++-- tools/perf/tests/attr.c | 9 ++++++++- tools/perf/tests/parse-metric.c | 7 ++----- tools/perf/tests/switch-tracking.c | 12 +----------- tools/perf/tests/topology.c | 14 ++------------ tools/perf/util/cputopo.c | 10 ++-------- tools/perf/util/evsel.c | 2 +- tools/perf/util/header.c | 2 +- tools/perf/util/mem-events.c | 18 +++++------------- tools/perf/util/metricgroup.c | 2 +- tools/perf/util/pmus.c | 18 ------------------ tools/perf/util/pmus.h | 1 - tools/perf/util/python.c | 4 ++-- tools/perf/util/stat-display.c | 2 +- 17 files changed, 31 insertions(+), 80 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c index e466735d68d5..eb152770f148 100644 --- a/tools/perf/arch/x86/tests/hybrid.c +++ b/tools/perf/arch/x86/tests/hybrid.c @@ -281,7 +281,7 @@ static int test_events(const struct evlist_test *events, int cnt) int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - if (!perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() == 1) return TEST_SKIP; return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events)); diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 8a6a0b98b976..cbd582182932 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -18,7 +18,7 @@ static int ___evlist__add_default_attrs(struct evlist *evlist, for (i = 0; i < nr_attrs; i++) event_attr_init(attrs + i); - if (!perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() == 1) return evlist__add_attrs(evlist, attrs, nr_attrs); for (i = 0; i < nr_attrs; i++) { diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c index 116384f19baf..8ad4112ad10c 100644 --- a/tools/perf/arch/x86/util/perf_regs.c +++ b/tools/perf/arch/x86/util/perf_regs.c @@ -292,7 +292,7 @@ uint64_t arch__intr_reg_mask(void) */ attr.sample_period = 1; - if (perf_pmus__has_hybrid()) { + if (perf_pmus__num_core_pmus() > 1) { struct perf_pmu *pmu = NULL; __u64 type = PERF_TYPE_RAW; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4b9212f75493..aec18db7ff23 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1294,7 +1294,7 @@ static int record__open(struct record *rec) * of waiting or event synthesis. */ if (opts->target.initial_delay || target__has_cpu(&opts->target) || - perf_pmus__has_hybrid()) { + perf_pmus__num_core_pmus() > 1) { pos = evlist__get_tracking_event(evlist); if (!evsel__is_dummy_event(pos)) { /* Set up dummy event. */ @@ -2193,7 +2193,7 @@ static void record__uniquify_name(struct record *rec) char *new_name; int ret; - if (!perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() == 1) return; evlist__for_each_entry(evlist, pos) { diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 674876e6c8e6..61186d0d1cfa 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -185,8 +185,15 @@ static int test__attr(struct test_suite *test __maybe_unused, int subtest __mayb char path_dir[PATH_MAX]; char *exec_path; - if (perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() > 1) { + /* + * TODO: Attribute tests hard code the PMU type. If there are >1 + * core PMU then each PMU will have a different type whic + * requires additional support. + */ + pr_debug("Skip test on hybrid systems"); return TEST_SKIP; + } /* First try development tree tests. */ if (!lstat("./tests", &st)) diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c index 1d6493a5a956..2c28fb50dc24 100644 --- a/tools/perf/tests/parse-metric.c +++ b/tools/perf/tests/parse-metric.c @@ -302,11 +302,8 @@ static int test__parse_metric(struct test_suite *test __maybe_unused, int subtes TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0); TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0); TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0); - - if (!perf_pmus__has_hybrid()) { - TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0); - TEST_ASSERT_VAL("test metric group", test_metric_group() == 0); - } + TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0); + TEST_ASSERT_VAL("test metric group", test_metric_group() == 0); return 0; } diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index cff6ab87b2f6..e52b031bedc5 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -375,17 +375,7 @@ static int test__switch_tracking(struct test_suite *test __maybe_unused, int sub cpu_clocks_evsel = evlist__last(evlist); /* Second event */ - if (perf_pmus__has_hybrid()) { - cycles = "cpu_core/cycles/u"; - err = parse_event(evlist, cycles); - if (err) { - cycles = "cpu_atom/cycles/u"; - pr_debug("Trying %s\n", cycles); - err = parse_event(evlist, cycles); - } - } else { - err = parse_event(evlist, cycles); - } + err = parse_event(evlist, cycles); if (err) { pr_debug("Failed to parse event %s\n", cycles); goto out_err; diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index 49e80d15420b..9dee63734e66 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -41,18 +41,8 @@ static int session_write_header(char *path) session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); - if (!perf_pmus__has_hybrid()) { - session->evlist = evlist__new_default(); - TEST_ASSERT_VAL("can't get evlist", session->evlist); - } else { - struct parse_events_error err; - - session->evlist = evlist__new(); - TEST_ASSERT_VAL("can't get evlist", session->evlist); - parse_events_error__init(&err); - parse_events(session->evlist, "cpu_core/cycles/", &err); - parse_events_error__exit(&err); - } + session->evlist = evlist__new_default(); + TEST_ASSERT_VAL("can't get evlist", session->evlist); perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); perf_header__set_feat(&session->header, HEADER_NRCPUS); diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c index 729142ec9a9a..81cfc85f4668 100644 --- a/tools/perf/util/cputopo.c +++ b/tools/perf/util/cputopo.c @@ -472,15 +472,9 @@ struct hybrid_topology *hybrid_topology__new(void) { struct perf_pmu *pmu = NULL; struct hybrid_topology *tp = NULL; - u32 nr = 0, i = 0; + int nr = perf_pmus__num_core_pmus(), i = 0; - if (!perf_pmus__has_hybrid()) - return NULL; - - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) - nr++; - - if (nr == 0) + if (nr <= 1) return NULL; tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index b4237fc713d5..ec2ce39d66d8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3140,7 +3140,7 @@ void evsel__zero_per_pkg(struct evsel *evsel) */ bool evsel__is_hybrid(const struct evsel *evsel) { - if (!perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() == 1) return false; return evsel->core.is_pmu_core; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2dde3ca20de5..0c69109c0a3b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1605,7 +1605,7 @@ static int write_pmu_caps(struct feat_fd *ff, * Write hybrid pmu caps first to maintain compatibility with * older perf tool. */ - if (perf_pmus__has_hybrid()) { + if (perf_pmus__num_core_pmus() > 1) { pmu = NULL; while ((pmu = perf_pmus__scan_core(pmu))) { ret = __write_pmu_caps(ff, pmu, true); diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index c5596230a308..be15aadb6b14 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -121,6 +121,7 @@ int perf_mem_events__init(void) for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { struct perf_mem_event *e = perf_mem_events__ptr(j); char sysfs_name[100]; + struct perf_pmu *pmu = NULL; /* * If the event entry isn't valid, skip initialization @@ -129,18 +130,9 @@ int perf_mem_events__init(void) if (!e->tag) continue; - if (!perf_pmus__has_hybrid()) { - scnprintf(sysfs_name, sizeof(sysfs_name), - e->sysfs_name, "cpu"); - e->supported = perf_mem_event__supported(mnt, sysfs_name); - } else { - struct perf_pmu *pmu = NULL; - - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - scnprintf(sysfs_name, sizeof(sysfs_name), - e->sysfs_name, pmu->name); - e->supported |= perf_mem_event__supported(mnt, sysfs_name); - } + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, pmu->name); + e->supported |= perf_mem_event__supported(mnt, sysfs_name); } if (e->supported) @@ -196,7 +188,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, if (!e->record) continue; - if (!perf_pmus__has_hybrid()) { + if (perf_pmus__num_core_pmus() == 1) { if (!e->supported) { pr_err("failed: event '%s' not supported\n", perf_mem_events__name(j, NULL)); diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 092ed6386a39..70ef2e23a710 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -274,7 +274,7 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids, const char *metric_id; struct evsel *ev; size_t ids_size, matched_events, i; - bool all_pmus = !strcmp(pmu, "all") || !perf_pmus__has_hybrid() || !is_pmu_hybrid(pmu); + bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu); *out_metric_events = NULL; ids_size = hashmap__size(ids); diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index bf927aed162e..53f11f6ce878 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -464,24 +464,6 @@ bool perf_pmus__have_event(const char *pname, const char *name) return pmu && perf_pmu__have_event(pmu, name); } -bool perf_pmus__has_hybrid(void) -{ - static bool hybrid_scanned, has_hybrid; - - if (!hybrid_scanned) { - struct perf_pmu *pmu = NULL; - - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - if (is_pmu_hybrid(pmu->name)) { - has_hybrid = true; - break; - } - } - hybrid_scanned = true; - } - return has_hybrid; -} - int perf_pmus__num_core_pmus(void) { static int count; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 27400a027d41..1e710720aec7 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -18,7 +18,6 @@ const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); int perf_pmus__num_mem_pmus(void); void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state); bool perf_pmus__have_event(const char *pname, const char *name); -bool perf_pmus__has_hybrid(void); int perf_pmus__num_core_pmus(void); #endif /* __PMUS_H */ diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 7173f6fcdc11..8de1b759bbaa 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -103,9 +103,9 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, return EOF; } -bool perf_pmus__has_hybrid(void) +int perf_pmus__num_core_pmus(void) { - return false; + return 1; } bool evsel__is_aux_event(const struct evsel *evsel __maybe_unused) diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 7ca69151136b..a2bbdc25d979 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -696,7 +696,7 @@ static bool evlist__has_hybrid(struct evlist *evlist) { struct evsel *evsel; - if (!perf_pmus__has_hybrid()) + if (perf_pmus__num_core_pmus() == 1) return false; evlist__for_each_entry(evlist, evsel) { -- cgit v1.2.3 From f50b8357f8955c899b704db88ffc180c5bf3f680 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 26 May 2023 11:34:00 -0700 Subject: perf test pmu: Avoid 2 static path arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid two static paths that contributed 8,192 bytes to .bss are only used duing the perf parse pmu test. This change helps FORTIFY triggering 2 warnings like: ``` tests/pmu.c: In function ‘test__pmu’: tests/pmu.c:121:43: error: ‘%s’ directive output may be truncated writing up to 4095 bytes into a region of size 4090 [-Werror=format-truncation=] 121 | snprintf(buf, sizeof(buf), "rm -f %s/*\n", dir); ``` So make buf a little larger. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Paolo Bonzini Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Ross Zwisler Cc: Sean Christopherson Cc: Steven Rostedt (VMware) Cc: Tiezhu Yang Cc: Yang Jihong Link: https://lore.kernel.org/r/20230526183401.2326121-16-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 3cf25f883df7..a4452639a3d4 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -86,17 +86,16 @@ static struct parse_events_term test_terms[] = { * Prepare format directory data, exported by kernel * at /sys/bus/event_source/devices//format. */ -static char *test_format_dir_get(void) +static char *test_format_dir_get(char *dir, size_t sz) { - static char dir[PATH_MAX]; unsigned int i; - snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); + snprintf(dir, sz, "/tmp/perf-pmu-test-format-XXXXXX"); if (!mkdtemp(dir)) return NULL; for (i = 0; i < ARRAY_SIZE(test_formats); i++) { - static char name[PATH_MAX]; + char name[PATH_MAX]; struct test_format *format = &test_formats[i]; FILE *file; @@ -118,12 +117,13 @@ static char *test_format_dir_get(void) /* Cleanup format directory. */ static int test_format_dir_put(char *dir) { - char buf[PATH_MAX]; - snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); + char buf[PATH_MAX + 20]; + + snprintf(buf, sizeof(buf), "rm -f %s/*\n", dir); if (system(buf)) return -1; - snprintf(buf, PATH_MAX, "rmdir %s\n", dir); + snprintf(buf, sizeof(buf), "rmdir %s\n", dir); return system(buf); } @@ -140,7 +140,8 @@ static struct list_head *test_terms_list(void) static int test__pmu(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - char *format = test_format_dir_get(); + char dir[PATH_MAX]; + char *format = test_format_dir_get(dir, sizeof(dir)); LIST_HEAD(formats); struct list_head *terms = test_terms_list(); int ret; -- cgit v1.2.3 From 27c9fcfc1e14ca9dff930d55cadd8ee4a34e4321 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 1 Jun 2023 01:29:54 -0700 Subject: perf test: Update parse-events expectations to test for multiple events With PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE events opening on multiple PMUs, the test expectations need updating to test for multiple events. TODOs are added to document existing hybrid perf bugs. Tested on hybrid alderlake and non-hybrid tigerlake. Signed-off-by: Ian Rogers Tested-by: Kan Liang Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Rob Herring Cc: Thomas Richter Cc: Xing Zhengjun Link: https://lore.kernel.org/r/20230601082954.754318-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 1108 +++++++++++++++++++++------------------ 1 file changed, 590 insertions(+), 518 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 9d05bc551791..bba1cd655a1d 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -324,13 +324,17 @@ static int test__checkevent_numeric_modifier(struct evlist *evlist) static int test__checkevent_symbolic_name_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct perf_evsel *evsel; - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == perf_pmus__num_core_pmus()); + perf_evlist__for_each_entry(&evlist->core, evsel) { + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + } return test__checkevent_symbolic_name(evlist); } @@ -620,24 +624,28 @@ static int test__checkevent_pmu_events(struct evlist *evlist) static int test__checkevent_pmu_events_mix(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); - - /* pmu-event:u */ - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong exclude_user", - !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", - evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); - TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + struct evsel *evsel = NULL; + /* + * The wild card event will be opened at least once, but it may be + * opened on each core PMU. + */ + TEST_ASSERT_VAL("wrong number of entries", evlist->core.nr_entries >= 2); + for (int i = 0; i < evlist->core.nr_entries - 1; i++) { + evsel = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + /* pmu-event:u */ + TEST_ASSERT_VAL("wrong exclude_user", + !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", + evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); + TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + } /* cpu/pmu-event/u*/ evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || - strcmp(evsel->pmu_name, "cpu")); + TEST_ASSERT_VAL("wrong type", evsel__find_pmu(evsel)->is_core); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -734,181 +742,207 @@ static int test__group1(struct evlist *evlist) { struct evsel *evsel, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); - - /* instructions:k */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* cycles:upp */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (perf_pmus__num_core_pmus() * 2)); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* instructions:k */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + /* cycles:upp */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } return TEST_OK; } static int test__group2(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel, *leader = NULL; - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus() + 1)); + /* + * TODO: Currently the software event won't be grouped with the hardware + * event except for 1 PMU. + */ TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); - /* faults + :ku modifier */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_PAGE_FAULTS)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* cache-references + :u modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_REFERENCES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* cycles:k */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.type == PERF_TYPE_SOFTWARE) { + /* faults + :ku modifier */ + leader = evsel; + TEST_ASSERT_VAL("wrong config", + test_config(evsel, PERF_COUNT_SW_PAGE_FAULTS)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + continue; + } + if (evsel->core.attr.type == PERF_TYPE_HARDWARE && + test_config(evsel, PERF_COUNT_HW_CACHE_REFERENCES)) { + /* cache-references + :u modifier */ + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + if (evsel__has_leader(evsel, leader)) + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + continue; + } + /* cycles:k */ + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } return TEST_OK; } #ifdef HAVE_LIBTRACEEVENT static int test__group3(struct evlist *evlist __maybe_unused) { - struct evsel *evsel, *leader; + struct evsel *evsel, *group1_leader = NULL, *group2_leader = NULL; - TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus() + 2)); + /* + * Currently the software event won't be grouped with the hardware event + * except for 1 PMU. This means there are always just 2 groups + * regardless of the number of core PMUs. + */ TEST_ASSERT_VAL("wrong number of groups", 2 == evlist__nr_groups(evlist)); - /* group1 syscalls:sys_enter_openat:H */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong sample_type", - PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type); - TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->core.attr.sample_period); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong group name", - !strcmp(leader->group_name, "group1")); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* group1 cycles:kppp */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 3); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* group2 cycles + G modifier */ - evsel = leader = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong group name", - !strcmp(leader->group_name, "group2")); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* group2 1:3 + G modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, 3)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* instructions:u */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) { + /* group1 syscalls:sys_enter_openat:H */ + group1_leader = evsel; + TEST_ASSERT_VAL("wrong sample_type", + evsel->core.attr.sample_type == PERF_TP_SAMPLE_TYPE); + TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->core.attr.sample_period); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong group name", !strcmp(evsel->group_name, "group1")); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + continue; + } + if (evsel->core.attr.type == PERF_TYPE_HARDWARE && + test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)) { + if (evsel->core.attr.exclude_user) { + /* group1 cycles:kppp */ + TEST_ASSERT_VAL("wrong exclude_user", + evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", + !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", + evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", + !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", + evsel->core.attr.precise_ip == 3); + if (evsel__has_leader(evsel, group1_leader)) { + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong group_idx", + evsel__group_idx(evsel) == 1); + } + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } else { + /* group2 cycles + G modifier */ + group2_leader = evsel; + TEST_ASSERT_VAL("wrong exclude_kernel", + !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", + !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", + !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", + evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + if (evsel->core.nr_members == 2) { + TEST_ASSERT_VAL("wrong group_idx", + evsel__group_idx(evsel) == 0); + } + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } + continue; + } + if (evsel->core.attr.type == 1) { + /* group2 1:3 + G modifier */ + TEST_ASSERT_VAL("wrong config", test_config(evsel, 3)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + if (evsel__has_leader(evsel, group2_leader)) + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + continue; + } + /* instructions:u */ + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } return TEST_OK; } #endif @@ -917,402 +951,435 @@ static int test__group4(struct evlist *evlist __maybe_unused) { struct evsel *evsel, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); - - /* cycles:u + p */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* instructions:kp + p */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (perf_pmus__num_core_pmus() * 2)); + TEST_ASSERT_VAL("wrong number of groups", + perf_pmus__num_core_pmus() == evlist__nr_groups(evlist)); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles:u + p */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + + /* instructions:kp + p */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } return TEST_OK; } static int test__group5(struct evlist *evlist __maybe_unused) { - struct evsel *evsel, *leader; - - TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 2 == evlist__nr_groups(evlist)); - - /* cycles + G */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + struct evsel *evsel = NULL, *leader; - /* instructions + G */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (5 * perf_pmus__num_core_pmus())); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == (2 * perf_pmus__num_core_pmus())); - /* cycles:G */ - evsel = leader = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - - /* instructions:G */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles + G */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); - /* cycles */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + /* instructions + G */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + } + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles:G */ + evsel = leader = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); + /* instructions:G */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + } + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + } return TEST_OK; } static int test__group_gh1(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); - /* cycles + :H group modifier */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - - /* cache-misses:G + :H group modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles + :H group modifier */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + /* cache-misses:G + :H group modifier */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + } return TEST_OK; } static int test__group_gh2(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); - - /* cycles + :G group modifier */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); - /* cache-misses:H + :G group modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles + :G group modifier */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + /* cache-misses:H + :G group modifier */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + } return TEST_OK; } static int test__group_gh3(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); - /* cycles:G + :u group modifier */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - - /* cache-misses:H + :u group modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles:G + :u group modifier */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + /* cache-misses:H + :u group modifier */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + } return TEST_OK; } static int test__group_gh4(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + TEST_ASSERT_VAL("wrong number of groups", + evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); - /* cycles:G + :uG group modifier */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - - /* cache-misses:H + :uG group modifier */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles:G + :uG group modifier */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); + /* cache-misses:H + :uG group modifier */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + } return TEST_OK; } static int test__leader_sample1(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); - /* cycles - sampling group leader */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); - - /* cache-misses - not sampling */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles - sampling group leader */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); - /* branch-misses - not sampling */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + /* cache-misses - not sampling */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + /* branch-misses - not sampling */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + } return TEST_OK; } static int test__leader_sample2(struct evlist *evlist __maybe_unused) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); - /* instructions - sampling group leader */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); - - /* branch-misses - not sampling */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* instructions - sampling group leader */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_INSTRUCTIONS)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + /* branch-misses - not sampling */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); + } return TEST_OK; } static int test__checkevent_pinned_modifier(struct evlist *evlist) { - struct evsel *evsel = evlist__first(evlist); + struct evsel *evsel = NULL; - TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == perf_pmus__num_core_pmus()); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + evsel = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned); + } return test__checkevent_symbolic_name(evlist); } static int test__pinned_group(struct evlist *evlist) { - struct evsel *evsel, *leader; + struct evsel *evsel = NULL, *leader; - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); - /* cycles - group leader */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles - group leader */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + /* TODO: The group modifier is not copied to the split group leader. */ + if (perf_pmus__num_core_pmus() == 1) + TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned); - /* cache-misses - can not be pinned, but will go on with the leader */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); - - /* branch-misses - ditto */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); - TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); + /* cache-misses - can not be pinned, but will go on with the leader */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); + /* branch-misses - ditto */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); + TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned); + } return TEST_OK; } @@ -1331,29 +1398,33 @@ static int test__checkevent_exclusive_modifier(struct evlist *evlist) static int test__exclusive_group(struct evlist *evlist) { - struct evsel *evsel, *leader; - - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); + struct evsel *evsel = NULL, *leader; - /* cycles - group leader */ - evsel = leader = evlist__first(evlist); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); - TEST_ASSERT_VAL("wrong group name", !evsel->group_name); - TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); - TEST_ASSERT_VAL("wrong exclusive", evsel->core.attr.exclusive); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); - /* cache-misses - can not be pinned, but will go on with the leader */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); - TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + /* cycles - group leader */ + evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES)); + TEST_ASSERT_VAL("wrong group name", !evsel->group_name); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + /* TODO: The group modifier is not copied to the split group leader. */ + if (perf_pmus__num_core_pmus() == 1) + TEST_ASSERT_VAL("wrong exclusive", evsel->core.attr.exclusive); - /* branch-misses - ditto */ - evsel = evsel__next(evsel); - TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); - TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + /* cache-misses - can not be pinned, but will go on with the leader */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CACHE_MISSES)); + TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + /* branch-misses - ditto */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_MISSES)); + TEST_ASSERT_VAL("wrong exclusive", !evsel->core.attr.exclusive); + } return TEST_OK; } static int test__checkevent_breakpoint_len(struct evlist *evlist) @@ -1403,7 +1474,8 @@ static int test__checkevent_precise_max_modifier(struct evlist *evlist) { struct evsel *evsel = evlist__first(evlist); - TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + TEST_ASSERT_VAL("wrong number of entries", + evlist->core.nr_entries == (1 + perf_pmus__num_core_pmus())); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_TASK_CLOCK)); return TEST_OK; -- cgit v1.2.3 From 6f765bbbfb3c8c5993796402a3cba311e9506eed Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 18 May 2023 23:37:18 -0700 Subject: perf expr: Make the evaluation of & and | logical and lazy Currently the & and | operators are only used in metric thresholds like (from the tma_retiring metric): tma_retiring > 0.7 | tma_heavy_operations > 0.1 Thresholds are always computed when present, but a lack of events may mean the threshold can't be computed. This happens with the option --metric-no-threshold for say the metric tma_retiring on Tigerlake model CPUs. To fully compute the threshold tma_heavy_operations is needed and it needs the extra events of IDQ.MS_UOPS, UOPS_DECODED.DEC0, cpu/UOPS_DECODED.DEC0,cmask=1/ and IDQ.MITE_UOPS. So --metric-no-threshold is a useful option to reduce the number of events needed and potentially multiplexing of events. Rather than just fail threshold computations like this, we may know a result from just the left or right-hand side. So, for tma_retiring if its value is "> 0.7" we know it is over the threshold. This allows the metric to have the threshold coloring, when possible, without all the counters being programmed. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Tested-by: Kan Liang Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Alexander Shishkin Cc: Andi Kleen Cc: Andrii Nakryiko Cc: Caleb Biggers Cc: Eduard Zingerman Cc: Edward Baker Cc: Ingo Molnar Cc: James Clark Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Cc: Stephane Eranian Cc: Weilin Wang Link: https://lore.kernel.org/r/20230519063719.1029596-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/expr.c | 40 +++++++++++++++++++++++ tools/perf/util/expr.y | 86 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 17 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index 733ead151c63..3d01eb5e2512 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -185,6 +185,46 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u NULL, ctx) == 0); TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0); + /* The expression is a constant 0.0 without needing to evaluate EVENT1. */ + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("0 & EVENT1 > 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("EVENT1 > 0 & 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("1 & EVENT1 > 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("EVENT1 > 0 & 1", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); + + /* The expression is a constant 1.0 without needing to evaluate EVENT1. */ + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("1 | EVENT1 > 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("EVENT1 > 0 | 1", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("0 | EVENT1 > 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); + expr__ctx_clear(ctx); + TEST_ASSERT_VAL("find ids", + expr__find_ids("EVENT1 > 0 | 0", NULL, ctx) == 0); + TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); + /* Test toplogy constants appear well ordered. */ expr__ctx_clear(ctx); TEST_ASSERT_VAL("#num_cpus", expr__parse(&num_cpus, ctx, "#num_cpus") == 0); diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index 4ce931cccb63..f04963eb6be0 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -123,20 +123,6 @@ static struct ids handle_id(struct expr_parse_ctx *ctx, char *id, * constant value using OP. Its invariant that there are no ids. If computing * ids for non-constants union the set of IDs that must be computed. */ -#define BINARY_LONG_OP(RESULT, OP, LHS, RHS) \ - if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ - assert(LHS.ids == NULL); \ - assert(RHS.ids == NULL); \ - if (isnan(LHS.val) || isnan(RHS.val)) { \ - RESULT.val = NAN; \ - } else { \ - RESULT.val = (long)LHS.val OP (long)RHS.val; \ - } \ - RESULT.ids = NULL; \ - } else { \ - RESULT = union_expr(LHS, RHS); \ - } - #define BINARY_OP(RESULT, OP, LHS, RHS) \ if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ assert(LHS.ids == NULL); \ @@ -213,9 +199,75 @@ expr: NUMBER } | ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } | SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } -| expr '|' expr { BINARY_LONG_OP($$, |, $1, $3); } -| expr '&' expr { BINARY_LONG_OP($$, &, $1, $3); } -| expr '^' expr { BINARY_LONG_OP($$, ^, $1, $3); } +| expr '|' expr +{ + if (is_const($1.val) && is_const($3.val)) { + assert($1.ids == NULL); + assert($3.ids == NULL); + $$.ids = NULL; + $$.val = (fpclassify($1.val) == FP_ZERO && fpclassify($3.val) == FP_ZERO) ? 0 : 1; + } else if (is_const($1.val)) { + assert($1.ids == NULL); + if (fpclassify($1.val) == FP_ZERO) { + $$ = $3; + } else { + $$.val = 1; + $$.ids = NULL; + ids__free($3.ids); + } + } else if (is_const($3.val)) { + assert($3.ids == NULL); + if (fpclassify($3.val) == FP_ZERO) { + $$ = $1; + } else { + $$.val = 1; + $$.ids = NULL; + ids__free($1.ids); + } + } else { + $$ = union_expr($1, $3); + } +} +| expr '&' expr +{ + if (is_const($1.val) && is_const($3.val)) { + assert($1.ids == NULL); + assert($3.ids == NULL); + $$.val = (fpclassify($1.val) != FP_ZERO && fpclassify($3.val) != FP_ZERO) ? 1 : 0; + $$.ids = NULL; + } else if (is_const($1.val)) { + assert($1.ids == NULL); + if (fpclassify($1.val) != FP_ZERO) { + $$ = $3; + } else { + $$.val = 0; + $$.ids = NULL; + ids__free($3.ids); + } + } else if (is_const($3.val)) { + assert($3.ids == NULL); + if (fpclassify($3.val) != FP_ZERO) { + $$ = $1; + } else { + $$.val = 0; + $$.ids = NULL; + ids__free($1.ids); + } + } else { + $$ = union_expr($1, $3); + } +} +| expr '^' expr +{ + if (is_const($1.val) && is_const($3.val)) { + assert($1.ids == NULL); + assert($3.ids == NULL); + $$.val = (fpclassify($1.val) == FP_ZERO) != (fpclassify($3.val) == FP_ZERO) ? 1 : 0; + $$.ids = NULL; + } else { + $$ = union_expr($1, $3); + } +} | expr '<' expr { BINARY_OP($$, <, $1, $3); } | expr '>' expr { BINARY_OP($$, >, $1, $3); } | expr '+' expr { BINARY_OP($$, +, $1, $3); } -- cgit v1.2.3 From f0617f526cb0c482dd46ed798db28d3991f6f872 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 25 May 2023 11:29:02 +0300 Subject: perf parse: Allow config terms with breakpoints Add config terms to the parsing of breakpoint events. Extend "Test event parsing" to also cover using a confg term. This makes breakpoint events consistent with other events which already support config terms. Example: $ cat dr_test.c #include #include void func0(void) { } int main() { printf("func0 %p\n", &func0); while (1) { func0(); usleep(100000); } return 0; } $ gcc -g -O0 -o dr_test dr_test.c $ ./dr_test & [2] 19646 func0 0x55feb98dd169 $ perf record -e mem:0x55feb98dd169:x/name=breakpoint/ -p 19646 -- sleep 0.5 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data (5 samples) ] $ perf script dr_test 19646 5632.956628: 1 breakpoint: 55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test) dr_test 19646 5633.056866: 1 breakpoint: 55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test) dr_test 19646 5633.157084: 1 breakpoint: 55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test) dr_test 19646 5633.257309: 1 breakpoint: 55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test) dr_test 19646 5633.357532: 1 breakpoint: 55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test) $ sudo perf test "Test event parsing" 6: Parse event definition strings : 6.1: Test event parsing : Ok $ sudo perf test -v "Test event parsing" |& grep mem running test 8 'mem:0' running test 9 'mem:0:x' running test 10 'mem:0:r' running test 11 'mem:0:w' running test 19 'mem:0:u' running test 20 'mem:0:x:k' running test 21 'mem:0:r:hp' running test 22 'mem:0:w:up' running test 26 'mem:0:rw' running test 27 'mem:0:rw:kp' running test 42 'mem:0/1' running test 43 'mem:0/2:w' running test 44 'mem:0/4:rw:u' running test 58 'mem:0/name=breakpoint/' running test 59 'mem:0:x/name=breakpoint/' running test 60 'mem:0:r/name=breakpoint/' running test 61 'mem:0:w/name=breakpoint/' running test 62 'mem:0/name=breakpoint/u' running test 63 'mem:0:x/name=breakpoint/k' running test 64 'mem:0:r/name=breakpoint/hp' running test 65 'mem:0:w/name=breakpoint/up' running test 66 'mem:0:rw/name=breakpoint/' running test 67 'mem:0:rw/name=breakpoint/kp' running test 68 'mem:0/1/name=breakpoint/' running test 69 'mem:0/2:w/name=breakpoint/' running test 70 'mem:0/4:rw/name=breakpoint/u' running test 71 'mem:0/1/name=breakpoint1/,mem:0/4:rw/name=breakpoint2/' Committer notes: Folded follow up patch (see 2nd link below) to address warnings about unused tokens: perf tools: Suppress bison unused value warnings Patch "perf tools: Allow config terms with breakpoints" introduced parse tokens for colons and slashes within breakpoint parsing to prevent mix up with colons and slashes related to config terms. The token values are not needed but introduce bison "unused value" warnings. Suppress those warnings. Committer testing: # cat ~acme/c/mem_breakpoint.c #include #include void func1(void) { } void func2(void) { } void func3(void) { } void func4(void) { } void func5(void) { } int main() { printf("func1 %p\n", &func1); printf("func2 %p\n", &func2); printf("func3 %p\n", &func3); printf("func4 %p\n", &func4); printf("func5 %p\n", &func5); while (1) { func1(); func2(); func3(); func4(); func5(); usleep(100000); } return 0; } # ~acme/c/mem_breakpoint & [1] 3186153 func1 0x401136 func2 0x40113d func3 0x401144 func4 0x40114b func5 0x401152 # Trying to watch the first 4 functions for eXecutable access: # perf record -e mem:0x401136:x/name=breakpoint1/,mem:0x40113d:x/name=breakpoint2/,mem:0x401144:x/name=breakpoint3/,mem:0x40114b:x/name=breakpoint4/ -p 3186153 -- sleep 0.5 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.026 MB perf.data (20 samples) ] [root@five ~]# perf script mem_breakpoint 3186153 131612.864793: 1 breakpoint1: 401136 func1+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.864795: 1 breakpoint2: 40113d func2+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.864796: 1 breakpoint3: 401144 func3+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.864797: 1 breakpoint4: 40114b func4+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.964868: 1 breakpoint1: 401136 func1+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.964870: 1 breakpoint2: 40113d func2+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.964871: 1 breakpoint3: 401144 func3+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131612.964872: 1 breakpoint4: 40114b func4+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.064945: 1 breakpoint1: 401136 func1+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.064948: 1 breakpoint2: 40113d func2+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.064948: 1 breakpoint3: 401144 func3+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.064949: 1 breakpoint4: 40114b func4+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.165024: 1 breakpoint1: 401136 func1+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.165026: 1 breakpoint2: 40113d func2+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.165027: 1 breakpoint3: 401144 func3+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.165028: 1 breakpoint4: 40114b func4+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.265103: 1 breakpoint1: 401136 func1+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.265105: 1 breakpoint2: 40113d func2+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.265106: 1 breakpoint3: 401144 func3+0x0 (/var/home/acme/c/mem_breakpoint) mem_breakpoint 3186153 131613.265107: 1 breakpoint4: 40114b func4+0x0 (/var/home/acme/c/mem_breakpoint) # Then all the 5 functions: # perf record -e mem:0x401136:x/name=breakpoint1/,mem:0x40113d:x/name=breakpoint2/,mem:0x401144:x/name=breakpoint3/,mem:0x40114b:x/name=breakpoint4/,mem:0x401152:x/name=breakpoint5/ -p 3186153 -- sleep 0.5 Error: The sys_perf_event_open() syscall returned with 28 (No space left on device) for event (breakpoint5). /bin/dmesg | grep -i perf may provide additional information. # grep -m1 'model name' /proc/cpuinfo model name : AMD Ryzen 9 5950X 16-Core Processor # Reviewed-by: Ian Rogers Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20230525082902.25332-2-adrian.hunter@intel.com Link: https://lore.kernel.org/r/f7228dc9-fe18-a8e3-7d3f-52922e0e1113@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 157 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/parse-events.c | 23 +++++- tools/perf/util/parse-events.h | 10 ++- tools/perf/util/parse-events.l | 23 +++++- tools/perf/util/parse-events.y | 49 ++++++++----- 5 files changed, 235 insertions(+), 27 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index bba1cd655a1d..133218e51ab4 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -486,6 +486,93 @@ static int test__checkevent_breakpoint_rw_modifier(struct evlist *evlist) return test__checkevent_breakpoint_rw(evlist); } +static int test__checkevent_breakpoint_modifier_name(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong name", + !strcmp(evsel__name(evsel), "breakpoint")); + + return test__checkevent_breakpoint(evlist); +} + +static int test__checkevent_breakpoint_x_modifier_name(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong name", + !strcmp(evsel__name(evsel), "breakpoint")); + + return test__checkevent_breakpoint_x(evlist); +} + +static int test__checkevent_breakpoint_r_modifier_name(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong name", + !strcmp(evsel__name(evsel), "breakpoint")); + + return test__checkevent_breakpoint_r(evlist); +} + +static int test__checkevent_breakpoint_w_modifier_name(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong name", + !strcmp(evsel__name(evsel), "breakpoint")); + + return test__checkevent_breakpoint_w(evlist); +} + +static int test__checkevent_breakpoint_rw_modifier_name(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); + TEST_ASSERT_VAL("wrong name", + !strcmp(evsel__name(evsel), "breakpoint")); + + return test__checkevent_breakpoint_rw(evlist); +} + +static int test__checkevent_breakpoint_2_events(struct evlist *evlist) +{ + struct evsel *evsel = evlist__first(evlist); + + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); + + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "breakpoint1")); + + evsel = evsel__next(evsel); + + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); + TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "breakpoint2")); + + return TEST_OK; +} + static int test__checkevent_pmu(struct evlist *evlist) { @@ -1973,6 +2060,76 @@ static const struct evlist_test test__events[] = { .check = test__term_equal_legacy, /* 9 */ }, + { + .name = "mem:0/name=breakpoint/", + .check = test__checkevent_breakpoint, + /* 0 */ + }, + { + .name = "mem:0:x/name=breakpoint/", + .check = test__checkevent_breakpoint_x, + /* 1 */ + }, + { + .name = "mem:0:r/name=breakpoint/", + .check = test__checkevent_breakpoint_r, + /* 2 */ + }, + { + .name = "mem:0:w/name=breakpoint/", + .check = test__checkevent_breakpoint_w, + /* 3 */ + }, + { + .name = "mem:0/name=breakpoint/u", + .check = test__checkevent_breakpoint_modifier_name, + /* 4 */ + }, + { + .name = "mem:0:x/name=breakpoint/k", + .check = test__checkevent_breakpoint_x_modifier_name, + /* 5 */ + }, + { + .name = "mem:0:r/name=breakpoint/hp", + .check = test__checkevent_breakpoint_r_modifier_name, + /* 6 */ + }, + { + .name = "mem:0:w/name=breakpoint/up", + .check = test__checkevent_breakpoint_w_modifier_name, + /* 7 */ + }, + { + .name = "mem:0:rw/name=breakpoint/", + .check = test__checkevent_breakpoint_rw, + /* 8 */ + }, + { + .name = "mem:0:rw/name=breakpoint/kp", + .check = test__checkevent_breakpoint_rw_modifier_name, + /* 9 */ + }, + { + .name = "mem:0/1/name=breakpoint/", + .check = test__checkevent_breakpoint_len, + /* 0 */ + }, + { + .name = "mem:0/2:w/name=breakpoint/", + .check = test__checkevent_breakpoint_len_w, + /* 1 */ + }, + { + .name = "mem:0/4:rw/name=breakpoint/u", + .check = test__checkevent_breakpoint_len_rw_modifier, + /* 2 */ + }, + { + .name = "mem:0/1/name=breakpoint1/,mem:0/4:rw/name=breakpoint2/", + .check = test__checkevent_breakpoint_2_events, + /* 3 */ + }, }; static const struct evlist_test test__events_pmu[] = { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 629f7bd9fd59..2d36cadf35ec 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -946,10 +946,14 @@ do { \ return 0; } -int parse_events_add_breakpoint(struct list_head *list, int *idx, - u64 addr, char *type, u64 len) +int parse_events_add_breakpoint(struct parse_events_state *parse_state, + struct list_head *list, + u64 addr, char *type, u64 len, + struct list_head *head_config __maybe_unused) { struct perf_event_attr attr; + LIST_HEAD(config_terms); + const char *name; memset(&attr, 0, sizeof(attr)); attr.bp_addr = addr; @@ -970,8 +974,19 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, attr.type = PERF_TYPE_BREAKPOINT; attr.sample_period = 1; - return add_event(list, idx, &attr, /*name=*/NULL, /*mertic_id=*/NULL, - /*config_terms=*/NULL); + if (head_config) { + if (config_attr(&attr, head_config, parse_state->error, + config_term_common)) + return -EINVAL; + + if (get_config_terms(head_config, &config_terms)) + return -ENOMEM; + } + + name = get_config_name(head_config); + + return add_event(list, &parse_state->idx, &attr, name, /*mertic_id=*/NULL, + &config_terms); } static int check_type_val(struct parse_events_term *term, diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 2021fe145410..5fdc1f33f57e 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -185,8 +185,10 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, struct parse_events_state *parse_state, struct list_head *head_config); int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config); -int parse_events_add_breakpoint(struct list_head *list, int *idx, - u64 addr, char *type, u64 len); +int parse_events_add_breakpoint(struct parse_events_state *parse_state, + struct list_head *list, + u64 addr, char *type, u64 len, + struct list_head *head_config); int parse_events_add_pmu(struct parse_events_state *parse_state, struct list_head *list, char *name, struct list_head *head_config, @@ -226,6 +228,10 @@ void parse_events_error__handle(struct parse_events_error *err, int idx, void parse_events_error__print(struct parse_events_error *err, const char *event); +static inline void parse_events_unused_value(const void *x __maybe_unused) +{ +} + #ifdef HAVE_LIBELF_SUPPORT /* * If the probe point starts with '%', diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 6deb70c25984..7629af3d5c7c 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -190,11 +190,16 @@ name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!\-]* name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\'] name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]* drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)? -/* If you add a modifier you need to update check_modifier() */ +/* + * If you add a modifier you need to update check_modifier(). + * Also, the letters in modifier_event must not be in modifier_bp. + */ modifier_event [ukhpPGHSDIWeb]+ modifier_bp [rwx]{1,3} lc_type (L1-dcache|l1-d|l1d|L1-data|L1-icache|l1-i|l1i|L1-instruction|LLC|L2|dTLB|d-tlb|Data-TLB|iTLB|i-tlb|Instruction-TLB|branch|branches|bpu|btb|bpc|node) lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss) +digit [0-9] +non_digit [^0-9] %% @@ -304,8 +309,20 @@ r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } { {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } -: { return ':'; } -"/" { return '/'; } + /* + * The colon before memory access modifiers can get mixed up with the + * colon before event modifiers. Fortunately none of the option letters + * are the same, so trailing context can be used disambiguate the two + * cases. + */ +":"/{modifier_bp} { return str(yyscanner, PE_BP_COLON); } + /* + * The slash before memory length can get mixed up with the slash before + * config terms. Fortunately config terms do not start with a numeric + * digit, so trailing context can be used disambiguate the two cases. + */ +"/"/{digit} { return str(yyscanner, PE_BP_SLASH); } +"/"/{non_digit} { BEGIN(config); return '/'; } {num_dec} { return value(yyscanner, 10); } {num_hex} { return value(yyscanner, 16); } /* diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index f96afb0edd0c..0c3d086cc22a 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -59,7 +59,7 @@ static void free_list_evsel(struct list_head* list_evsel) %token PE_EVENT_NAME %token PE_RAW PE_NAME %token PE_BPF_OBJECT PE_BPF_SOURCE -%token PE_MODIFIER_EVENT PE_MODIFIER_BP +%token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH %token PE_LEGACY_CACHE %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP %token PE_ERROR @@ -80,6 +80,8 @@ static void free_list_evsel(struct list_head* list_evsel) %type PE_LEGACY_CACHE %type PE_MODIFIER_EVENT %type PE_MODIFIER_BP +%type PE_BP_COLON +%type PE_BP_SLASH %type PE_EVENT_NAME %type PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %type PE_DRV_CFG_TERM @@ -275,7 +277,7 @@ event_def event_def: event_pmu | event_legacy_symbol | event_legacy_cache sep_dc | - event_legacy_mem | + event_legacy_mem sep_dc | event_legacy_tracepoint sep_dc | event_legacy_numeric sep_dc | event_legacy_raw sep_dc | @@ -503,16 +505,19 @@ PE_LEGACY_CACHE opt_event_config } event_legacy_mem: -PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc +PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event_config { - struct parse_events_state *parse_state = _parse_state; struct list_head *list; int err; + parse_events_unused_value(&$3); + parse_events_unused_value(&$5); + list = alloc_list(); ABORT_ON(!list); - err = parse_events_add_breakpoint(list, &parse_state->idx, - $2, $6, $4); + err = parse_events_add_breakpoint(_parse_state, list, + $2, $6, $4, $7); + parse_events_terms__delete($7); free($6); if (err) { free(list); @@ -521,31 +526,37 @@ PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc $$ = list; } | -PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc +PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE opt_event_config { - struct parse_events_state *parse_state = _parse_state; struct list_head *list; + int err; + + parse_events_unused_value(&$3); list = alloc_list(); ABORT_ON(!list); - if (parse_events_add_breakpoint(list, &parse_state->idx, - $2, NULL, $4)) { + err = parse_events_add_breakpoint(_parse_state, list, + $2, NULL, $4, $5); + parse_events_terms__delete($5); + if (err) { free(list); YYABORT; } $$ = list; } | -PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc +PE_PREFIX_MEM PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event_config { - struct parse_events_state *parse_state = _parse_state; struct list_head *list; int err; + parse_events_unused_value(&$3); + list = alloc_list(); ABORT_ON(!list); - err = parse_events_add_breakpoint(list, &parse_state->idx, - $2, $4, 0); + err = parse_events_add_breakpoint(_parse_state, list, + $2, $4, 0, $5); + parse_events_terms__delete($5); free($4); if (err) { free(list); @@ -554,15 +565,17 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc $$ = list; } | -PE_PREFIX_MEM PE_VALUE sep_dc +PE_PREFIX_MEM PE_VALUE opt_event_config { - struct parse_events_state *parse_state = _parse_state; struct list_head *list; + int err; list = alloc_list(); ABORT_ON(!list); - if (parse_events_add_breakpoint(list, &parse_state->idx, - $2, NULL, 0)) { + err = parse_events_add_breakpoint(_parse_state, list, + $2, NULL, 0, $3); + parse_events_terms__delete($3); + if (err) { free(list); YYABORT; } -- cgit v1.2.3 From dcf7a17714e63d6e38c8c9612fee2dbc2e64c57e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:24:00 -0700 Subject: perf test: Add test of libpfm4 events $ ./perf test -v 102 102: perf all libpfm4 events test : --- start --- test child forked, pid 3030994 Testing ix86arch::UNHALTED_CORE_CYCLES Testing ix86arch::INSTRUCTION_RETIRED Testing ix86arch::UNHALTED_REFERENCE_CYCLES Testing ix86arch::LLC_REFERENCES Testing ix86arch::LLC_MISSES Testing ix86arch::BRANCH_INSTRUCTIONS_RETIRED Testing ix86arch::MISPREDICTED_BRANCH_RETIRED Testing perf_raw::r0000 Testing icl::UNHALTED_CORE_CYCLES Testing icl::UNHALTED_REFERENCE_CYCLES ... test child finished with 0 ---- end ---- perf all libpfm4 events test: Ok Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20230608232400.3056312-4-namhyung@kernel.org Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat_all_pfm.sh | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 tools/perf/tests/shell/stat_all_pfm.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat_all_pfm.sh b/tools/perf/tests/shell/stat_all_pfm.sh new file mode 100755 index 000000000000..4d004f777a6e --- /dev/null +++ b/tools/perf/tests/shell/stat_all_pfm.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# perf all libpfm4 events test +# SPDX-License-Identifier: GPL-2.0 + +if perf version --build-options | grep HAVE_LIBPFM | grep -q OFF +then + echo "Skipping, no libpfm4 support" + exit 2 +fi + +err=0 +for p in $(perf list --raw-dump pfm) +do + if echo "$p" | grep -q unc_ + then + echo "Skipping uncore event '$p' that may require additional options." + continue + fi + echo "Testing $p" + result=$(perf stat --pfm-events "$p" true 2>&1) + x=$? + if echo "$result" | grep -q "failed to parse event $p : invalid or missing unit mask" + then + continue + fi + if test "$x" -ne "0" + then + echo "Unexpected exit code '$x'" + err=1 + fi + if ! echo "$result" | grep -q "$p" && ! echo "$result" | grep -q "" + then + # We failed to see the event and it is supported. Possibly the workload was + # too small so retry with something longer. + result=$(perf stat --pfm-events "$p" perf bench internals synthesize 2>&1) + x=$? + if test "$x" -ne "0" + then + echo "Unexpected exit code '$x'" + err=1 + fi + if ! echo "$result" | grep -q "$p" + then + echo "Event '$p' not printed in:" + echo "$result" + err=1 + fi + fi +done + +exit "$err" -- cgit v1.2.3 From ee84a3032b74055feed192a727e872b0a18d1140 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:28:00 -0700 Subject: perf thread: Add accessor functions for thread Using accessors will make it easier to add reference count checking in later patches. Committer notes: thread->nsinfo wasn't wrapped as it is used together with nsinfo__zput(), where does a trick to set the field with a refcount being dropped to NULL, and that doesn't work well with using thread__nsinfo(thread), that loses the &thread->nsinfo pointer. When refcount checking is added to 'struct thread', later in this series, nsinfo__zput(RC_CHK_ACCESS(thread)->nsinfo) will be used to check the thread pointer. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Andi Kleen Cc: Athira Rajeev Cc: Brian Robbins Cc: Changbin Du Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song Cc: German Gomez Cc: Ingo Molnar Cc: Ivan Babrou Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Liam Howlett Cc: Mark Rutland Cc: Miguel Ojeda Cc: Mike Leach Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Suzuki Poulouse Cc: Wenyu Liu Cc: Will Deacon Cc: Yang Jihong Cc: Ye Xingchen Cc: Yuan Can Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/tests/dwarf-unwind.c | 2 +- tools/perf/arch/arm64/tests/dwarf-unwind.c | 2 +- tools/perf/arch/powerpc/tests/dwarf-unwind.c | 2 +- tools/perf/arch/x86/tests/dwarf-unwind.c | 2 +- tools/perf/builtin-c2c.c | 6 +- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-report.c | 12 +- tools/perf/builtin-sched.c | 51 +++--- tools/perf/builtin-script.c | 20 +-- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 26 +-- .../perf/scripts/python/Perf-Trace-Util/Context.c | 4 +- tools/perf/tests/code-reading.c | 2 +- tools/perf/tests/hists_common.c | 2 +- tools/perf/tests/hists_cumulate.c | 1 - tools/perf/tests/hists_output.c | 2 +- tools/perf/tests/perf-targz-src-pkg | 5 +- tools/perf/tests/thread-maps-share.c | 13 +- tools/perf/trace/beauty/pid.c | 4 +- tools/perf/ui/browsers/hists.c | 19 ++- tools/perf/ui/stdio/hist.c | 2 +- tools/perf/util/arm-spe.c | 4 +- tools/perf/util/cs-etm.c | 2 +- tools/perf/util/data-convert-json.c | 8 +- tools/perf/util/db-export.c | 16 +- tools/perf/util/dlfilter.c | 4 +- tools/perf/util/event.c | 6 +- tools/perf/util/hist.c | 6 +- tools/perf/util/intel-bts.c | 2 +- tools/perf/util/intel-pt.c | 12 +- tools/perf/util/jitdump.c | 10 +- tools/perf/util/machine.c | 91 +++++----- tools/perf/util/map.c | 2 +- tools/perf/util/maps.c | 2 +- .../util/scripting-engines/trace-event-python.c | 14 +- tools/perf/util/session.c | 2 +- tools/perf/util/sort.c | 10 +- tools/perf/util/thread-stack.c | 25 +-- tools/perf/util/thread.c | 161 +++++++++--------- tools/perf/util/thread.h | 188 ++++++++++++++++++++- tools/perf/util/unwind-libdw.c | 6 +- tools/perf/util/unwind-libunwind-local.c | 6 +- tools/perf/util/unwind-libunwind.c | 2 +- tools/perf/util/vdso.c | 2 +- 45 files changed, 485 insertions(+), 279 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c index 566fb6c0eae7..9bc304cb7762 100644 --- a/tools/perf/arch/arm/tests/dwarf-unwind.c +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c @@ -26,7 +26,7 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_ARM_SP]; - map = maps__find(thread->maps, (u64)sp); + map = maps__find(thread__maps(thread), (u64)sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); diff --git a/tools/perf/arch/arm64/tests/dwarf-unwind.c b/tools/perf/arch/arm64/tests/dwarf-unwind.c index 90a7ef293ce7..b2603d0d3737 100644 --- a/tools/perf/arch/arm64/tests/dwarf-unwind.c +++ b/tools/perf/arch/arm64/tests/dwarf-unwind.c @@ -26,7 +26,7 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_ARM64_SP]; - map = maps__find(thread->maps, (u64)sp); + map = maps__find(thread__maps(thread), (u64)sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); diff --git a/tools/perf/arch/powerpc/tests/dwarf-unwind.c b/tools/perf/arch/powerpc/tests/dwarf-unwind.c index 32fffb593fbf..5ecf82893b84 100644 --- a/tools/perf/arch/powerpc/tests/dwarf-unwind.c +++ b/tools/perf/arch/powerpc/tests/dwarf-unwind.c @@ -26,7 +26,7 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_POWERPC_R1]; - map = maps__find(thread->maps, (u64)sp); + map = maps__find(thread__maps(thread), (u64)sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index 497593be80f2..5bfec3345d59 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -26,7 +26,7 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_X86_SP]; - map = maps__find(thread->maps, (u64)sp); + map = maps__find(thread__maps(thread), (u64)sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 05dfd98af170..ee41a96f0c73 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -293,7 +293,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, } if (c2c.stitch_lbr) - al.thread->lbr_stitch_enable = true; + thread__set_lbr_stitch_enable(al.thread, true); ret = sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, sysctl_perf_event_max_stack); @@ -1149,14 +1149,14 @@ pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, { int width = c2c_width(fmt, hpp, he->hists); - return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_); + return scnprintf(hpp->buf, hpp->size, "%*d", width, thread__pid(he->thread)); } static int64_t pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { - return left->thread->pid_ - right->thread->pid_; + return thread__pid(left->thread) - thread__pid(right->thread); } static int64_t diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 61766eead4f4..d9e96d4624c6 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -417,7 +417,7 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, } vdso = is_vdso_map(filename); - nsi = nsinfo__get(thread->nsinfo); + nsi = nsinfo__get(thread__nsinfo(thread)); if (vdso) { /* The vdso maps are always on the host and not the diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 2150eeced892..fe9439a4fd66 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -964,7 +964,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (perf_kmem__skip_sample(sample)) return 0; - dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread)); if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c7d526283baf..8ea6ab18534a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -292,7 +292,7 @@ static int process_sample_event(struct perf_tool *tool, } if (rep->stitch_lbr) - al.thread->lbr_stitch_enable = true; + thread__set_lbr_stitch_enable(al.thread, true); if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; @@ -829,10 +829,10 @@ static struct task *tasks_list(struct task *task, struct machine *machine) return NULL; /* Last one in the chain. */ - if (thread->ppid == -1) + if (thread__ppid(thread) == -1) return task; - parent_thread = machine__find_thread(machine, -1, thread->ppid); + parent_thread = machine__find_thread(machine, -1, thread__ppid(thread)); if (!parent_thread) return ERR_PTR(-ENOENT); @@ -869,12 +869,12 @@ static void task__print_level(struct task *task, FILE *fp, int level) struct thread *thread = task->thread; struct task *child; int comm_indent = fprintf(fp, " %8d %8d %8d |%*s", - thread->pid_, thread->tid, thread->ppid, - level, ""); + thread__pid(thread), thread__tid(thread), + thread__ppid(thread), level, ""); fprintf(fp, "%s\n", thread__comm_str(thread)); - maps__fprintf_task(thread->maps, comm_indent, fp); + maps__fprintf_task(thread__maps(thread), comm_indent, fp); if (!list_empty(&task->children)) { list_for_each_entry(child, &task->children, list) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3a30c2ac5b47..fd37468c4f62 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -916,12 +916,12 @@ static int replay_fork_event(struct perf_sched *sched, if (verbose > 0) { printf("fork event\n"); - printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid); - printf("... child: %s/%d\n", thread__comm_str(child), child->tid); + printf("... parent: %s/%d\n", thread__comm_str(parent), thread__tid(parent)); + printf("... child: %s/%d\n", thread__comm_str(child), thread__tid(child)); } - register_pid(sched, parent->tid, thread__comm_str(parent)); - register_pid(sched, child->tid, thread__comm_str(child)); + register_pid(sched, thread__tid(parent), thread__comm_str(parent)); + register_pid(sched, thread__tid(child), thread__comm_str(child)); out_put: thread__put(child); thread__put(parent); @@ -1316,7 +1316,7 @@ static int latency_migrate_task_event(struct perf_sched *sched, if (!atoms) { if (thread_atoms_insert(sched, migrant)) goto out_put; - register_pid(sched, migrant->tid, thread__comm_str(migrant)); + register_pid(sched, thread__tid(migrant), thread__comm_str(migrant)); atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); if (!atoms) { pr_err("migration-event: Internal tree error"); @@ -1359,10 +1359,13 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ sched->all_runtime += work_list->total_runtime; sched->all_count += work_list->nb_atoms; - if (work_list->num_merged > 1) - ret = printf(" %s:(%d) ", thread__comm_str(work_list->thread), work_list->num_merged); - else - ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid); + if (work_list->num_merged > 1) { + ret = printf(" %s:(%d) ", thread__comm_str(work_list->thread), + work_list->num_merged); + } else { + ret = printf(" %s:%d ", thread__comm_str(work_list->thread), + thread__tid(work_list->thread)); + } for (i = 0; i < 24 - ret; i++) printf(" "); @@ -1380,11 +1383,15 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ static int pid_cmp(struct work_atoms *l, struct work_atoms *r) { + pid_t l_tid, r_tid; + if (l->thread == r->thread) return 0; - if (l->thread->tid < r->thread->tid) + l_tid = thread__tid(l->thread); + r_tid = thread__tid(r->thread); + if (l_tid < r_tid) return -1; - if (l->thread->tid > r->thread->tid) + if (l_tid > r_tid) return 1; return (int)(l->thread - r->thread); } @@ -1679,14 +1686,14 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp)); color_fprintf(stdout, color, " %12s secs ", stimestamp); - if (new_shortname || tr->comm_changed || (verbose > 0 && sched_in->tid)) { + if (new_shortname || tr->comm_changed || (verbose > 0 && thread__tid(sched_in))) { const char *pid_color = color; if (thread__has_color(sched_in)) pid_color = COLOR_PIDS; color_fprintf(stdout, pid_color, "%s => %s:%d", - tr->shortname, thread__comm_str(sched_in), sched_in->tid); + tr->shortname, thread__comm_str(sched_in), thread__tid(sched_in)); tr->comm_changed = false; } @@ -1948,8 +1955,8 @@ static char *timehist_get_commstr(struct thread *thread) { static char str[32]; const char *comm = thread__comm_str(thread); - pid_t tid = thread->tid; - pid_t pid = thread->pid_; + pid_t tid = thread__tid(thread); + pid_t pid = thread__pid(thread); int n; if (pid == 0) @@ -2032,7 +2039,7 @@ static char task_state_char(struct thread *thread, int state) unsigned bit = state ? ffs(state) : 0; /* 'I' for idle */ - if (thread->tid == 0) + if (thread__tid(thread) == 0) return 'I'; return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; @@ -2067,7 +2074,7 @@ static void timehist_print_sample(struct perf_sched *sched, for (i = 0; i < max_cpus; ++i) { /* flag idle times with 'i'; others are sched events */ if (i == sample->cpu) - c = (thread->tid == 0) ? 'i' : 's'; + c = (thread__tid(thread) == 0) ? 'i' : 's'; else c = ' '; printf("%c", c); @@ -2094,7 +2101,7 @@ static void timehist_print_sample(struct perf_sched *sched, if (sched->show_wakeups && !sched->show_next) printf(" %-*s", comm_width, ""); - if (thread->tid == 0) + if (thread__tid(thread) == 0) goto out; if (sched->show_callchain) @@ -2626,7 +2633,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, t = ptime->end; } - if (!sched->idle_hist || thread->tid == 0) { + if (!sched->idle_hist || thread__tid(thread) == 0) { if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) timehist_update_runtime_stats(tr, t, tprev); @@ -2634,7 +2641,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, struct idle_thread_runtime *itr = (void *)tr; struct thread_runtime *last_tr; - BUG_ON(thread->tid != 0); + BUG_ON(thread__tid(thread) != 0); if (itr->last_thread == NULL) goto out; @@ -2719,7 +2726,7 @@ static void print_thread_runtime(struct thread *t, float stddev; printf("%*s %5d %9" PRIu64 " ", - comm_width, timehist_get_commstr(t), t->ppid, + comm_width, timehist_get_commstr(t), thread__ppid(t), (u64) r->run_stats.n); print_sched_time(r->total_run_time, 8); @@ -2739,7 +2746,7 @@ static void print_thread_waittime(struct thread *t, struct thread_runtime *r) { printf("%*s %5d %9" PRIu64 " ", - comm_width, timehist_get_commstr(t), t->ppid, + comm_width, timehist_get_commstr(t), thread__ppid(t), (u64) r->run_stats.n); print_sched_time(r->total_run_time, 8); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b02ad386a55b..e756290de2ac 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1142,7 +1142,7 @@ static int print_srccode(struct thread *thread, u8 cpumode, uint64_t addr) if (!al.map) return 0; ret = map__fprintf_srccode(al.map, al.addr, stdout, - &thread->srccode_state); + thread__srccode_state(thread)); if (ret) ret += printf("\n"); return ret; @@ -1439,7 +1439,7 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, * The 'return' has already been popped off the stack so the depth has * to be adjusted to match the 'call'. */ - if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) + if (thread__ts(thread) && sample->flags & PERF_IP_FLAG_RETURN) depth += 1; name = resolve_branch_sym(sample, evsel, thread, al, addr_al, &ip); @@ -1577,7 +1577,7 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, printed += fprintf(fp, "\n"); if (PRINT_FIELD(SRCCODE)) { int ret = map__fprintf_srccode(al->map, al->addr, stdout, - &thread->srccode_state); + thread__srccode_state(thread)); if (ret) { printed += ret; printed += printf("\n"); @@ -2086,9 +2086,9 @@ static bool show_event(struct perf_sample *sample, if (!symbol_conf.graph_function) return true; - if (thread->filter) { - if (depth <= thread->filter_entry_depth) { - thread->filter = false; + if (thread__filter(thread)) { + if (depth <= thread__filter_entry_depth(thread)) { + thread__set_filter(thread, false); return false; } return true; @@ -2105,8 +2105,8 @@ static bool show_event(struct perf_sample *sample, while (*s) { unsigned len = strcspn(s, ","); if (nlen == len && !strncmp(name, s, len)) { - thread->filter = true; - thread->filter_entry_depth = depth; + thread__set_filter(thread, true); + thread__set_filter_entry_depth(thread, depth); return true; } s += len; @@ -2186,7 +2186,7 @@ static void process_event(struct perf_script *script, struct callchain_cursor *cursor = NULL; if (script->stitch_lbr) - al->thread->lbr_stitch_enable = true; + thread__set_lbr_stitch_enable(al->thread, true); if (symbol_conf.use_callchain && sample->callchain && thread__resolve_callchain(al->thread, &callchain_cursor, evsel, @@ -2241,7 +2241,7 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(SRCCODE)) { if (map__fprintf_srccode(al->map, al->addr, stdout, - &thread->srccode_state)) + thread__srccode_state(thread))) printf("\n"); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 27a7f068207d..9d3cbebb9b79 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -777,7 +777,7 @@ static void perf_event__process_sample(struct perf_tool *tool, return; if (top->stitch_lbr) - al.thread->lbr_stitch_enable = true; + thread__set_lbr_stitch_enable(al.thread, true); if (!machine->kptr_restrict_warned && symbol_conf.kptr_restrict && diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index b0dd202d14eb..4c9bec39423b 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1386,12 +1386,13 @@ static int thread__read_fd_path(struct thread *thread, int fd) struct stat st; int ret; - if (thread->pid_ == thread->tid) { + if (thread__pid(thread) == thread__tid(thread)) { scnprintf(linkname, sizeof(linkname), - "/proc/%d/fd/%d", thread->pid_, fd); + "/proc/%d/fd/%d", thread__pid(thread), fd); } else { scnprintf(linkname, sizeof(linkname), - "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); + "/proc/%d/task/%d/fd/%d", + thread__pid(thread), thread__tid(thread), fd); } if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) @@ -1559,7 +1560,7 @@ static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread if (trace->multiple_threads) { if (trace->show_comm) printed += fprintf(fp, "%.14s/", thread__comm_str(thread)); - printed += fprintf(fp, "%d ", thread->tid); + printed += fprintf(fp, "%d ", thread__tid(thread)); } return printed; @@ -2205,7 +2206,8 @@ static void thread__update_stats(struct thread *thread, struct thread_trace *ttr memset(new_errnos + stats->max_errno, 0, (err - stats->max_errno) * sizeof(u32)); } else { pr_debug("Not enough memory for errno stats for thread \"%s\"(%d/%d), results will be incomplete\n", - thread__comm_str(thread), thread->pid_, thread->tid); + thread__comm_str(thread), thread__pid(thread), + thread__tid(thread)); return; } @@ -2550,7 +2552,7 @@ errno_print: { if (child != NULL) { fprintf(trace->output, "%ld", ret); - if (child->comm_set) + if (thread__comm_set(child)) fprintf(trace->output, " (%s)", thread__comm_str(child)); thread__put(child); } @@ -3616,14 +3618,16 @@ static int trace__set_filter_loop_pids(struct trace *trace) struct thread *thread = machine__find_thread(trace->host, pids[0], pids[0]); while (thread && nr < ARRAY_SIZE(pids)) { - struct thread *parent = machine__find_thread(trace->host, thread->ppid, thread->ppid); + struct thread *parent = machine__find_thread(trace->host, + thread__ppid(thread), + thread__ppid(thread)); if (parent == NULL) break; if (!strcmp(thread__comm_str(parent), "sshd") || strstarts(thread__comm_str(parent), "gnome-terminal")) { - pids[nr++] = parent->tid; + pids[nr++] = thread__tid(parent); break; } thread = parent; @@ -4322,7 +4326,7 @@ static size_t trace__fprintf_thread(FILE *fp, struct thread *thread, struct trac ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; - printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); + printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread__tid(thread)); printed += fprintf(fp, "%lu events, ", ttrace->nr_events); printed += fprintf(fp, "%.1f%%", ratio); if (ttrace->pfmaj) @@ -4344,7 +4348,9 @@ static unsigned long thread__nr_events(struct thread_trace *ttrace) return ttrace ? ttrace->nr_events : 0; } -DEFINE_RESORT_RB(threads, (thread__nr_events(a->thread->priv) < thread__nr_events(b->thread->priv)), +DEFINE_RESORT_RB(threads, + (thread__nr_events(thread__priv(a->thread)) < + thread__nr_events(thread__priv(b->thread))), struct thread *thread; ) { diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 53b1587db403..3954bd1587ce 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -100,8 +100,8 @@ static PyObject *perf_sample_insn(PyObject *obj, PyObject *args) if (!c) return NULL; - if (c->sample->ip && !c->sample->insn_len && c->al->thread->maps) { - struct machine *machine = maps__machine(c->al->thread->maps); + if (c->sample->ip && !c->sample->insn_len && thread__maps(c->al->thread)) { + struct machine *machine = maps__machine(thread__maps(c->al->thread)); script_fetch_insn(c->sample, c->al->thread, machine); } diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index efe026a35010..9d8eefbebd48 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -269,7 +269,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, len = map__end(al.map) - addr; /* Read the object code using perf */ - ret_len = dso__data_read_offset(dso, maps__machine(thread->maps), + ret_len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), al.addr, buf1, len); if (ret_len != len) { pr_debug("dso__data_read_offset failed\n"); diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index 745ab18d17db..d08add0f4da6 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c @@ -211,7 +211,7 @@ void print_hists_out(struct hists *hists) struct dso *dso = map__dso(he->ms.map); pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n", - i, thread__comm_str(he->thread), he->thread->tid, + i, thread__comm_str(he->thread), thread__tid(he->thread), dso->short_name, he->ms.sym->name, he->stat.period, he->stat_acc ? he->stat_acc->period : 0); diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 8c0e3f334747..62b9c6461ea6 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -162,7 +162,6 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *); #define DSO(he) (map__dso(he->ms.map)->short_name) #define SYM(he) (he->ms.sym->name) #define CPU(he) (he->cpu) -#define PID(he) (he->thread->tid) #define DEPTH(he) (he->callchain->max_depth) #define CDSO(cl) (map__dso(cl->ms.map)->short_name) #define CSYM(cl) (cl->ms.sym->name) diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index cebd5226bb12..cd2094c13e1e 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -128,7 +128,7 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *); #define DSO(he) (map__dso(he->ms.map)->short_name) #define SYM(he) (he->ms.sym->name) #define CPU(he) (he->cpu) -#define PID(he) (he->thread->tid) +#define PID(he) (thread__tid(he->thread)) /* default sort keys (no field) */ static int test1(struct evsel *evsel, struct machine *machine) diff --git a/tools/perf/tests/perf-targz-src-pkg b/tools/perf/tests/perf-targz-src-pkg index fae26b1cf08f..b3075c168cb2 100755 --- a/tools/perf/tests/perf-targz-src-pkg +++ b/tools/perf/tests/perf-targz-src-pkg @@ -7,16 +7,17 @@ # be in such tarball, which sometimes gets broken when we move files around, # like when we made some files that were in tools/perf/ available to other tools/ # codebases by moving it to tools/include/, etc. +set -e PERF=$1 cd ${PERF}/../.. -make perf-targz-src-pkg > /dev/null +make perf-targz-src-pkg TARBALL=$(ls -rt perf-*.tar.gz) TMP_DEST=$(mktemp -d) tar xf ${TARBALL} -C $TMP_DEST rm -f ${TARBALL} cd - > /dev/null -make -C $TMP_DEST/perf*/tools/perf > /dev/null +make -C $TMP_DEST/perf*/tools/perf RC=$? rm -rf ${TMP_DEST} exit $RC diff --git a/tools/perf/tests/thread-maps-share.c b/tools/perf/tests/thread-maps-share.c index 858e725318a9..faf980b26252 100644 --- a/tools/perf/tests/thread-maps-share.c +++ b/tools/perf/tests/thread-maps-share.c @@ -42,13 +42,13 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s TEST_ASSERT_VAL("failed to create threads", leader && t1 && t2 && t3 && other); - maps = leader->maps; + maps = thread__maps(leader); TEST_ASSERT_EQUAL("wrong refcnt", refcount_read(maps__refcnt(maps)), 4); /* test the maps pointer is shared */ - TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(t1->maps)); - TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(t2->maps)); - TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(t3->maps)); + TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(thread__maps(t1))); + TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(thread__maps(t2))); + TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(maps) == RC_CHK_ACCESS(thread__maps(t3))); /* * Verify the other leader was created by previous call. @@ -70,10 +70,11 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s machine__remove_thread(machine, other); machine__remove_thread(machine, other_leader); - other_maps = other->maps; + other_maps = thread__maps(other); TEST_ASSERT_EQUAL("wrong refcnt", refcount_read(maps__refcnt(other_maps)), 2); - TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(other_maps) == RC_CHK_ACCESS(other_leader->maps)); + TEST_ASSERT_VAL("maps don't match", RC_CHK_ACCESS(other_maps) == + RC_CHK_ACCESS(thread__maps(other_leader))); /* release thread group */ thread__put(t3); diff --git a/tools/perf/trace/beauty/pid.c b/tools/perf/trace/beauty/pid.c index 1a6acc46807b..8f9c9950f8ba 100644 --- a/tools/perf/trace/beauty/pid.c +++ b/tools/perf/trace/beauty/pid.c @@ -8,10 +8,10 @@ size_t syscall_arg__scnprintf_pid(char *bf, size_t size, struct syscall_arg *arg struct thread *thread = machine__findnew_thread(trace->host, pid, pid); if (thread != NULL) { - if (!thread->comm_set) + if (!thread__comm_set(thread)) thread__set_comm_from_proc(thread); - if (thread->comm_set) + if (thread__comm_set(thread)) printed += scnprintf(bf + printed, size - printed, " (%s)", thread__comm_str(thread)); thread__put(thread); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 69c81759a64f..c7ad9e003080 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2533,13 +2533,15 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act) thread__zput(browser->hists->thread_filter); ui_helpline__pop(); } else { + const char *comm_set_str = + thread__comm_set(thread) ? thread__comm_str(thread) : ""; + if (hists__has(browser->hists, thread)) { ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", - thread->comm_set ? thread__comm_str(thread) : "", - thread->tid); + comm_set_str, thread__tid(thread)); } else { ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"", - thread->comm_set ? thread__comm_str(thread) : ""); + comm_set_str); } browser->hists->thread_filter = thread__get(thread); @@ -2557,20 +2559,19 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct thread *thread) { int ret; + const char *comm_set_str, *in_out; if ((!hists__has(browser->hists, thread) && !hists__has(browser->hists, comm)) || thread == NULL) return 0; + in_out = browser->hists->thread_filter ? "out of" : "into"; + comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : ""; if (hists__has(browser->hists, thread)) { ret = asprintf(optstr, "Zoom %s %s(%d) thread", - browser->hists->thread_filter ? "out of" : "into", - thread->comm_set ? thread__comm_str(thread) : "", - thread->tid); + in_out, comm_set_str, thread__tid(thread)); } else { - ret = asprintf(optstr, "Zoom %s %s thread", - browser->hists->thread_filter ? "out of" : "into", - thread->comm_set ? thread__comm_str(thread) : ""); + ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str); } if (ret < 0) return 0; diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index f36270485168..b849caace398 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -885,7 +885,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, } if (h->ms.map == NULL && verbose > 1) { - maps__fprintf(h->thread->maps, fp); + maps__fprintf(thread__maps(h->thread), fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 7b36ba6b4079..afbd5869f6bf 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -254,9 +254,9 @@ static void arm_spe_set_pid_tid_cpu(struct arm_spe *spe, } if (speq->thread) { - speq->pid = speq->thread->pid_; + speq->pid = thread__pid(speq->thread); if (queue->cpu == -1) - speq->cpu = speq->thread->cpu; + speq->cpu = thread__cpu(speq->thread); } } diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 0f5be4ad24ba..b550c7393155 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1311,7 +1311,7 @@ static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm, tidq->tid); if (tidq->thread) - tidq->pid = tidq->thread->pid_; + tidq->pid = thread__pid(tidq->thread); } int cs_etm__etmq_set_tid(struct cs_etm_queue *etmq, diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 653709ab867a..291591e303cd 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -172,13 +172,13 @@ static int process_sample_event(struct perf_tool *tool, output_json_format(out, false, 2, "{"); output_json_key_format(out, false, 3, "timestamp", "%" PRIi64, sample->time); - output_json_key_format(out, true, 3, "pid", "%i", al.thread->pid_); - output_json_key_format(out, true, 3, "tid", "%i", al.thread->tid); + output_json_key_format(out, true, 3, "pid", "%i", thread__pid(al.thread)); + output_json_key_format(out, true, 3, "tid", "%i", thread__tid(al.thread)); if ((sample_type & PERF_SAMPLE_CPU)) output_json_key_format(out, true, 3, "cpu", "%i", sample->cpu); - else if (al.thread->cpu >= 0) - output_json_key_format(out, true, 3, "cpu", "%i", al.thread->cpu); + else if (thread__cpu(al.thread) >= 0) + output_json_key_format(out, true, 3, "cpu", "%i", thread__cpu(al.thread)); output_json_key_string(out, true, 3, "comm", thread__comm_str(al.thread)); diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 84c970c11794..751fd53bfd93 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -64,13 +64,13 @@ int db_export__thread(struct db_export *dbe, struct thread *thread, { u64 main_thread_db_id = 0; - if (thread->db_id) + if (thread__db_id(thread)) return 0; - thread->db_id = ++dbe->thread_last_db_id; + thread__set_db_id(thread, ++dbe->thread_last_db_id); if (main_thread) - main_thread_db_id = main_thread->db_id; + main_thread_db_id = thread__db_id(main_thread); if (dbe->export_thread) return dbe->export_thread(dbe, thread, main_thread_db_id, @@ -251,7 +251,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, */ al.sym = node->ms.sym; al.map = node->ms.map; - al.maps = thread->maps; + al.maps = thread__maps(thread); al.addr = node->ip; if (al.map && !al.sym) @@ -321,7 +321,7 @@ static int db_export__threads(struct db_export *dbe, struct thread *thread, * For a non-main thread, db_export__comm_thread() must be * called only if thread has not previously been exported. */ - bool export_comm_thread = comm && !thread->db_id; + bool export_comm_thread = comm && !thread__db_id(thread); err = db_export__thread(dbe, thread, machine, main_thread); if (err) @@ -529,16 +529,16 @@ static int db_export__pid_tid(struct db_export *dbe, struct machine *machine, struct thread *main_thread; int err = 0; - if (!thread || !thread->comm_set) + if (!thread || !thread__comm_set(thread)) goto out_put; - *is_idle = !thread->pid_ && !thread->tid; + *is_idle = !thread__pid(thread) && !thread__tid(thread); main_thread = thread__main_thread(machine, thread); err = db_export__threads(dbe, thread, main_thread, machine, comm_ptr); - *db_id = thread->db_id; + *db_id = thread__db_id(thread); thread__put(main_thread); out_put: diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index 16238f823a5e..8016f21dc0b8 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -197,8 +197,8 @@ static const __u8 *dlfilter__insn(void *ctx, __u32 *len) if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0) return NULL; - if (al->thread->maps) { - struct machine *machine = maps__machine(al->thread->maps); + if (thread__maps(al->thread)) { + struct machine *machine = maps__machine(thread__maps(al->thread)); if (machine) script_fetch_insn(d->sample, al->thread, machine); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e8b0666d913c..e1ce7cb5e421 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -573,7 +573,7 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, struct addr_location *al) { - struct maps *maps = thread->maps; + struct maps *maps = thread__maps(thread); struct machine *machine = maps__machine(maps); bool load_map = false; @@ -639,7 +639,7 @@ struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr, struct addr_location *al) { struct map *map = thread__find_map(thread, cpumode, addr, al); - struct machine *machine = maps__machine(thread->maps); + struct machine *machine = maps__machine(thread__maps(thread)); u8 addr_cpumode = machine__addr_cpumode(machine, cpumode, addr); if (map || addr_cpumode == cpumode) @@ -696,7 +696,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al, if (thread == NULL) return -1; - dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread)); thread__find_map(thread, sample->cpumode, sample->ip, al); dso = al->map ? map__dso(al->map) : NULL; dump_printf(" ...... dso: %s\n", diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3c9301a26dfc..4bc3affbe891 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2778,12 +2778,12 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh if (hists__has(hists, thread)) { printed += scnprintf(bf + printed, size - printed, ", Thread: %s(%d)", - (thread->comm_set ? thread__comm_str(thread) : ""), - thread->tid); + (thread__comm_set(thread) ? thread__comm_str(thread) : ""), + thread__tid(thread)); } else { printed += scnprintf(bf + printed, size - printed, ", Thread: %s", - (thread->comm_set ? thread__comm_str(thread) : "")); + (thread__comm_set(thread) ? thread__comm_str(thread) : "")); } } if (dso) diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 2c8147a62203..ec1b3bd9f530 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -456,7 +456,7 @@ static int intel_bts_process_queue(struct intel_bts_queue *btsq, u64 *timestamp) thread = machine__find_thread(btsq->bts->machine, -1, btsq->tid); if (thread) - btsq->pid = thread->pid_; + btsq->pid = thread__pid(thread); } else { thread = machine__findnew_thread(btsq->bts->machine, btsq->pid, btsq->tid); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dde2ca77a005..45c7e7722916 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1428,13 +1428,13 @@ static int intel_pt_get_guest_from_sideband(struct intel_pt_queue *ptq) ptq->guest_machine = machine; } - vcpu = ptq->thread ? ptq->thread->guest_cpu : -1; + vcpu = ptq->thread ? thread__guest_cpu(ptq->thread) : -1; if (vcpu < 0) return -1; tid = machine__get_current_tid(machine, vcpu); - if (ptq->guest_thread && ptq->guest_thread->tid != tid) + if (ptq->guest_thread && thread__tid(ptq->guest_thread) != tid) thread__zput(ptq->guest_thread); if (!ptq->guest_thread) { @@ -1444,7 +1444,7 @@ static int intel_pt_get_guest_from_sideband(struct intel_pt_queue *ptq) } ptq->guest_machine_pid = machine_pid; - ptq->guest_pid = ptq->guest_thread->pid_; + ptq->guest_pid = thread__pid(ptq->guest_thread); ptq->guest_tid = tid; ptq->vcpu = vcpu; @@ -1467,9 +1467,9 @@ static void intel_pt_set_pid_tid_cpu(struct intel_pt *pt, ptq->thread = machine__find_thread(pt->machine, -1, ptq->tid); if (ptq->thread) { - ptq->pid = ptq->thread->pid_; + ptq->pid = thread__pid(ptq->thread); if (queue->cpu == -1) - ptq->cpu = ptq->thread->cpu; + ptq->cpu = thread__cpu(ptq->thread); } if (pt->have_guest_sideband && intel_pt_get_guest_from_sideband(ptq)) { @@ -3074,7 +3074,7 @@ static void intel_pt_sample_set_pid_tid_cpu(struct intel_pt_queue *ptq, if (ptq->pid == -1) { ptq->thread = machine__find_thread(m, -1, ptq->tid); if (ptq->thread) - ptq->pid = ptq->thread->pid_; + ptq->pid = thread__pid(ptq->thread); return; } diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 28e49502db5e..2380b41a4caa 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -799,17 +799,19 @@ static void jit_add_pid(struct machine *machine, pid_t pid) return; } - thread->priv = (void *)1; + thread__set_priv(thread, (void *)true); } static bool jit_has_pid(struct machine *machine, pid_t pid) { struct thread *thread = machine__find_thread(machine, pid, pid); + void *priv; if (!thread) - return 0; + return false; - return (bool)thread->priv; + priv = thread__priv(thread); + return (bool)priv; } int @@ -833,7 +835,7 @@ jit_process(struct perf_session *session, return 0; } - nsi = nsinfo__get(thread->nsinfo); + nsi = nsinfo__get(thread__nsinfo(thread)); thread__put(thread); /* diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index cbf092e32ee9..5d34d60a0045 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -77,13 +77,14 @@ static int thread_rb_node__cmp_tid(const void *key, const struct rb_node *nd) { int to_find = (int) *((pid_t *)key); - return to_find - (int)rb_entry(nd, struct thread_rb_node, rb_node)->thread->tid; + return to_find - (int)thread__tid(rb_entry(nd, struct thread_rb_node, rb_node)->thread); } static struct thread_rb_node *thread_rb_node__find(const struct thread *th, struct rb_root *tree) { - struct rb_node *nd = rb_find(&th->tid, tree, thread_rb_node__cmp_tid); + pid_t to_find = thread__tid(th); + struct rb_node *nd = rb_find(&to_find, tree, thread_rb_node__cmp_tid); return rb_entry(nd, struct thread_rb_node, rb_node); } @@ -440,7 +441,7 @@ static struct thread *findnew_guest_code(struct machine *machine, return NULL; /* Assume maps are set up if there are any */ - if (maps__nr_maps(thread->maps)) + if (maps__nr_maps(thread__maps(thread))) return thread; host_thread = machine__find_thread(host_machine, -1, pid); @@ -453,7 +454,7 @@ static struct thread *findnew_guest_code(struct machine *machine, * Guest code can be found in hypervisor process at the same address * so copy host maps. */ - err = maps__clone(thread, host_thread->maps); + err = maps__clone(thread, thread__maps(host_thread)); thread__put(host_thread); if (err) goto out_err; @@ -518,45 +519,45 @@ static void machine__update_thread_pid(struct machine *machine, { struct thread *leader; - if (pid == th->pid_ || pid == -1 || th->pid_ != -1) + if (pid == thread__pid(th) || pid == -1 || thread__pid(th) != -1) return; - th->pid_ = pid; + thread__set_pid(th, pid); - if (th->pid_ == th->tid) + if (thread__pid(th) == thread__tid(th)) return; - leader = __machine__findnew_thread(machine, th->pid_, th->pid_); + leader = __machine__findnew_thread(machine, thread__pid(th), thread__pid(th)); if (!leader) goto out_err; - if (!leader->maps) - leader->maps = maps__new(machine); + if (!thread__maps(leader)) + thread__set_maps(leader, maps__new(machine)); - if (!leader->maps) + if (!thread__maps(leader)) goto out_err; - if (th->maps == leader->maps) + if (thread__maps(th) == thread__maps(leader)) return; - if (th->maps) { + if (thread__maps(th)) { /* * Maps are created from MMAP events which provide the pid and * tid. Consequently there never should be any maps on a thread * with an unknown pid. Just print an error if there are. */ - if (!maps__empty(th->maps)) + if (!maps__empty(thread__maps(th))) pr_err("Discarding thread maps for %d:%d\n", - th->pid_, th->tid); - maps__put(th->maps); + thread__pid(th), thread__tid(th)); + maps__put(thread__maps(th)); } - th->maps = maps__get(leader->maps); + thread__set_maps(th, maps__get(thread__maps(leader))); out_put: thread__put(leader); return; out_err: - pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid); + pr_err("Failed to join map groups for %d:%d\n", thread__pid(th), thread__tid(th)); goto out_put; } @@ -573,7 +574,7 @@ __threads__get_last_match(struct threads *threads, struct machine *machine, th = threads->last_match; if (th != NULL) { - if (th->tid == tid) { + if (thread__tid(th) == tid) { machine__update_thread_pid(machine, th, pid); return thread__get(th); } @@ -632,13 +633,13 @@ static struct thread *____machine__findnew_thread(struct machine *machine, parent = *p; th = rb_entry(parent, struct thread_rb_node, rb_node)->thread; - if (th->tid == tid) { + if (thread__tid(th) == tid) { threads__set_last_match(threads, th); machine__update_thread_pid(machine, th, pid); return thread__get(th); } - if (tid < th->tid) + if (tid < thread__tid(th)) p = &(*p)->rb_left; else { p = &(*p)->rb_right; @@ -2049,7 +2050,7 @@ out_problem: static void __machine__remove_thread(struct machine *machine, struct thread_rb_node *nd, struct thread *th, bool lock) { - struct threads *threads = machine__threads(machine, th->tid); + struct threads *threads = machine__threads(machine, thread__tid(th)); if (!nd) nd = thread_rb_node__find(th, &threads->entries.rb_root); @@ -2060,7 +2061,7 @@ static void __machine__remove_thread(struct machine *machine, struct thread_rb_n if (lock) down_write(&threads->lock); - BUG_ON(refcount_read(&th->refcnt) == 0); + BUG_ON(refcount_read(thread__refcnt(th)) == 0); thread__put(nd->thread); rb_erase_cached(&nd->rb_node, &threads->entries); @@ -2099,9 +2100,9 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event * (fork) event that would have removed the thread was lost. Assume the * latter case and continue on as best we can. */ - if (parent->pid_ != (pid_t)event->fork.ppid) { + if (thread__pid(parent) != (pid_t)event->fork.ppid) { dump_printf("removing erroneous parent thread %d/%d\n", - parent->pid_, parent->tid); + thread__pid(parent), thread__tid(parent)); machine__remove_thread(machine, parent); thread__put(parent); parent = machine__findnew_thread(machine, event->fork.ppid, @@ -2511,7 +2512,7 @@ static void save_lbr_cursor_node(struct thread *thread, struct callchain_cursor *cursor, int idx) { - struct lbr_stitch *lbr_stitch = thread->lbr_stitch; + struct lbr_stitch *lbr_stitch = thread__lbr_stitch(thread); if (!lbr_stitch) return; @@ -2553,7 +2554,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, * in callchain_cursor_commit() when the writing session is closed. * Using curr and pos to track the current cursor node. */ - if (thread->lbr_stitch) { + if (thread__lbr_stitch(thread)) { cursor->curr = NULL; cursor->pos = cursor->nr; if (cursor->nr) { @@ -2581,7 +2582,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, * But does not need to save current cursor node for entry 0. * It's impossible to stitch the whole LBRs of previous sample. */ - if (thread->lbr_stitch && (cursor->pos != cursor->nr)) { + if (thread__lbr_stitch(thread) && (cursor->pos != cursor->nr)) { if (!cursor->curr) cursor->curr = cursor->first; else @@ -2634,7 +2635,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, static int lbr_callchain_add_stitched_lbr_ip(struct thread *thread, struct callchain_cursor *cursor) { - struct lbr_stitch *lbr_stitch = thread->lbr_stitch; + struct lbr_stitch *lbr_stitch = thread__lbr_stitch(thread); struct callchain_cursor_node *cnode; struct stitch_list *stitch_node; int err; @@ -2658,7 +2659,7 @@ static int lbr_callchain_add_stitched_lbr_ip(struct thread *thread, static struct stitch_list *get_stitch_node(struct thread *thread) { - struct lbr_stitch *lbr_stitch = thread->lbr_stitch; + struct lbr_stitch *lbr_stitch = thread__lbr_stitch(thread); struct stitch_list *stitch_node; if (!list_empty(&lbr_stitch->free_lists)) { @@ -2682,7 +2683,7 @@ static bool has_stitched_lbr(struct thread *thread, struct branch_entry *cur_entries = perf_sample__branch_entries(cur); struct branch_stack *prev_stack = prev->branch_stack; struct branch_entry *prev_entries = perf_sample__branch_entries(prev); - struct lbr_stitch *lbr_stitch = thread->lbr_stitch; + struct lbr_stitch *lbr_stitch = thread__lbr_stitch(thread); int i, j, nr_identical_branches = 0; struct stitch_list *stitch_node; u64 cur_base, distance; @@ -2746,27 +2747,29 @@ static bool has_stitched_lbr(struct thread *thread, static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr) { - if (thread->lbr_stitch) + if (thread__lbr_stitch(thread)) return true; - thread->lbr_stitch = zalloc(sizeof(*thread->lbr_stitch)); - if (!thread->lbr_stitch) + thread__set_lbr_stitch(thread, zalloc(sizeof(struct lbr_stitch))); + if (!thread__lbr_stitch(thread)) goto err; - thread->lbr_stitch->prev_lbr_cursor = calloc(max_lbr + 1, sizeof(struct callchain_cursor_node)); - if (!thread->lbr_stitch->prev_lbr_cursor) + thread__lbr_stitch(thread)->prev_lbr_cursor = + calloc(max_lbr + 1, sizeof(struct callchain_cursor_node)); + if (!thread__lbr_stitch(thread)->prev_lbr_cursor) goto free_lbr_stitch; - INIT_LIST_HEAD(&thread->lbr_stitch->lists); - INIT_LIST_HEAD(&thread->lbr_stitch->free_lists); + INIT_LIST_HEAD(&thread__lbr_stitch(thread)->lists); + INIT_LIST_HEAD(&thread__lbr_stitch(thread)->free_lists); return true; free_lbr_stitch: - zfree(&thread->lbr_stitch); + free(thread__lbr_stitch(thread)); + thread__set_lbr_stitch(thread, NULL); err: pr_warning("Failed to allocate space for stitched LBRs. Disable LBR stitch\n"); - thread->lbr_stitch_enable = false; + thread__set_lbr_stitch_enable(thread, false); return false; } @@ -2802,9 +2805,9 @@ static int resolve_lbr_callchain_sample(struct thread *thread, if (i == chain_nr) return 0; - if (thread->lbr_stitch_enable && !sample->no_hw_idx && + if (thread__lbr_stitch_enable(thread) && !sample->no_hw_idx && (max_lbr > 0) && alloc_lbr_stitch(thread, max_lbr)) { - lbr_stitch = thread->lbr_stitch; + lbr_stitch = thread__lbr_stitch(thread); stitched_lbr = has_stitched_lbr(thread, sample, &lbr_stitch->prev_sample, @@ -2884,7 +2887,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, static u64 get_leaf_frame_caller(struct perf_sample *sample, struct thread *thread, int usr_idx) { - if (machine__normalized_is(maps__machine(thread->maps), "arm64")) + if (machine__normalized_is(maps__machine(thread__maps(thread)), "arm64")) return get_leaf_frame_caller_aarch64(sample, thread, usr_idx); else return 0; @@ -3265,7 +3268,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, if (!thread) return -ENOMEM; - thread->cpu = cpu; + thread__set_cpu(thread, cpu); thread__put(thread); return 0; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4d9944bbf5e4..ae1d54d4880a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -137,7 +137,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, no_dso = is_no_dso_memory(filename); map->prot = prot; map->flags = flags; - nsi = nsinfo__get(thread->nsinfo); + nsi = nsinfo__get(thread__nsinfo(thread)); if ((anon || no_dso) && nsi && (prot & PROT_EXEC)) { snprintf(newfilename, sizeof(newfilename), diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 1aeb1db58fe5..5ae6379a1b42 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -384,7 +384,7 @@ put_map: */ int maps__clone(struct thread *thread, struct maps *parent) { - struct maps *maps = thread->maps; + struct maps *maps = thread__maps(thread); int err; struct map_rb_node *rb_node; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 40964078f92f..f3d262e871ac 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1163,11 +1163,11 @@ static int python_export_thread(struct db_export *dbe, struct thread *thread, t = tuple_new(5); - tuple_set_d64(t, 0, thread->db_id); + tuple_set_d64(t, 0, thread__db_id(thread)); tuple_set_d64(t, 1, machine->db_id); tuple_set_d64(t, 2, main_thread_db_id); - tuple_set_s32(t, 3, thread->pid_); - tuple_set_s32(t, 4, thread->tid); + tuple_set_s32(t, 3, thread__pid(thread)); + tuple_set_s32(t, 4, thread__tid(thread)); call_object(tables->thread_handler, t, "thread_table"); @@ -1186,7 +1186,7 @@ static int python_export_comm(struct db_export *dbe, struct comm *comm, tuple_set_d64(t, 0, comm->db_id); tuple_set_string(t, 1, comm__str(comm)); - tuple_set_d64(t, 2, thread->db_id); + tuple_set_d64(t, 2, thread__db_id(thread)); tuple_set_d64(t, 3, comm->start); tuple_set_s32(t, 4, comm->exec); @@ -1207,7 +1207,7 @@ static int python_export_comm_thread(struct db_export *dbe, u64 db_id, tuple_set_d64(t, 0, db_id); tuple_set_d64(t, 1, comm->db_id); - tuple_set_d64(t, 2, thread->db_id); + tuple_set_d64(t, 2, thread__db_id(thread)); call_object(tables->comm_thread_handler, t, "comm_thread_table"); @@ -1292,7 +1292,7 @@ static void python_export_sample_table(struct db_export *dbe, tuple_set_d64(t, 0, es->db_id); tuple_set_d64(t, 1, es->evsel->db_id); tuple_set_d64(t, 2, maps__machine(es->al->maps)->db_id); - tuple_set_d64(t, 3, es->al->thread->db_id); + tuple_set_d64(t, 3, thread__db_id(es->al->thread)); tuple_set_d64(t, 4, es->comm_db_id); tuple_set_d64(t, 5, es->dso_db_id); tuple_set_d64(t, 6, es->sym_db_id); @@ -1382,7 +1382,7 @@ static int python_export_call_return(struct db_export *dbe, t = tuple_new(14); tuple_set_d64(t, 0, cr->db_id); - tuple_set_d64(t, 1, cr->thread->db_id); + tuple_set_d64(t, 1, thread__db_id(cr->thread)); tuple_set_d64(t, 2, comm_db_id); tuple_set_d64(t, 3, cr->cp->db_id); tuple_set_d64(t, 4, cr->call_time); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e2806791c76a..65ac9f7fdf7e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2807,7 +2807,7 @@ static int perf_session__set_guest_cpu(struct perf_session *session, pid_t pid, if (!thread) return -ENOMEM; - thread->guest_cpu = guest_cpu; + thread__set_guest_cpu(thread, guest_cpu); thread__put(thread); return 0; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 650cd8df4041..5e45c770f91d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -108,7 +108,7 @@ static int64_t cmp_null(const void *l, const void *r) static int64_t sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) { - return right->thread->tid - left->thread->tid; + return thread__tid(right->thread) - thread__tid(left->thread); } static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, @@ -117,7 +117,7 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, const char *comm = thread__comm_str(he->thread); width = max(7U, width) - 8; - return repsep_snprintf(bf, size, "%7d:%-*.*s", he->thread->tid, + return repsep_snprintf(bf, size, "%7d:%-*.*s", thread__tid(he->thread), width, width, comm ?: ""); } @@ -1543,8 +1543,10 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) !l_dso->id.ino && !l_dso->id.ino_generation) { /* userspace anonymous */ - if (left->thread->pid_ > right->thread->pid_) return -1; - if (left->thread->pid_ < right->thread->pid_) return 1; + if (thread__pid(left->thread) > thread__pid(right->thread)) + return -1; + if (thread__pid(left->thread) < thread__pid(right->thread)) + return 1; } addr: diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c index 4b85c1728012..374d142e7390 100644 --- a/tools/perf/util/thread-stack.c +++ b/tools/perf/util/thread-stack.c @@ -112,7 +112,7 @@ struct thread_stack { */ static inline bool thread_stack__per_cpu(struct thread *thread) { - return !(thread->tid || thread->pid_); + return !(thread__tid(thread) || thread__pid(thread)); } static int thread_stack__grow(struct thread_stack *ts) @@ -155,8 +155,8 @@ static int thread_stack__init(struct thread_stack *ts, struct thread *thread, ts->br_stack_sz = br_stack_sz; } - if (thread->maps && maps__machine(thread->maps)) { - struct machine *machine = maps__machine(thread->maps); + if (thread__maps(thread) && maps__machine(thread__maps(thread))) { + struct machine *machine = maps__machine(thread__maps(thread)); const char *arch = perf_env__arch(machine->env); ts->kernel_start = machine__kernel_start(machine); @@ -175,7 +175,7 @@ static struct thread_stack *thread_stack__new(struct thread *thread, int cpu, bool callstack, unsigned int br_stack_sz) { - struct thread_stack *ts = thread->ts, *new_ts; + struct thread_stack *ts = thread__ts(thread), *new_ts; unsigned int old_sz = ts ? ts->arr_sz : 0; unsigned int new_sz = 1; @@ -189,8 +189,8 @@ static struct thread_stack *thread_stack__new(struct thread *thread, int cpu, if (ts) memcpy(new_ts, ts, old_sz * sizeof(*ts)); new_ts->arr_sz = new_sz; - zfree(&thread->ts); - thread->ts = new_ts; + free(thread__ts(thread)); + thread__set_ts(thread, new_ts); ts = new_ts; } @@ -207,7 +207,7 @@ static struct thread_stack *thread_stack__new(struct thread *thread, int cpu, static struct thread_stack *thread__cpu_stack(struct thread *thread, int cpu) { - struct thread_stack *ts = thread->ts; + struct thread_stack *ts = thread__ts(thread); if (cpu < 0) cpu = 0; @@ -232,7 +232,7 @@ static inline struct thread_stack *thread__stack(struct thread *thread, if (thread_stack__per_cpu(thread)) return thread__cpu_stack(thread, cpu); - return thread->ts; + return thread__ts(thread); } static int thread_stack__push(struct thread_stack *ts, u64 ret_addr, @@ -363,7 +363,7 @@ static int __thread_stack__flush(struct thread *thread, struct thread_stack *ts) int thread_stack__flush(struct thread *thread) { - struct thread_stack *ts = thread->ts; + struct thread_stack *ts = thread__ts(thread); unsigned int pos; int err = 0; @@ -502,13 +502,14 @@ static void thread_stack__reset(struct thread *thread, struct thread_stack *ts) void thread_stack__free(struct thread *thread) { - struct thread_stack *ts = thread->ts; + struct thread_stack *ts = thread__ts(thread); unsigned int pos; if (ts) { for (pos = 0; pos < ts->arr_sz; pos++) __thread_stack__free(thread, ts + pos); - zfree(&thread->ts); + free(thread__ts(thread)); + thread__set_ts(thread, NULL); } } @@ -1127,7 +1128,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm, ts->rstate = X86_RETPOLINE_POSSIBLE; /* Flush stack on exec */ - if (ts->comm != comm && thread->pid_ == thread->tid) { + if (ts->comm != comm && thread__pid(thread) == thread__tid(thread)) { err = __thread_stack__flush(thread, ts); if (err) return err; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 38d300e3e4d3..9a1db3be6436 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -21,19 +21,20 @@ int thread__init_maps(struct thread *thread, struct machine *machine) { - pid_t pid = thread->pid_; + pid_t pid = thread__pid(thread); - if (pid == thread->tid || pid == -1) { - thread->maps = maps__new(machine); + if (pid == thread__tid(thread) || pid == -1) { + thread__set_maps(thread, maps__new(machine)); } else { struct thread *leader = __machine__findnew_thread(machine, pid, pid); + if (leader) { - thread->maps = maps__get(leader->maps); + thread__set_maps(thread, maps__get(thread__maps(leader))); thread__put(leader); } } - return thread->maps ? 0 : -1; + return thread__maps(thread) ? 0 : -1; } struct thread *thread__new(pid_t pid, pid_t tid) @@ -43,16 +44,16 @@ struct thread *thread__new(pid_t pid, pid_t tid) struct thread *thread = zalloc(sizeof(*thread)); if (thread != NULL) { - thread->pid_ = pid; - thread->tid = tid; - thread->ppid = -1; - thread->cpu = -1; - thread->guest_cpu = -1; - thread->lbr_stitch_enable = false; - INIT_LIST_HEAD(&thread->namespaces_list); - INIT_LIST_HEAD(&thread->comm_list); - init_rwsem(&thread->namespaces_lock); - init_rwsem(&thread->comm_lock); + thread__set_pid(thread, pid); + thread__set_tid(thread, tid); + thread__set_ppid(thread, -1); + thread__set_cpu(thread, -1); + thread__set_guest_cpu(thread, -1); + thread__set_lbr_stitch_enable(thread, false); + INIT_LIST_HEAD(thread__namespaces_list(thread)); + INIT_LIST_HEAD(thread__comm_list(thread)); + init_rwsem(thread__namespaces_lock(thread)); + init_rwsem(thread__comm_lock(thread)); comm_str = malloc(32); if (!comm_str) @@ -64,11 +65,11 @@ struct thread *thread__new(pid_t pid, pid_t tid) if (!comm) goto err_thread; - list_add(&comm->list, &thread->comm_list); - refcount_set(&thread->refcnt, 1); + list_add(&comm->list, thread__comm_list(thread)); + refcount_set(thread__refcnt(thread), 1); /* Thread holds first ref to nsdata. */ thread->nsinfo = nsinfo__new(pid); - srccode_state_init(&thread->srccode_state); + srccode_state_init(thread__srccode_state(thread)); } return thread; @@ -85,30 +86,30 @@ void thread__delete(struct thread *thread) thread_stack__free(thread); - if (thread->maps) { - maps__put(thread->maps); - thread->maps = NULL; + if (thread__maps(thread)) { + maps__put(thread__maps(thread)); + thread__set_maps(thread, NULL); } - down_write(&thread->namespaces_lock); + down_write(thread__namespaces_lock(thread)); list_for_each_entry_safe(namespaces, tmp_namespaces, - &thread->namespaces_list, list) { + thread__namespaces_list(thread), list) { list_del_init(&namespaces->list); namespaces__free(namespaces); } - up_write(&thread->namespaces_lock); + up_write(thread__namespaces_lock(thread)); - down_write(&thread->comm_lock); - list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) { + down_write(thread__comm_lock(thread)); + list_for_each_entry_safe(comm, tmp_comm, thread__comm_list(thread), list) { list_del_init(&comm->list); comm__free(comm); } - up_write(&thread->comm_lock); + up_write(thread__comm_lock(thread)); nsinfo__zput(thread->nsinfo); - srccode_state_free(&thread->srccode_state); + srccode_state_free(thread__srccode_state(thread)); - exit_rwsem(&thread->namespaces_lock); - exit_rwsem(&thread->comm_lock); + exit_rwsem(thread__namespaces_lock(thread)); + exit_rwsem(thread__comm_lock(thread)); thread__free_stitch_list(thread); free(thread); } @@ -116,31 +117,31 @@ void thread__delete(struct thread *thread) struct thread *thread__get(struct thread *thread) { if (thread) - refcount_inc(&thread->refcnt); + refcount_inc(thread__refcnt(thread)); return thread; } void thread__put(struct thread *thread) { - if (thread && refcount_dec_and_test(&thread->refcnt)) + if (thread && refcount_dec_and_test(thread__refcnt(thread))) thread__delete(thread); } -static struct namespaces *__thread__namespaces(const struct thread *thread) +static struct namespaces *__thread__namespaces(struct thread *thread) { - if (list_empty(&thread->namespaces_list)) + if (list_empty(thread__namespaces_list(thread))) return NULL; - return list_first_entry(&thread->namespaces_list, struct namespaces, list); + return list_first_entry(thread__namespaces_list(thread), struct namespaces, list); } struct namespaces *thread__namespaces(struct thread *thread) { struct namespaces *ns; - down_read(&thread->namespaces_lock); + down_read(thread__namespaces_lock(thread)); ns = __thread__namespaces(thread); - up_read(&thread->namespaces_lock); + up_read(thread__namespaces_lock(thread)); return ns; } @@ -154,7 +155,7 @@ static int __thread__set_namespaces(struct thread *thread, u64 timestamp, if (!new) return -ENOMEM; - list_add(&new->list, &thread->namespaces_list); + list_add(&new->list, thread__namespaces_list(thread)); if (timestamp && curr) { /* @@ -174,25 +175,25 @@ int thread__set_namespaces(struct thread *thread, u64 timestamp, { int ret; - down_write(&thread->namespaces_lock); + down_write(thread__namespaces_lock(thread)); ret = __thread__set_namespaces(thread, timestamp, event); - up_write(&thread->namespaces_lock); + up_write(thread__namespaces_lock(thread)); return ret; } -struct comm *thread__comm(const struct thread *thread) +struct comm *thread__comm(struct thread *thread) { - if (list_empty(&thread->comm_list)) + if (list_empty(thread__comm_list(thread))) return NULL; - return list_first_entry(&thread->comm_list, struct comm, list); + return list_first_entry(thread__comm_list(thread), struct comm, list); } -struct comm *thread__exec_comm(const struct thread *thread) +struct comm *thread__exec_comm(struct thread *thread) { struct comm *comm, *last = NULL, *second_last = NULL; - list_for_each_entry(comm, &thread->comm_list, list) { + list_for_each_entry(comm, thread__comm_list(thread), list) { if (comm->exec) return comm; second_last = last; @@ -205,7 +206,7 @@ struct comm *thread__exec_comm(const struct thread *thread) * thread, that is very probably wrong. Prefer a later comm to avoid * that case. */ - if (second_last && !last->start && thread->pid_ == thread->tid) + if (second_last && !last->start && thread__pid(thread) == thread__tid(thread)) return second_last; return last; @@ -217,7 +218,7 @@ static int ____thread__set_comm(struct thread *thread, const char *str, struct comm *new, *curr = thread__comm(thread); /* Override the default :tid entry */ - if (!thread->comm_set) { + if (!thread__comm_set(thread)) { int err = comm__override(curr, str, timestamp, exec); if (err) return err; @@ -225,13 +226,13 @@ static int ____thread__set_comm(struct thread *thread, const char *str, new = comm__new(str, timestamp, exec); if (!new) return -ENOMEM; - list_add(&new->list, &thread->comm_list); + list_add(&new->list, thread__comm_list(thread)); if (exec) - unwind__flush_access(thread->maps); + unwind__flush_access(thread__maps(thread)); } - thread->comm_set = true; + thread__set_comm_set(thread, true); return 0; } @@ -241,9 +242,9 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, { int ret; - down_write(&thread->comm_lock); + down_write(thread__comm_lock(thread)); ret = ____thread__set_comm(thread, str, timestamp, exec); - up_write(&thread->comm_lock); + up_write(thread__comm_lock(thread)); return ret; } @@ -255,7 +256,7 @@ int thread__set_comm_from_proc(struct thread *thread) int err = -1; if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", - thread->pid_, thread->tid) >= (int)sizeof(path)) && + thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && procfs__read_str(path, &comm, &sz) == 0) { comm[sz - 1] = '\0'; err = thread__set_comm(thread, comm, 0); @@ -264,7 +265,7 @@ int thread__set_comm_from_proc(struct thread *thread) return err; } -static const char *__thread__comm_str(const struct thread *thread) +static const char *__thread__comm_str(struct thread *thread) { const struct comm *comm = thread__comm(thread); @@ -278,9 +279,9 @@ const char *thread__comm_str(struct thread *thread) { const char *str; - down_read(&thread->comm_lock); + down_read(thread__comm_lock(thread)); str = __thread__comm_str(thread); - up_read(&thread->comm_lock); + up_read(thread__comm_lock(thread)); return str; } @@ -289,23 +290,23 @@ static int __thread__comm_len(struct thread *thread, const char *comm) { if (!comm) return 0; - thread->comm_len = strlen(comm); + thread__set_comm_len(thread, strlen(comm)); - return thread->comm_len; + return thread__var_comm_len(thread); } /* CHECKME: it should probably better return the max comm len from its comm list */ int thread__comm_len(struct thread *thread) { - int comm_len = thread->comm_len; + int comm_len = thread__var_comm_len(thread); if (!comm_len) { const char *comm; - down_read(&thread->comm_lock); + down_read(thread__comm_lock(thread)); comm = __thread__comm_str(thread); comm_len = __thread__comm_len(thread, comm); - up_read(&thread->comm_lock); + up_read(thread__comm_lock(thread)); } return comm_len; @@ -313,33 +314,33 @@ int thread__comm_len(struct thread *thread) size_t thread__fprintf(struct thread *thread, FILE *fp) { - return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + - maps__fprintf(thread->maps, fp); + return fprintf(fp, "Thread %d %s\n", thread__tid(thread), thread__comm_str(thread)) + + maps__fprintf(thread__maps(thread), fp); } int thread__insert_map(struct thread *thread, struct map *map) { int ret; - ret = unwind__prepare_access(thread->maps, map, NULL); + ret = unwind__prepare_access(thread__maps(thread), map, NULL); if (ret) return ret; - maps__fixup_overlappings(thread->maps, map, stderr); - return maps__insert(thread->maps, map); + maps__fixup_overlappings(thread__maps(thread), map, stderr); + return maps__insert(thread__maps(thread), map); } static int __thread__prepare_access(struct thread *thread) { bool initialized = false; int err = 0; - struct maps *maps = thread->maps; + struct maps *maps = thread__maps(thread); struct map_rb_node *rb_node; down_read(maps__lock(maps)); maps__for_each_entry(maps, rb_node) { - err = unwind__prepare_access(thread->maps, rb_node->map, &initialized); + err = unwind__prepare_access(thread__maps(thread), rb_node->map, &initialized); if (err || initialized) break; } @@ -362,21 +363,22 @@ static int thread__prepare_access(struct thread *thread) static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone) { /* This is new thread, we share map groups for process. */ - if (thread->pid_ == parent->pid_) + if (thread__pid(thread) == thread__pid(parent)) return thread__prepare_access(thread); - if (thread->maps == parent->maps) { + if (thread__maps(thread) == thread__maps(parent)) { pr_debug("broken map groups on thread %d/%d parent %d/%d\n", - thread->pid_, thread->tid, parent->pid_, parent->tid); + thread__pid(thread), thread__tid(thread), + thread__pid(parent), thread__tid(parent)); return 0; } /* But this one is new process, copy maps. */ - return do_maps_clone ? maps__clone(thread, parent->maps) : 0; + return do_maps_clone ? maps__clone(thread, thread__maps(parent)) : 0; } int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone) { - if (parent->comm_set) { + if (thread__comm_set(parent)) { const char *comm = thread__comm_str(parent); int err; if (!comm) @@ -386,7 +388,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bo return err; } - thread->ppid = parent->tid; + thread__set_ppid(thread, thread__tid(parent)); return thread__clone_maps(thread, parent, do_maps_clone); } @@ -410,13 +412,13 @@ void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, struct thread *thread__main_thread(struct machine *machine, struct thread *thread) { - if (thread->pid_ == thread->tid) + if (thread__pid(thread) == thread__tid(thread)) return thread__get(thread); - if (thread->pid_ == -1) + if (thread__pid(thread) == -1) return NULL; - return machine__find_thread(machine, thread->pid_, thread->pid_); + return machine__find_thread(machine, thread__pid(thread), thread__pid(thread)); } int thread__memcpy(struct thread *thread, struct machine *machine, @@ -447,7 +449,7 @@ int thread__memcpy(struct thread *thread, struct machine *machine, void thread__free_stitch_list(struct thread *thread) { - struct lbr_stitch *lbr_stitch = thread->lbr_stitch; + struct lbr_stitch *lbr_stitch = thread__lbr_stitch(thread); struct stitch_list *pos, *tmp; if (!lbr_stitch) @@ -464,5 +466,6 @@ void thread__free_stitch_list(struct thread *thread) } zfree(&lbr_stitch->prev_lbr_cursor); - zfree(&thread->lbr_stitch); + free(thread__lbr_stitch(thread)); + thread__set_lbr_stitch(thread, NULL); } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 3b3f9fb5a916..b103992c3831 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -96,8 +96,8 @@ static inline int thread__set_comm(struct thread *thread, const char *comm, int thread__set_comm_from_proc(struct thread *thread); int thread__comm_len(struct thread *thread); -struct comm *thread__comm(const struct thread *thread); -struct comm *thread__exec_comm(const struct thread *thread); +struct comm *thread__comm(struct thread *thread); +struct comm *thread__exec_comm(struct thread *thread); const char *thread__comm_str(struct thread *thread); int thread__insert_map(struct thread *thread, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone); @@ -121,6 +121,126 @@ void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, int thread__memcpy(struct thread *thread, struct machine *machine, void *buf, u64 ip, int len, bool *is64bit); +static inline struct maps *thread__maps(struct thread *thread) +{ + return thread->maps; +} + +static inline void thread__set_maps(struct thread *thread, struct maps *maps) +{ + thread->maps = maps; +} + +static inline pid_t thread__pid(const struct thread *thread) +{ + return thread->pid_; +} + +static inline void thread__set_pid(struct thread *thread, pid_t pid_) +{ + thread->pid_ = pid_; +} + +static inline pid_t thread__tid(const struct thread *thread) +{ + return thread->tid; +} + +static inline void thread__set_tid(struct thread *thread, pid_t tid) +{ + thread->tid = tid; +} + +static inline pid_t thread__ppid(const struct thread *thread) +{ + return thread->ppid; +} + +static inline void thread__set_ppid(struct thread *thread, pid_t ppid) +{ + thread->ppid = ppid; +} + +static inline int thread__cpu(const struct thread *thread) +{ + return thread->cpu; +} + +static inline void thread__set_cpu(struct thread *thread, int cpu) +{ + thread->cpu = cpu; +} + +static inline int thread__guest_cpu(const struct thread *thread) +{ + return thread->guest_cpu; +} + +static inline void thread__set_guest_cpu(struct thread *thread, int guest_cpu) +{ + thread->guest_cpu = guest_cpu; +} + +static inline refcount_t *thread__refcnt(struct thread *thread) +{ + return &thread->refcnt; +} + +static inline bool thread__comm_set(const struct thread *thread) +{ + return thread->comm_set; +} + +static inline void thread__set_comm_set(struct thread *thread, bool set) +{ + thread->comm_set = set; +} + +static inline int thread__var_comm_len(const struct thread *thread) +{ + return thread->comm_len; +} + +static inline void thread__set_comm_len(struct thread *thread, int len) +{ + thread->comm_len = len; +} + +static inline struct list_head *thread__namespaces_list(struct thread *thread) +{ + return &thread->namespaces_list; +} + +static inline int thread__namespaces_list_empty(const struct thread *thread) +{ + return list_empty(&thread->namespaces_list); +} + +static inline struct rw_semaphore *thread__namespaces_lock(struct thread *thread) +{ + return &thread->namespaces_lock; +} + +static inline struct list_head *thread__comm_list(struct thread *thread) +{ + return &thread->comm_list; +} + +static inline struct rw_semaphore *thread__comm_lock(struct thread *thread) +{ + return &thread->comm_lock; +} + +static inline u64 thread__db_id(const struct thread *thread) +{ + return thread->db_id; +} + +static inline void thread__set_db_id(struct thread *thread, u64 db_id) +{ + thread->db_id = db_id; +} + static inline void *thread__priv(struct thread *thread) { return thread->priv; @@ -131,6 +251,66 @@ static inline void thread__set_priv(struct thread *thread, void *p) thread->priv = p; } +static inline struct thread_stack *thread__ts(struct thread *thread) +{ + return thread->ts; +} + +static inline void thread__set_ts(struct thread *thread, struct thread_stack *ts) +{ + thread->ts = ts; +} + +static inline struct nsinfo *thread__nsinfo(struct thread *thread) +{ + return thread->nsinfo; +} + +static inline struct srccode_state *thread__srccode_state(struct thread *thread) +{ + return &thread->srccode_state; +} + +static inline bool thread__filter(const struct thread *thread) +{ + return thread->filter; +} + +static inline void thread__set_filter(struct thread *thread, bool filter) +{ + thread->filter = filter; +} + +static inline int thread__filter_entry_depth(const struct thread *thread) +{ + return thread->filter_entry_depth; +} + +static inline void thread__set_filter_entry_depth(struct thread *thread, int depth) +{ + thread->filter_entry_depth = depth; +} + +static inline bool thread__lbr_stitch_enable(const struct thread *thread) +{ + return thread->lbr_stitch_enable; +} + +static inline void thread__set_lbr_stitch_enable(struct thread *thread, bool en) +{ + thread->lbr_stitch_enable = en; +} + +static inline struct lbr_stitch *thread__lbr_stitch(struct thread *thread) +{ + return thread->lbr_stitch; +} + +static inline void thread__set_lbr_stitch(struct thread *thread, struct lbr_stitch *lbrs) +{ + thread->lbr_stitch = lbrs; +} + static inline bool thread__is_filtered(struct thread *thread) { if (symbol_conf.comm_list && @@ -139,12 +319,12 @@ static inline bool thread__is_filtered(struct thread *thread) } if (symbol_conf.pid_list && - !intlist__has_entry(symbol_conf.pid_list, thread->pid_)) { + !intlist__has_entry(symbol_conf.pid_list, thread__pid(thread))) { return true; } if (symbol_conf.tid_list && - !intlist__has_entry(symbol_conf.tid_list, thread->tid)) { + !intlist__has_entry(symbol_conf.tid_list, thread__tid(thread))) { return true; } diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index bdccfc511b7e..3723b5e31b2a 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -230,7 +230,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct unwind_info *ui, ui_buf = { .sample = data, .thread = thread, - .machine = RC_CHK_ACCESS(thread->maps)->machine, + .machine = RC_CHK_ACCESS(thread__maps(thread))->machine, .cb = cb, .arg = arg, .max_stack = max_stack, @@ -260,11 +260,11 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, if (err) goto out; - err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui); + err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), &callbacks, ui); if (err) goto out; - err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui); + err = dwfl_getthread_frames(ui->dwfl, thread__tid(thread), frame_callback, ui); if (err && ui->max_stack != max_stack) err = 0; diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 83dd79dcd597..11f3fc95aa11 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -325,7 +325,7 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui, return -EINVAL; } - maps__for_each_entry(ui->thread->maps, map_node) { + maps__for_each_entry(thread__maps(ui->thread), map_node) { struct map *map = map_node->map; u64 start = map__start(map); @@ -719,7 +719,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, */ if (max_stack - 1 > 0) { WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); - addr_space = maps__addr_space(ui->thread->maps); + addr_space = maps__addr_space(thread__maps(ui->thread)); if (addr_space == NULL) return -1; @@ -769,7 +769,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct unwind_info ui = { .sample = data, .thread = thread, - .machine = maps__machine(thread->maps), + .machine = maps__machine(thread__maps(thread)), .best_effort = best_effort }; diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 375d23d9a590..76cd63de80a8 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -89,7 +89,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct perf_sample *data, int max_stack, bool best_effort) { - const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread->maps); + const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread)); if (ops) return ops->get_entries(cb, arg, thread, data, max_stack, best_effort); diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index ec777ee11493..ae3eee69b659 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -146,7 +146,7 @@ static enum dso_type machine__thread_dso_type(struct machine *machine, enum dso_type dso_type = DSO__TYPE_UNKNOWN; struct map_rb_node *rb_node; - maps__for_each_entry(thread->maps, rb_node) { + maps__for_each_entry(thread__maps(thread), rb_node) { struct dso *dso = map__dso(rb_node->map); if (!dso || dso->long_name[0] != '/') -- cgit v1.2.3 From 46125590e0df7602d02602fcb0134a4085aca442 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:28:01 -0700 Subject: perf maps: Make delete static, always use put Address/leak sanitizer with reference count checking can identify the location of leaks, so use put rather than delete to avoid free-ing memory when the reference count is >1. Add maps__zput to ensure the variable is cleared. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Andi Kleen Cc: Athira Rajeev Cc: Brian Robbins Cc: Changbin Du Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song Cc: German Gomez Cc: Ingo Molnar Cc: Ivan Babrou Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Liam Howlett Cc: Mark Rutland Cc: Miguel Ojeda Cc: Mike Leach Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Suzuki Poulouse Cc: Wenyu Liu Cc: Will Deacon Cc: Yang Jihong Cc: Ye Xingchen Cc: Yuan Can Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/maps.c | 2 +- tools/perf/util/machine.c | 2 +- tools/perf/util/maps.c | 2 +- tools/perf/util/maps.h | 9 ++++++++- 4 files changed, 11 insertions(+), 4 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/maps.c b/tools/perf/tests/maps.c index 8c0eb5cf8bb5..5bb1123a91a7 100644 --- a/tools/perf/tests/maps.c +++ b/tools/perf/tests/maps.c @@ -140,7 +140,7 @@ static int test__maps__merge_in(struct test_suite *t __maybe_unused, int subtest ret = check_maps(merged3, ARRAY_SIZE(merged3), maps); TEST_ASSERT_VAL("merge check failed", !ret); - maps__delete(maps); + maps__zput(maps); return TEST_OK; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 5d34d60a0045..8972c852d3bd 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -248,7 +248,7 @@ void machine__exit(struct machine *machine) return; machine__destroy_kernel_maps(machine); - maps__delete(machine->kmaps); + maps__zput(machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 5ae6379a1b42..5206a6433117 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -171,7 +171,7 @@ struct maps *maps__new(struct machine *machine) return result; } -void maps__delete(struct maps *maps) +static void maps__delete(struct maps *maps) { maps__exit(maps); unwind__finish_access(maps); diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index d2963456cfbe..83144e0645ed 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -57,13 +57,20 @@ struct kmap { }; struct maps *maps__new(struct machine *machine); -void maps__delete(struct maps *maps); bool maps__empty(struct maps *maps); int maps__clone(struct thread *thread, struct maps *parent); struct maps *maps__get(struct maps *maps); void maps__put(struct maps *maps); +static inline void __maps__zput(struct maps **map) +{ + maps__put(*map); + *map = NULL; +} + +#define maps__zput(map) __maps__zput(&map) + static inline struct rb_root *maps__entries(struct maps *maps) { return &RC_CHK_ACCESS(maps)->entries; -- cgit v1.2.3 From 0dd5041c9a0eaf8c5c3fd46df4ee60f877799f44 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:28:03 -0700 Subject: perf addr_location: Add init/exit/copy functions struct addr_location holds references to multiple reference counted objects. Add init/exit functions to make maintenance of those more consistent with the rest of the code and to try to avoid leaks. Modification of thread reference counts isn't included in this change. Committer notes: I needed to initialize result to sample->ip to make sure is set to something, fixing a compile time error, mostly keeping the previous logic as build_alloc_func_list() already does debugging/error prints about what went wrong if it takes the 'goto out'. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Andi Kleen Cc: Athira Rajeev Cc: Brian Robbins Cc: Changbin Du Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song Cc: German Gomez Cc: Ingo Molnar Cc: Ivan Babrou Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Liam Howlett Cc: Mark Rutland Cc: Miguel Ojeda Cc: Mike Leach Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Suzuki Poulouse Cc: Wenyu Liu Cc: Will Deacon Cc: Yang Jihong Cc: Ye Xingchen Cc: Yuan Can Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-7-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 28 +++++--- tools/perf/builtin-c2c.c | 12 ++-- tools/perf/builtin-diff.c | 16 +++-- tools/perf/builtin-inject.c | 2 + tools/perf/builtin-kmem.c | 10 ++- tools/perf/builtin-kwork.c | 15 +++-- tools/perf/builtin-mem.c | 4 +- tools/perf/builtin-report.c | 6 +- tools/perf/builtin-sched.c | 2 + tools/perf/builtin-script.c | 77 +++++++++++++--------- tools/perf/builtin-timechart.c | 11 ++-- tools/perf/builtin-top.c | 6 +- tools/perf/builtin-trace.c | 10 ++- tools/perf/tests/code-reading.c | 3 +- tools/perf/tests/hists_cumulate.c | 17 +++-- tools/perf/tests/hists_filter.c | 11 ++-- tools/perf/tests/hists_link.c | 18 +++-- tools/perf/tests/hists_output.c | 10 ++- tools/perf/tests/mmap-thread-lookup.c | 4 +- tools/perf/util/addr_location.c | 30 ++++++++- tools/perf/util/addr_location.h | 5 +- tools/perf/util/build-id.c | 2 + tools/perf/util/cs-etm.c | 20 +++--- tools/perf/util/data-convert-json.c | 8 ++- tools/perf/util/db-export.c | 4 +- tools/perf/util/dlfilter.c | 13 +++- tools/perf/util/event.c | 16 +++-- tools/perf/util/evsel_fprintf.c | 8 ++- tools/perf/util/hist.c | 8 ++- tools/perf/util/intel-pt.c | 66 +++++++++++++------ tools/perf/util/machine.c | 35 +++++----- .../util/scripting-engines/trace-event-python.c | 10 ++- tools/perf/util/thread.c | 13 +++- tools/perf/util/unwind-libdw.c | 21 ++++-- tools/perf/util/unwind-libunwind-local.c | 13 +++- 35 files changed, 368 insertions(+), 166 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 425a7e2fd6fb..aeeb801f1ed7 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -184,7 +184,7 @@ out: static int process_branch_callback(struct evsel *evsel, struct perf_sample *sample, - struct addr_location *al __maybe_unused, + struct addr_location *al, struct perf_annotate *ann, struct machine *machine) { @@ -195,21 +195,29 @@ static int process_branch_callback(struct evsel *evsel, .hide_unresolved = symbol_conf.hide_unresolved, .ops = &hist_iter_branch, }; - struct addr_location a; + int ret; - if (machine__resolve(machine, &a, sample) < 0) - return -1; + addr_location__init(&a); + if (machine__resolve(machine, &a, sample) < 0) { + ret = -1; + goto out; + } - if (a.sym == NULL) - return 0; + if (a.sym == NULL) { + ret = 0; + goto out; + } if (a.map != NULL) map__dso(a.map)->hit = 1; hist__account_cycles(sample->branch_stack, al, sample, false, NULL); - return hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann); + ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann); +out: + addr_location__exit(&a); + return ret; } static bool has_annotation(struct perf_annotate *ann) @@ -272,10 +280,12 @@ static int process_sample_event(struct perf_tool *tool, struct addr_location al; int ret = 0; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); - return -1; + ret = -1; + goto out_put; } if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) @@ -288,7 +298,7 @@ static int process_sample_event(struct perf_tool *tool, ret = -1; } out_put: - addr_location__put(&al); + addr_location__exit(&al); return ret; } diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index ee41a96f0c73..530a44a59f41 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -286,10 +286,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, struct mem_info *mi, *mi_dup; int ret; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); - return -1; + ret = -1; + goto out; } if (c2c.stitch_lbr) @@ -301,8 +303,10 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, goto out; mi = sample__resolve_mem(sample, &al); - if (mi == NULL) - return -ENOMEM; + if (mi == NULL) { + ret = -ENOMEM; + goto out; + } /* * The mi object is released in hists__add_entry_ops, @@ -368,7 +372,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, } out: - addr_location__put(&al); + addr_location__exit(&al); return ret; free_mi: diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index dbb0562d6a4f..ca39657ee407 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -409,15 +409,17 @@ static int diff__process_sample_event(struct perf_tool *tool, return 0; } + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); - return -1; + ret = -1; + goto out; } if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { ret = 0; - goto out_put; + goto out; } switch (compute) { @@ -426,7 +428,7 @@ static int diff__process_sample_event(struct perf_tool *tool, NULL, NULL, NULL, sample, true)) { pr_warning("problem incrementing symbol period, " "skipping event\n"); - goto out_put; + goto out; } hist__account_cycles(sample->branch_stack, &al, sample, false, @@ -437,7 +439,7 @@ static int diff__process_sample_event(struct perf_tool *tool, if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, NULL)) { pr_debug("problem adding hist entry, skipping event\n"); - goto out_put; + goto out; } break; @@ -446,7 +448,7 @@ static int diff__process_sample_event(struct perf_tool *tool, true)) { pr_warning("problem incrementing symbol period, " "skipping event\n"); - goto out_put; + goto out; } } @@ -460,8 +462,8 @@ static int diff__process_sample_event(struct perf_tool *tool, if (!al.filtered) hists->stats.total_non_filtered_period += sample->period; ret = 0; -out_put: - addr_location__put(&al); +out: + addr_location__exit(&al); return ret; } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index d9e96d4624c6..d19a1b862306 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -743,6 +743,7 @@ int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, struct addr_location al; struct thread *thread; + addr_location__init(&al); thread = machine__findnew_thread(machine, sample->pid, sample->tid); if (thread == NULL) { pr_err("problem processing %d event, skipping it.\n", @@ -763,6 +764,7 @@ int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, thread__put(thread); repipe: perf_event__repipe(tool, event, sample, machine); + addr_location__exit(&al); return 0; } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index fe9439a4fd66..96a6611e4e53 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -399,7 +399,9 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample) struct addr_location al; struct machine *machine = &kmem_session->machines.host; struct callchain_cursor_node *node; + u64 result = sample->ip; + addr_location__init(&al); if (alloc_func_list == NULL) { if (build_alloc_func_list() < 0) goto out; @@ -427,16 +429,18 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample) else addr = node->ip; - return addr; + result = addr; + goto out; } else pr_debug3("skipping alloc function: %s\n", caller->name); callchain_cursor_advance(&callchain_cursor); } -out: pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip); - return sample->ip; +out: + addr_location__exit(&al); + return result; } struct sort_dimension { diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index a9395c52b23b..2d80aef4eccc 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -739,17 +739,22 @@ static int timehist_exit_event(struct perf_kwork *kwork, struct kwork_atom *atom = NULL; struct kwork_work *work = NULL; struct addr_location al; + int ret = 0; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_debug("Problem processing event, skipping it\n"); - return -1; + ret = -1; + goto out; } atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, KWORK_TRACE_ENTRY, evsel, sample, machine, &work); - if (work == NULL) - return -1; + if (work == NULL) { + ret = -1; + goto out; + } if (atom != NULL) { work->nr_atoms++; @@ -757,7 +762,9 @@ static int timehist_exit_event(struct perf_kwork *kwork, atom_del(atom); } - return 0; +out: + addr_location__exit(&al); + return ret; } static struct kwork_class kwork_irq; diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 960bfd4b732a..51499c20da01 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -199,9 +199,11 @@ dump_raw_samples(struct perf_tool *tool, char str[PAGE_SIZE_NAME_LEN]; struct dso *dso = NULL; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { fprintf(stderr, "problem processing %d event, skipping it.\n", event->header.type); + addr_location__exit(&al); return -1; } @@ -256,7 +258,7 @@ dump_raw_samples(struct perf_tool *tool, dso ? dso->long_name : "???", al.sym ? al.sym->name : "???"); out_put: - addr_location__put(&al); + addr_location__exit(&al); return 0; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8ea6ab18534a..0b091a8983a5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -285,10 +285,12 @@ static int process_sample_event(struct perf_tool *tool, if (evswitch__discard(&rep->evswitch, evsel)) return 0; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); - return -1; + ret = -1; + goto out_put; } if (rep->stitch_lbr) @@ -331,7 +333,7 @@ static int process_sample_event(struct perf_tool *tool, if (ret < 0) pr_debug("problem adding hist entry, skipping event\n"); out_put: - addr_location__put(&al); + addr_location__exit(&al); return ret; } diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fd37468c4f62..c75ad82a6729 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2584,6 +2584,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, int rc = 0; int state = evsel__intval(evsel, sample, "prev_state"); + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_err("problem processing %d event. skipping it\n", event->header.type); @@ -2692,6 +2693,7 @@ out: evsel__save_time(evsel, sample->time, sample->cpu); + addr_location__exit(&al); return rc; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index e756290de2ac..784d478c2e05 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -919,7 +919,6 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample, { struct branch_stack *br = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); - struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -930,20 +929,22 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample, from = entries[i].from; to = entries[i].to; + printed += fprintf(fp, " 0x%"PRIx64, from); if (PRINT_FIELD(DSO)) { - memset(&alf, 0, sizeof(alf)); - memset(&alt, 0, sizeof(alt)); + struct addr_location alf, alt; + + addr_location__init(&alf); + addr_location__init(&alt); thread__find_map_fb(thread, sample->cpumode, from, &alf); thread__find_map_fb(thread, sample->cpumode, to, &alt); - } - printed += fprintf(fp, " 0x%"PRIx64, from); - if (PRINT_FIELD(DSO)) printed += map__fprintf_dsoname_dsoff(alf.map, PRINT_FIELD(DSOFF), alf.addr, fp); - - printed += fprintf(fp, "/0x%"PRIx64, to); - if (PRINT_FIELD(DSO)) + printed += fprintf(fp, "/0x%"PRIx64, to); printed += map__fprintf_dsoname_dsoff(alt.map, PRINT_FIELD(DSOFF), alt.addr, fp); + addr_location__exit(&alt); + addr_location__exit(&alf); + } else + printed += fprintf(fp, "/0x%"PRIx64, to); printed += print_bstack_flags(fp, entries + i); } @@ -957,7 +958,6 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, { struct branch_stack *br = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); - struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -965,9 +965,10 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, return 0; for (i = 0; i < br->nr; i++) { + struct addr_location alf, alt; - memset(&alf, 0, sizeof(alf)); - memset(&alt, 0, sizeof(alt)); + addr_location__init(&alf); + addr_location__init(&alt); from = entries[i].from; to = entries[i].to; @@ -982,6 +983,8 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, if (PRINT_FIELD(DSO)) printed += map__fprintf_dsoname_dsoff(alt.map, PRINT_FIELD(DSOFF), alt.addr, fp); printed += print_bstack_flags(fp, entries + i); + addr_location__exit(&alt); + addr_location__exit(&alf); } return printed; @@ -993,7 +996,6 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, { struct branch_stack *br = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); - struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -1001,9 +1003,10 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, return 0; for (i = 0; i < br->nr; i++) { + struct addr_location alf, alt; - memset(&alf, 0, sizeof(alf)); - memset(&alt, 0, sizeof(alt)); + addr_location__init(&alf); + addr_location__init(&alt); from = entries[i].from; to = entries[i].to; @@ -1022,6 +1025,8 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, if (PRINT_FIELD(DSO)) printed += map__fprintf_dsoname_dsoff(alt.map, PRINT_FIELD(DSOFF), alt.addr, fp); printed += print_bstack_flags(fp, entries + i); + addr_location__exit(&alt); + addr_location__exit(&alf); } return printed; @@ -1036,6 +1041,7 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, struct addr_location al; bool kernel; struct dso *dso; + int ret = 0; if (!start || !end) return 0; @@ -1057,7 +1063,6 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, return -ENXIO; } - memset(&al, 0, sizeof(al)); if (end - start > MAXBB - MAXINSN) { if (last) pr_debug("\tbrstack does not reach to final jump (%" PRIx64 "-%" PRIx64 ")\n", start, end); @@ -1066,13 +1071,14 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, return 0; } + addr_location__init(&al); if (!thread__find_map(thread, *cpumode, start, &al) || (dso = map__dso(al.map)) == NULL) { pr_debug("\tcannot resolve %" PRIx64 "-%" PRIx64 "\n", start, end); - return 0; + goto out; } if (dso->data.status == DSO_DATA_STATUS_ERROR) { pr_debug("\tcannot resolve %" PRIx64 "-%" PRIx64 "\n", start, end); - return 0; + goto out; } /* Load maps to ensure dso->is_64_bit has been updated */ @@ -1086,7 +1092,10 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, if (len <= 0) pr_debug("\tcannot fetch code for block at %" PRIx64 "-%" PRIx64 "\n", start, end); - return len; + ret = len; +out: + addr_location__exit(&al); + return ret; } static int map__fprintf_srccode(struct map *map, u64 addr, FILE *fp, struct srccode_state *state) @@ -1137,14 +1146,16 @@ static int print_srccode(struct thread *thread, u8 cpumode, uint64_t addr) struct addr_location al; int ret = 0; - memset(&al, 0, sizeof(al)); + addr_location__init(&al); thread__find_map(thread, cpumode, addr, &al); if (!al.map) - return 0; + goto out; ret = map__fprintf_srccode(al.map, al.addr, stdout, thread__srccode_state(thread)); if (ret) ret += printf("\n"); +out: + addr_location__exit(&al); return ret; } @@ -1179,14 +1190,13 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread, struct perf_event_attr *attr, FILE *fp) { struct addr_location al; - int off, printed = 0; - - memset(&al, 0, sizeof(al)); + int off, printed = 0, ret = 0; + addr_location__init(&al); thread__find_map(thread, cpumode, addr, &al); if ((*lastsym) && al.addr >= (*lastsym)->start && al.addr < (*lastsym)->end) - return 0; + goto out; al.cpu = cpu; al.sym = NULL; @@ -1194,7 +1204,7 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread, al.sym = map__find_symbol(al.map, al.addr); if (!al.sym) - return 0; + goto out; if (al.addr < al.sym->end) off = al.addr - al.sym->start; @@ -1209,7 +1219,10 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread, printed += fprintf(fp, "\n"); *lastsym = al.sym; - return printed; + ret = printed; +out: + addr_location__exit(&al); + return ret; } static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, @@ -1371,6 +1384,7 @@ static int perf_sample__fprintf_addr(struct perf_sample *sample, struct addr_location al; int printed = fprintf(fp, "%16" PRIx64, sample->addr); + addr_location__init(&al); if (!sample_addr_correlates_sym(attr)) goto out; @@ -1387,6 +1401,7 @@ static int perf_sample__fprintf_addr(struct perf_sample *sample, if (PRINT_FIELD(DSO)) printed += map__fprintf_dsoname_dsoff(al.map, PRINT_FIELD(DSOFF), al.addr, fp); out: + addr_location__exit(&al); return printed; } @@ -2338,8 +2353,8 @@ static int process_sample_event(struct perf_tool *tool, int ret = 0; /* Set thread to NULL to indicate addr_al and al are not initialized */ - addr_al.thread = NULL; - al.thread = NULL; + addr_location__init(&al); + addr_location__init(&addr_al); ret = dlfilter__filter_event_early(dlfilter, event, sample, evsel, machine, &al, &addr_al); if (ret) { @@ -2405,8 +2420,8 @@ static int process_sample_event(struct perf_tool *tool, } out_put: - if (al.thread) - addr_location__put(&al); + addr_location__exit(&addr_al); + addr_location__exit(&al); return ret; } diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 829d99fecfd0..19d4542ea18a 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -498,7 +498,6 @@ static const char *cat_backtrace(union perf_event *event, char *p = NULL; size_t p_len; u8 cpumode = PERF_RECORD_MISC_USER; - struct addr_location tal; struct ip_callchain *chain = sample->callchain; FILE *f = open_memstream(&p, &p_len); @@ -507,6 +506,7 @@ static const char *cat_backtrace(union perf_event *event, return NULL; } + addr_location__init(&al); if (!chain) goto exit; @@ -518,6 +518,7 @@ static const char *cat_backtrace(union perf_event *event, for (i = 0; i < chain->nr; i++) { u64 ip; + struct addr_location tal; if (callchain_param.order == ORDER_CALLEE) ip = chain->ips[i]; @@ -544,20 +545,22 @@ static const char *cat_backtrace(union perf_event *event, * Discard all. */ zfree(&p); - goto exit_put; + goto exit; } continue; } + addr_location__init(&tal); tal.filtered = 0; if (thread__find_symbol(al.thread, cpumode, ip, &tal)) fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name); else fprintf(f, "..... %016" PRIx64 "\n", ip); + + addr_location__exit(&tal); } -exit_put: - addr_location__put(&al); exit: + addr_location__exit(&al); fclose(f); return p; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 9d3cbebb9b79..99010dfa5760 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -773,8 +773,9 @@ static void perf_event__process_sample(struct perf_tool *tool, if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) top->exact_samples++; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) - return; + goto out; if (top->stitch_lbr) thread__set_lbr_stitch_enable(al.thread, true); @@ -848,7 +849,8 @@ static void perf_event__process_sample(struct perf_tool *tool, mutex_unlock(&hists->lock); } - addr_location__put(&al); +out: + addr_location__exit(&al); } static void diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 4c9bec39423b..6a1e75f06832 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2418,13 +2418,15 @@ static int trace__resolve_callchain(struct trace *trace, struct evsel *evsel, int max_stack = evsel->core.attr.sample_max_stack ? evsel->core.attr.sample_max_stack : trace->max_stack; - int err; + int err = -1; + addr_location__init(&al); if (machine__resolve(trace->host, &al, sample) < 0) - return -1; + goto out; err = thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack); - addr_location__put(&al); +out: + addr_location__exit(&al); return err; } @@ -2893,6 +2895,7 @@ static int trace__pgfault(struct trace *trace, int err = -1; int callchain_ret = 0; + addr_location__init(&al); thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); if (sample->callchain) { @@ -2953,6 +2956,7 @@ out: err = 0; out_put: thread__put(thread); + addr_location__exit(&al); return err; } diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 9d8eefbebd48..2a7b2b6f5286 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -241,6 +241,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); + addr_location__init(&al); if (!thread__find_map(thread, cpumode, addr, &al) || !map__dso(al.map)) { if (cpumode == PERF_RECORD_MISC_HYPERVISOR) { pr_debug("Hypervisor address can not be resolved - skipping\n"); @@ -366,7 +367,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, } pr_debug("Bytes read match those read by objdump\n"); out: - map__put(al.map); + addr_location__exit(&al); return err; } diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 62b9c6461ea6..71dacb0fec4d 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -8,8 +8,8 @@ #include "util/evsel.h" #include "util/evlist.h" #include "util/machine.h" -#include "util/thread.h" #include "util/parse-events.h" +#include "util/thread.h" #include "tests/tests.h" #include "tests/hists_common.h" #include @@ -84,6 +84,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) struct perf_sample sample = { .period = 1000, }; size_t i; + addr_location__init(&al); for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { struct hist_entry_iter iter = { .evsel = evsel, @@ -107,20 +108,22 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack, NULL) < 0) { - addr_location__put(&al); goto out; } - fake_samples[i].thread = al.thread; + thread__put(fake_samples[i].thread); + fake_samples[i].thread = thread__get(al.thread); map__put(fake_samples[i].map); - fake_samples[i].map = al.map; + fake_samples[i].map = map__get(al.map); fake_samples[i].sym = al.sym; } + addr_location__exit(&al); return TEST_OK; out: pr_debug("Not enough memory for adding a hist entry\n"); + addr_location__exit(&al); return TEST_FAIL; } @@ -152,8 +155,10 @@ static void put_fake_samples(void) { size_t i; - for (i = 0; i < ARRAY_SIZE(fake_samples); i++) - map__put(fake_samples[i].map); + for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { + map__zput(fake_samples[i].map); + thread__zput(fake_samples[i].thread); + } } typedef int (*test_fn_t)(struct evsel *, struct machine *); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 98eff5935a1c..4b2e4f2fbe48 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -8,6 +8,7 @@ #include "util/evlist.h" #include "util/machine.h" #include "util/parse-events.h" +#include "util/thread.h" #include "tests/tests.h" #include "tests/hists_common.h" #include @@ -53,6 +54,7 @@ static int add_hist_entries(struct evlist *evlist, struct perf_sample sample = { .period = 100, }; size_t i; + addr_location__init(&al); /* * each evsel will have 10 samples but the 4th sample * (perf [perf] main) will be collapsed to an existing entry @@ -84,21 +86,22 @@ static int add_hist_entries(struct evlist *evlist, al.socket = fake_samples[i].socket; if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack, NULL) < 0) { - addr_location__put(&al); goto out; } - fake_samples[i].thread = al.thread; + thread__put(fake_samples[i].thread); + fake_samples[i].thread = thread__get(al.thread); map__put(fake_samples[i].map); - fake_samples[i].map = al.map; + fake_samples[i].map = map__get(al.map); fake_samples[i].sym = al.sym; } } - + addr_location__exit(&al); return 0; out: pr_debug("Not enough memory for adding a hist entry\n"); + addr_location__exit(&al); return TEST_FAIL; } diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 141e2972e34f..12bad8840699 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -8,6 +8,7 @@ #include "machine.h" #include "map.h" #include "parse-events.h" +#include "thread.h" #include "hists_common.h" #include "util/mmap.h" #include @@ -70,6 +71,7 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine) struct perf_sample sample = { .period = 1, .weight = 1, }; size_t i = 0, k; + addr_location__init(&al); /* * each evsel will have 10 samples - 5 common and 5 distinct. * However the second evsel also has a collapsed entry for @@ -90,13 +92,13 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine) he = hists__add_entry(hists, &al, NULL, NULL, NULL, NULL, &sample, true); if (he == NULL) { - addr_location__put(&al); goto out; } - fake_common_samples[k].thread = al.thread; + thread__put(fake_common_samples[k].thread); + fake_common_samples[k].thread = thread__get(al.thread); map__put(fake_common_samples[k].map); - fake_common_samples[k].map = al.map; + fake_common_samples[k].map = map__get(al.map); fake_common_samples[k].sym = al.sym; } @@ -110,20 +112,22 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine) he = hists__add_entry(hists, &al, NULL, NULL, NULL, NULL, &sample, true); if (he == NULL) { - addr_location__put(&al); goto out; } - fake_samples[i][k].thread = al.thread; - fake_samples[i][k].map = al.map; + thread__put(fake_samples[i][k].thread); + fake_samples[i][k].thread = thread__get(al.thread); + map__put(fake_samples[i][k].map); + fake_samples[i][k].map = map__get(al.map); fake_samples[i][k].sym = al.sym; } i++; } + addr_location__exit(&al); return 0; - out: + addr_location__exit(&al); pr_debug("Not enough memory for adding a hist entry\n"); return -1; } diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index cd2094c13e1e..ba1cccf57049 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -54,6 +54,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) struct perf_sample sample = { .period = 100, }; size_t i; + addr_location__init(&al); for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { struct hist_entry_iter iter = { .evsel = evsel, @@ -73,20 +74,21 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack, NULL) < 0) { - addr_location__put(&al); goto out; } fake_samples[i].thread = al.thread; map__put(fake_samples[i].map); - fake_samples[i].map = al.map; + fake_samples[i].map = map__get(al.map); fake_samples[i].sym = al.sym; } + addr_location__exit(&al); return TEST_OK; out: pr_debug("Not enough memory for adding a hist entry\n"); + addr_location__exit(&al); return TEST_FAIL; } @@ -118,8 +120,10 @@ static void put_fake_samples(void) { size_t i; - for (i = 0; i < ARRAY_SIZE(fake_samples); i++) + for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { map__put(fake_samples[i].map); + fake_samples[i].map = NULL; + } } typedef int (*test_fn_t)(struct evsel *, struct machine *); diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 898eda55b7a8..3891a2a3b46f 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -187,6 +187,7 @@ static int mmap_events(synth_cb synth) struct addr_location al; struct thread *thread; + addr_location__init(&al); thread = machine__findnew_thread(machine, getpid(), td->tid); pr_debug("looking for map %p\n", td->map); @@ -199,11 +200,12 @@ static int mmap_events(synth_cb synth) if (!al.map) { pr_debug("failed, couldn't find map\n"); err = -1; + addr_location__exit(&al); break; } pr_debug("map %p, addr %" PRIx64 "\n", al.map, map__start(al.map)); - map__put(al.map); + addr_location__exit(&al); } machine__delete_threads(machine); diff --git a/tools/perf/util/addr_location.c b/tools/perf/util/addr_location.c index c73fc2aa236c..51825ef8c0ab 100644 --- a/tools/perf/util/addr_location.c +++ b/tools/perf/util/addr_location.c @@ -1,16 +1,44 @@ // SPDX-License-Identifier: GPL-2.0 #include "addr_location.h" #include "map.h" +#include "maps.h" #include "thread.h" +void addr_location__init(struct addr_location *al) +{ + al->thread = NULL; + al->maps = NULL; + al->map = NULL; + al->sym = NULL; + al->srcline = NULL; + al->addr = 0; + al->level = 0; + al->filtered = 0; + al->cpumode = 0; + al->cpu = 0; + al->socket = 0; +} + /* * The preprocess_sample method will return with reference counts for the * in it, when done using (and perhaps getting ref counts if needing to * keep a pointer to one of those entries) it must be paired with * addr_location__put(), so that the refcounts can be decremented. */ -void addr_location__put(struct addr_location *al) +void addr_location__exit(struct addr_location *al) { map__zput(al->map); thread__zput(al->thread); + maps__zput(al->maps); +} + +void addr_location__copy(struct addr_location *dst, struct addr_location *src) +{ + thread__put(dst->thread); + maps__put(dst->maps); + map__put(dst->map); + *dst = *src; + dst->thread = thread__get(src->thread); + dst->maps = maps__get(src->maps); + dst->map = map__get(src->map); } diff --git a/tools/perf/util/addr_location.h b/tools/perf/util/addr_location.h index 7dfa7417c0fe..d8ac0428dff2 100644 --- a/tools/perf/util/addr_location.h +++ b/tools/perf/util/addr_location.h @@ -23,6 +23,9 @@ struct addr_location { s32 socket; }; -void addr_location__put(struct addr_location *al); +void addr_location__init(struct addr_location *al); +void addr_location__exit(struct addr_location *al); + +void addr_location__copy(struct addr_location *dst, struct addr_location *src); #endif /* __PERF_ADDR_LOCATION */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 06a8cd88cbef..36728222a5b4 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -58,9 +58,11 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, return -1; } + addr_location__init(&al); if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) map__dso(al.map)->hit = 1; + addr_location__exit(&al); thread__put(thread); return 0; } diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index b550c7393155..416f2ddc3895 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -910,33 +910,35 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, struct addr_location al; struct dso *dso; struct cs_etm_traceid_queue *tidq; + int ret = 0; if (!etmq) return 0; + addr_location__init(&al); machine = etmq->etm->machine; cpumode = cs_etm__cpu_mode(etmq, address); tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); if (!tidq) - return 0; + goto out; thread = tidq->thread; if (!thread) { if (cpumode != PERF_RECORD_MISC_KERNEL) - return 0; + goto out; thread = etmq->etm->unknown_thread; } if (!thread__find_map(thread, cpumode, address, &al)) - return 0; + goto out; dso = map__dso(al.map); if (!dso) - return 0; + goto out; if (dso->data.status == DSO_DATA_STATUS_ERROR && dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE)) - return 0; + goto out; offset = map__map_ip(al.map, address); @@ -953,10 +955,12 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, dso->long_name ? dso->long_name : "Unknown"); dso->auxtrace_warned = true; } - return 0; + goto out; } - - return len; + ret = len; +out: + addr_location__exit(&al); + return ret; } static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm, diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 291591e303cd..5bb3c2ba95ca 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -154,12 +154,14 @@ static int process_sample_event(struct perf_tool *tool, { struct convert_json *c = container_of(tool, struct convert_json, tool); FILE *out = c->out; - struct addr_location al, tal; + struct addr_location al; u64 sample_type = __evlist__combined_sample_type(evsel->evlist); u8 cpumode = PERF_RECORD_MISC_USER; + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_err("Sample resolution failed!\n"); + addr_location__exit(&al); return -1; } @@ -190,6 +192,7 @@ static int process_sample_event(struct perf_tool *tool, for (i = 0; i < sample->callchain->nr; ++i) { u64 ip = sample->callchain->ips[i]; + struct addr_location tal; if (ip >= PERF_CONTEXT_MAX) { switch (ip) { @@ -215,8 +218,10 @@ static int process_sample_event(struct perf_tool *tool, else fputc(',', out); + addr_location__init(&tal); ok = thread__find_symbol(al.thread, cpumode, ip, &tal); output_sample_callchain_entry(tool, ip, ok ? &tal : NULL); + addr_location__exit(&tal); } } else { output_sample_callchain_entry(tool, sample->ip, &al); @@ -245,6 +250,7 @@ static int process_sample_event(struct perf_tool *tool, } #endif output_json_format(out, false, 2, "}"); + addr_location__exit(&al); return 0; } diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 751fd53bfd93..6184696dc266 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -239,16 +239,17 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, struct addr_location al; u64 dso_db_id = 0, sym_db_id = 0, offset = 0; - memset(&al, 0, sizeof(al)); node = callchain_cursor_current(&callchain_cursor); if (!node) break; + /* * Handle export of symbol and dso for this node by * constructing an addr_location struct and then passing it to * db_ids_from_al() to perform the export. */ + addr_location__init(&al); al.sym = node->ms.sym; al.map = node->ms.map; al.maps = thread__maps(thread); @@ -265,6 +266,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, kernel_start); callchain_cursor_advance(&callchain_cursor); + addr_location__exit(&al); } /* Reset the callchain order to its prior value. */ diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index 8016f21dc0b8..46f74b2344db 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -258,6 +258,7 @@ static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len) struct addr_location a; struct map *map; u64 offset; + __s32 ret; if (!d->ctx_valid) return -1; @@ -272,16 +273,22 @@ static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len) machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip)) goto have_map; + addr_location__init(&a); thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a); - if (!a.map) - return -1; + if (!a.map) { + ret = -1; + goto out; + } map = a.map; have_map: offset = map__map_ip(map, ip); if (ip + len >= map__end(map)) len = map__end(map) - ip; - return dso__data_read_offset(map__dso(map), d->machine, offset, buf, len); + ret = dso__data_read_offset(map__dso(map), d->machine, offset, buf, len); +out: + addr_location__exit(&a); + return ret; } static const struct perf_dlfilter_fns perf_dlfilter_fns = { diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6ee23145ee7e..2fcfba38fc48 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -486,6 +486,7 @@ size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *ma if (machine) { struct addr_location al; + addr_location__init(&al); al.map = map__get(maps__find(machine__kernel_maps(machine), tp->addr)); if (al.map && map__load(al.map) >= 0) { al.addr = map__map_ip(al.map, tp->addr); @@ -493,7 +494,7 @@ size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *ma if (al.sym) ret += symbol__fprintf_symname_offs(al.sym, &al, fp); } - map__put(al.map); + addr_location__exit(&al); } ret += fprintf(fp, " old len %u new len %u\n", tp->old_len, tp->new_len); old = true; @@ -577,8 +578,10 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, struct machine *machine = maps__machine(maps); bool load_map = false; - al->maps = maps; - al->thread = thread; + maps__zput(al->maps); + map__zput(al->map); + thread__zput(al->thread); + al->addr = addr; al->cpumode = cpumode; al->filtered = 0; @@ -590,13 +593,13 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; - al->maps = maps = machine__kernel_maps(machine); + maps = machine__kernel_maps(machine); load_map = true; } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { al->level = 'g'; - al->maps = maps = machine__kernel_maps(machine); + maps = machine__kernel_maps(machine); load_map = true; } else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { al->level = 'u'; @@ -615,7 +618,8 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, return NULL; } - + al->maps = maps__get(maps); + al->thread = thread__get(thread); al->map = map__get(maps__find(maps, al->addr)); if (al->map != NULL) { /* diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index a1655fd7ed9b..cf45ca0e768f 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -128,8 +128,6 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, bool first = true; if (sample->callchain) { - struct addr_location node_al; - callchain_cursor_commit(cursor); while (1) { @@ -159,9 +157,12 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); if (print_sym) { + struct addr_location node_al; + + addr_location__init(&node_al); printed += fprintf(fp, " "); node_al.addr = addr; - node_al.map = map; + node_al.map = map__get(map); if (print_symoffset) { printed += __symbol__fprintf_symname_offs(sym, &node_al, @@ -171,6 +172,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, printed += __symbol__fprintf_symname(sym, &node_al, print_unknown_as_addr, fp); } + addr_location__exit(&node_al); } if (print_dso && (!sym || !sym->inlined)) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4bc3affbe891..a4c1b617f6e4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -588,7 +588,7 @@ static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period) static struct hist_entry *hists__findnew_entry(struct hists *hists, struct hist_entry *entry, - struct addr_location *al, + const struct addr_location *al, bool sample_self) { struct rb_node **p; @@ -927,8 +927,10 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) if (iter->curr >= iter->total) return 0; - al->maps = bi[i].to.ms.maps; - al->map = bi[i].to.ms.map; + maps__put(al->maps); + al->maps = maps__get(bi[i].to.ms.maps); + map__put(al->map); + al->map = map__get(bi[i].to.ms.map); al->sym = bi[i].to.ms.sym; al->addr = bi[i].to.addr; return 1; diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 45c7e7722916..783ce61c6d25 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -754,13 +754,15 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, struct addr_location al; unsigned char buf[INTEL_PT_INSN_BUF_SZ]; ssize_t len; - int x86_64; + int x86_64, ret = 0; u8 cpumode; u64 offset, start_offset, start_ip; u64 insn_cnt = 0; bool one_map = true; bool nr; + + addr_location__init(&al); intel_pt_insn->length = 0; if (to_ip && *ip == to_ip) @@ -773,19 +775,22 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, if (ptq->pt->have_guest_sideband) { if (!ptq->guest_machine || ptq->guest_machine_pid != ptq->pid) { intel_pt_log("ERROR: guest sideband but no guest machine\n"); - return -EINVAL; + ret = -EINVAL; + goto out_ret; } } else if ((!symbol_conf.guest_code && cpumode != PERF_RECORD_MISC_GUEST_KERNEL) || intel_pt_get_guest(ptq)) { intel_pt_log("ERROR: no guest machine\n"); - return -EINVAL; + ret = -EINVAL; + goto out_ret; } machine = ptq->guest_machine; thread = ptq->guest_thread; if (!thread) { if (cpumode != PERF_RECORD_MISC_GUEST_KERNEL) { intel_pt_log("ERROR: no guest thread\n"); - return -EINVAL; + ret = -EINVAL; + goto out_ret; } thread = ptq->unknown_guest_thread; } @@ -794,7 +799,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, if (!thread) { if (cpumode != PERF_RECORD_MISC_KERNEL) { intel_pt_log("ERROR: no thread\n"); - return -EINVAL; + ret = -EINVAL; + goto out_ret; } thread = ptq->pt->unknown_thread; } @@ -808,13 +814,17 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, intel_pt_log("ERROR: thread has no dso for %#" PRIx64 "\n", *ip); else intel_pt_log("ERROR: thread has no map for %#" PRIx64 "\n", *ip); - return -EINVAL; + addr_location__exit(&al); + ret = -EINVAL; + goto out_ret; } dso = map__dso(al.map); if (dso->data.status == DSO_DATA_STATUS_ERROR && - dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE)) - return -ENOENT; + dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE)) { + ret = -ENOENT; + goto out_ret; + } offset = map__map_ip(al.map, *ip); @@ -833,7 +843,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, intel_pt_insn->rel = e->rel; memcpy(intel_pt_insn->buf, e->insn, INTEL_PT_INSN_BUF_SZ); intel_pt_log_insn_no_data(intel_pt_insn, *ip); - return 0; + ret = 0; + goto out_ret; } } @@ -854,11 +865,14 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, offset); if (intel_pt_enable_logging) dso__fprintf(dso, intel_pt_log_fp()); - return -EINVAL; + ret = -EINVAL; + goto out_ret; } - if (intel_pt_get_insn(buf, len, x86_64, intel_pt_insn)) - return -EINVAL; + if (intel_pt_get_insn(buf, len, x86_64, intel_pt_insn)) { + ret = -EINVAL; + goto out_ret; + } intel_pt_log_insn(intel_pt_insn, *ip); @@ -909,17 +923,20 @@ out: e = intel_pt_cache_lookup(map__dso(al.map), machine, start_offset); if (e) - return 0; + goto out_ret; } /* Ignore cache errors */ intel_pt_cache_add(map__dso(al.map), machine, start_offset, insn_cnt, *ip - start_ip, intel_pt_insn); - return 0; +out_ret: + addr_location__exit(&al); + return ret; out_no_cache: *insn_cnt_ptr = insn_cnt; + addr_location__exit(&al); return 0; } @@ -968,6 +985,7 @@ static int __intel_pt_pgd_ip(uint64_t ip, void *data) struct addr_location al; u8 cpumode; u64 offset; + int res; if (ptq->state->to_nr) { if (intel_pt_guest_kernel_ip(ip)) @@ -984,12 +1002,15 @@ static int __intel_pt_pgd_ip(uint64_t ip, void *data) if (!thread) return -EINVAL; + addr_location__init(&al); if (!thread__find_map(thread, cpumode, ip, &al) || !map__dso(al.map)) return -EINVAL; offset = map__map_ip(al.map, ip); - return intel_pt_match_pgd_ip(ptq->pt, ip, offset, map__dso(al.map)->long_name); + res = intel_pt_match_pgd_ip(ptq->pt, ip, offset, map__dso(al.map)->long_name); + addr_location__exit(&al); + return res; } static bool intel_pt_pgd_ip(uint64_t ip, void *data) @@ -3372,20 +3393,22 @@ static int intel_pt_text_poke(struct intel_pt *pt, union perf_event *event) /* Assume text poke begins in a basic block no more than 4096 bytes */ int cnt = 4096 + event->text_poke.new_len; struct thread *thread = pt->unknown_thread; - struct addr_location al = { .map = NULL }; + struct addr_location al; struct machine *machine = pt->machine; struct intel_pt_cache_entry *e; u64 offset; + int ret = 0; + addr_location__init(&al); if (!event->text_poke.new_len) - return 0; + goto out; for (; cnt; cnt--, addr--) { struct dso *dso; if (intel_pt_find_map(thread, cpumode, addr, &al)) { if (addr < event->text_poke.addr) - return 0; + goto out; continue; } @@ -3406,15 +3429,16 @@ static int intel_pt_text_poke(struct intel_pt *pt, union perf_event *event) * branch instruction before the text poke address. */ if (e->branch != INTEL_PT_BR_NO_BRANCH) - return 0; + goto out; } else { intel_pt_cache_invalidate(dso, machine, offset); intel_pt_log("Invalidated instruction cache for %s at %#"PRIx64"\n", dso->long_name, addr); } } - - return 0; +out: + addr_location__exit(&al); + return ret; } static int intel_pt_process_event(struct perf_session *session, diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 8972c852d3bd..9fcf357a4d53 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2221,7 +2221,7 @@ static void ip__resolve_ams(struct thread *thread, { struct addr_location al; - memset(&al, 0, sizeof(al)); + addr_location__init(&al); /* * We cannot use the header.misc hint to determine whether a * branch stack address is user, kernel, guest, hypervisor. @@ -2234,11 +2234,12 @@ static void ip__resolve_ams(struct thread *thread, ams->addr = ip; ams->al_addr = al.addr; ams->al_level = al.level; - ams->ms.maps = al.maps; + ams->ms.maps = maps__get(al.maps); ams->ms.sym = al.sym; - ams->ms.map = al.map; + ams->ms.map = map__get(al.map); ams->phys_addr = 0; ams->data_page_size = 0; + addr_location__exit(&al); } static void ip__resolve_data(struct thread *thread, @@ -2247,18 +2248,19 @@ static void ip__resolve_data(struct thread *thread, { struct addr_location al; - memset(&al, 0, sizeof(al)); + addr_location__init(&al); thread__find_symbol(thread, m, addr, &al); ams->addr = addr; ams->al_addr = al.addr; ams->al_level = al.level; - ams->ms.maps = al.maps; + ams->ms.maps = maps__get(al.maps); ams->ms.sym = al.sym; - ams->ms.map = al.map; + ams->ms.map = map__get(al.map); ams->phys_addr = phys_addr; ams->data_page_size = daddr_page_size; + addr_location__exit(&al); } struct mem_info *sample__resolve_mem(struct perf_sample *sample, @@ -2319,10 +2321,11 @@ static int add_callchain_ip(struct thread *thread, { struct map_symbol ms; struct addr_location al; - int nr_loop_iter = 0, err; + int nr_loop_iter = 0, err = 0; u64 iter_cycles = 0; const char *srcline = NULL; + addr_location__init(&al); al.filtered = 0; al.sym = NULL; al.srcline = NULL; @@ -2348,9 +2351,10 @@ static int add_callchain_ip(struct thread *thread, * Discard all. */ callchain_cursor_reset(cursor); - return 1; + err = 1; + goto out; } - return 0; + goto out; } thread__find_symbol(thread, *cpumode, ip, &al); } @@ -2363,31 +2367,32 @@ static int add_callchain_ip(struct thread *thread, symbol__match_regex(al.sym, &ignore_callees_regex)) { /* Treat this symbol as the root, forgetting its callees. */ - *root_al = al; + addr_location__copy(root_al, &al); callchain_cursor_reset(cursor); } } if (symbol_conf.hide_unresolved && al.sym == NULL) - return 0; + goto out; if (iter) { nr_loop_iter = iter->nr_loop_iter; iter_cycles = iter->cycles; } - ms.maps = al.maps; - ms.map = al.map; + ms.maps = maps__get(al.maps); + ms.map = map__get(al.map); ms.sym = al.sym; if (!branch && append_inlines(cursor, &ms, ip) == 0) - return 0; + goto out; srcline = callchain_srcline(&ms, al.addr); err = callchain_cursor_append(cursor, ip, &ms, branch, flags, nr_loop_iter, iter_cycles, branch_from, srcline); - map__put(al.map); +out: + addr_location__exit(&al); return err; } diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index f3d262e871ac..d7c99028c6e6 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -469,9 +469,11 @@ static PyObject *python_process_callchain(struct perf_sample *sample, struct addr_location node_al; unsigned long offset; + addr_location__init(&node_al); node_al.addr = map__map_ip(map, node->ip); - node_al.map = map; + node_al.map = map__get(map); offset = get_offset(node->ms.sym, &node_al); + addr_location__exit(&node_al); pydict_set_item_string_decref( pyelem, "sym_off", @@ -539,6 +541,7 @@ static PyObject *python_process_brstack(struct perf_sample *sample, pydict_set_item_string_decref(pyelem, "cycles", PyLong_FromUnsignedLongLong(entries[i].flags.cycles)); + addr_location__init(&al); thread__find_map_fb(thread, sample->cpumode, entries[i].from, &al); dsoname = get_dsoname(al.map); @@ -551,6 +554,7 @@ static PyObject *python_process_brstack(struct perf_sample *sample, pydict_set_item_string_decref(pyelem, "to_dsoname", _PyUnicode_FromString(dsoname)); + addr_location__exit(&al); PyList_Append(pylist, pyelem); Py_DECREF(pyelem); } @@ -594,7 +598,6 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample, PyObject *pylist; u64 i; char bf[512]; - struct addr_location al; pylist = PyList_New(0); if (!pylist) @@ -605,7 +608,9 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample, for (i = 0; i < br->nr; i++) { PyObject *pyelem; + struct addr_location al; + addr_location__init(&al); pyelem = PyDict_New(); if (!pyelem) Py_FatalError("couldn't create Python dictionary"); @@ -644,6 +649,7 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample, PyList_Append(pylist, pyelem); Py_DECREF(pyelem); + addr_location__exit(&al); } exit: diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9a1db3be6436..bee4ac1051ee 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -432,18 +432,25 @@ int thread__memcpy(struct thread *thread, struct machine *machine, if (machine__kernel_ip(machine, ip)) cpumode = PERF_RECORD_MISC_KERNEL; - if (!thread__find_map(thread, cpumode, ip, &al)) - return -1; + addr_location__init(&al); + if (!thread__find_map(thread, cpumode, ip, &al)) { + addr_location__exit(&al); + return -1; + } dso = map__dso(al.map); - if( !dso || dso->data.status == DSO_DATA_STATUS_ERROR || map__load(al.map) < 0) + if (!dso || dso->data.status == DSO_DATA_STATUS_ERROR || map__load(al.map) < 0) { + addr_location__exit(&al); return -1; + } offset = map__map_ip(al.map, ip); if (is64bit) *is64bit = dso->is_64_bit; + addr_location__exit(&al); + return dso__data_read_offset(dso, machine, offset, buf, len); } diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 3723b5e31b2a..83eea968482e 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -90,8 +90,12 @@ static int __report_module(struct addr_location *al, u64 ip, static int report_module(u64 ip, struct unwind_info *ui) { struct addr_location al; + int res; - return __report_module(&al, ip, ui); + addr_location__init(&al); + res = __report_module(&al, ip, ui); + addr_location__exit(&al); + return res; } /* @@ -104,8 +108,11 @@ static int entry(u64 ip, struct unwind_info *ui) struct unwind_entry *e = &ui->entries[ui->idx++]; struct addr_location al; - if (__report_module(&al, ip, ui)) + addr_location__init(&al); + if (__report_module(&al, ip, ui)) { + addr_location__exit(&al); return -1; + } e->ip = ip; e->ms.maps = al.maps; @@ -116,6 +123,7 @@ static int entry(u64 ip, struct unwind_info *ui) al.sym ? al.sym->name : "''", ip, al.map ? map__map_ip(al.map, ip) : (u64) 0); + addr_location__exit(&al); return 0; } @@ -136,17 +144,22 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, ssize_t size; struct dso *dso; + addr_location__init(&al); if (!thread__find_map(ui->thread, PERF_RECORD_MISC_USER, addr, &al)) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); - return -1; + goto out_fail; } dso = map__dso(al.map); if (!dso) - return -1; + goto out_fail; size = dso__data_read_addr(dso, al.map, ui->machine, addr, (u8 *) data, sizeof(*data)); + addr_location__exit(&al); return !(size == sizeof(*data)); +out_fail: + addr_location__exit(&al); + return -1; } static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result, diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 11f3fc95aa11..36bf5100bad2 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -416,7 +416,12 @@ static int read_unwind_spec_debug_frame(struct dso *dso, static struct map *find_map(unw_word_t ip, struct unwind_info *ui) { struct addr_location al; - return thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + struct map *ret; + + addr_location__init(&al); + ret = thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + addr_location__exit(&al); + return ret; } static int @@ -631,7 +636,9 @@ static int entry(u64 ip, struct thread *thread, { struct unwind_entry e; struct addr_location al; + int ret; + addr_location__init(&al); e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); e.ip = ip; e.ms.map = al.map; @@ -642,7 +649,9 @@ static int entry(u64 ip, struct thread *thread, ip, al.map ? map__map_ip(al.map, ip) : (u64) 0); - return cb(&e, arg); + ret = cb(&e, arg); + addr_location__exit(&al); + return ret; } static void display_error(int err) -- cgit v1.2.3 From f6005cafebab72f8c02100dc896d6cfd5b8918cb Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:28:04 -0700 Subject: perf thread: Add reference count checking Modify struct declaration and accessor functions for the reference count checkers additional layer of indirection. Make sure pid_cmp in builtin-sched.c uses the underlying/original struct in pointer arithmetic, and not the temporary get/put indirection. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Andi Kleen Cc: Athira Rajeev Cc: Brian Robbins Cc: Changbin Du Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song Cc: German Gomez Cc: Ingo Molnar Cc: Ivan Babrou Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Liam Howlett Cc: Mark Rutland Cc: Miguel Ojeda Cc: Mike Leach Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Suzuki Poulouse Cc: Wenyu Liu Cc: Will Deacon Cc: Yang Jihong Cc: Ye Xingchen Cc: Yuan Can Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-8-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 4 +-- tools/perf/tests/hists_link.c | 2 +- tools/perf/ui/hist.c | 5 ++- tools/perf/util/hist.c | 2 +- tools/perf/util/machine.c | 2 +- tools/perf/util/sort.c | 2 +- tools/perf/util/thread.c | 20 +++++++---- tools/perf/util/thread.h | 79 ++++++++++++++++++++++--------------------- 8 files changed, 63 insertions(+), 53 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c75ad82a6729..cd79068200e5 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1385,7 +1385,7 @@ static int pid_cmp(struct work_atoms *l, struct work_atoms *r) { pid_t l_tid, r_tid; - if (l->thread == r->thread) + if (RC_CHK_ACCESS(l->thread) == RC_CHK_ACCESS(r->thread)) return 0; l_tid = thread__tid(l->thread); r_tid = thread__tid(r->thread); @@ -1393,7 +1393,7 @@ static int pid_cmp(struct work_atoms *l, struct work_atoms *r) return -1; if (l_tid > r_tid) return 1; - return (int)(l->thread - r->thread); + return (int)(RC_CHK_ACCESS(l->thread) - RC_CHK_ACCESS(r->thread)); } static int avg_cmp(struct work_atoms *l, struct work_atoms *r) diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 12bad8840699..2d19657ab5e0 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -148,7 +148,7 @@ static int find_sample(struct sample *samples, size_t nr_samples, struct thread *t, struct map *m, struct symbol *s) { while (nr_samples--) { - if (samples->thread == t && + if (RC_CHK_ACCESS(samples->thread) == RC_CHK_ACCESS(t) && RC_CHK_ACCESS(samples->map) == RC_CHK_ACCESS(m) && samples->sym == s) return 1; diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index f164bd26fc41..2bf959d08354 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -11,6 +11,7 @@ #include "../util/sort.h" #include "../util/evsel.h" #include "../util/evlist.h" +#include "../util/thread.h" #include "../util/util.h" /* hist period print (hpp) functions */ @@ -274,7 +275,9 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, if (ret) return ret; - if (a->thread != b->thread || !hist_entry__has_callchains(a) || !symbol_conf.use_callchain) + if ((a->thread == NULL ? NULL : RC_CHK_ACCESS(a->thread)) != + (b->thread == NULL ? NULL : RC_CHK_ACCESS(b->thread)) || + !hist_entry__has_callchains(a) || !symbol_conf.use_callchain) return 0; ret = b->callchain->max_depth - a->callchain->max_depth; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a4c1b617f6e4..dfda52d348a3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2124,7 +2124,7 @@ static bool hists__filter_entry_by_thread(struct hists *hists, struct hist_entry *he) { if (hists->thread_filter != NULL && - he->thread != hists->thread_filter) { + RC_CHK_ACCESS(he->thread) != RC_CHK_ACCESS(hists->thread_filter)) { he->filtered |= (1 << HIST_FILTER__THREAD); return true; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9fcf357a4d53..261188766307 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2055,7 +2055,7 @@ static void __machine__remove_thread(struct machine *machine, struct thread_rb_n if (!nd) nd = thread_rb_node__find(th, &threads->entries.rb_root); - if (threads->last_match == th) + if (threads->last_match && RC_CHK_ACCESS(threads->last_match) == RC_CHK_ACCESS(th)) threads__set_last_match(threads, NULL); if (lock) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5e45c770f91d..047c3606802f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -128,7 +128,7 @@ static int hist_entry__thread_filter(struct hist_entry *he, int type, const void if (type != HIST_FILTER__THREAD) return -1; - return th && he->thread != th; + return th && RC_CHK_ACCESS(he->thread) != RC_CHK_ACCESS(th); } struct sort_entry sort_thread = { diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index bee4ac1051ee..0b166404c5c3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -41,9 +41,10 @@ struct thread *thread__new(pid_t pid, pid_t tid) { char *comm_str; struct comm *comm; - struct thread *thread = zalloc(sizeof(*thread)); + RC_STRUCT(thread) *_thread = zalloc(sizeof(*_thread)); + struct thread *thread; - if (thread != NULL) { + if (ADD_RC_CHK(thread, _thread) != NULL) { thread__set_pid(thread, pid); thread__set_tid(thread, tid); thread__set_ppid(thread, -1); @@ -68,7 +69,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) list_add(&comm->list, thread__comm_list(thread)); refcount_set(thread__refcnt(thread), 1); /* Thread holds first ref to nsdata. */ - thread->nsinfo = nsinfo__new(pid); + RC_CHK_ACCESS(thread)->nsinfo = nsinfo__new(pid); srccode_state_init(thread__srccode_state(thread)); } @@ -105,26 +106,31 @@ void thread__delete(struct thread *thread) } up_write(thread__comm_lock(thread)); - nsinfo__zput(thread->nsinfo); + nsinfo__zput(RC_CHK_ACCESS(thread)->nsinfo); srccode_state_free(thread__srccode_state(thread)); exit_rwsem(thread__namespaces_lock(thread)); exit_rwsem(thread__comm_lock(thread)); thread__free_stitch_list(thread); - free(thread); + RC_CHK_FREE(thread); } struct thread *thread__get(struct thread *thread) { - if (thread) + struct thread *result; + + if (RC_CHK_GET(result, thread)) refcount_inc(thread__refcnt(thread)); - return thread; + + return result; } void thread__put(struct thread *thread) { if (thread && refcount_dec_and_test(thread__refcnt(thread))) thread__delete(thread); + else + RC_CHK_PUT(thread); } static struct namespaces *__thread__namespaces(struct thread *thread) diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index b103992c3831..9068a21ce0fa 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -15,6 +15,7 @@ #include "rwsem.h" #include "event.h" #include "callchain.h" +#include struct addr_location; struct map; @@ -34,7 +35,7 @@ struct thread_rb_node { struct thread *thread; }; -struct thread { +DECLARE_RC_STRUCT(thread) { struct maps *maps; pid_t pid_; /* Not all tools update this */ pid_t tid; @@ -123,192 +124,192 @@ int thread__memcpy(struct thread *thread, struct machine *machine, static inline struct maps *thread__maps(struct thread *thread) { - return thread->maps; + return RC_CHK_ACCESS(thread)->maps; } static inline void thread__set_maps(struct thread *thread, struct maps *maps) { - thread->maps = maps; + RC_CHK_ACCESS(thread)->maps = maps; } static inline pid_t thread__pid(const struct thread *thread) { - return thread->pid_; + return RC_CHK_ACCESS(thread)->pid_; } static inline void thread__set_pid(struct thread *thread, pid_t pid_) { - thread->pid_ = pid_; + RC_CHK_ACCESS(thread)->pid_ = pid_; } static inline pid_t thread__tid(const struct thread *thread) { - return thread->tid; + return RC_CHK_ACCESS(thread)->tid; } static inline void thread__set_tid(struct thread *thread, pid_t tid) { - thread->tid = tid; + RC_CHK_ACCESS(thread)->tid = tid; } static inline pid_t thread__ppid(const struct thread *thread) { - return thread->ppid; + return RC_CHK_ACCESS(thread)->ppid; } static inline void thread__set_ppid(struct thread *thread, pid_t ppid) { - thread->ppid = ppid; + RC_CHK_ACCESS(thread)->ppid = ppid; } static inline int thread__cpu(const struct thread *thread) { - return thread->cpu; + return RC_CHK_ACCESS(thread)->cpu; } static inline void thread__set_cpu(struct thread *thread, int cpu) { - thread->cpu = cpu; + RC_CHK_ACCESS(thread)->cpu = cpu; } static inline int thread__guest_cpu(const struct thread *thread) { - return thread->guest_cpu; + return RC_CHK_ACCESS(thread)->guest_cpu; } static inline void thread__set_guest_cpu(struct thread *thread, int guest_cpu) { - thread->guest_cpu = guest_cpu; + RC_CHK_ACCESS(thread)->guest_cpu = guest_cpu; } static inline refcount_t *thread__refcnt(struct thread *thread) { - return &thread->refcnt; + return &RC_CHK_ACCESS(thread)->refcnt; } static inline bool thread__comm_set(const struct thread *thread) { - return thread->comm_set; + return RC_CHK_ACCESS(thread)->comm_set; } static inline void thread__set_comm_set(struct thread *thread, bool set) { - thread->comm_set = set; + RC_CHK_ACCESS(thread)->comm_set = set; } static inline int thread__var_comm_len(const struct thread *thread) { - return thread->comm_len; + return RC_CHK_ACCESS(thread)->comm_len; } static inline void thread__set_comm_len(struct thread *thread, int len) { - thread->comm_len = len; + RC_CHK_ACCESS(thread)->comm_len = len; } static inline struct list_head *thread__namespaces_list(struct thread *thread) { - return &thread->namespaces_list; + return &RC_CHK_ACCESS(thread)->namespaces_list; } static inline int thread__namespaces_list_empty(const struct thread *thread) { - return list_empty(&thread->namespaces_list); + return list_empty(&RC_CHK_ACCESS(thread)->namespaces_list); } static inline struct rw_semaphore *thread__namespaces_lock(struct thread *thread) { - return &thread->namespaces_lock; + return &RC_CHK_ACCESS(thread)->namespaces_lock; } static inline struct list_head *thread__comm_list(struct thread *thread) { - return &thread->comm_list; + return &RC_CHK_ACCESS(thread)->comm_list; } static inline struct rw_semaphore *thread__comm_lock(struct thread *thread) { - return &thread->comm_lock; + return &RC_CHK_ACCESS(thread)->comm_lock; } static inline u64 thread__db_id(const struct thread *thread) { - return thread->db_id; + return RC_CHK_ACCESS(thread)->db_id; } static inline void thread__set_db_id(struct thread *thread, u64 db_id) { - thread->db_id = db_id; + RC_CHK_ACCESS(thread)->db_id = db_id; } static inline void *thread__priv(struct thread *thread) { - return thread->priv; + return RC_CHK_ACCESS(thread)->priv; } static inline void thread__set_priv(struct thread *thread, void *p) { - thread->priv = p; + RC_CHK_ACCESS(thread)->priv = p; } static inline struct thread_stack *thread__ts(struct thread *thread) { - return thread->ts; + return RC_CHK_ACCESS(thread)->ts; } static inline void thread__set_ts(struct thread *thread, struct thread_stack *ts) { - thread->ts = ts; + RC_CHK_ACCESS(thread)->ts = ts; } static inline struct nsinfo *thread__nsinfo(struct thread *thread) { - return thread->nsinfo; + return RC_CHK_ACCESS(thread)->nsinfo; } static inline struct srccode_state *thread__srccode_state(struct thread *thread) { - return &thread->srccode_state; + return &RC_CHK_ACCESS(thread)->srccode_state; } static inline bool thread__filter(const struct thread *thread) { - return thread->filter; + return RC_CHK_ACCESS(thread)->filter; } static inline void thread__set_filter(struct thread *thread, bool filter) { - thread->filter = filter; + RC_CHK_ACCESS(thread)->filter = filter; } static inline int thread__filter_entry_depth(const struct thread *thread) { - return thread->filter_entry_depth; + return RC_CHK_ACCESS(thread)->filter_entry_depth; } static inline void thread__set_filter_entry_depth(struct thread *thread, int depth) { - thread->filter_entry_depth = depth; + RC_CHK_ACCESS(thread)->filter_entry_depth = depth; } static inline bool thread__lbr_stitch_enable(const struct thread *thread) { - return thread->lbr_stitch_enable; + return RC_CHK_ACCESS(thread)->lbr_stitch_enable; } static inline void thread__set_lbr_stitch_enable(struct thread *thread, bool en) { - thread->lbr_stitch_enable = en; + RC_CHK_ACCESS(thread)->lbr_stitch_enable = en; } static inline struct lbr_stitch *thread__lbr_stitch(struct thread *thread) { - return thread->lbr_stitch; + return RC_CHK_ACCESS(thread)->lbr_stitch; } static inline void thread__set_lbr_stitch(struct thread *thread, struct lbr_stitch *lbrs) { - thread->lbr_stitch = lbrs; + RC_CHK_ACCESS(thread)->lbr_stitch = lbrs; } static inline bool thread__is_filtered(struct thread *thread) -- cgit v1.2.3 From cf078c838181366867091b024ff351ea2a414e0c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Jun 2023 16:28:05 -0700 Subject: perf machine: Make delete_threads part of machine__exit The code required threads to be deleted before machine__exit was called or the threads would be leaked. This was error prone so move the delete_threads into machine__exit. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ali Saidi Cc: Andi Kleen Cc: Athira Rajeev Cc: Brian Robbins Cc: Changbin Du Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song Cc: German Gomez Cc: Ingo Molnar Cc: Ivan Babrou Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: K Prateek Nayak Cc: Kan Liang Cc: Leo Yan Cc: Liam Howlett Cc: Mark Rutland Cc: Miguel Ojeda Cc: Mike Leach Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Suzuki Poulouse Cc: Wenyu Liu Cc: Will Deacon Cc: Yang Jihong Cc: Ye Xingchen Cc: Yuan Can Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-9-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/code-reading.c | 1 - tools/perf/tests/dwarf-unwind.c | 1 - tools/perf/tests/mmap-thread-lookup.c | 1 - tools/perf/tests/symbols.c | 1 - tools/perf/util/machine.c | 1 + tools/perf/util/session.c | 6 ------ 6 files changed, 1 insertion(+), 10 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 2a7b2b6f5286..ed3815163d1b 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -721,7 +721,6 @@ out_err: evlist__delete(evlist); perf_cpu_map__put(cpus); perf_thread_map__put(threads); - machine__delete_threads(machine); machine__delete(machine); return err; diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index ee983b677a6a..d01aa931fe81 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -235,7 +235,6 @@ noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused, thread__put(thread); out: - machine__delete_threads(machine); machine__delete(machine); return err; } diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 3891a2a3b46f..ddd1da9a4ba9 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -208,7 +208,6 @@ static int mmap_events(synth_cb synth) addr_location__exit(&al); } - machine__delete_threads(machine); machine__delete(machine); return err; } diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c index 2d1aa42d36a9..16e1c5502b09 100644 --- a/tools/perf/tests/symbols.c +++ b/tools/perf/tests/symbols.c @@ -38,7 +38,6 @@ static int init_test_info(struct test_info *ti) static void exit_test_info(struct test_info *ti) { thread__put(ti->thread); - machine__delete_threads(ti->machine); machine__delete(ti->machine); } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 261188766307..46af5e9748c9 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -256,6 +256,7 @@ void machine__exit(struct machine *machine) zfree(&machine->current_tid); zfree(&machine->kallsyms_filename); + machine__delete_threads(machine); for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 65ac9f7fdf7e..00d18c74c090 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -278,11 +278,6 @@ struct perf_session *__perf_session__new(struct perf_data *data, return ERR_PTR(ret); } -static void perf_session__delete_threads(struct perf_session *session) -{ - machine__delete_threads(&session->machines.host); -} - static void perf_decomp__release_events(struct decomp *next) { struct decomp *decomp; @@ -305,7 +300,6 @@ void perf_session__delete(struct perf_session *session) auxtrace__free(session); auxtrace_index__free(&session->auxtrace_index); perf_session__destroy_kernel_maps(session); - perf_session__delete_threads(session); perf_decomp__release_events(session->decomp_data.decomp); perf_env__exit(&session->header.env); machines__exit(&session->machines); -- cgit v1.2.3 From d436373a75f53cafa37df0ace3b329b119739699 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 13 Jun 2023 16:22:26 +0300 Subject: perf tests: Make x86 new instructions test optional at build time The "x86 instruction decoder - new instructions" test takes up space but is only really useful to developers. Make it optional at build time. Add variable EXTRA_TESTS which must be defined in order to build perf with the test. Example: Before: $ make -C tools/perf clean >/dev/null $ make -C tools/perf >/dev/null Makefile.config:650: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR Makefile.config:1149: libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev PERF_VERSION = 6.4.rc3.gd15b8c76c964 $ readelf -SW tools/perf/perf | grep '\.rela.dyn\|.rodata\|\.data.rel.ro' [10] .rela.dyn RELA 000000000002fcb0 02fcb0 0748b0 18 A 6 0 8 [18] .rodata PROGBITS 00000000002eb000 2eb000 6bac00 00 A 0 0 32 [25] .data.rel.ro PROGBITS 00000000009ea180 9e9180 04b540 00 WA 0 0 32 After: $ make -C tools/perf clean >/dev/null $ make -C tools/perf >/dev/null Makefile.config:650: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR Makefile.config:1154: libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev PERF_VERSION = 6.4.rc3.g4ea9c1569ea4 $ readelf -SW tools/perf/perf | grep '\.rela.dyn\|.rodata\|\.data.rel.ro' [10] .rela.dyn RELA 000000000002f3c8 02f3c8 036d68 18 A 6 0 8 [18] .rodata PROGBITS 00000000002ac000 2ac000 68da80 00 A 0 0 32 [25] .data.rel.ro PROGBITS 000000000097d440 97c440 022280 00 WA 0 0 32 Committer notes: Build with 'make EXTRA_TESTS=1 -C tools/perf O=/tmp/build/perf" and reproduced the ELF section size differences. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/683fea7c-f5e9-fa20-f96b-f6233ed5d2a7@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 5 +++++ tools/perf/Makefile.perf | 4 ++++ tools/perf/arch/x86/include/arch-tests.h | 2 ++ tools/perf/arch/x86/tests/Build | 5 ++++- tools/perf/arch/x86/tests/arch-tests.c | 4 ++++ tools/perf/tests/make | 1 + 6 files changed, 20 insertions(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index a794d9eca93d..9c5aa14a44cf 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -1075,6 +1075,11 @@ ifndef NO_AUXTRACE endif endif +ifdef EXTRA_TESTS + $(call detected,CONFIG_EXTRA_TESTS) + CFLAGS += -DHAVE_EXTRA_TESTS +endif + ifndef NO_JVMTI ifneq (,$(wildcard /usr/sbin/update-java-alternatives)) JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | awk '{print $$3}') diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index f48794816d82..b1e62a621f92 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -128,6 +128,10 @@ include ../scripts/utilities.mak # # Define BUILD_NONDISTRO to enable building an linking against libbfd and # libiberty distribution license incompatible libraries. +# +# Define EXTRA_TESTS to enable building extra tests useful mainly to perf +# developers, such as: +# x86 instruction decoder - new instructions test # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 33d39c1d3e64..df133020d582 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -6,7 +6,9 @@ struct test_suite; /* Tests */ int test__rdpmc(struct test_suite *test, int subtest); +#ifdef HAVE_EXTRA_TESTS int test__insn_x86(struct test_suite *test, int subtest); +#endif int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest); int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest); int test__bp_modify(struct test_suite *test, int subtest); diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 08cc8b9c931e..394771c00dca 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build @@ -4,5 +4,8 @@ perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-y += arch-tests.o perf-y += sample-parsing.o perf-y += hybrid.o -perf-$(CONFIG_AUXTRACE) += insn-x86.o intel-pt-test.o +perf-$(CONFIG_AUXTRACE) += intel-pt-test.o +ifeq ($(CONFIG_EXTRA_TESTS),y) +perf-$(CONFIG_AUXTRACE) += insn-x86.o +endif perf-$(CONFIG_X86_64) += bp-modify.o diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index 147ad0638bbb..3f2b90c59f92 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -4,7 +4,9 @@ #include "arch-tests.h" #ifdef HAVE_AUXTRACE_SUPPORT +#ifdef HAVE_EXTRA_TESTS DEFINE_SUITE("x86 instruction decoder - new instructions", insn_x86); +#endif static struct test_case intel_pt_tests[] = { TEST_CASE("Intel PT packet decoder", intel_pt_pkt_decoder), @@ -37,7 +39,9 @@ struct test_suite *arch_tests[] = { &suite__dwarf_unwind, #endif #ifdef HAVE_AUXTRACE_SUPPORT +#ifdef HAVE_EXTRA_TESTS &suite__insn_x86, +#endif &suite__intel_pt, #endif #if defined(__x86_64__) diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 8dd3f8090352..885cd321d67b 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -69,6 +69,7 @@ make_clean_all := clean all make_python_perf_so := $(python_perf_so) make_debug := DEBUG=1 make_nondistro := BUILD_NONDISTRO=1 +make_extra_tests := EXTRA_TESTS=1 make_no_libperl := NO_LIBPERL=1 make_no_libpython := NO_LIBPYTHON=1 make_no_scripts := NO_LIBPYTHON=1 NO_LIBPERL=1 -- cgit v1.2.3 From b3839ff1f40eba632177bc4775a35ed65a2262a6 Mon Sep 17 00:00:00 2001 From: Disha Goel Date: Tue, 13 Jun 2023 22:11:30 +0530 Subject: perf tests stat+json_output: Address shellcheck warnings Running shellcheck on stat+json_output testcase, generates below warning: [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ] ^------^ SC2046 (warning): Quote this to prevent word splitting. ^-- SC2046 (warning): Quote this to prevent word splitting. Fixed the warning by adding quotes to avoid word splitting. ShellCheck result with patch: # shellcheck -S warning stat+json_output.sh # perf test result after the change: 94: perf stat JSON output linter : Ok Signed-off-by: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/linux-perf-users/20230613164145.50488-3-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat+json_output.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/shell/stat+json_output.sh index c282afa6217c..196e22672c50 100755 --- a/tools/perf/tests/shell/stat+json_output.sh +++ b/tools/perf/tests/shell/stat+json_output.sh @@ -40,7 +40,7 @@ trap trap_cleanup EXIT TERM INT # Return true if perf_event_paranoid is > $1 and not running as root. function ParanoidAndNotRoot() { - [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ] + [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] } check_no_args() -- cgit v1.2.3 From 1bb17b4c6c91ad4d9468247cf5f5464fa6440668 Mon Sep 17 00:00:00 2001 From: Spoorthy S Date: Tue, 13 Jun 2023 22:11:31 +0530 Subject: perf tests arm_callgraph_fp: Address shellcheck warnings about signal names and adding double quotes for expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running shellcheck -S on test_arm_calligraph_fp throws warnings SC2086 and SC3049,       $shellcheck -S warning tests/shell/test_arm_callgraph_fp.sh          rm -f $PERF_DATA             : Double quote to prevent globbing and word splitting.          trap cleanup_files exit term int       : In POSIX sh, using lower/mixed case for signal names is undefined. After fixing the warnings,       $shellcheck tests/shell/test_arm_callgraph_fp.sh       $ echo $?       0 To address the POSIX shell warnings added changes to convert Lowercase signal names to uppercase in the script and double quoted the command substitutions($fix to "$fix") to solve Globbing warnings. Signed-off-by: Spoorthy S Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-4-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_callgraph_fp.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_arm_callgraph_fp.sh b/tools/perf/tests/shell/test_arm_callgraph_fp.sh index e61d8deaa0c4..1380e0d12dce 100755 --- a/tools/perf/tests/shell/test_arm_callgraph_fp.sh +++ b/tools/perf/tests/shell/test_arm_callgraph_fp.sh @@ -9,13 +9,13 @@ TEST_PROGRAM="perf test -w leafloop" cleanup_files() { - rm -f $PERF_DATA + rm -f "$PERF_DATA" } -trap cleanup_files exit term int +trap cleanup_files EXIT TERM INT # Add a 1 second delay to skip samples that are not in the leaf() function -perf record -o $PERF_DATA --call-graph fp -e cycles//u -D 1000 --user-callchains -- $TEST_PROGRAM 2> /dev/null & +perf record -o "$PERF_DATA" --call-graph fp -e cycles//u -D 1000 --user-callchains -- "$TEST_PROGRAM" 2> /dev/null & PID=$! echo " + Recording (PID=$PID)..." -- cgit v1.2.3 From 5bd35dfb48b0af870093f2ee130883228b49352a Mon Sep 17 00:00:00 2001 From: Shirisha G Date: Tue, 13 Jun 2023 22:11:32 +0530 Subject: perf tests daemon: Address shellcheck warnings Running shellcheck -S on daemon.sh throws below warnings: Result from shellcheck: # shellcheck -S warning daemon.sh local line_name=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $2 }'` ^-------^ SC2155: Declare and assign separately to avoid masking return values. trap "echo 'FAILED: Signal caught'; daemon_exit ${config}; exit 1" SIGINT SIGTERM ^-------^ SC2064: Use single quotes, otherwise this expands now rather than when signalled. count=`ls ${base}/session-test/ | grep perf.data | wc -l` ^-- SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames. if [ ${size} != "OK" -o ${type} != "OK" ]; then ^-- SC2166: Prefer [ p ] || [ q ] as [ p -o q ] is not well defined. Fixed above warnings by: - declaring and assigning local variables separately - To fix SC2010, instead of using "ls | grep", used glob to allow non-alphanumeric filenames - Used single quotes to prevent expanding. Result from shellcheck after patch changes: $ shellcheck -S warning daemon.sh $ echo $? 0 Signed-off-by: Shirisha G Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-5-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/daemon.sh | 113 ++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 38 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh index 45fc24af5b07..4c598cfc5afa 100755 --- a/tools/perf/tests/shell/daemon.sh +++ b/tools/perf/tests/shell/daemon.sh @@ -11,11 +11,16 @@ check_line_first() local lock=$5 local up=$6 - local line_name=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $2 }'` - local line_base=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $3 }'` - local line_output=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $4 }'` - local line_lock=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $5 }'` - local line_up=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $6 }'` + local line_name + line_name=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $2 }'` + local line_base + line_base=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $3 }'` + local line_output + line_output=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $4 }'` + local line_lock + line_lock=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $5 }'` + local line_up + line_up=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $6 }'` if [ "${name}" != "${line_name}" ]; then echo "FAILED: wrong name" @@ -54,13 +59,20 @@ check_line_other() local ack=$7 local up=$8 - local line_name=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $2 }'` - local line_run=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $3 }'` - local line_base=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $4 }'` - local line_output=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $5 }'` - local line_control=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $6 }'` - local line_ack=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $7 }'` - local line_up=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $8 }'` + local line_name + line_name=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $2 }'` + local line_run + line_run=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $3 }'` + local line_base + line_base=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $4 }'` + local line_output + line_output=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $5 }'` + local line_control + line_control=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $6 }'` + local line_ack + line_ack=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $7 }'` + local line_up + line_up=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $8 }'` if [ "${name}" != "${line_name}" ]; then echo "FAILED: wrong name" @@ -102,8 +114,10 @@ daemon_exit() { local config=$1 - local line=`perf daemon --config ${config} -x: | head -1` - local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'` + local line + line=`perf daemon --config ${config} -x: | head -1` + local pid + pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'` # Reset trap handler. trap - SIGINT SIGTERM @@ -123,7 +137,7 @@ daemon_start() perf daemon start --config ${config} # Clean up daemon if interrupted. - trap "echo 'FAILED: Signal caught'; daemon_exit ${config}; exit 1" SIGINT SIGTERM + trap 'echo "FAILED: Signal caught"; daemon_exit "${config}"; exit 1' SIGINT SIGTERM # wait for the session to ping local state="FAIL" @@ -144,8 +158,10 @@ test_list() { echo "test daemon list" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) cat < ${config} [daemon] @@ -165,19 +181,22 @@ EOF # check first line # pid:daemon:base:base/output:base/lock - local line=`perf daemon --config ${config} -x: | head -1` + local line + line=`perf daemon --config ${config} -x: | head -1` check_line_first ${line} daemon ${base} ${base}/output ${base}/lock "0" # check 1st session # pid:size:-e cpu-clock:base/size:base/size/output:base/size/control:base/size/ack:0 - local line=`perf daemon --config ${config} -x: | head -2 | tail -1` + local line + line=`perf daemon --config ${config} -x: | head -2 | tail -1` check_line_other "${line}" size "-e cpu-clock -m 1 sleep 10" ${base}/session-size \ ${base}/session-size/output ${base}/session-size/control \ ${base}/session-size/ack "0" # check 2nd session # pid:time:-e task-clock:base/time:base/time/output:base/time/control:base/time/ack:0 - local line=`perf daemon --config ${config} -x: | head -3 | tail -1` + local line + line=`perf daemon --config ${config} -x: | head -3 | tail -1` check_line_other "${line}" time "-e task-clock -m 1 sleep 10" ${base}/session-time \ ${base}/session-time/output ${base}/session-time/control \ ${base}/session-time/ack "0" @@ -193,8 +212,10 @@ test_reconfig() { echo "test daemon reconfig" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) # prepare config cat < ${config} @@ -215,10 +236,12 @@ EOF # check 2nd session # pid:time:-e task-clock:base/time:base/time/output:base/time/control:base/time/ack:0 - local line=`perf daemon --config ${config} -x: | head -3 | tail -1` + local line + line=`perf daemon --config ${config} -x: | head -3 | tail -1` check_line_other "${line}" time "-e task-clock -m 1 sleep 10" ${base}/session-time \ ${base}/session-time/output ${base}/session-time/control ${base}/session-time/ack "0" - local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'` + local pid + pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'` # prepare new config local config_new=${config}.new @@ -249,7 +272,8 @@ EOF # check reconfigured 2nd session # pid:time:-e task-clock:base/time:base/time/output:base/time/control:base/time/ack:0 - local line=`perf daemon --config ${config} -x: | head -3 | tail -1` + local line + line=`perf daemon --config ${config} -x: | head -3 | tail -1` check_line_other "${line}" time "-e cpu-clock -m 1 sleep 10" ${base}/session-time \ ${base}/session-time/output ${base}/session-time/control ${base}/session-time/ack "0" @@ -276,7 +300,8 @@ EOF state=`perf daemon ping --config ${config} --session size | awk '{ print $1 }'` done - local one=`perf daemon --config ${config} -x: | wc -l` + local one + one=`perf daemon --config ${config} -x: | wc -l` if [ ${one} -ne "1" ]; then echo "FAILED: wrong list output" @@ -312,8 +337,10 @@ test_stop() { echo "test daemon stop" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) # prepare config cat < ${config} @@ -332,8 +359,12 @@ EOF # start daemon daemon_start ${config} size - local pid_size=`perf daemon --config ${config} -x: | head -2 | tail -1 | awk 'BEGIN { FS = ":" } ; { print $1 }'` - local pid_time=`perf daemon --config ${config} -x: | head -3 | tail -1 | awk 'BEGIN { FS = ":" } ; { print $1 }'` + local pid_size + pid_size=`perf daemon --config ${config} -x: | head -2 | tail -1 | + awk 'BEGIN { FS = ":" } ; { print $1 }'` + local pid_time + pid_time=`perf daemon --config ${config} -x: | head -3 | tail -1 | + awk 'BEGIN { FS = ":" } ; { print $1 }'` # check that sessions are running if [ ! -d "/proc/${pid_size}" ]; then @@ -364,8 +395,10 @@ test_signal() { echo "test daemon signal" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) # prepare config cat < ${config} @@ -389,7 +422,7 @@ EOF daemon_exit ${config} # count is 2 perf.data for signals and 1 for perf record finished - count=`ls ${base}/session-test/ | grep perf.data | wc -l` + count=`ls ${base}/session-test/*perf.data* | wc -l` if [ ${count} -ne 3 ]; then error=1 echo "FAILED: perf data no generated" @@ -403,8 +436,10 @@ test_ping() { echo "test daemon ping" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) # prepare config cat < ${config} @@ -426,7 +461,7 @@ EOF size=`perf daemon ping --config ${config} --session size | awk '{ print $1 }'` type=`perf daemon ping --config ${config} --session time | awk '{ print $1 }'` - if [ ${size} != "OK" -o ${type} != "OK" ]; then + if [ ${size} != "OK" ] || [ ${type} != "OK" ]; then error=1 echo "FAILED: daemon ping failed" fi @@ -442,8 +477,10 @@ test_lock() { echo "test daemon lock" - local config=$(mktemp /tmp/perf.daemon.config.XXX) - local base=$(mktemp -d /tmp/perf.daemon.base.XXX) + local config + config=$(mktemp /tmp/perf.daemon.config.XXX) + local base + base=$(mktemp -d /tmp/perf.daemon.base.XXX) # prepare config cat < ${config} -- cgit v1.2.3 From 9e9d07a71fa44ead54eda05754d17aa02f18b5b2 Mon Sep 17 00:00:00 2001 From: Korrapati Likhitha Date: Tue, 13 Jun 2023 22:11:33 +0530 Subject: perf tests stat+csv_output: Fix shellcheck warnings Running the shellcheck on stat+csv_output resulted in the following warning. Result with shellcheck without patch: ===== $ shellcheck -S warning stat+csv_output.sh In stat+csv_output.sh line 23: [ $(uname -m) = "s390x" ] && exp='^[6-7]$' ^---------^ SC2046: Quote this to prevent word splitting. In stat+csv_output.sh line 51: [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ] ^------^ SC2046: Quote this to prevent word splitting. ^-- SC2046: Quote this to prevent word splitting. ===== Fixed the warning SC2046 by adding quotes to prevent word splitting. Result with shellcheck with patch: ===== $ shellcheck -S warning tests/shell/stat+csv_output.sh $ ./perf test "stat CSV output linter" 96: perf stat CSV output linter : Ok ===== Signed-off-by: Korrapati Likhitha Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-6-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Sathvika Vasireddy Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat+csv_output.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh index a1969f236a0a..ed082daf839c 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -35,7 +35,7 @@ function commachecker() ;; "--interval") exp=7 ;; "--per-thread") exp=7 ;; "--system-wide-no-aggr") exp=7 - [ $(uname -m) = "s390x" ] && exp='^[6-7]$' + [ "$(uname -m)" = "s390x" ] && exp='^[6-7]$' ;; "--per-core") exp=8 ;; "--per-socket") exp=8 ;; "--per-node") exp=8 @@ -66,7 +66,7 @@ function commachecker() # Return true if perf_event_paranoid is > $1 and not running as root. function ParanoidAndNotRoot() { - [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ] + [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] } check_no_args() -- cgit v1.2.3 From 0ed4b531e7da1193fa10786672f28d7734eb06ec Mon Sep 17 00:00:00 2001 From: Anushree Mathur Date: Tue, 13 Jun 2023 22:11:35 +0530 Subject: perf tests test_arm_coresight: Shellcheck fixes Fixed the following shellcheck issues in test_arm_coresight.sh file: In tools/perf/tests/shell/test_arm_coresight.sh line 31: trap - exit term int ^--^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. ^--^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. ^-^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. In tools/perf/tests/shell/test_arm_coresight.sh line 35: trap cleanup_files exit term int ^--^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. ^--^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. ^-^ SC2039: In POSIX sh, using lower/mixed case for signal names is undefined. In tools/perf/tests/shell/test_arm_coresight.sh line 92: if [ $? -eq 0 -a -e "$1/enable_sink" ]; then ^-- SC2166: Prefer [ p ] && [ q ] as [ p -a q ] is not well defined. Fixed above warnings by: 1)Capitalize signals(INT, TERM, EXIT) to avoid mixed/lower case naming of signals. 2)Expression [p -a q] was not defined,changed it to [p] && [q] to avoid the ambiguity as this is older format using -a or -o ,now we use [p] && [q] in place of [p -a q] and [p] || [q] in place of [p -o q]. Result after fixing the issues: shell$ shellcheck -S warning test_arm_coresight.sh shell$ Signed-off-by: Anushree Mathur Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-8-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index 482009e17bda..f1bf5621160f 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -28,11 +28,11 @@ cleanup_files() rm -f ${perfdata} rm -f ${file} rm -f "${perfdata}.old" - trap - exit term int + trap - EXIT TERM INT exit $glb_err } -trap cleanup_files exit term int +trap cleanup_files EXIT TERM INT record_touch_file() { echo "Recording trace (only user mode) with path: CPU$2 => $1" @@ -89,7 +89,7 @@ is_device_sink() { # cannot support perf PMU. echo "$1" | grep -E -q -v "tpiu" - if [ $? -eq 0 -a -e "$1/enable_sink" ]; then + if [ $? -eq 0 ] && [ -e "$1/enable_sink" ]; then pmu_dev="/sys/bus/event_source/devices/cs_etm/sinks/$2" -- cgit v1.2.3 From a6bdb815ad60f35f581ee0b48a886f7e451e34a3 Mon Sep 17 00:00:00 2001 From: Barnali Guha Thakurata Date: Tue, 13 Jun 2023 22:11:36 +0530 Subject: perf tests stat_all_metrics: Fix shellcheck warning SC2076 Fixed shellcheck warning SC2076 in stat_all_metrics.sh. Before the patch: shell$ shellcheck stat_all_metrics.sh In stat_all_metrics.sh line 9: if [[ "$result" =~ "${m:0:50}" ]] || [[ "$result" =~ "" ]] ^---------^ SC2076: Don't quote right-hand side of =~, it'll match literally rather than as a regex. In stat_all_metrics.sh line 15: if [[ "$result" =~ "${m:0:50}" ]] ^---------^ SC2076: Don't quote right-hand side of =~, it'll match literally rather than as a regex. In stat_all_metrics.sh line 22: if [[ "$result" =~ "${m:0:50}" ]] ^---------^ SC2076: Don't quote right-hand side of =~, it'll match literally rather than as a regex. After the patch: shell$ shellcheck stat_all_metrics.sh shell$ Signed-off-by: Barnali Guha Thakurata Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-9-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat_all_metrics.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh index 22e9cb294b40..54774525e18a 100755 --- a/tools/perf/tests/shell/stat_all_metrics.sh +++ b/tools/perf/tests/shell/stat_all_metrics.sh @@ -6,20 +6,20 @@ err=0 for m in $(perf list --raw-dump metrics); do echo "Testing $m" result=$(perf stat -M "$m" true 2>&1) - if [[ "$result" =~ "${m:0:50}" ]] || [[ "$result" =~ "" ]] + if [[ "$result" =~ ${m:0:50} ]] || [[ "$result" =~ "" ]] then continue fi # Failed so try system wide. result=$(perf stat -M "$m" -a sleep 0.01 2>&1) - if [[ "$result" =~ "${m:0:50}" ]] + if [[ "$result" =~ ${m:0:50} ]] then continue fi # Failed again, possibly the workload was too small so retry with something # longer. result=$(perf stat -M "$m" perf bench internals synthesize 2>&1) - if [[ "$result" =~ "${m:0:50}" ]] + if [[ "$result" =~ ${m:0:50} ]] then continue fi -- cgit v1.2.3 From 9694dfe0a3fc81309f4c0a9a6a5f99b64caa851a Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Tue, 13 Jun 2023 22:11:37 +0530 Subject: perf tests test_task_analyzer: Fix shellcheck issues Fixed the following shellcheck issues in test_task_analyzer.sh file: SC2086: Double quote to prevent globbing and word splitting warnings in shell-check. Fixes the following shellcheck issues, SC2086: Double quote to prevent globbing and word splitting warnings in shell-check. Before Patch: $ shellcheck ./test_task_analyzer.sh | grep "SC2086" | ... In ./test_task_analyzer.sh line 13: SC2086: Double quote to prevent globbing and word splitting. In ./test_task_analyzer.sh line 24: SC2086: Double quote to prevent globbing and word splitting. In ./test_task_analyzer.sh line 39: SC2086: Double quote to prevent globbing and word splitting. After Patch: $ shellcheck ./test_task_analyzer.sh | grep -i "SC2086" None perf test result after patch: PASS: "test_basic" PASS: "test_ns_rename" PASS: "test_ms_filtertasks_highlight" PASS: "test_extended_times_timelimit_limittasks" PASS: "test_summary" PASS: "test_summaryextended" PASS: "test_summaryonly" PASS: "test_extended_times_summary_ns" PASS: "test_extended_times_summary_ns" PASS: "test_csv" PASS: "test_csvsummary" PASS: "test_csv_extended_times" PASS: "test_csvsummary_extended" Signed-off-by: Aboorva Devarajan Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-10-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_task_analyzer.sh | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index a98e4ab66040..4264b54b654b 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -10,7 +10,7 @@ cleanup() { rm -f perf.data.old rm -f csv rm -f csvsummary - rm -rf $tmpdir + rm -rf "$tmpdir" trap - exit term int } @@ -21,7 +21,7 @@ trap_cleanup() { trap trap_cleanup exit term int report() { - if [ $1 = 0 ]; then + if [ "$1" = 0 ]; then echo "PASS: \"$2\"" else echo "FAIL: \"$2\" Error message: \"$3\"" @@ -36,11 +36,11 @@ check_exec_0() { } find_str_or_fail() { - grep -q "$1" $2 - if [ $? != 0 ]; then - report 1 $3 "Failed to find required string:'${1}'." + grep -q "$1" "$2" + if [ "$?" != 0 ]; then + report 1 "$3" "Failed to find required string:'${1}'." else - report 0 $3 + report 0 "$3" fi } @@ -52,86 +52,86 @@ prepare_perf_data() { # check standard inkvokation with no arguments test_basic() { out="$tmpdir/perf.out" - perf script report task-analyzer > $out + perf script report task-analyzer > "$out" check_exec_0 "perf" - find_str_or_fail "Comm" $out ${FUNCNAME[0]} + find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } test_ns_rename(){ out="$tmpdir/perf.out" - perf script report task-analyzer --ns --rename-comms-by-tids 0:random > $out + perf script report task-analyzer --ns --rename-comms-by-tids 0:random > "$out" check_exec_0 "perf" - find_str_or_fail "Comm" $out ${FUNCNAME[0]} + find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } test_ms_filtertasks_highlight(){ out="$tmpdir/perf.out" perf script report task-analyzer --ms --filter-tasks perf --highlight-tasks perf \ - > $out + > "$out" check_exec_0 "perf" - find_str_or_fail "Comm" $out ${FUNCNAME[0]} + find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } test_extended_times_timelimit_limittasks() { out="$tmpdir/perf.out" perf script report task-analyzer --extended-times --time-limit :99999 \ - --limit-to-tasks perf > $out + --limit-to-tasks perf > "$out" check_exec_0 "perf" - find_str_or_fail "Out-Out" $out ${FUNCNAME[0]} + find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" } test_summary() { out="$tmpdir/perf.out" - perf script report task-analyzer --summary > $out + perf script report task-analyzer --summary > "$out" check_exec_0 "perf" - find_str_or_fail "Summary" $out ${FUNCNAME[0]} + find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_summaryextended() { out="$tmpdir/perf.out" - perf script report task-analyzer --summary-extended > $out + perf script report task-analyzer --summary-extended > "$out" check_exec_0 "perf" - find_str_or_fail "Inter Task Times" $out ${FUNCNAME[0]} + find_str_or_fail "Inter Task Times" "$out" "${FUNCNAME[0]}" } test_summaryonly() { out="$tmpdir/perf.out" - perf script report task-analyzer --summary-only > $out + perf script report task-analyzer --summary-only > "$out" check_exec_0 "perf" - find_str_or_fail "Summary" $out ${FUNCNAME[0]} + find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_extended_times_summary_ns() { out="$tmpdir/perf.out" - perf script report task-analyzer --extended-times --summary --ns > $out + perf script report task-analyzer --extended-times --summary --ns > "$out" check_exec_0 "perf" - find_str_or_fail "Out-Out" $out ${FUNCNAME[0]} - find_str_or_fail "Summary" $out ${FUNCNAME[0]} + find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" + find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_csv() { perf script report task-analyzer --csv csv > /dev/null check_exec_0 "perf" - find_str_or_fail "Comm;" csv ${FUNCNAME[0]} + find_str_or_fail "Comm;" csv "${FUNCNAME[0]}" } test_csv_extended_times() { perf script report task-analyzer --csv csv --extended-times > /dev/null check_exec_0 "perf" - find_str_or_fail "Out-Out;" csv ${FUNCNAME[0]} + find_str_or_fail "Out-Out;" csv "${FUNCNAME[0]}" } test_csvsummary() { perf script report task-analyzer --csv-summary csvsummary > /dev/null check_exec_0 "perf" - find_str_or_fail "Comm;" csvsummary ${FUNCNAME[0]} + find_str_or_fail "Comm;" csvsummary "${FUNCNAME[0]}" } test_csvsummary_extended() { perf script report task-analyzer --csv-summary csvsummary --summary-extended \ >/dev/null check_exec_0 "perf" - find_str_or_fail "Out-Out;" csvsummary ${FUNCNAME[0]} + find_str_or_fail "Out-Out;" csvsummary "${FUNCNAME[0]}" } prepare_perf_data -- cgit v1.2.3 From e0da03c7b16b466750f0bd91865a2a000f1422b7 Mon Sep 17 00:00:00 2001 From: Abhirup Deb Date: Tue, 13 Jun 2023 22:11:38 +0530 Subject: perf tests test_arm_spe: Address shellcheck warnings about signal name case Running shellcheck -S on test_arm_spe.sh throws below warnings: #shellcheck -S warning tests/shell/test_arm_spe.sh In tests/shell/test_arm_spe.sh line 30: trap cleanup_files exit term int ^--^ SC3049 (warning): In POSIX sh, using lower/mixed case for signal names is undefined. ^--^ SC3049 (warning): In POSIX sh, using lower/mixed case for signal names is undefined. ^-^ SC3049 (warning): In POSIX sh, using lower/mixed case for signal names is undefined. Fixed this issue by using uppercase for "EXIT", "TERM" and "INIT" signals to avoid using lower/mixed case for signal names as input. Signed-off-by: Abhirup Deb Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-11-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Mukesh Chaurasiya Signed-off-by: Ojaswin Mujoo Signed-off-by: Piyush Sachdeva Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_spe.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_arm_spe.sh b/tools/perf/tests/shell/test_arm_spe.sh index aa094d71f5b4..03d5c7d12ee5 100755 --- a/tools/perf/tests/shell/test_arm_spe.sh +++ b/tools/perf/tests/shell/test_arm_spe.sh @@ -27,7 +27,7 @@ cleanup_files() exit $glb_err } -trap cleanup_files exit term int +trap cleanup_files EXIT TERM INT arm_spe_report() { if [ $2 = 0 ]; then -- cgit v1.2.3 From fa33cbe26683607f69ed3b6885356e94fadc5ca2 Mon Sep 17 00:00:00 2001 From: Abhirup Deb Date: Tue, 13 Jun 2023 22:11:39 +0530 Subject: perf tests lock_contention: Fix shellscript errors Use quotes around variables to prevent POSIX word expansion, use uppercase for signals(INT, TERM, EXIT) to avoid mixed/lower case naming of signals and replace "==" with "=" as "==" is not supported by POSIX shell. Signed-off-by: Abhirup Deb Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-12-atrajeev@linux.vnet.ibm.com Signed-off-by: Anushree Mathur Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lock_contention.sh | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index be5fcafb26aa..f2cc187b6186 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -11,14 +11,14 @@ result=$(mktemp /tmp/__perf_test.result.XXXXX) cleanup() { rm -f ${perfdata} rm -f ${result} - trap - exit term int + trap - EXIT TERM INT } trap_cleanup() { cleanup exit ${err} } -trap trap_cleanup exit term int +trap trap_cleanup EXIT TERM INT check() { if [ `id -u` != 0 ]; then @@ -40,8 +40,8 @@ test_record() perf lock record -o ${perfdata} -- perf bench sched messaging > /dev/null 2>&1 # the output goes to the stderr and we expect only 1 output (-E 1) perf lock contention -i ${perfdata} -E 1 -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -58,8 +58,8 @@ test_bpf() # the perf lock contention output goes to the stderr perf lock con -a -b -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] BPF result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -70,8 +70,8 @@ test_record_concurrent() echo "Testing perf lock record and perf lock contention at the same time" perf lock record -o- -- perf bench sched messaging 2> /dev/null | \ perf lock contention -i- -E 1 -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -81,8 +81,8 @@ test_aggr_task() { echo "Testing perf lock contention --threads" perf lock contention -i ${perfdata} -t -E 1 -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -93,8 +93,8 @@ test_aggr_task() # the perf lock contention output goes to the stderr perf lock con -a -b -t -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] BPF result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -104,8 +104,8 @@ test_aggr_addr() { echo "Testing perf lock contention --lock-addr" perf lock contention -i ${perfdata} -l -E 1 -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -116,8 +116,8 @@ test_aggr_addr() # the perf lock contention output goes to the stderr perf lock con -a -b -l -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] BPF result count is not 1:" $(cat "${result}" | wc -l) + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)" err=1 exit fi @@ -127,8 +127,8 @@ test_type_filter() { echo "Testing perf lock contention --type-filter (w/ spinlock)" perf lock contention -i ${perfdata} -Y spinlock -q 2> ${result} - if [ $(grep -c -v spinlock "${result}") != "0" ]; then - echo "[Fail] Recorded result should not have non-spinlocks:" $(cat "${result}") + if [ "$(grep -c -v spinlock "${result}")" != "0" ]; then + echo "[Fail] Recorded result should not have non-spinlocks:" "$(cat "${result}")" err=1 exit fi @@ -138,8 +138,8 @@ test_type_filter() fi perf lock con -a -b -Y spinlock -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(grep -c -v spinlock "${result}") != "0" ]; then - echo "[Fail] BPF result should not have non-spinlocks:" $(cat "${result}") + if [ "$(grep -c -v spinlock "${result}")" != "0" ]; then + echo "[Fail] BPF result should not have non-spinlocks:" "$(cat "${result}")" err=1 exit fi @@ -149,7 +149,7 @@ test_lock_filter() { echo "Testing perf lock contention --lock-filter (w/ tasklist_lock)" perf lock contention -i ${perfdata} -l -q 2> ${result} - if [ $(grep -c tasklist_lock "${result}") != "1" ]; then + if [ "$(grep -c tasklist_lock "${result}")" != "1" ]; then echo "[Skip] Could not find 'tasklist_lock'" return fi @@ -159,8 +159,8 @@ test_lock_filter() # find out the type of tasklist_lock local type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//') - if [ $(grep -c -v "${type}" "${result}") != "0" ]; then - echo "[Fail] Recorded result should not have non-${type} locks:" $(cat "${result}") + if [ "$(grep -c -v "${type}" "${result}")" != "0" ]; then + echo "[Fail] Recorded result should not have non-${type} locks:" "$(cat "${result}")" err=1 exit fi @@ -170,8 +170,8 @@ test_lock_filter() fi perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(grep -c -v "${type}" "${result}") != "0" ]; then - echo "[Fail] BPF result should not have non-${type} locks:" $(cat "${result}") + if [ "$(grep -c -v "${type}" "${result}")" != "0" ]; then + echo "[Fail] BPF result should not have non-${type} locks:" "$(cat "${result}")" err=1 exit fi @@ -181,14 +181,14 @@ test_stack_filter() { echo "Testing perf lock contention --callstack-filter (w/ unix_stream)" perf lock contention -i ${perfdata} -v -q 2> ${result} - if [ $(grep -c unix_stream "${result}") == "0" ]; then + if [ "$(grep -c unix_stream "${result}")" = "0" ]; then echo "[Skip] Could not find 'unix_stream'" return fi perf lock contention -i ${perfdata} -E 1 -S unix_stream -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result should have a lock from unix_stream:" $(cat "${result}") + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result should have a lock from unix_stream:" "$(cat "${result}")" err=1 exit fi @@ -198,8 +198,8 @@ test_stack_filter() fi perf lock con -a -b -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] BPF result should have a lock from unix_stream:" $(cat "${result}") + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] BPF result should have a lock from unix_stream:" "$(cat "${result}")" err=1 exit fi @@ -209,14 +209,14 @@ test_aggr_task_stack_filter() { echo "Testing perf lock contention --callstack-filter with task aggregation" perf lock contention -i ${perfdata} -v -q 2> ${result} - if [ $(grep -c unix_stream "${result}") == "0" ]; then + if [ "$(grep -c unix_stream "${result}")" = "0" ]; then echo "[Skip] Could not find 'unix_stream'" return fi perf lock contention -i ${perfdata} -t -E 1 -S unix_stream -q 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] Recorded result should have a task from unix_stream:" $(cat "${result}") + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] Recorded result should have a task from unix_stream:" "$(cat "${result}")" err=1 exit fi @@ -226,8 +226,8 @@ test_aggr_task_stack_filter() fi perf lock con -a -b -t -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ $(cat "${result}" | wc -l) != "1" ]; then - echo "[Fail] BPF result should have a task from unix_stream:" $(cat "${result}") + if [ "$(cat "${result}" | wc -l)" != "1" ]; then + echo "[Fail] BPF result should have a task from unix_stream:" "$(cat "${result}")" err=1 exit fi -- cgit v1.2.3 From ed46a9994956b5ee53d62f848b1b69579201a7ec Mon Sep 17 00:00:00 2001 From: Samir Mulani Date: Tue, 13 Jun 2023 22:11:40 +0530 Subject: perf tests shell: Fixed shellcheck warnings Fixed the shellcheck warnings in buildid.sh, record+probe_libc_inet_pton.sh and record+script_probe_vfs_getname.sh perf shell scripts: 1. Prefer [ p ] && [ q ] as [ p -a q ] is not well defined. 2. Prefer [ p ] || [ q ] as [ p -o q ] is not well defined. 3. Used * argument to avoid the argument mixes string and array 4. Resolved issue for variable refernce, where the variable is being used before it has been initialized. 5. Resolved word splitting issue (syntax error). 6. The "err" variable has been removed from buildid.sh since it is not used anywhere in the code. Signed-off-by: Samir Mulani Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-13-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/buildid.sh | 12 ++++++------ tools/perf/tests/shell/record+probe_libc_inet_pton.sh | 6 +++--- tools/perf/tests/shell/record+script_probe_vfs_getname.sh | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/buildid.sh b/tools/perf/tests/shell/buildid.sh index 0ce22ea0a7f1..3383ca3399d4 100755 --- a/tools/perf/tests/shell/buildid.sh +++ b/tools/perf/tests/shell/buildid.sh @@ -83,12 +83,12 @@ check() # in case of pe-file.exe file echo $1 | grep ".exe" if [ $? -eq 0 ]; then - if [ -x $1 -a ! -x $file ]; then + if [ -x $1 ] && [ ! -x $file ]; then echo "failed: file ${file} executable does not exist" exit 1 fi - if [ ! -x $file -a ! -e $file ]; then + if [ ! -x $file ] && [ ! -e $file ]; then echo "failed: file ${file} does not exist" exit 1 fi @@ -136,10 +136,10 @@ test_record() log_err=$(mktemp /tmp/perf.log.err.XXX) perf="perf --buildid-dir ${build_id_dir}" - echo "running: perf record $@" - ${perf} record --buildid-all -o ${data} $@ 1>${log_out} 2>${log_err} + echo "running: perf record $*" + ${perf} record --buildid-all -o ${data} "$@" 1>${log_out} 2>${log_err} if [ $? -ne 0 ]; then - echo "failed: record $@" + echo "failed: record $*" echo "see log: ${log_err}" exit 1 fi @@ -172,4 +172,4 @@ if [ ${run_pe} -eq 1 ]; then rm -r ${wineprefix} fi -exit ${err} +exit 0 diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index bbb5b3d185fa..0934fb0cd68f 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -10,8 +10,8 @@ # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo , 2017 -. $(dirname $0)/lib/probe.sh -. $(dirname $0)/lib/probe_vfs_getname.sh +. "$(dirname "$0")/lib/probe.sh" +. "$(dirname "$0")/lib/probe_vfs_getname.sh" libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g') nm -Dg $libc 2>/dev/null | fgrep -q inet_pton || exit 254 @@ -23,7 +23,7 @@ add_libc_inet_pton_event() { event_name=$(perf probe -f -x $libc -a inet_pton 2>&1 | tail -n +2 | head -n -5 | \ grep -P -o "$event_pattern(?=[[:space:]]\(on inet_pton in $libc\))") - if [ $? -ne 0 -o -z "$event_name" ] ; then + if [ $? -ne 0 ] || [ -z "$event_name" ] ; then printf "FAIL: could not add event\n" return 1 fi diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh index 1341437e1bd9..7f664f1889d9 100755 --- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh +++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh @@ -9,11 +9,11 @@ # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo , 2017 -. $(dirname $0)/lib/probe.sh +. "$(dirname "$0")/lib/probe.sh" skip_if_no_perf_probe || exit 2 -. $(dirname $0)/lib/probe_vfs_getname.sh +. "$(dirname "$0")/lib/probe_vfs_getname.sh" record_open_file() { echo "Recording open file:" -- cgit v1.2.3 From 3b3bf0d112163524e61d1d6456c65e2157aade74 Mon Sep 17 00:00:00 2001 From: Geetika Date: Tue, 13 Jun 2023 22:11:41 +0530 Subject: perf tests test_brstack.sh: Fix all POSIX sh warnings Fix all the POSIX sh warnings in perf shell test test_brstack.sh Warnings fixed : * In POSIX sh, using lower/mixed case for signal names is undefined. Correcting that in this script. * In POSIX sh, 'local' is undefined. local is supported in many shells, but it's not in POSIX. In POSIX sh, you can adopt some convention to avoid accidentally overwriting variables names, e.g. prefixing with the function name, that is what I have done here. Signed-off-by: Geetika Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-14-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_brstack.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh index 1c49d8293003..09908d71c994 100755 --- a/tools/perf/tests/shell/test_brstack.sh +++ b/tools/perf/tests/shell/test_brstack.sh @@ -18,7 +18,7 @@ cleanup() { rm -rf $TMPDIR } -trap cleanup exit term int +trap cleanup EXIT TERM INT test_user_branches() { echo "Testing user branch stack sampling" @@ -47,17 +47,17 @@ test_user_branches() { # first argument is the argument passed to "--branch-stack ,save_type,u" # second argument are the expected branch types for the given filter test_filter() { - local filter=$1 - local expect=$2 + test_filter_filter=$1 + test_filter_expect=$2 - echo "Testing branch stack filtering permutation ($filter,$expect)" + echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)" - perf record -o $TMPDIR/perf.data --branch-filter $filter,save_type,u -- ${TESTPROG} > /dev/null 2>&1 + perf record -o $TMPDIR/perf.data --branch-filter $test_filter_filter,save_type,u -- ${TESTPROG} > /dev/null 2>&1 perf script -i $TMPDIR/perf.data --fields brstack | xargs -n1 > $TMPDIR/perf.script # fail if we find any branch type that doesn't match any of the expected ones # also consider UNKNOWN branch types (-) - if grep -E -vm1 "^[^ ]*/($expect|-|( *))/.*$" $TMPDIR/perf.script; then + if grep -E -vm1 "^[^ ]*/($test_filter_expect|-|( *))/.*$" $TMPDIR/perf.script; then return 1 fi } -- cgit v1.2.3 From c4a1a7763da3a7345f338e3666e3f7749b96b734 Mon Sep 17 00:00:00 2001 From: Spoorthy S Date: Tue, 13 Jun 2023 22:11:42 +0530 Subject: perf tests stat+shadow_stat.sh: Fix all POSIX sh warnings found using shellcheck Running shellcheck -S on stat+shadow_stat.sh testcase, generates SC2046 and SC2034 warnings, $ shellcheck -S warning tests/shell/stat+shadow_stat.sh res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)` : Quote this to prevent word splitting To address the POSIX shell warnings used quotes in the printf expressions, to prevent word splitting. Signed-off-by: Spoorthy S Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-15-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat+shadow_stat.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat+shadow_stat.sh b/tools/perf/tests/shell/stat+shadow_stat.sh index e6e35fc6c882..0e9cba84e757 100755 --- a/tools/perf/tests/shell/stat+shadow_stat.sh +++ b/tools/perf/tests/shell/stat+shadow_stat.sh @@ -33,7 +33,7 @@ test_global_aggr() fi # use printf for rounding and a leading zero - res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)` + res=`printf "%.2f" "$(echo "scale=6; $num / $cyc" | bc -q)"` if [ "$ipc" != "$res" ]; then echo "IPC is different: $res != $ipc ($num / $cyc)" exit 1 @@ -67,7 +67,7 @@ test_no_aggr() fi # use printf for rounding and a leading zero - res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)` + res=`printf "%.2f" "$(echo "scale=6; $num / $cyc" | bc -q)"` if [ "$ipc" != "$res" ]; then echo "IPC is different for $cpu: $res != $ipc ($num / $cyc)" exit 1 -- cgit v1.2.3 From 5c4396efb53ef07d046a2e9456b240880e0c3076 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Tue, 13 Jun 2023 22:11:43 +0530 Subject: perf tests task_analyzer: Fix bad substitution ${$1} ${$1} gives bad substitution error on sh, bash, and zsh. This seems like a typo, and this patch modifies it to $1, since that is what it's usage looks like from wherever `check_exec_0` is called. This issue due to ${$1} caused all function calls to give error in `find_str_or_fail` line, and so no test runs completely. But 'perf test "perf script task-analyzer tests"' wrongly reports that tests passed with the status OK, which is wrong considering the tests didn't even run completely Fixes: e8478b84d6ba9ccf ("perf test: add new task-analyzer tests") Signed-off-by: Aditya Gupta Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Cc: Disha Goel Cc: Hagen Paul Pfeifer Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Petar Gligoric Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-16-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_task_analyzer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 4264b54b654b..84ab7e7f57d5 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -31,7 +31,7 @@ report() { check_exec_0() { if [ $? != 0 ]; then - report 1 "invokation of ${$1} command failed" + report 1 "invocation of $1 command failed" fi } -- cgit v1.2.3 From b8e55fde9f663bd582d8f0b673fa8735f0dcca47 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Tue, 13 Jun 2023 22:11:44 +0530 Subject: perf tests task_analyzer: Print command that failed instead of just "perf" Instead of printing "perf command failed" everytime, print the exact command that run earlier Signed-off-by: Aditya Gupta Acked-by: Hagen Paul Pfeifer Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-17-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_task_analyzer.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 84ab7e7f57d5..b094eeb3bf66 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -53,14 +53,14 @@ prepare_perf_data() { test_basic() { out="$tmpdir/perf.out" perf script report task-analyzer > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } test_ns_rename(){ out="$tmpdir/perf.out" perf script report task-analyzer --ns --rename-comms-by-tids 0:random > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --ns --rename-comms-by-tids 0:random" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } @@ -68,7 +68,7 @@ test_ms_filtertasks_highlight(){ out="$tmpdir/perf.out" perf script report task-analyzer --ms --filter-tasks perf --highlight-tasks perf \ > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --ms --filter-tasks perf --highlight-tasks perf" find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}" } @@ -76,61 +76,61 @@ test_extended_times_timelimit_limittasks() { out="$tmpdir/perf.out" perf script report task-analyzer --extended-times --time-limit :99999 \ --limit-to-tasks perf > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --extended-times --time-limit :99999 --limit-to-tasks perf" find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" } test_summary() { out="$tmpdir/perf.out" perf script report task-analyzer --summary > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --summary" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_summaryextended() { out="$tmpdir/perf.out" perf script report task-analyzer --summary-extended > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --summary-extended" find_str_or_fail "Inter Task Times" "$out" "${FUNCNAME[0]}" } test_summaryonly() { out="$tmpdir/perf.out" perf script report task-analyzer --summary-only > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --summary-only" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_extended_times_summary_ns() { out="$tmpdir/perf.out" perf script report task-analyzer --extended-times --summary --ns > "$out" - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --extended-times --summary --ns" find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}" find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}" } test_csv() { perf script report task-analyzer --csv csv > /dev/null - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --csv csv" find_str_or_fail "Comm;" csv "${FUNCNAME[0]}" } test_csv_extended_times() { perf script report task-analyzer --csv csv --extended-times > /dev/null - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --csv csv --extended-times" find_str_or_fail "Out-Out;" csv "${FUNCNAME[0]}" } test_csvsummary() { perf script report task-analyzer --csv-summary csvsummary > /dev/null - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --csv-summary csvsummary" find_str_or_fail "Comm;" csvsummary "${FUNCNAME[0]}" } test_csvsummary_extended() { perf script report task-analyzer --csv-summary csvsummary --summary-extended \ >/dev/null - check_exec_0 "perf" + check_exec_0 "perf script report task-analyzer --csv-summary csvsummary --summary-extended" find_str_or_fail "Out-Out;" csvsummary "${FUNCNAME[0]}" } -- cgit v1.2.3 From c3ac3b0779770acd3ad7eecb5099ab4419ef2e2e Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Tue, 13 Jun 2023 22:11:45 +0530 Subject: perf tests task_analyzer: Skip tests if no libtraceevent support Test "perf script task-analyzer tests" fails in environment with missing libtraceevent support, as perf record fails to create the perf.data file, which further tests depend on. Instead, when perf is not compiled with libtraceevent support, skip those tests instead of failing them, by checking the output of `perf record --dry-run` to see if it prints the error "libtraceevent is necessary for tracepoint support" For the following output, perf compiled with: `make NO_LIBTRACEEVENT=1` Before the patch: 108: perf script task-analyzer tests : test child forked, pid 24105 failed to open perf.data: No such file or directory (try 'perf record' first) FAIL: "invokation of perf script report task-analyzer command failed" Error message: "" FAIL: "test_basic" Error message: "Failed to find required string:'Comm'." failed to open perf.data: No such file or directory (try 'perf record' first) FAIL: "invokation of perf script report task-analyzer --ns --rename-comms-by-tids 0:random command failed" Error message: "" FAIL: "test_ns_rename" Error message: "Failed to find required string:'Comm'." failed to open perf.data: No such file or directory (try 'perf record' first) <...> perf script task-analyzer tests: FAILED! With this patch, the script instead returns 2 signifying SKIP, and after the patch: 108: perf script task-analyzer tests : test child forked, pid 26010 libtraceevent is necessary for tracepoint support WARN: Skipping tests. No libtraceevent support test child finished with -2 perf script task-analyzer tests: Skip Fixes: e8478b84d6ba9ccf ("perf test: Add new task-analyzer tests") Signed-off-by: Aditya Gupta Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: John Garry Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: Petar Gligoric Cc: Ravi Bangoria Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20230613164145.50488-18-atrajeev@linux.vnet.ibm.com Signed-off-by: Athira Rajeev Signed-off-by: Kajol Jain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_task_analyzer.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index b094eeb3bf66..59785dfc11f8 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -44,9 +44,20 @@ find_str_or_fail() { fi } +# check if perf is compiled with libtraceevent support +skip_no_probe_record_support() { + perf record -e "sched:sched_switch" -a -- sleep 1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2 + return 0 +} + prepare_perf_data() { # 1s should be sufficient to catch at least some switches perf record -e sched:sched_switch -a -- sleep 1 > /dev/null 2>&1 + # check if perf data file got created in above step. + if [ ! -e "perf.data" ]; then + printf "FAIL: perf record failed to create \"perf.data\" \n" + return 1 + fi } # check standard inkvokation with no arguments @@ -134,6 +145,13 @@ test_csvsummary_extended() { find_str_or_fail "Out-Out;" csvsummary "${FUNCNAME[0]}" } +skip_no_probe_record_support +err=$? +if [ $err -ne 0 ]; then + echo "WARN: Skipping tests. No libtraceevent support" + cleanup + exit $err +fi prepare_perf_data test_basic test_ns_rename -- cgit v1.2.3 From e2595550177d8ea42083c9fd8e8a9d4acd5604ec Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Wed, 7 Jun 2023 09:26:59 -0700 Subject: pert tests: Support metricgroup perf stat JSON output A new field metricgroup has been added in the perf stat JSON output. Support it in the test case. Signed-off-by: Kan Liang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20230607162700.3234712-8-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/perf_json_output_lint.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index b81582a89d36..5e9bd68c83fe 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -55,6 +55,7 @@ def check_json_output(expected_items): 'interval': lambda x: isfloat(x), 'metric-unit': lambda x: True, 'metric-value': lambda x: isfloat(x), + 'metricgroup': lambda x: True, 'node': lambda x: True, 'pcnt-running': lambda x: isfloat(x), 'socket': lambda x: True, @@ -70,6 +71,8 @@ def check_json_output(expected_items): # values and possibly other prefixes like interval, core and # aggregate-number. pass + elif count != expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: + pass elif count != expected_items: raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}' f' in \'{item}\'') -- cgit v1.2.3 From 18b687d7ef90d1dd56c4df3be7365977861f5d82 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 15 Jun 2023 06:53:13 -0700 Subject: pert tests: Update metric-value for perf stat JSON output There may be multiplexing triggered, e.g., e-core of ADL. Reviewed-by: Ian Rogers Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20230615135315.3662428-7-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/perf_json_output_lint.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index 5e9bd68c83fe..ea55d5ea1ced 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -66,10 +66,10 @@ def check_json_output(expected_items): for item in json.loads(input): if expected_items != -1: count = len(item) - if count != expected_items and count >= 1 and count <= 4 and 'metric-value' in item: + if count != expected_items and count >= 1 and count <= 6 and 'metric-value' in item: # Events that generate >1 metric may have isolated metric - # values and possibly other prefixes like interval, core and - # aggregate-number. + # values and possibly other prefixes like interval, core, + # aggregate-number, or event-runtime/pcnt-running from multiplexing. pass elif count != expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: pass -- cgit v1.2.3 From fc51fc87b1b84db83907c125bda6b05d52ec21ca Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 15 Jun 2023 20:14:18 -0700 Subject: perf test: Move all the check functions of stat CSV output to lib These functions can be shared with the stat std output test. There is no functional change. Reviewed-by: Ian Rogers Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20230616031420.3751973-4-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/stat_output.sh | 169 +++++++++++++++++++++++++++ tools/perf/tests/shell/stat+csv_output.sh | 188 +++--------------------------- 2 files changed, 184 insertions(+), 173 deletions(-) create mode 100755 tools/perf/tests/shell/lib/stat_output.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/stat_output.sh b/tools/perf/tests/shell/lib/stat_output.sh new file mode 100755 index 000000000000..363979b1123d --- /dev/null +++ b/tools/perf/tests/shell/lib/stat_output.sh @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Return true if perf_event_paranoid is > $1 and not running as root. +function ParanoidAndNotRoot() +{ + [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] +} + +# $1 name $2 extra_opt +check_no_args() +{ + echo -n "Checking $1 output: no args" + perf stat $2 true + commachecker --no-args + echo "[Success]" +} + +check_system_wide() +{ + echo -n "Checking $1 output: system wide " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat -a $2 true + commachecker --system-wide + echo "[Success]" +} + +check_system_wide_no_aggr() +{ + echo -n "Checking $1 output: system wide no aggregation " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat -A -a --no-merge $2 true + commachecker --system-wide-no-aggr + echo "[Success]" +} + +check_interval() +{ + echo -n "Checking $1 output: interval " + perf stat -I 1000 $2 true + commachecker --interval + echo "[Success]" +} + +check_event() +{ + echo -n "Checking $1 output: event " + perf stat -e cpu-clock $2 true + commachecker --event + echo "[Success]" +} + +check_per_core() +{ + echo -n "Checking $1 output: per core " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-core -a $2 true + commachecker --per-core + echo "[Success]" +} + +check_per_thread() +{ + echo -n "Checking $1 output: per thread " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-thread -a $2 true + commachecker --per-thread + echo "[Success]" +} + +check_per_cache_instance() +{ + echo -n "Checking $1 output: per cache instance " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-cache -a $2 true + commachecker --per-cache + echo "[Success]" +} + +check_per_die() +{ + echo -n "Checking $1 output: per die " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-die -a $2 true + commachecker --per-die + echo "[Success]" +} + +check_per_node() +{ + echo -n "Checking $1 output: per node " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-node -a $2 true + commachecker --per-node + echo "[Success]" +} + +check_per_socket() +{ + echo -n "Checking $1 output: per socket " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-socket -a $2 true + commachecker --per-socket + echo "[Success]" +} + +# The perf stat options for per-socket, per-core, per-die +# and -A ( no_aggr mode ) uses the info fetched from this +# directory: "/sys/devices/system/cpu/cpu*/topology". For +# example, socket value is fetched from "physical_package_id" +# file in topology directory. +# Reference: cpu__get_topology_int in util/cpumap.c +# If the platform doesn't expose topology information, values +# will be set to -1. For example, incase of pSeries platform +# of powerpc, value for "physical_package_id" is restricted +# and set to -1. Check here validates the socket-id read from +# topology file before proceeding further + +FILE_LOC="/sys/devices/system/cpu/cpu*/topology/" +FILE_NAME="physical_package_id" + +function check_for_topology() +{ + if ! ParanoidAndNotRoot 0 + then + socket_file=`ls $FILE_LOC/$FILE_NAME | head -n 1` + [ -z $socket_file ] && { + echo 0 + return + } + socket_id=`cat $socket_file` + [ $socket_id == -1 ] && { + echo 1 + return + } + fi + echo 0 +} diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh index ed082daf839c..34a0701fee05 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -6,7 +6,8 @@ set -e -skip_test=0 +. $(dirname $0)/lib/stat_output.sh + csv_sep=@ stat_output=$(mktemp /tmp/__perf_test.stat_output.csv.XXXXX) @@ -63,181 +64,22 @@ function commachecker() return 0 } -# Return true if perf_event_paranoid is > $1 and not running as root. -function ParanoidAndNotRoot() -{ - [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] -} - -check_no_args() -{ - echo -n "Checking CSV output: no args " - perf stat -x$csv_sep -o "${stat_output}" true - commachecker --no-args - echo "[Success]" -} - -check_system_wide() -{ - echo -n "Checking CSV output: system wide " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep -a -o "${stat_output}" true - commachecker --system-wide - echo "[Success]" -} - -check_system_wide_no_aggr() -{ - echo -n "Checking CSV output: system wide no aggregation " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep -A -a --no-merge -o "${stat_output}" true - commachecker --system-wide-no-aggr - echo "[Success]" -} - -check_interval() -{ - echo -n "Checking CSV output: interval " - perf stat -x$csv_sep -I 1000 -o "${stat_output}" true - commachecker --interval - echo "[Success]" -} - - -check_event() -{ - echo -n "Checking CSV output: event " - perf stat -x$csv_sep -e cpu-clock -o "${stat_output}" true - commachecker --event - echo "[Success]" -} - -check_per_core() -{ - echo -n "Checking CSV output: per core " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-core -a -o "${stat_output}" true - commachecker --per-core - echo "[Success]" -} - -check_per_thread() -{ - echo -n "Checking CSV output: per thread " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-thread -a -o "${stat_output}" true - commachecker --per-thread - echo "[Success]" -} - -check_per_cache_instance() -{ - echo -n "Checking CSV output: per cache instance " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-cache -a true 2>&1 | commachecker --per-cache - echo "[Success]" -} - -check_per_die() -{ - echo -n "Checking CSV output: per die " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-die -a -o "${stat_output}" true - commachecker --per-die - echo "[Success]" -} - -check_per_node() -{ - echo -n "Checking CSV output: per node " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-node -a -o "${stat_output}" true - commachecker --per-node - echo "[Success]" -} - -check_per_socket() -{ - echo -n "Checking CSV output: per socket " - if ParanoidAndNotRoot 0 - then - echo "[Skip] paranoid and not root" - return - fi - perf stat -x$csv_sep --per-socket -a -o "${stat_output}" true - commachecker --per-socket - echo "[Success]" -} - -# The perf stat options for per-socket, per-core, per-die -# and -A ( no_aggr mode ) uses the info fetched from this -# directory: "/sys/devices/system/cpu/cpu*/topology". For -# example, socket value is fetched from "physical_package_id" -# file in topology directory. -# Reference: cpu__get_topology_int in util/cpumap.c -# If the platform doesn't expose topology information, values -# will be set to -1. For example, incase of pSeries platform -# of powerpc, value for "physical_package_id" is restricted -# and set to -1. Check here validates the socket-id read from -# topology file before proceeding further - -FILE_LOC="/sys/devices/system/cpu/cpu*/topology/" -FILE_NAME="physical_package_id" - -check_for_topology() -{ - if ! ParanoidAndNotRoot 0 - then - socket_file=`ls $FILE_LOC/$FILE_NAME | head -n 1` - [ -z $socket_file ] && return 0 - socket_id=`cat $socket_file` - [ $socket_id == -1 ] && skip_test=1 - return 0 - fi -} +perf_cmd="-x$csv_sep -o ${stat_output}" -check_for_topology -check_no_args -check_system_wide -check_interval -check_event -check_per_thread -check_per_node +skip_test=$(check_for_topology) +check_no_args "CSV" "$perf_cmd" +check_system_wide "CSV" "$perf_cmd" +check_interval "CSV" "$perf_cmd" +check_event "CSV" "$perf_cmd" +check_per_thread "CSV" "$perf_cmd" +check_per_node "CSV" "$perf_cmd" if [ $skip_test -ne 1 ] then - check_system_wide_no_aggr - check_per_core - check_per_cache_instance - check_per_die - check_per_socket + check_system_wide_no_aggr "CSV" "$perf_cmd" + check_per_core "CSV" "$perf_cmd" + check_per_cache_instance "CSV" "$perf_cmd" + check_per_die "CSV" "$perf_cmd" + check_per_socket "CSV" "$perf_cmd" else echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die and per_socket since socket id exposed via topology is invalid" fi -- cgit v1.2.3 From 99a04a48f22504fc6319a24835e2db7934fae41e Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 15 Jun 2023 20:14:19 -0700 Subject: perf test: Add test case for the standard 'perf stat' output Add a new test case to verify the standard 'perf stat' output with different options. Reviewed-by: Ian Rogers Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Ahmad Yasin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20230616031420.3751973-5-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat+std_output.sh | 108 ++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100755 tools/perf/tests/shell/stat+std_output.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh new file mode 100755 index 000000000000..98cc3356a04a --- /dev/null +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# perf stat STD output linter +# SPDX-License-Identifier: GPL-2.0 +# Tests various perf stat STD output commands for +# default event and metricgroup + +set -e + +. $(dirname $0)/lib/stat_output.sh + +stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) + +event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults cycles instructions branches branch-misses stalled-cycles-frontend stalled-cycles-backend) +event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "GHz" "insn per cycle" "/sec" "of all branches" "frontend cycles idle" "backend cycles idle") + +metricgroup_name=(TopdownL1 TopdownL2) + +cleanup() { + rm -f "${stat_output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +function commachecker() +{ + local -i cnt=0 + local prefix=1 + + case "$1" + in "--interval") prefix=2 + ;; "--per-thread") prefix=2 + ;; "--system-wide-no-aggr") prefix=2 + ;; "--per-core") prefix=3 + ;; "--per-socket") prefix=3 + ;; "--per-node") prefix=3 + ;; "--per-die") prefix=3 + ;; "--per-cache") prefix=3 + esac + + while read line + do + # Ignore initial "started on" comment. + x=${line:0:1} + [ "$x" = "#" ] && continue + # Ignore initial blank line. + [ "$line" = "" ] && continue + # Ignore "Performance counter stats" + x=${line:0:25} + [ "$x" = "Performance counter stats" ] && continue + # Ignore "seconds time elapsed" and break + [[ "$line" == *"time elapsed"* ]] && break + + main_body=$(echo $line | cut -d' ' -f$prefix-) + x=${main_body%#*} + # Check default metricgroup + y=$(echo $x | tr -d ' ') + [ "$y" = "" ] && continue + for i in "${!metricgroup_name[@]}"; do + [[ "$y" == *"${metricgroup_name[$i]}"* ]] && break + done + [[ "$y" == *"${metricgroup_name[$i]}"* ]] && continue + + # Check default event + for i in "${!event_name[@]}"; do + [[ "$x" == *"${event_name[$i]}"* ]] && break + done + + [[ ! "$x" == *"${event_name[$i]}"* ]] && { + echo "Unknown event name in $line" 1>&2 + exit 1; + } + + # Check event metric if it exists + [[ ! "$main_body" == *"#"* ]] && continue + [[ ! "$main_body" == *"${event_metric[$i]}"* ]] && { + echo "wrong event metric. expected ${event_metric[$i]} in $line" 1>&2 + exit 1; + } + done < "${stat_output}" + return 0 +} + +perf_cmd="-o ${stat_output}" + +skip_test=$(check_for_topology) +check_no_args "STD" "$perf_cmd" +check_system_wide "STD" "$perf_cmd" +check_interval "STD" "$perf_cmd" +check_per_thread "STD" "$perf_cmd" +check_per_node "STD" "$perf_cmd" +if [ $skip_test -ne 1 ] +then + check_system_wide_no_aggr "STD" "$perf_cmd" + check_per_core "STD" "$perf_cmd" + check_per_cache_instance "STD" "$perf_cmd" + check_per_die "STD" "$perf_cmd" + check_per_socket "STD" "$perf_cmd" +else + echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die and per_socket since socket id exposed via topology is invalid" +fi +cleanup +exit 0 -- cgit v1.2.3 From bb6b369cb42737afa4114f371868eb1d858ff36e Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 16 Jun 2023 09:56:07 +0800 Subject: perf test record+probe_libc_inet_pton.sh: Use "grep -F" instead of obsolescent "fgrep" There exists the following warning when executing 'perf test record+probe_libc_inet_pton.sh': fgrep: warning: fgrep is obsolescent; using grep -F This is tested on Fedora 38, the version of grep is 3.8, the latest version of grep claims the fgrep is obsolete, use "grep -F" instead of "fgrep" to silence the warning. Signed-off-by: Tiezhu Yang Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: loongson-kernel@lists.loongnix.cn Link: https://lore.kernel.org/r/1686880567-30017-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record+probe_libc_inet_pton.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 0934fb0cd68f..89214a6d9951 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -14,7 +14,7 @@ . "$(dirname "$0")/lib/probe_vfs_getname.sh" libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g') -nm -Dg $libc 2>/dev/null | fgrep -q inet_pton || exit 254 +nm -Dg $libc 2>/dev/null | grep -F -q inet_pton || exit 254 event_pattern='probe_libc:inet_pton(\_[[:digit:]]+)?' @@ -94,7 +94,7 @@ delete_libc_inet_pton_event() { } # Check for IPv6 interface existence -ip a sh lo | fgrep -q inet6 || exit 2 +ip a sh lo | grep -F -q inet6 || exit 2 skip_if_no_perf_probe && \ add_libc_inet_pton_event && \ -- cgit v1.2.3 From 3ad7092f5145aab4118f575b57f0ab1707b1cd36 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Tue, 20 Jun 2023 10:00:25 -0700 Subject: perf test: Add metric value validation test Add metric value validation test to check if metric values are with in correct value ranges. There are three types of tests included: 1) positive-value test checks if all the metrics collected are non-negative; 2) single-value test checks if the list of metrics have values in given value ranges; 3) relationship test checks if multiple metrics follow a given relationship, e.g. memory_bandwidth_read + memory_bandwidth_write = memory_bandwidth_total. Signed-off-by: Weilin Wang Tested-by: Namhyung Kim Cc: ravi.bangoria@amd.com Cc: Ian Rogers Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Caleb Biggers Cc: Perry Taylor Cc: Samantha Alt Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Kan Liang Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230620170027.1861012-2-weilin.wang@intel.com Signed-off-by: Namhyung Kim --- .../perf/tests/shell/lib/perf_metric_validation.py | 514 +++++++++++++++++++++ .../shell/lib/perf_metric_validation_rules.json | 387 ++++++++++++++++ tools/perf/tests/shell/stat_metrics_values.sh | 30 ++ 3 files changed, 931 insertions(+) create mode 100644 tools/perf/tests/shell/lib/perf_metric_validation.py create mode 100644 tools/perf/tests/shell/lib/perf_metric_validation_rules.json create mode 100755 tools/perf/tests/shell/stat_metrics_values.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py new file mode 100644 index 000000000000..81bd2bf38b67 --- /dev/null +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -0,0 +1,514 @@ +#SPDX-License-Identifier: GPL-2.0 +import re +import csv +import json +import argparse +from pathlib import Path +import subprocess + +class Validator: + def __init__(self, rulefname, reportfname='', t=5, debug=False, datafname='', fullrulefname='', workload='true', metrics=''): + self.rulefname = rulefname + self.reportfname = reportfname + self.rules = None + self.collectlist=metrics + self.metrics = set() + self.tolerance = t + + self.workloads = [x for x in workload.split(",") if x] + self.wlidx = 0 # idx of current workloads + self.allresults = dict() # metric results of all workload + self.allignoremetrics = dict() # metrics with no results or negative results + self.allfailtests = dict() + self.alltotalcnt = dict() + self.allpassedcnt = dict() + self.allerrlist = dict() + + self.results = dict() # metric results of current workload + # vars for test pass/failure statistics + self.ignoremetrics= set() # metrics with no results or negative results, neg result counts as a failed test + self.failtests = dict() + self.totalcnt = 0 + self.passedcnt = 0 + # vars for errors + self.errlist = list() + + # vars for Rule Generator + self.pctgmetrics = set() # Percentage rule + + # vars for debug + self.datafname = datafname + self.debug = debug + self.fullrulefname = fullrulefname + + def read_json(self, filename: str) -> dict: + try: + with open(Path(filename).resolve(), "r") as f: + data = json.loads(f.read()) + except OSError as e: + print(f"Error when reading file {e}") + sys.exit() + + return data + + def json_dump(self, data, output_file): + parent = Path(output_file).parent + if not parent.exists(): + parent.mkdir(parents=True) + + with open(output_file, "w+") as output_file: + json.dump(data, + output_file, + ensure_ascii=True, + indent=4) + + def get_results(self, idx:int = 0): + return self.results[idx] + + def get_bounds(self, lb, ub, error, alias={}, ridx:int = 0) -> list: + """ + Get bounds and tolerance from lb, ub, and error. + If missing lb, use 0.0; missing ub, use float('inf); missing error, use self.tolerance. + + @param lb: str/float, lower bound + @param ub: str/float, upper bound + @param error: float/str, error tolerance + @returns: lower bound, return inf if the lower bound is a metric value and is not collected + upper bound, return -1 if the upper bound is a metric value and is not collected + tolerance, denormalized base on upper bound value + """ + # init ubv and lbv to invalid values + def get_bound_value (bound, initval, ridx): + val = initval + if isinstance(bound, int) or isinstance(bound, float): + val = bound + elif isinstance(bound, str): + if bound == '': + val = float("inf") + elif bound in alias: + vall = self.get_value(alias[ub], ridx) + if vall: + val = vall[0] + elif bound.replace('.', '1').isdigit(): + val = float(bound) + else: + print("Wrong bound: {0}".format(bound)) + else: + print("Wrong bound: {0}".format(bound)) + return val + + ubv = get_bound_value(ub, -1, ridx) + lbv = get_bound_value(lb, float('inf'), ridx) + t = get_bound_value(error, self.tolerance, ridx) + + # denormalize error threshold + denormerr = t * ubv / 100 if ubv != 100 and ubv > 0 else t + + return lbv, ubv, denormerr + + def get_value(self, name:str, ridx:int = 0) -> list: + """ + Get value of the metric from self.results. + If result of this metric is not provided, the metric name will be added into self.ignoremetics and self.errlist. + All future test(s) on this metric will fail. + + @param name: name of the metric + @returns: list with value found in self.results; list is empty when not value found. + """ + results = [] + data = self.results[ridx] if ridx in self.results else self.results[0] + if name not in self.ignoremetrics: + if name in data: + results.append(data[name]) + elif name.replace('.', '1').isdigit(): + results.append(float(name)) + else: + self.errlist.append("Metric '%s' is not collected or the value format is incorrect"%(name)) + self.ignoremetrics.add(name) + return results + + def check_bound(self, val, lb, ub, err): + return True if val <= ub + err and val >= lb - err else False + + # Positive Value Sanity check + def pos_val_test(self): + """ + Check if metrics value are non-negative. + One metric is counted as one test. + Failure: when metric value is negative or not provided. + Metrics with negative value will be added into the self.failtests['PositiveValueTest'] and self.ignoremetrics. + """ + negmetric = set() + missmetric = set() + pcnt = 0 + tcnt = 0 + for name, val in self.get_results().items(): + if val is None or val == '': + missmetric.add(name) + self.errlist.append("Metric '%s' is not collected"%(name)) + elif val < 0: + negmetric.add("{0}(={1:.4f})".format(name, val)) + else: + pcnt += 1 + tcnt += 1 + + self.failtests['PositiveValueTest']['Total Tests'] = tcnt + self.failtests['PositiveValueTest']['Passed Tests'] = pcnt + if len(negmetric) or len(missmetric)> 0: + self.ignoremetrics.update(negmetric) + self.ignoremetrics.update(missmetric) + self.failtests['PositiveValueTest']['Failed Tests'].append({'NegativeValue':list(negmetric), 'MissingValue':list(missmetric)}) + + return + + def evaluate_formula(self, formula:str, alias:dict, ridx:int = 0): + """ + Evaluate the value of formula. + + @param formula: the formula to be evaluated + @param alias: the dict has alias to metric name mapping + @returns: value of the formula is success; -1 if the one or more metric value not provided + """ + stack = [] + b = 0 + errs = [] + sign = "+" + f = str() + + #TODO: support parenthesis? + for i in range(len(formula)): + if i+1 == len(formula) or formula[i] in ('+', '-', '*', '/'): + s = alias[formula[b:i]] if i+1 < len(formula) else alias[formula[b:]] + v = self.get_value(s, ridx) + if not v: + errs.append(s) + else: + f = f + "{0}(={1:.4f})".format(s, v[0]) + if sign == "*": + stack[-1] = stack[-1] * v + elif sign == "/": + stack[-1] = stack[-1] / v + elif sign == '-': + stack.append(-v[0]) + else: + stack.append(v[0]) + if i + 1 < len(formula): + sign = formula[i] + f += sign + b = i + 1 + + if len(errs) > 0: + return -1, "Metric value missing: "+','.join(errs) + + val = sum(stack) + return val, f + + # Relationships Tests + def relationship_test(self, rule: dict): + """ + Validate if the metrics follow the required relationship in the rule. + eg. lower_bound <= eval(formula)<= upper_bound + One rule is counted as ont test. + Failure: when one or more metric result(s) not provided, or when formula evaluated outside of upper/lower bounds. + + @param rule: dict with metric name(+alias), formula, and required upper and lower bounds. + """ + alias = dict() + for m in rule['Metrics']: + alias[m['Alias']] = m['Name'] + lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'], alias, ridx=rule['RuleIndex']) + val, f = self.evaluate_formula(rule['Formula'], alias, ridx=rule['RuleIndex']) + if val == -1: + self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Description':f}) + elif not self.check_bound(val, lbv, ubv, t): + lb = rule['RangeLower'] + ub = rule['RangeUpper'] + if isinstance(lb, str): + if lb in alias: + lb = alias[lb] + if isinstance(ub, str): + if ub in alias: + ub = alias[ub] + self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Formula':f, + 'RangeLower': lb, 'LowerBoundValue': self.get_value(lb), + 'RangeUpper': ub, 'UpperBoundValue':self.get_value(ub), + 'ErrorThreshold': t, 'CollectedValue': val}) + else: + self.passedcnt += 1 + self.failtests['RelationshipTest']['Passed Tests'] += 1 + self.totalcnt += 1 + self.failtests['RelationshipTest']['Total Tests'] += 1 + + return + + + # Single Metric Test + def single_test(self, rule:dict): + """ + Validate if the metrics are in the required value range. + eg. lower_bound <= metrics_value <= upper_bound + One metric is counted as one test in this type of test. + One rule may include one or more metrics. + Failure: when the metric value not provided or the value is outside the bounds. + This test updates self.total_cnt and records failed tests in self.failtest['SingleMetricTest']. + + @param rule: dict with metrics to validate and the value range requirement + """ + lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold']) + metrics = rule['Metrics'] + passcnt = 0 + totalcnt = 0 + faillist = [] + for m in metrics: + totalcnt += 1 + result = self.get_value(m['Name']) + if len(result) > 0 and self.check_bound(result[0], lbv, ubv, t): + passcnt += 1 + else: + faillist.append({'MetricName':m['Name'], 'CollectedValue':result}) + + self.totalcnt += totalcnt + self.passedcnt += passcnt + self.failtests['SingleMetricTest']['Total Tests'] += totalcnt + self.failtests['SingleMetricTest']['Passed Tests'] += passcnt + if len(faillist) != 0: + self.failtests['SingleMetricTest']['Failed Tests'].append({'RuleIndex':rule['RuleIndex'], + 'RangeLower': rule['RangeLower'], + 'RangeUpper': rule['RangeUpper'], + 'ErrorThreshold':rule['ErrorThreshold'], + 'Failure':faillist}) + + return + + def create_report(self): + """ + Create final report and write into a JSON file. + """ + alldata = list() + for i in range(0, len(self.workloads)): + reportstas = {"Total Rule Count": self.alltotalcnt[i], "Passed Rule Count": self.allpassedcnt[i]} + data = {"Metric Validation Statistics": reportstas, "Tests in Category": self.allfailtests[i], + "Errors":self.allerrlist[i]} + alldata.append({"Workload": self.workloads[i], "Report": data}) + + json_str = json.dumps(alldata, indent=4) + print("Test validation finished. Final report: ") + print(json_str) + + if self.debug: + allres = [{"Workload": self.workloads[i], "Results": self.allresults[i]} for i in range(0, len(self.workloads))] + self.json_dump(allres, self.datafname) + + def check_rule(self, testtype, metric_list): + """ + Check if the rule uses metric(s) that not exist in current platform. + + @param metric_list: list of metrics from the rule. + @return: False when find one metric out in Metric file. (This rule should not skipped.) + True when all metrics used in the rule are found in Metric file. + """ + if testtype == "RelationshipTest": + for m in metric_list: + if m['Name'] not in self.metrics: + return False + return True + + # Start of Collector and Converter + def convert(self, data: list, idx: int): + """ + Convert collected metric data from the -j output to dict of {metric_name:value}. + """ + for json_string in data: + try: + result =json.loads(json_string) + if "metric-unit" in result and result["metric-unit"] != "(null)" and result["metric-unit"] != "": + name = result["metric-unit"].split(" ")[1] if len(result["metric-unit"].split(" ")) > 1 \ + else result["metric-unit"] + if idx not in self.results: self.results[idx] = dict() + self.results[idx][name.lower()] = float(result["metric-value"]) + except ValueError as error: + continue + return + + def collect_perf(self, data_file: str, workload: str): + """ + Collect metric data with "perf stat -M" on given workload with -a and -j. + """ + self.results = dict() + tool = 'perf' + print(f"Starting perf collection") + print(f"Workload: {workload}") + collectlist = dict() + if self.collectlist != "": + collectlist[0] = {x for x in self.collectlist.split(",")} + else: + collectlist[0] = set(list(self.metrics)) + # Create metric set for relationship rules + for rule in self.rules: + if rule["TestType"] == "RelationshipTest": + metrics = [m["Name"] for m in rule["Metrics"]] + if not any(m not in collectlist[0] for m in metrics): + collectlist[rule["RuleIndex"]] = set(metrics) + + for idx, metrics in collectlist.items(): + if idx == 0: wl = "sleep 0.5".split() + else: wl = workload.split() + for metric in metrics: + command = [tool, 'stat', '-j', '-M', f"{metric}", "-a"] + command.extend(wl) + cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8') + data = [x+'}' for x in cmd.stderr.split('}\n') if x] + self.convert(data, idx) + # End of Collector and Converter + + # Start of Rule Generator + def parse_perf_metrics(self): + """ + Read and parse perf metric file: + 1) find metrics with '1%' or '100%' as ScaleUnit for Percent check + 2) create metric name list + """ + command = ['perf', 'list', '-j', '--details', 'metrics'] + cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8') + try: + data = json.loads(cmd.stdout) + for m in data: + if 'MetricName' not in m: + print("Warning: no metric name") + continue + name = m['MetricName'] + self.metrics.add(name) + if 'ScaleUnit' in m and (m['ScaleUnit'] == '1%' or m['ScaleUnit'] == '100%'): + self.pctgmetrics.add(name.lower()) + except ValueError as error: + print(f"Error when parsing metric data") + sys.exit() + + return + + def create_rules(self): + """ + Create full rules which includes: + 1) All the rules from the "relationshi_rules" file + 2) SingleMetric rule for all the 'percent' metrics + + Reindex all the rules to avoid repeated RuleIndex + """ + self.rules = self.read_json(self.rulefname)['RelationshipRules'] + pctgrule = {'RuleIndex':0, + 'TestType':'SingleMetricTest', + 'RangeLower':'0', + 'RangeUpper': '100', + 'ErrorThreshold': self.tolerance, + 'Description':'Metrics in percent unit have value with in [0, 100]', + 'Metrics': [{'Name': m} for m in self.pctgmetrics]} + self.rules.append(pctgrule) + + # Re-index all rules to avoid repeated RuleIndex + idx = 1 + for r in self.rules: + r['RuleIndex'] = idx + idx += 1 + + if self.debug: + #TODO: need to test and generate file name correctly + data = {'RelationshipRules':self.rules, 'SupportedMetrics': [{"MetricName": name} for name in self.metrics]} + self.json_dump(data, self.fullrulefname) + + return + # End of Rule Generator + + def _storewldata(self, key): + ''' + Store all the data of one workload into the corresponding data structure for all workloads. + @param key: key to the dictionaries (index of self.workloads). + ''' + self.allresults[key] = self.results + self.allignoremetrics[key] = self.ignoremetrics + self.allfailtests[key] = self.failtests + self.alltotalcnt[key] = self.totalcnt + self.allpassedcnt[key] = self.passedcnt + self.allerrlist[key] = self.errlist + + #Initialize data structures before data validation of each workload + def _init_data(self): + + testtypes = ['PositiveValueTest', 'RelationshipTest', 'SingleMetricTest'] + self.results = dict() + self.ignoremetrics= set() + self.errlist = list() + self.failtests = {k:{'Total Tests':0, 'Passed Tests':0, 'Failed Tests':[]} for k in testtypes} + self.totalcnt = 0 + self.passedcnt = 0 + + def test(self): + ''' + The real entry point of the test framework. + This function loads the validation rule JSON file and Standard Metric file to create rules for + testing and namemap dictionaries. + It also reads in result JSON file for testing. + + In the test process, it passes through each rule and launch correct test function bases on the + 'TestType' field of the rule. + + The final report is written into a JSON file. + ''' + self.parse_perf_metrics() + self.create_rules() + for i in range(0, len(self.workloads)): + self._init_data() + self.collect_perf(self.datafname, self.workloads[i]) + # Run positive value test + self.pos_val_test() + for r in self.rules: + # skip rules that uses metrics not exist in this platform + testtype = r['TestType'] + if not self.check_rule(testtype, r['Metrics']): + continue + if testtype == 'RelationshipTest': + self.relationship_test(r) + elif testtype == 'SingleMetricTest': + self.single_test(r) + else: + print("Unsupported Test Type: ", testtype) + self.errlist.append("Unsupported Test Type from rule: " + r['RuleIndex']) + self._storewldata(i) + print("Workload: ", self.workloads[i]) + print("Total metrics collected: ", self.failtests['PositiveValueTest']['Total Tests']) + print("Non-negative metric count: ", self.failtests['PositiveValueTest']['Passed Tests']) + print("Total Test Count: ", self.totalcnt) + print("Passed Test Count: ", self.passedcnt) + + self.create_report() + return sum(self.alltotalcnt.values()) != sum(self.allpassedcnt.values()) +# End of Class Validator + + +def main() -> None: + parser = argparse.ArgumentParser(description="Launch metric value validation") + + parser.add_argument("-rule", help="Base validation rule file", required=True) + parser.add_argument("-output_dir", help="Path for validator output file, report file", required=True) + parser.add_argument("-debug", help="Debug run, save intermediate data to files", action="store_true", default=False) + parser.add_argument("-wl", help="Workload to run while data collection", default="true") + parser.add_argument("-m", help="Metric list to validate", default="") + args = parser.parse_args() + outpath = Path(args.output_dir) + reportf = Path.joinpath(outpath, 'perf_report.json') + fullrule = Path.joinpath(outpath, 'full_rule.json') + datafile = Path.joinpath(outpath, 'perf_data.json') + + validator = Validator(args.rule, reportf, debug=args.debug, + datafname=datafile, fullrulefname=fullrule, workload=args.wl, + metrics=args.m) + ret = validator.test() + + return ret + + +if __name__ == "__main__": + import sys + sys.exit(main()) + + + diff --git a/tools/perf/tests/shell/lib/perf_metric_validation_rules.json b/tools/perf/tests/shell/lib/perf_metric_validation_rules.json new file mode 100644 index 000000000000..debaa910da9f --- /dev/null +++ b/tools/perf/tests/shell/lib/perf_metric_validation_rules.json @@ -0,0 +1,387 @@ +{ + "RelationshipRules": [ + { + "RuleIndex": 1, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "Intel(R) Optane(TM) Persistent Memory(PMEM) bandwidth total includes Intel(R) Optane(TM) Persistent Memory(PMEM) read bandwidth and Intel(R) Optane(TM) Persistent Memory(PMEM) write bandwidth", + "Metrics": [ + { + "Name": "pmem_memory_bandwidth_read", + "Alias": "a" + }, + { + "Name": "pmem_memory_bandwidth_write", + "Alias": "b" + }, + { + "Name": "pmem_memory_bandwidth_total", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 2, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "DDR memory bandwidth total includes DDR memory read bandwidth and DDR memory write bandwidth", + "Metrics": [ + { + "Name": "memory_bandwidth_read", + "Alias": "a" + }, + { + "Name": "memory_bandwidth_write", + "Alias": "b" + }, + { + "Name": "memory_bandwidth_total", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 3, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "100", + "RangeUpper": "100", + "ErrorThreshold": 5.0, + "Description": "Total memory read accesses includes memory reads from last level cache (LLC) addressed to local DRAM and memory reads from the last level cache (LLC) addressed to remote DRAM.", + "Metrics": [ + { + "Name": "numa_reads_addressed_to_local_dram", + "Alias": "a" + }, + { + "Name": "numa_reads_addressed_to_remote_dram", + "Alias": "b" + } + ] + }, + { + "RuleIndex": 4, + "Formula": "a", + "TestType": "SingleMetricTest", + "RangeLower": "0.125", + "RangeUpper": "", + "ErrorThreshold": "", + "Description": "", + "Metrics": [ + { + "Name": "cpi", + "Alias": "a" + } + ] + }, + { + "RuleIndex": 5, + "Formula": "", + "TestType": "SingleMetricTest", + "RangeLower": "0", + "RangeUpper": "1", + "ErrorThreshold": 5.0, + "Description": "Ratio values should be within value range [0,1)", + "Metrics": [ + { + "Name": "loads_per_instr", + "Alias": "" + }, + { + "Name": "stores_per_instr", + "Alias": "" + }, + { + "Name": "l1d_mpi", + "Alias": "" + }, + { + "Name": "l1d_demand_data_read_hits_per_instr", + "Alias": "" + }, + { + "Name": "l1_i_code_read_misses_with_prefetches_per_instr", + "Alias": "" + }, + { + "Name": "l2_demand_data_read_hits_per_instr", + "Alias": "" + }, + { + "Name": "l2_mpi", + "Alias": "" + }, + { + "Name": "l2_demand_data_read_mpi", + "Alias": "" + }, + { + "Name": "l2_demand_code_mpi", + "Alias": "" + } + ] + }, + { + "RuleIndex": 6, + "Formula": "a+b+c+d", + "TestType": "RelationshipTest", + "RangeLower": "100", + "RangeUpper": "100", + "ErrorThreshold": 5.0, + "Description": "Sum of TMA level 1 metrics should be 100%", + "Metrics": [ + { + "Name": "tma_frontend_bound", + "Alias": "a" + }, + { + "Name": "tma_bad_speculation", + "Alias": "b" + }, + { + "Name": "tma_backend_bound", + "Alias": "c" + }, + { + "Name": "tma_retiring", + "Alias": "d" + } + ] + }, + { + "RuleIndex": 7, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "Sum of the level 2 children should equal level 1 parent", + "Metrics": [ + { + "Name": "tma_fetch_latency", + "Alias": "a" + }, + { + "Name": "tma_fetch_bandwidth", + "Alias": "b" + }, + { + "Name": "tma_frontend_bound", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 8, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "Sum of the level 2 children should equal level 1 parent", + "Metrics": [ + { + "Name": "tma_branch_mispredicts", + "Alias": "a" + }, + { + "Name": "tma_machine_clears", + "Alias": "b" + }, + { + "Name": "tma_bad_speculation", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 9, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "Sum of the level 2 children should equal level 1 parent", + "Metrics": [ + { + "Name": "tma_memory_bound", + "Alias": "a" + }, + { + "Name": "tma_core_bound", + "Alias": "b" + }, + { + "Name": "tma_backend_bound", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 10, + "Formula": "a+b", + "TestType": "RelationshipTest", + "RangeLower": "c", + "RangeUpper": "c", + "ErrorThreshold": 5.0, + "Description": "Sum of the level 2 children should equal level 1 parent", + "Metrics": [ + { + "Name": "tma_light_operations", + "Alias": "a" + }, + { + "Name": "tma_heavy_operations", + "Alias": "b" + }, + { + "Name": "tma_retiring", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 11, + "Formula": "a+b+c", + "TestType": "RelationshipTest", + "RangeLower": "100", + "RangeUpper": "100", + "ErrorThreshold": 5.0, + "Description": "The all_requests includes the memory_page_empty, memory_page_misses, and memory_page_hits equals.", + "Metrics": [ + { + "Name": "memory_page_empty_vs_all_requests", + "Alias": "a" + }, + { + "Name": "memory_page_misses_vs_all_requests", + "Alias": "b" + }, + { + "Name": "memory_page_hits_vs_all_requests", + "Alias": "c" + } + ] + }, + { + "RuleIndex": 12, + "Formula": "a-b", + "TestType": "RelationshipTest", + "RangeLower": "0", + "RangeUpper": "", + "ErrorThreshold": 5.0, + "Description": "CPU utilization in kernel mode should always be <= cpu utilization", + "Metrics": [ + { + "Name": "cpu_utilization", + "Alias": "a" + }, + { + "Name": "cpu_utilization_in_kernel_mode", + "Alias": "b" + } + ] + }, + { + "RuleIndex": 13, + "Formula": "a-b", + "TestType": "RelationshipTest", + "RangeLower": "0", + "RangeUpper": "", + "ErrorThreshold": 5.0, + "Description": "Total L2 misses per instruction should be >= L2 demand data read misses per instruction", + "Metrics": [ + { + "Name": "l2_mpi", + "Alias": "a" + }, + { + "Name": "l2_demand_data_read_mpi", + "Alias": "b" + } + ] + }, + { + "RuleIndex": 14, + "Formula": "a-b", + "TestType": "RelationshipTest", + "RangeLower": "0", + "RangeUpper": "", + "ErrorThreshold": 5.0, + "Description": "Total L2 misses per instruction should be >= L2 demand code misses per instruction", + "Metrics": [ + { + "Name": "l2_mpi", + "Alias": "a" + }, + { + "Name": "l2_demand_code_mpi", + "Alias": "b" + } + ] + }, + { + "RuleIndex": 15, + "Formula": "b+c+d", + "TestType": "RelationshipTest", + "RangeLower": "a", + "RangeUpper": "a", + "ErrorThreshold": 5.0, + "Description": "L3 data read, rfo, code misses per instruction equals total L3 misses per instruction.", + "Metrics": [ + { + "Name": "llc_mpi", + "Alias": "a" + }, + { + "Name": "llc_data_read_mpi_demand_plus_prefetch", + "Alias": "b" + }, + { + "Name": "llc_rfo_read_mpi_demand_plus_prefetch", + "Alias": "c" + }, + { + "Name": "llc_code_read_mpi_demand_plus_prefetch", + "Alias": "d" + } + ] + }, + { + "RuleIndex": 16, + "Formula": "a", + "TestType": "SingleMetricTest", + "RangeLower": "0", + "RangeUpper": "8", + "ErrorThreshold": 0.0, + "Description": "Setting generous range for allowable frequencies", + "Metrics": [ + { + "Name": "uncore_freq", + "Alias": "a" + } + ] + }, + { + "RuleIndex": 17, + "Formula": "a", + "TestType": "SingleMetricTest", + "RangeLower": "0", + "RangeUpper": "8", + "ErrorThreshold": 0.0, + "Description": "Setting generous range for allowable frequencies", + "Metrics": [ + { + "Name": "cpu_operating_frequency", + "Alias": "a" + } + ] + } + ] +} \ No newline at end of file diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tests/shell/stat_metrics_values.sh new file mode 100755 index 000000000000..ad94c936de7e --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_values.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# perf metrics value validation +# SPDX-License-Identifier: GPL-2.0 +if [ "x$PYTHON" == "x" ] +then + if which python3 > /dev/null + then + PYTHON=python3 + else + echo Skipping test, python3 not detected please set environment variable PYTHON. + exit 2 + fi +fi + +grep -q GenuineIntel /proc/cpuinfo || { echo Skipping non-Intel; exit 2; } + +pythonvalidator=$(dirname $0)/lib/perf_metric_validation.py +rulefile=$(dirname $0)/lib/perf_metric_validation_rules.json +tmpdir=$(mktemp -d /tmp/__perf_test.program.XXXXX) +workload="perf bench futex hash -r 2 -s" + +# Add -debug, save data file and full rule file +echo "Launch python validation script $pythonvalidator" +echo "Output will be stored in: $tmpdir" +$PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workload}" +ret=$? +rm -rf $tmpdir + +exit $ret + -- cgit v1.2.3 From a0f1cc18f91faf75a321135ac08385a4f260a87d Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Tue, 20 Jun 2023 10:00:26 -0700 Subject: perf test: Add skip list for metrics known would fail Add skip list for metrics known would fail because some of the metrics are very likely to fail due to multiplexing or other errors. So add all of the flaky tests into the skip list. Signed-off-by: Weilin Wang Tested-by: Namhyung Kim Cc: ravi.bangoria@amd.com Cc: Ian Rogers Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Caleb Biggers Cc: Perry Taylor Cc: Samantha Alt Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Kan Liang Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230620170027.1861012-3-weilin.wang@intel.com Signed-off-by: Namhyung Kim --- .../perf/tests/shell/lib/perf_metric_validation.py | 31 +++++++++++++++++++--- .../shell/lib/perf_metric_validation_rules.json | 11 ++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py index 81bd2bf38b67..3c3a9b4f8b82 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation.py +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -12,7 +12,7 @@ class Validator: self.reportfname = reportfname self.rules = None self.collectlist=metrics - self.metrics = set() + self.metrics = set(metrics) self.tolerance = t self.workloads = [x for x in workload.split(",") if x] @@ -148,6 +148,7 @@ class Validator: self.errlist.append("Metric '%s' is not collected"%(name)) elif val < 0: negmetric.add("{0}(={1:.4f})".format(name, val)) + self.collectlist[0].append(name) else: pcnt += 1 tcnt += 1 @@ -266,6 +267,7 @@ class Validator: passcnt += 1 else: faillist.append({'MetricName':m['Name'], 'CollectedValue':result}) + self.collectlist[0].append(m['Name']) self.totalcnt += totalcnt self.passedcnt += passcnt @@ -348,7 +350,7 @@ class Validator: if rule["TestType"] == "RelationshipTest": metrics = [m["Name"] for m in rule["Metrics"]] if not any(m not in collectlist[0] for m in metrics): - collectlist[rule["RuleIndex"]] = set(metrics) + collectlist[rule["RuleIndex"]] = [",".join(list(set(metrics)))] for idx, metrics in collectlist.items(): if idx == 0: wl = "sleep 0.5".split() @@ -356,9 +358,12 @@ class Validator: for metric in metrics: command = [tool, 'stat', '-j', '-M', f"{metric}", "-a"] command.extend(wl) + print(" ".join(command)) cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8') data = [x+'}' for x in cmd.stderr.split('}\n') if x] self.convert(data, idx) + self.collectlist = dict() + self.collectlist[0] = list() # End of Collector and Converter # Start of Rule Generator @@ -386,6 +391,20 @@ class Validator: return + def remove_unsupported_rules(self, rules, skiplist: set = None): + for m in skiplist: + self.metrics.discard(m) + new_rules = [] + for rule in rules: + add_rule = True + for m in rule["Metrics"]: + if m["Name"] not in self.metrics: + add_rule = False + break + if add_rule: + new_rules.append(rule) + return new_rules + def create_rules(self): """ Create full rules which includes: @@ -394,7 +413,10 @@ class Validator: Reindex all the rules to avoid repeated RuleIndex """ - self.rules = self.read_json(self.rulefname)['RelationshipRules'] + data = self.read_json(self.rulefname) + rules = data['RelationshipRules'] + skiplist = set(data['SkipList']) + self.rules = self.remove_unsupported_rules(rules, skiplist) pctgrule = {'RuleIndex':0, 'TestType':'SingleMetricTest', 'RangeLower':'0', @@ -453,7 +475,8 @@ class Validator: The final report is written into a JSON file. ''' - self.parse_perf_metrics() + if not self.collectlist: + self.parse_perf_metrics() self.create_rules() for i in range(0, len(self.workloads)): self._init_data() diff --git a/tools/perf/tests/shell/lib/perf_metric_validation_rules.json b/tools/perf/tests/shell/lib/perf_metric_validation_rules.json index debaa910da9f..eb6f59e018b7 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation_rules.json +++ b/tools/perf/tests/shell/lib/perf_metric_validation_rules.json @@ -1,4 +1,15 @@ { + "SkipList": [ + "tsx_aborted_cycles", + "tsx_transactional_cycles", + "C2_Pkg_Residency", + "C6_Pkg_Residency", + "C1_Core_Residency", + "C6_Core_Residency", + "tma_false_sharing", + "tma_remote_cache", + "tma_contested_accesses" + ], "RelationshipRules": [ { "RuleIndex": 1, -- cgit v1.2.3 From 1203a63da0461d0081ea6e3d5e52893985bfed42 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Tue, 20 Jun 2023 10:00:27 -0700 Subject: perf test: Rerun failed metrics with longer workload Rerun failed metrics with longer workload to avoid false failure because sometimes metric value test fails when running in very short amount of time. Skip rerun if equal to or more than 20 metrics fail. Signed-off-by: Weilin Wang Tested-by: Namhyung Kim Cc: ravi.bangoria@amd.com Cc: Ian Rogers Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Caleb Biggers Cc: Perry Taylor Cc: Samantha Alt Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Kan Liang Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230620170027.1861012-4-weilin.wang@intel.com Signed-off-by: Namhyung Kim --- .../perf/tests/shell/lib/perf_metric_validation.py | 129 +++++++++++++-------- 1 file changed, 83 insertions(+), 46 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py index 3c3a9b4f8b82..50a34a9cc040 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation.py +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -11,8 +11,9 @@ class Validator: self.rulefname = rulefname self.reportfname = reportfname self.rules = None - self.collectlist=metrics - self.metrics = set(metrics) + self.collectlist:str = metrics + self.metrics = self.__set_metrics(metrics) + self.skiplist = set() self.tolerance = t self.workloads = [x for x in workload.split(",") if x] @@ -41,6 +42,12 @@ class Validator: self.debug = debug self.fullrulefname = fullrulefname + def __set_metrics(self, metrics=''): + if metrics != '': + return set(metrics.split(",")) + else: + return set() + def read_json(self, filename: str) -> dict: try: with open(Path(filename).resolve(), "r") as f: @@ -113,7 +120,7 @@ class Validator: All future test(s) on this metric will fail. @param name: name of the metric - @returns: list with value found in self.results; list is empty when not value found. + @returns: list with value found in self.results; list is empty when value is not found. """ results = [] data = self.results[ridx] if ridx in self.results else self.results[0] @@ -123,7 +130,6 @@ class Validator: elif name.replace('.', '1').isdigit(): results.append(float(name)) else: - self.errlist.append("Metric '%s' is not collected or the value format is incorrect"%(name)) self.ignoremetrics.add(name) return results @@ -138,27 +144,32 @@ class Validator: Failure: when metric value is negative or not provided. Metrics with negative value will be added into the self.failtests['PositiveValueTest'] and self.ignoremetrics. """ - negmetric = set() - missmetric = set() + negmetric = dict() pcnt = 0 tcnt = 0 + rerun = list() for name, val in self.get_results().items(): - if val is None or val == '': - missmetric.add(name) - self.errlist.append("Metric '%s' is not collected"%(name)) - elif val < 0: - negmetric.add("{0}(={1:.4f})".format(name, val)) - self.collectlist[0].append(name) + if val < 0: + negmetric[name] = val + rerun.append(name) else: pcnt += 1 tcnt += 1 + if len(rerun) > 0 and len(rerun) < 20: + second_results = dict() + self.second_test(rerun, second_results) + for name, val in second_results.items(): + if name not in negmetric: continue + if val >= 0: + del negmetric[name] + pcnt += 1 self.failtests['PositiveValueTest']['Total Tests'] = tcnt self.failtests['PositiveValueTest']['Passed Tests'] = pcnt - if len(negmetric) or len(missmetric)> 0: - self.ignoremetrics.update(negmetric) - self.ignoremetrics.update(missmetric) - self.failtests['PositiveValueTest']['Failed Tests'].append({'NegativeValue':list(negmetric), 'MissingValue':list(missmetric)}) + if len(negmetric.keys()): + self.ignoremetrics.update(negmetric.keys()) + negmessage = ["{0}(={1:.4f})".format(name, val) for name, val in negmetric.items()] + self.failtests['PositiveValueTest']['Failed Tests'].append({'NegativeValue': negmessage}) return @@ -259,21 +270,36 @@ class Validator: metrics = rule['Metrics'] passcnt = 0 totalcnt = 0 - faillist = [] + faillist = list() + failures = dict() + rerun = list() for m in metrics: totalcnt += 1 result = self.get_value(m['Name']) - if len(result) > 0 and self.check_bound(result[0], lbv, ubv, t): + if len(result) > 0 and self.check_bound(result[0], lbv, ubv, t) or m['Name'] in self.skiplist: passcnt += 1 else: - faillist.append({'MetricName':m['Name'], 'CollectedValue':result}) - self.collectlist[0].append(m['Name']) + failures[m['Name']] = result + rerun.append(m['Name']) + + if len(rerun) > 0 and len(rerun) < 20: + second_results = dict() + self.second_test(rerun, second_results) + for name, val in second_results.items(): + if name not in failures: continue + if self.check_bound(val, lbv, ubv, t): + passcnt += 1 + del failures[name] + else: + failures[name] = val + self.results[0][name] = val self.totalcnt += totalcnt self.passedcnt += passcnt self.failtests['SingleMetricTest']['Total Tests'] += totalcnt self.failtests['SingleMetricTest']['Passed Tests'] += passcnt - if len(faillist) != 0: + if len(failures.keys()) != 0: + faillist = [{'MetricName':name, 'CollectedValue':val} for name, val in failures.items()] self.failtests['SingleMetricTest']['Failed Tests'].append({'RuleIndex':rule['RuleIndex'], 'RangeLower': rule['RangeLower'], 'RangeUpper': rule['RangeUpper'], @@ -316,7 +342,7 @@ class Validator: return True # Start of Collector and Converter - def convert(self, data: list, idx: int): + def convert(self, data: list, metricvalues:dict): """ Convert collected metric data from the -j output to dict of {metric_name:value}. """ @@ -326,20 +352,29 @@ class Validator: if "metric-unit" in result and result["metric-unit"] != "(null)" and result["metric-unit"] != "": name = result["metric-unit"].split(" ")[1] if len(result["metric-unit"].split(" ")) > 1 \ else result["metric-unit"] - if idx not in self.results: self.results[idx] = dict() - self.results[idx][name.lower()] = float(result["metric-value"]) + metricvalues[name.lower()] = float(result["metric-value"]) except ValueError as error: continue return - def collect_perf(self, data_file: str, workload: str): + def _run_perf(self, metric, workload: str): + tool = 'perf' + command = [tool, 'stat', '-j', '-M', f"{metric}", "-a"] + wl = workload.split() + command.extend(wl) + print(" ".join(command)) + cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8') + data = [x+'}' for x in cmd.stderr.split('}\n') if x] + return data + + + def collect_perf(self, workload: str): """ Collect metric data with "perf stat -M" on given workload with -a and -j. """ self.results = dict() - tool = 'perf' print(f"Starting perf collection") - print(f"Workload: {workload}") + print(f"Long workload: {workload}") collectlist = dict() if self.collectlist != "": collectlist[0] = {x for x in self.collectlist.split(",")} @@ -353,17 +388,20 @@ class Validator: collectlist[rule["RuleIndex"]] = [",".join(list(set(metrics)))] for idx, metrics in collectlist.items(): - if idx == 0: wl = "sleep 0.5".split() - else: wl = workload.split() + if idx == 0: wl = "true" + else: wl = workload for metric in metrics: - command = [tool, 'stat', '-j', '-M', f"{metric}", "-a"] - command.extend(wl) - print(" ".join(command)) - cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8') - data = [x+'}' for x in cmd.stderr.split('}\n') if x] - self.convert(data, idx) - self.collectlist = dict() - self.collectlist[0] = list() + data = self._run_perf(metric, wl) + if idx not in self.results: self.results[idx] = dict() + self.convert(data, self.results[idx]) + return + + def second_test(self, collectlist, second_results): + workload = self.workloads[self.wlidx] + for metric in collectlist: + data = self._run_perf(metric, workload) + self.convert(data, second_results) + # End of Collector and Converter # Start of Rule Generator @@ -381,7 +419,7 @@ class Validator: if 'MetricName' not in m: print("Warning: no metric name") continue - name = m['MetricName'] + name = m['MetricName'].lower() self.metrics.add(name) if 'ScaleUnit' in m and (m['ScaleUnit'] == '1%' or m['ScaleUnit'] == '100%'): self.pctgmetrics.add(name.lower()) @@ -391,14 +429,12 @@ class Validator: return - def remove_unsupported_rules(self, rules, skiplist: set = None): - for m in skiplist: - self.metrics.discard(m) + def remove_unsupported_rules(self, rules): new_rules = [] for rule in rules: add_rule = True for m in rule["Metrics"]: - if m["Name"] not in self.metrics: + if m["Name"] in self.skiplist or m["Name"] not in self.metrics: add_rule = False break if add_rule: @@ -415,15 +451,15 @@ class Validator: """ data = self.read_json(self.rulefname) rules = data['RelationshipRules'] - skiplist = set(data['SkipList']) - self.rules = self.remove_unsupported_rules(rules, skiplist) + self.skiplist = set([name.lower() for name in data['SkipList']]) + self.rules = self.remove_unsupported_rules(rules) pctgrule = {'RuleIndex':0, 'TestType':'SingleMetricTest', 'RangeLower':'0', 'RangeUpper': '100', 'ErrorThreshold': self.tolerance, 'Description':'Metrics in percent unit have value with in [0, 100]', - 'Metrics': [{'Name': m} for m in self.pctgmetrics]} + 'Metrics': [{'Name': m.lower()} for m in self.pctgmetrics]} self.rules.append(pctgrule) # Re-index all rules to avoid repeated RuleIndex @@ -479,8 +515,9 @@ class Validator: self.parse_perf_metrics() self.create_rules() for i in range(0, len(self.workloads)): + self.wlidx = i self._init_data() - self.collect_perf(self.datafname, self.workloads[i]) + self.collect_perf(self.workloads[i]) # Run positive value test self.pos_val_test() for r in self.rules: -- cgit v1.2.3 From d7c2d34d72bfeffca4983c4dcba55d1dd31012be Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 21 Jun 2023 22:58:32 -0700 Subject: perf test: Remove x permission from lib/stat_output.sh The commit fc51fc87b1b8 factored out the helper functions to a library but the new file had execute permission. Due to the way it detects the shell test scripts, it showed up in the perf test list unexpectedly. $ ./perf test list 2>&1 | grep 86 76: x86 bp modify 77: x86 Sample parsing 78: x86 hybrid 86: <---- (here) $ ./perf test -v 86 86: : --- start --- test child forked, pid 1932207 test child finished with 0 ---- end ---- : Ok As it's a collection of library functions, it should not run as is. Let's remove the execute permission. Fixes: fc51fc87b1b8 ("perf test: Move all the check functions of stat CSV output to lib") Acked-by: Ian Rogers Cc: Kan Liang Cc: Andi Kleen Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230622055832.83476-1-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/lib/stat_output.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tools/perf/tests/shell/lib/stat_output.sh (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/stat_output.sh b/tools/perf/tests/shell/lib/stat_output.sh old mode 100755 new mode 100644 -- cgit v1.2.3 From 33fe7c08446af6dda0ff08ff4fa9c921e574477f Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 22 Jun 2023 11:18:09 +0100 Subject: perf tests: Fix test_arm_callgraph_fp variable expansion $TEST_PROGRAM is a command with spaces so it's supposed to be word split. The referenced fix to fix the shellcheck warnings incorrectly quoted this string so unquote it to fix the test. At the same time silence the shellcheck warning for that line and fix two more shellcheck errors at the end of the script. Fixes: 1bb17b4c6c91 ("perf tests arm_callgraph_fp: Address shellcheck warnings about signal names and adding double quotes for expression") Signed-off-by: James Clark Acked-by: Namhyung Kim Cc: Mark Rutland Cc: Ian Rogers Cc: spoorts2@in.ibm.com Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Kajol Jain Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230622101809.2431897-1-james.clark@arm.com Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/test_arm_callgraph_fp.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_arm_callgraph_fp.sh b/tools/perf/tests/shell/test_arm_callgraph_fp.sh index 1380e0d12dce..66dfdfdad553 100755 --- a/tools/perf/tests/shell/test_arm_callgraph_fp.sh +++ b/tools/perf/tests/shell/test_arm_callgraph_fp.sh @@ -15,7 +15,8 @@ cleanup_files() trap cleanup_files EXIT TERM INT # Add a 1 second delay to skip samples that are not in the leaf() function -perf record -o "$PERF_DATA" --call-graph fp -e cycles//u -D 1000 --user-callchains -- "$TEST_PROGRAM" 2> /dev/null & +# shellcheck disable=SC2086 +perf record -o "$PERF_DATA" --call-graph fp -e cycles//u -D 1000 --user-callchains -- $TEST_PROGRAM 2> /dev/null & PID=$! echo " + Recording (PID=$PID)..." @@ -33,8 +34,8 @@ wait $PID # 76c leafloop # ... -perf script -i $PERF_DATA -F comm,ip,sym | head -n4 -perf script -i $PERF_DATA -F comm,ip,sym | head -n4 | \ +perf script -i "$PERF_DATA" -F comm,ip,sym | head -n4 +perf script -i "$PERF_DATA" -F comm,ip,sym | head -n4 | \ awk '{ if ($2 != "") sym[i++] = $2 } END { if (sym[0] != "leaf" || sym[1] != "parent" || sym[2] != "leafloop") exit 1 }' -- cgit v1.2.3 From e4ef3ef1bc0a3d2535427da78b8095ef657eb474 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 22 Jun 2023 16:53:57 -0700 Subject: perf test: Set PERF_EXEC_PATH for script execution The task-analyzer.py script (actually every other scripts too) requires PERF_EXEC_PATH env to find dependent libraries and scripts. For scripts test to run correctly, it needs to set PERF_EXEC_PATH to the perf tool source directory. Instead of blindly update the env, let's check the directory structure to make sure it points to the correct location. Fixes: e8478b84d6ba ("perf test: add new task-analyzer tests") Cc: Petar Gligoric Cc: Hagen Paul Pfeifer Cc: Aditya Gupta Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Acked-by: Ian Rogers Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/test_task_analyzer.sh | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 59785dfc11f8..0095abbe20ca 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -5,6 +5,12 @@ tmpdir=$(mktemp -d /tmp/perf-script-task-analyzer-XXXXX) err=0 +# set PERF_EXEC_PATH to find scripts in the source directory +perfdir=$(dirname "$0")/../.. +if [ -e "$perfdir/scripts/python/Perf-Trace-Util" ]; then + export PERF_EXEC_PATH=$perfdir +fi + cleanup() { rm -f perf.data rm -f perf.data.old -- cgit v1.2.3 From 8d3df7c39b10d4ff24a605f7b80bb6fefb990798 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 23 Jun 2023 16:01:38 -0700 Subject: perf test: Reorder event name checks in stat STD output linter On AMD machines, the perf stat STD output test failed like below: $ sudo ./perf test -v 98 98: perf stat STD output linter : --- start --- test child forked, pid 1841901 Checking STD output: no argswrong event metric. expected 'GHz' in 108,121 stalled-cycles-frontend # 10.88% frontend cycles idle test child finished with -1 ---- end ---- perf stat STD output linter: FAILED! This is because there are stalled-cycles-{frontend,backend} events are used by default. The current logic checks the event_name array to find which event it's running. But 'cycles' event comes before those stalled cycles event and it matches first. So it tries to find 'GHz' metric in the output (which is for the 'cycles') and fails. Move the stalled-cycles-{frontend,backend} events before 'cycles' so that it can find the stalled cycles events first. Also add a space after 'no args' test name for consistency. Fixes: 99a04a48f225 ("perf test: Add test case for the standard 'perf stat' output") Acked-by: Ian Rogers Cc: Kan Liang Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230623230139.985594-1-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/lib/stat_output.sh | 2 +- tools/perf/tests/shell/stat+std_output.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lib/stat_output.sh b/tools/perf/tests/shell/lib/stat_output.sh index 363979b1123d..698343f0ecf9 100644 --- a/tools/perf/tests/shell/lib/stat_output.sh +++ b/tools/perf/tests/shell/lib/stat_output.sh @@ -9,7 +9,7 @@ function ParanoidAndNotRoot() # $1 name $2 extra_opt check_no_args() { - echo -n "Checking $1 output: no args" + echo -n "Checking $1 output: no args " perf stat $2 true commachecker --no-args echo "[Success]" diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh index 98cc3356a04a..1f70aab45184 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -10,8 +10,8 @@ set -e stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) -event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults cycles instructions branches branch-misses stalled-cycles-frontend stalled-cycles-backend) -event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "GHz" "insn per cycle" "/sec" "of all branches" "frontend cycles idle" "backend cycles idle") +event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults stalled-cycles-frontend stalled-cycles-backend cycles instructions branches branch-misses) +event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "frontend cycles idle" "backend cycles idle" "GHz" "insn per cycle" "/sec" "of all branches") metricgroup_name=(TopdownL1 TopdownL2) -- cgit v1.2.3 From 4d60e83dfcee794213878155463d8f7353a80864 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 23 Jun 2023 16:01:39 -0700 Subject: perf test: Skip metrics w/o event name in stat STD output linter This test checks if the output of perf stat to match event names and metrics. So it wants the output lines to have both event name and metric. Otherwise it should skip the line. On AMD machines, the instruction event has two metrics and they are printed in separate lines. It makes the line without event name like below: # perf stat -a sleep 1 Performance counter stats for 'system wide': 64,383.34 msec cpu-clock # 64.048 CPUs utilized 14,526 context-switches # 225.617 /sec 112 cpu-migrations # 1.740 /sec 190 page-faults # 2.951 /sec 807,558,652 cycles # 0.013 GHz (83.30%) 69,809,799 stalled-cycles-frontend # 8.64% frontend cycles idle (83.30%) 196,983,266 stalled-cycles-backend # 24.39% backend cycles idle (83.30%) 424,876,008 instructions # 0.53 insn per cycle (here) ---> # 0.46 stalled cycles per insn (83.30%) 97,788,321 branches # 1.519 M/sec (83.34%) 4,147,377 branch-misses # 4.24% of all branches (83.46%) 1.005241409 seconds time elapsed Also modern Intel machines have TopDown metrics which also don't have event names. # perf stat -a sleep 1 Performance counter stats for 'system wide': 8,015.39 msec cpu-clock # 7.996 CPUs utilized 5,823 context-switches # 726.477 /sec 189 cpu-migrations # 23.580 /sec 139 page-faults # 17.342 /sec 435,139,308 cycles # 0.054 GHz 193,891,345 instructions # 0.45 insn per cycle 42,773,028 branches # 5.336 M/sec 2,298,113 branch-misses # 5.37% of all branches TopdownL1 # 25.5 % tma_backend_bound /--> # 7.9 % tma_bad_speculation (here) --+ # 55.7 % tma_frontend_bound \--> # 10.9 % tma_retiring 1.002395924 seconds time elapsed There is a check to skip TopdownL1 and TopdownL2 specifically but it does not cover every affected lines. So there is another check to skip the line if it has nothing on the left side of # sign. Well.. it seems ok but that's not enough too. When aggregation mode (like --per-socket or --per-thread) is used, it adds some prefix (e.g. CPU socket, task name and PID) in the output line. So the test code ignores them to normalize result. A problem can happen for per-thread mode when task name contains one or more spaces. It'd only ignore the first part of the task name, and it thinks there's something more in the line so it would not skip. # perf stat -a --perf-thread sleep 1 ... perf-21276 # 70.2 % tma_backend_bound perf-21276 # 3.9 % tma_bad_speculation perf-21276 # 10.5 % tma_frontend_bound perf-21276 # 15.3 % tma_retiring ^^^^^^^^^^ (ignored) my task-21328 # 70.2 % tma_backend_bound my task-21328 # 3.9 % tma_bad_speculation my task-21328 # 10.5 % tma_frontend_bound my task-21328 # 15.3 % tma_retiring ^^ (ignored) So I think it should look at the metric names instead. Add skip_metric to hold the list of names to skip. It would contain 'stalled cycles per insn' and metrics started by 'tma_'. Fixes: 99a04a48f225 ("perf test: Add test case for the standard 'perf stat' output") Acked-by: Ian Rogers Cc: Kan Liang Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230623230139.985594-2-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/stat+std_output.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh index 1f70aab45184..f972b31fa0c2 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -12,8 +12,7 @@ stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults stalled-cycles-frontend stalled-cycles-backend cycles instructions branches branch-misses) event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "frontend cycles idle" "backend cycles idle" "GHz" "insn per cycle" "/sec" "of all branches") - -metricgroup_name=(TopdownL1 TopdownL2) +skip_metric=("stalled cycles per insn" "tma_") cleanup() { rm -f "${stat_output}" @@ -58,13 +57,14 @@ function commachecker() main_body=$(echo $line | cut -d' ' -f$prefix-) x=${main_body%#*} - # Check default metricgroup - y=$(echo $x | tr -d ' ') - [ "$y" = "" ] && continue - for i in "${!metricgroup_name[@]}"; do - [[ "$y" == *"${metricgroup_name[$i]}"* ]] && break + [ "$x" = "" ] && continue + + # Skip metrics without event name + y=${main_body#*#} + for i in "${!skip_metric[@]}"; do + [[ "$y" == *"${skip_metric[$i]}"* ]] && break done - [[ "$y" == *"${metricgroup_name[$i]}"* ]] && continue + [[ "$y" == *"${skip_metric[$i]}"* ]] && continue # Check default event for i in "${!event_name[@]}"; do -- cgit v1.2.3 From 06c39e742d46eb0a360563f0888ac8c3a9539f47 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Jun 2023 21:14:04 -0700 Subject: perf test: Add build tests for BUILD_BPF_SKEL Add tests with and without generating vmlinux.h. Signed-off-by: Ian Rogers Acked-by: Andrii Nakryiko Acked-by: Namhyung Kim Acked-by: Jiri Olsa Cc: James Clark Cc: Mark Rutland Cc: Yang Jihong Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Tiezhu Yang Cc: Ingo Molnar Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20230623041405.4039475-4-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/tests/make | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 885cd321d67b..58cf96d762d0 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -70,6 +70,8 @@ make_python_perf_so := $(python_perf_so) make_debug := DEBUG=1 make_nondistro := BUILD_NONDISTRO=1 make_extra_tests := EXTRA_TESTS=1 +make_bpf_skel := BUILD_BPF_SKEL=1 +make_gen_vmlinux_h := BUILD_BPF_SKEL=1 GEN_VMLINUX_H=1 make_no_libperl := NO_LIBPERL=1 make_no_libpython := NO_LIBPYTHON=1 make_no_scripts := NO_LIBPYTHON=1 NO_LIBPERL=1 @@ -137,6 +139,8 @@ endif run += make_python_perf_so run += make_debug run += make_nondistro +run += make_build_bpf_skel +run += make_gen_vmlinux_h run += make_no_libperl run += make_no_libpython run += make_no_scripts -- cgit v1.2.3 From d82257d7f604ba6e714c9f1087a6dc599ccb1879 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Jun 2023 22:45:20 -0700 Subject: perf symbol: Remove now unused symbol_conf.sort_by_name Previously used to specify symbol_name_rb_node was in use. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Carsten Haitzler Cc: Mark Rutland Cc: Jason Wang Cc: Changbin Du Cc: Yang Jihong Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Alexander Shishkin Cc: Kan Liang Cc: Athira Rajeev Cc: Ingo Molnar Cc: Christophe JAILLET Link: https://lore.kernel.org/r/20230623054520.4118442-4-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/builtin-kallsyms.c | 1 - tools/perf/builtin-lock.c | 2 -- tools/perf/builtin-report.c | 1 - tools/perf/tests/builtin-test.c | 1 - tools/perf/util/probe-event.c | 1 - tools/perf/util/symbol_conf.h | 1 - 6 files changed, 7 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c index 3751df744577..7f75c5b73f26 100644 --- a/tools/perf/builtin-kallsyms.c +++ b/tools/perf/builtin-kallsyms.c @@ -62,7 +62,6 @@ int cmd_kallsyms(int argc, const char **argv) if (argc < 1) usage_with_options(kallsyms_usage, options); - symbol_conf.sort_by_name = true; symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); if (symbol__init(NULL) < 0) return -1; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 8b505e1e5002..da36ace66d68 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -1774,7 +1774,6 @@ static int __cmd_report(bool display_info) } /* for lock function check */ - symbol_conf.sort_by_name = true; symbol_conf.allow_aliases = true; symbol__init(&session->header.env); @@ -1904,7 +1903,6 @@ static int __cmd_contention(int argc, const char **argv) con.save_callstack = true; /* for lock function check */ - symbol_conf.sort_by_name = true; symbol_conf.allow_aliases = true; symbol__init(&session->header.env); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index a31a23af5547..dcedfe00f04d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1676,7 +1676,6 @@ repeat: * See symbol__browser_index. */ symbol_conf.priv_size += sizeof(u32); - symbol_conf.sort_by_name = true; } annotation_config__init(&report.annotation_opts); } diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index aa44fdc84763..1f6557ce3b0a 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -542,7 +542,6 @@ int cmd_test(int argc, const char **argv) return run_workload(workload, argc, argv); symbol_conf.priv_size = sizeof(int); - symbol_conf.sort_by_name = true; symbol_conf.try_vmlinux_path = true; if (symbol__init(NULL) < 0) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 277cb8f84cbc..16822a8a540f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -74,7 +74,6 @@ int init_probe_symbol_maps(bool user_only) { int ret; - symbol_conf.sort_by_name = true; symbol_conf.allow_aliases = true; ret = symbol__init(NULL); if (ret < 0) { diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index f26f81eb8252..0b589570d1d0 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -18,7 +18,6 @@ struct symbol_conf { show_kernel_path, use_modules, allow_aliases, - sort_by_name, show_nr_samples, show_total_period, use_callchain, -- cgit v1.2.3 From ad5f604e186ac08d12c401e34ea96c09c38ddbc5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 26 Jun 2023 23:32:57 -0700 Subject: perf test: Fix a compile error on pe-file-parsing.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dso__find_symbol_by_name() should be have idx pointer argument. Found during the build-test. $ make build-test ... CC /tmp/tmp.6JwPK1xbWG/tests/pe-file-parsing.o tests/pe-file-parsing.c: In function ‘run_dir’: tests/pe-file-parsing.c:64:15: error: too few arguments to function ‘dso__find_symbol_by_name’ 64 | sym = dso__find_symbol_by_name(dso, "main"); | ^~~~~~~~~~~~~~~~~~~~~~~~ In file included from tests/pe-file-parsing.c:16: /usr/local/google/home/namhyung/project/linux/tools/perf/util/symbol.h:135:16: note: declared here 135 | struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx); | ^~~~~~~~~~~~~~~~~~~~~~~~ Fixes: 259dce914e93 ("perf symbol: Remove symbol_name_rb_node") Acked-by: Ian Rogers Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230627063257.549005-1-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/Makefile.config | 13 ++++++++++--- tools/perf/tests/pe-file-parsing.c | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 78411252b72a..0609c19caabd 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -315,6 +315,9 @@ FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) FEATURE_CHECK_LDFLAGS-libaio = -lrt +FEATURE_CHECK_LDFLAGS-disassembler-four-args = -lbfd -lopcodes -ldl +FEATURE_CHECK_LDFLAGS-disassembler-init-styled = -lbfd -lopcodes -ldl + CORE_CFLAGS += -fno-omit-frame-pointer CORE_CFLAGS += -ggdb3 CORE_CFLAGS += -funwind-tables @@ -344,8 +347,8 @@ ifneq ($(TCMALLOC),) endif ifeq ($(FEATURES_DUMP),) -# We will display at the end of this Makefile.config, using $(call feature_display_entries), -# as we may retry some feature detection here. +# We will display at the end of this Makefile.config, using $(call feature_display_entries) +# As we may retry some feature detection here, see the disassembler-four-args case, for instance FEATURE_DISPLAY_DEFERRED := 1 include $(srctree)/tools/build/Makefile.feature else @@ -907,9 +910,13 @@ ifdef BUILD_NONDISTRO ifeq ($(feature-libbfd-liberty), 1) EXTLIBS += -lbfd -lopcodes -liberty + FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -ldl + FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -ldl else ifeq ($(feature-libbfd-liberty-z), 1) EXTLIBS += -lbfd -lopcodes -liberty -lz + FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -lz -ldl + FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -lz -ldl endif endif $(call feature_check,disassembler-four-args) @@ -1333,6 +1340,6 @@ endif # re-generate FEATURE-DUMP as we may have called feature_check, found out # extra libraries to add to LDFLAGS of some other test and then redo those -# tests. +# tests, see the block about libbfd, disassembler-four-args, for instance. $(shell rm -f $(FEATURE_DUMP_FILENAME)) $(foreach feat,$(FEATURE_TESTS),$(shell echo "$(call feature_assign,$(feat))" >> $(FEATURE_DUMP_FILENAME))) diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c index c09a9fae1689..fff58b220c07 100644 --- a/tools/perf/tests/pe-file-parsing.c +++ b/tools/perf/tests/pe-file-parsing.c @@ -34,6 +34,7 @@ static int run_dir(const char *d) struct dso *dso; struct symbol *sym; int ret; + size_t idx; scnprintf(filename, PATH_MAX, "%s/pe-file.exe", d); ret = filename__read_build_id(filename, &bid); @@ -61,7 +62,7 @@ static int run_dir(const char *d) TEST_ASSERT_VAL("Failed to load symbols", ret == 0); dso__sort_by_name(dso); - sym = dso__find_symbol_by_name(dso, "main"); + sym = dso__find_symbol_by_name(dso, "main", &idx); TEST_ASSERT_VAL("Failed to find main", sym); dso__delete(dso); -- cgit v1.2.3 From 4a4a9bf9075fbc753ab20f05347fd1482d4801e4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Jun 2023 08:10:05 -0700 Subject: perf expr: Add has_event function Some events are dependent on firmware/kernel enablement. Allow such events to be detected when the metric is parsed so that the metric's event parsing doesn't fail. Signed-off-by: Ian Rogers Tested-by: Namhyung Kim Cc: Mark Rutland Cc: Eduard Zingerman Cc: Sohom Datta Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Caleb Biggers Cc: Edward Baker Cc: Perry Taylor Cc: Samantha Alt Cc: Weilin Wang Cc: Arnaldo Carvalho de Melo Cc: Andrii Nakryiko Cc: Jiri Olsa Cc: Jing Zhang Cc: Kajol Jain Cc: Alexander Shishkin Cc: Kan Liang Cc: Zhengjun Xing Cc: John Garry Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230623151016.4193660-2-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/tests/expr.c | 4 ++++ tools/perf/util/expr.c | 21 +++++++++++++++++++++ tools/perf/util/expr.h | 1 + tools/perf/util/expr.l | 1 + tools/perf/util/expr.y | 8 +++++++- 5 files changed, 34 insertions(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index 3d01eb5e2512..c1c3fcbc2753 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -254,6 +254,10 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u TEST_ASSERT_VAL("source count", hashmap__size(ctx->ids) == 1); TEST_ASSERT_VAL("source count", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); + /* has_event returns 1 when an event exists. */ + expr__add_id_val(ctx, strdup("cycles"), 2); + ret = test(ctx, "has_event(cycles)", 1); + expr__ctx_free(ctx); return 0; diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index f4e52919324e..4814262e3805 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -8,6 +8,7 @@ #include "cpumap.h" #include "cputopo.h" #include "debug.h" +#include "evlist.h" #include "expr.h" #include "expr-bison.h" #include "expr-flex.h" @@ -474,3 +475,23 @@ out: pr_debug2("literal: %s = %f\n", literal, result); return result; } + +/* Does the event 'id' parse? Determine via ctx->ids if possible. */ +double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id) +{ + struct evlist *tmp; + double ret; + + if (hashmap__find(ctx->ids, id, /*value=*/NULL)) + return 1.0; + + if (!compute_ids) + return 0.0; + + tmp = evlist__new(); + if (!tmp) + return NAN; + ret = parse_event(tmp, id) ? 0 : 1; + evlist__delete(tmp); + return ret; +} diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h index eaa44b24c555..3c1e49b3e35d 100644 --- a/tools/perf/util/expr.h +++ b/tools/perf/util/expr.h @@ -54,5 +54,6 @@ int expr__find_ids(const char *expr, const char *one, double expr_id_data__value(const struct expr_id_data *data); double expr_id_data__source_count(const struct expr_id_data *data); double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); +double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); #endif diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l index 4fbf353e78e7..dbb117414710 100644 --- a/tools/perf/util/expr.l +++ b/tools/perf/util/expr.l @@ -113,6 +113,7 @@ min { return MIN; } if { return IF; } else { return ELSE; } source_count { return SOURCE_COUNT; } +has_event { return HAS_EVENT; } {literal} { return literal(yyscanner, sctx); } {number} { return value(yyscanner); } {symbol} { return str(yyscanner, ID, sctx->runtime); } diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index f04963eb6be0..dd504afd8f36 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -37,7 +37,7 @@ } ids; } -%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR +%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR %left MIN MAX IF %left '|' %left '^' @@ -199,6 +199,12 @@ expr: NUMBER } | ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } | SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } +| HAS_EVENT '(' ID ')' +{ + $$.val = expr__has_event(ctx, compute_ids, $3); + $$.ids = NULL; + free($3); +} | expr '|' expr { if (is_const($1.val) && is_const($3.val)) { -- cgit v1.2.3 From 2aefb4cc904f17aad03acc3e05f44cef7801c497 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 28 Jun 2023 13:01:41 -0700 Subject: perf test: Test perf lock contention CSV output To verify CSV output, just check the number of separators (",") using the tr and wc commands like this. grep -v "^#" ${result} | tr -d -c | wc -c Now it expects 6 columns (and 5 separators) in the output, but it may be changed later so count the field in the header first and compare it to the actual output lines. $ cat ${result} # output: contended, total wait, max wait, avg wait, type, caller 1, 28787, 28787, 28787, spinlock, raw_spin_rq_lock_nested+0x1b The test looks like below now: $ sudo ./perf test -v contention 86: kernel lock contention analysis test : --- start --- test child forked, pid 2705822 Testing perf lock record and perf lock contention Testing perf lock contention --use-bpf Testing perf lock record and perf lock contention at the same time Testing perf lock contention --threads Testing perf lock contention --lock-addr Testing perf lock contention --type-filter (w/ spinlock) Testing perf lock contention --lock-filter (w/ tasklist_lock) Testing perf lock contention --callstack-filter (w/ unix_stream) Testing perf lock contention --callstack-filter with task aggregation Testing perf lock contention CSV output test child finished with 0 ---- end ---- kernel lock contention analysis test: Ok Acked-by: Ian Rogers Cc: Hao Luo Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Ingo Molnar Cc: Song Liu Link: https://lore.kernel.org/r/20230628200141.2739587-5-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/tests/shell/lock_contention.sh | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index f2cc187b6186..4a194420416e 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -233,6 +233,41 @@ test_aggr_task_stack_filter() fi } +test_csv_output() +{ + echo "Testing perf lock contention CSV output" + perf lock contention -i ${perfdata} -E 1 -x , --output ${result} + # count the number of commas in the header + # it should have 5: contended, total-wait, max-wait, avg-wait, type, caller + header=$(grep "# output:" ${result} | tr -d -c , | wc -c) + if [ "${header}" != "5" ]; then + echo "[Fail] Recorded result does not have enough output columns: ${header} != 5" + err=1 + exit + fi + # count the number of commas in the output + output=$(grep -v "^#" ${result} | tr -d -c , | wc -c) + if [ "${header}" != "${output}" ]; then + echo "[Fail] Recorded result does not match the number of commas: ${header} != ${output}" + err=1 + exit + fi + + if ! perf lock con -b true > /dev/null 2>&1 ; then + echo "[Skip] No BPF support" + return + fi + + # the perf lock contention output goes to the stderr + perf lock con -a -b -E 1 -x , --output ${result} -- perf bench sched messaging > /dev/null 2>&1 + output=$(grep -v "^#" ${result} | tr -d -c , | wc -c) + if [ "${header}" != "${output}" ]; then + echo "[Fail] BPF result does not match the number of commas: ${header} != ${output}" + err=1 + exit + fi +} + check test_record @@ -244,5 +279,6 @@ test_type_filter test_lock_filter test_stack_filter test_aggr_task_stack_filter +test_csv_output exit ${err} -- cgit v1.2.3 From 808ce56e7d6bfc144b18ded8c11819c45a889520 Mon Sep 17 00:00:00 2001 From: James Clark Date: Wed, 5 Jul 2023 09:26:51 +0100 Subject: perf test: Fix event parsing test on Arm The test looks for a PMU from sysfs with type = PERF_TYPE_RAW when opening a raw event. Arm doesn't have a real raw PMU, only core PMUs with unique types other than raw. Instead of looking for a matching PMU, just test that the event type was parsed as raw and skip the PMU search on Arm. The raw event type test should also apply to all platforms so add it outside of the ifdef. Fixes: aefde50a446b ("perf test: Fix parse-events tests for >1 core PMU") Acked-by: Ian Rogers Signed-off-by: James Clark Cc: Mark Rutland Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Alexander Shishkin Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230705082653.23566-2-james.clark@arm.com Signed-off-by: Namhyung Kim --- tools/perf/tests/parse-events.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 133218e51ab4..21f79aa31233 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -108,10 +108,21 @@ static int test__checkevent_raw(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries); perf_evlist__for_each_evsel(&evlist->core, evsel) { - struct perf_pmu *pmu = NULL; + struct perf_pmu *pmu __maybe_unused = NULL; bool type_matched = false; TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a)); + TEST_ASSERT_VAL("event not parsed as raw type", + evsel->attr.type == PERF_TYPE_RAW); +#if defined(__aarch64__) + /* + * Arm doesn't have a real raw type PMU in sysfs, so raw events + * would never match any PMU. However, RAW events on Arm will + * always successfully open on the first available core PMU + * so no need to test for a matching type here. + */ + type_matched = raw_type_match = true; +#else while ((pmu = perf_pmus__scan(pmu)) != NULL) { if (pmu->type == evsel->attr.type) { TEST_ASSERT_VAL("PMU type expected once", !type_matched); @@ -120,6 +131,7 @@ static int test__checkevent_raw(struct evlist *evlist) raw_type_match = true; } } +#endif TEST_ASSERT_VAL("No PMU found for type", type_matched); } TEST_ASSERT_VAL("Raw PMU not matched", raw_type_match); -- cgit v1.2.3 From bcd981db12e6d26111609802fc7c358f30a8c72a Mon Sep 17 00:00:00 2001 From: James Clark Date: Wed, 5 Jul 2023 09:26:52 +0100 Subject: perf test: Fix event parsing test when PERF_PMU_CAP_EXTENDED_HW_TYPE isn't supported. Arm has multiple PMU types for heterogeneous systems, but doesn't currently support PERF_PMU_CAP_EXTENDED_HW_TYPE. Make the tests support both scenarios so that they pass on Arm, and will still pass once PERF_PMU_CAP_EXTENDED_HW_TYPE support is added. Fixes: 27c9fcfc1e14 ("perf test: Update parse-events expectations to test for multiple events") Acked-by: Ian Rogers Signed-off-by: James Clark Cc: Mark Rutland Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Alexander Shishkin Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230705082653.23566-3-james.clark@arm.com Signed-off-by: Namhyung Kim --- tools/perf/tests/parse-events.c | 86 ++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 36 deletions(-) (limited to 'tools/perf/tests') diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 21f79aa31233..b2f82847e4c3 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -20,6 +20,20 @@ #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) +static int num_core_entries(void) +{ + /* + * If the kernel supports extended type, expect events to be + * opened once for each core PMU type. Otherwise fall back to the legacy + * behavior of opening only one event even though there are multiple + * PMUs + */ + if (perf_pmus__supports_extended_type()) + return perf_pmus__num_core_pmus(); + + return 1; +} + static bool test_config(const struct evsel *evsel, __u64 expected_config) { __u32 type = evsel->core.attr.type; @@ -339,7 +353,7 @@ static int test__checkevent_symbolic_name_modifier(struct evlist *evlist) struct perf_evsel *evsel; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == perf_pmus__num_core_pmus()); + evlist->core.nr_entries == num_core_entries()); perf_evlist__for_each_entry(&evlist->core, evsel) { TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); @@ -842,11 +856,11 @@ static int test__group1(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (perf_pmus__num_core_pmus() * 2)); + evlist->core.nr_entries == (num_core_entries() * 2)); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + evlist__nr_groups(evlist) == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* instructions:k */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -885,7 +899,7 @@ static int test__group2(struct evlist *evlist) struct evsel *evsel, *leader = NULL; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus() + 1)); + evlist->core.nr_entries == (2 * num_core_entries() + 1)); /* * TODO: Currently the software event won't be grouped with the hardware * event except for 1 PMU. @@ -1051,11 +1065,11 @@ static int test__group4(struct evlist *evlist __maybe_unused) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (perf_pmus__num_core_pmus() * 2)); + evlist->core.nr_entries == (num_core_entries() * 2)); TEST_ASSERT_VAL("wrong number of groups", - perf_pmus__num_core_pmus() == evlist__nr_groups(evlist)); + num_core_entries() == evlist__nr_groups(evlist)); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles:u + p */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1096,11 +1110,11 @@ static int test__group5(struct evlist *evlist __maybe_unused) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (5 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (5 * num_core_entries())); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == (2 * perf_pmus__num_core_pmus())); + evlist__nr_groups(evlist) == (2 * num_core_entries())); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles + G */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1131,7 +1145,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); } - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles:G */ evsel = leader = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1161,7 +1175,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); } - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1182,11 +1196,11 @@ static int test__group_gh1(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (2 * num_core_entries())); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + evlist__nr_groups(evlist) == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles + :H group modifier */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1223,11 +1237,11 @@ static int test__group_gh2(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (2 * num_core_entries())); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + evlist__nr_groups(evlist) == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles + :G group modifier */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1264,11 +1278,11 @@ static int test__group_gh3(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (2 * num_core_entries())); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + evlist__nr_groups(evlist) == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles:G + :u group modifier */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1305,11 +1319,11 @@ static int test__group_gh4(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (2 * num_core_entries())); TEST_ASSERT_VAL("wrong number of groups", - evlist__nr_groups(evlist) == perf_pmus__num_core_pmus()); + evlist__nr_groups(evlist) == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles:G + :uG group modifier */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1346,9 +1360,9 @@ static int test__leader_sample1(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (3 * num_core_entries())); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles - sampling group leader */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1398,9 +1412,9 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (2 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (2 * num_core_entries())); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* instructions - sampling group leader */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1437,9 +1451,9 @@ static int test__checkevent_pinned_modifier(struct evlist *evlist) struct evsel *evsel = NULL; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == perf_pmus__num_core_pmus()); + evlist->core.nr_entries == num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { evsel = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); @@ -1455,9 +1469,9 @@ static int test__pinned_group(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == (3 * num_core_entries())); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles - group leader */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1500,9 +1514,9 @@ static int test__exclusive_group(struct evlist *evlist) struct evsel *evsel = NULL, *leader; TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (3 * perf_pmus__num_core_pmus())); + evlist->core.nr_entries == 3 * num_core_entries()); - for (int i = 0; i < perf_pmus__num_core_pmus(); i++) { + for (int i = 0; i < num_core_entries(); i++) { /* cycles - group leader */ evsel = leader = (i == 0 ? evlist__first(evlist) : evsel__next(evsel)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type); @@ -1574,7 +1588,7 @@ static int test__checkevent_precise_max_modifier(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", - evlist->core.nr_entries == (1 + perf_pmus__num_core_pmus())); + evlist->core.nr_entries == 1 + num_core_entries()); TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_SW_TASK_CLOCK)); return TEST_OK; -- cgit v1.2.3