It'll multiply given events for cgroups A, B and C. $ ./perf test -v 68 68: Event multiplication for cgroups : --- start --- test child forked, pid 983140 metric expr 1 / IPC for CPI metric expr instructions / cycles for IPC found event instructions found event cycles adding {instructions,cycles}:W copying metric event for cgroup 'A': instructions (idx=0) copying metric event for cgroup 'B': instructions (idx=0) copying metric event for cgroup 'C': instructions (idx=0) test child finished with 0 ---- end ---- Event multiplication for cgroups: Ok
Cc: John Garry <john.ga...@huawei.com> Signed-off-by: Namhyung Kim <namhy...@kernel.org> --- tools/perf/builtin-stat.c | 2 +- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 + tools/perf/tests/multiply-cgroup.c | 203 +++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/cgroup.c | 19 ++- tools/perf/util/cgroup.h | 2 +- 7 files changed, 223 insertions(+), 9 deletions(-) create mode 100644 tools/perf/tests/multiply-cgroup.c diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 55a7dc175cdf..c231972f3581 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -2262,7 +2262,7 @@ int cmd_stat(int argc, const char **argv) if (multiply_cgroup && stat_config.cgroups) { if (evlist__multiply_cgroup(evsel_list, stat_config.cgroups, - &stat_config.metric_events) < 0) + &stat_config.metric_events, true) < 0) goto out; } diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 84352fc49a20..e685a3441853 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -60,6 +60,7 @@ perf-y += api-io.o perf-y += demangle-java-test.o perf-y += pfm.o perf-y += parse-metric.o +perf-y += multiply-cgroup.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index d328caaba45d..de48b9d28230 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -341,6 +341,10 @@ static struct test generic_tests[] = { .desc = "Parse and process metrics", .func = test__parse_metric, }, + { + .desc = "Event multiplication for cgroups", + .func = test__multiply_cgroup_events, + }, { .func = NULL, }, diff --git a/tools/perf/tests/multiply-cgroup.c b/tools/perf/tests/multiply-cgroup.c new file mode 100644 index 000000000000..89af1644cd6a --- /dev/null +++ b/tools/perf/tests/multiply-cgroup.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "tests.h" +#include "debug.h" +#include "evlist.h" +#include "cgroup.h" +#include "rblist.h" +#include "metricgroup.h" +#include "parse-events.h" +#include "pmu-events/pmu-events.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int test_multiply_events(struct evlist *evlist, + struct rblist *metric_events) +{ + int i, ret = TEST_FAIL; + int nr_events; + bool was_group_event; + int nr_members; /* for the first evsel only */ + const char cgrp_str[] = "A,B,C"; + const char *cgrp_name[] = { "A", "B", "C" }; + int nr_cgrps = ARRAY_SIZE(cgrp_name); + char **ev_name; + struct evsel *evsel; + + TEST_ASSERT_VAL("evlist is empty", !perf_evlist__empty(evlist)); + + nr_events = evlist->core.nr_entries; + ev_name = calloc(nr_events, sizeof(*ev_name)); + if (ev_name == NULL) { + pr_debug("memory allocation failure\n"); + return TEST_FAIL; + } + i = 0; + evlist__for_each_entry(evlist, evsel) { + ev_name[i] = strdup(evsel->name); + if (ev_name[i] == NULL) { + pr_debug("memory allocation failure\n"); + goto out; + } + i++; + } + /* remember grouping info */ + was_group_event = evsel__is_group_event(evlist__first(evlist)); + nr_members = evlist__first(evlist)->core.nr_members; + + ret = evlist__multiply_cgroup(evlist, cgrp_str, metric_events, false); + if (ret < 0) { + pr_debug("failed to multiply cgroup\n"); + goto out; + } + + ret = TEST_FAIL; + if (evlist->core.nr_entries != nr_events * nr_cgrps) { + pr_debug("event count doesn't match\n"); + goto out; + } + + i = 0; + evlist__for_each_entry(evlist, evsel) { + if (strcmp(evsel->name, ev_name[i % nr_events])) { + pr_debug("event name doesn't match:\n"); + pr_debug(" evsel[%d]: %s\n expected: %s\n", + i, evsel->name, ev_name[i % nr_events]); + goto out; + } + if (strcmp(evsel->cgrp->name, cgrp_name[i / nr_events])) { + pr_debug("cgroup name doesn't match:\n"); + pr_debug(" evsel[%d]: %s\n expected: %s\n", + i, evsel->cgrp->name, cgrp_name[i / nr_events]); + goto out; + } + + if ((i % nr_events) == 0) { + if (evsel__is_group_event(evsel) != was_group_event) { + pr_debug("event group doesn't match: got %s, expect %s\n", + evsel__is_group_event(evsel) ? "true" : "false", + was_group_event ? "true" : "false"); + goto out; + } + if (evsel->core.nr_members != nr_members) { + pr_debug("event group member doesn't match: %d vs %d\n", + evsel->core.nr_members, nr_members); + goto out; + } + } + i++; + } + ret = TEST_OK; + +out: for (i = 0; i < nr_events; i++) + free(ev_name[i]); + free(ev_name); + return ret; +} + +static int multiply_default_events(void) +{ + int ret; + struct evlist *evlist; + struct rblist metric_events; + + evlist = perf_evlist__new_default(); + TEST_ASSERT_VAL("failed to get evlist", evlist); + + rblist__init(&metric_events); + ret = test_multiply_events(evlist, &metric_events); + evlist__delete(evlist); + return ret; +} + +static int multiply_group_events(void) +{ + int ret; + struct evlist *evlist; + struct rblist metric_events; + struct parse_events_error err; + const char event_str[] = "{cycles,instructions}"; + + symbol_conf.event_group = true; + + evlist = evlist__new(); + TEST_ASSERT_VAL("failed to get evlist", evlist); + + ret = parse_events(evlist, event_str, &err); + if (ret < 0) { + pr_debug("failed to parse event '%s', err %d, str '%s'\n", + event_str, ret, err.str); + parse_events_print_error(&err, event_str); + evlist__delete(evlist); + return ret; + } + + rblist__init(&metric_events); + ret = test_multiply_events(evlist, &metric_events); + evlist__delete(evlist); + return ret; +} + +static int multiply_metric_events(void) +{ + int ret; + struct evlist *evlist; + struct rblist metric_events; + const char metric_str[] = "CPI"; + + struct pmu_event pme_test[] = { + { + .metric_expr = "instructions / cycles", + .metric_name = "IPC", + }, + { + .metric_expr = "1 / IPC", + .metric_name = "CPI", + }, + { + .metric_expr = NULL, + .metric_name = NULL, + }, + }; + struct pmu_events_map ev_map = { + .cpuid = "test", + .version = "1", + .type = "core", + .table = pme_test, + }; + + evlist = evlist__new(); + TEST_ASSERT_VAL("failed to get evlist", evlist); + + rblist__init(&metric_events); + ret = metricgroup__parse_groups_test(evlist, &ev_map, metric_str, + false, false, &metric_events); + if (ret < 0) { + pr_debug("failed to parse '%s' metric\n", metric_str); + goto out; + } + + ret = test_multiply_events(evlist, &metric_events); + +out: + metricgroup__rblist_exit(&metric_events); + evlist__delete(evlist); + return ret; +} + +int test__multiply_cgroup_events(struct test *test __maybe_unused, + int subtest __maybe_unused) +{ + int ret; + + ret = multiply_default_events(); + TEST_ASSERT_EQUAL("failed to multiply default events", ret, 0); + + ret = multiply_group_events(); + TEST_ASSERT_EQUAL("failed to multiply event group", ret, 0); + + ret = multiply_metric_events(); + TEST_ASSERT_EQUAL("failed to multiply metric events", ret, 0); + + return ret; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 4447a516c689..4b7c9e3a4fc5 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -122,6 +122,7 @@ int test__pfm(struct test *test, int subtest); const char *test__pfm_subtest_get_desc(int subtest); int test__pfm_subtest_get_nr(void); int test__parse_metric(struct test *test, int subtest); +int test__multiply_cgroup_events(struct test *test, int subtest); bool test__bp_signal_is_supported(void); bool test__bp_account_is_supported(void); diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 1ba61b5d66b4..0723632002fc 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -52,7 +52,7 @@ static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str return NULL; } -static struct cgroup *cgroup__new(const char *name) +static struct cgroup *cgroup__new(const char *name, bool do_open) { struct cgroup *cgroup = zalloc(sizeof(*cgroup)); @@ -62,9 +62,14 @@ static struct cgroup *cgroup__new(const char *name) cgroup->name = strdup(name); if (!cgroup->name) goto out_err; - cgroup->fd = open_cgroup(name); - if (cgroup->fd == -1) - goto out_free_name; + + if (do_open) { + cgroup->fd = open_cgroup(name); + if (cgroup->fd == -1) + goto out_free_name; + } else { + cgroup->fd = -1; + } } return cgroup; @@ -80,7 +85,7 @@ struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name) { struct cgroup *cgroup = evlist__find_cgroup(evlist, name); - return cgroup ?: cgroup__new(name); + return cgroup ?: cgroup__new(name, true); } static int add_cgroup(struct evlist *evlist, const char *str) @@ -202,7 +207,7 @@ int parse_cgroups(const struct option *opt, const char *str, } int evlist__multiply_cgroup(struct evlist *evlist, const char *str, - struct rblist *metric_events) + struct rblist *metric_events, bool open_cgroup) { struct evlist *orig_list, *tmp_list; struct evsel *pos, *evsel, *leader; @@ -240,7 +245,7 @@ int evlist__multiply_cgroup(struct evlist *evlist, const char *str, if (!name) break; - cgrp = cgroup__new(name); + cgrp = cgroup__new(name, open_cgroup); free(name); if (cgrp == NULL) break; diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h index 87dde992a172..b3d75f18b3ec 100644 --- a/tools/perf/util/cgroup.h +++ b/tools/perf/util/cgroup.h @@ -27,7 +27,7 @@ struct rblist; struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name); int evlist__multiply_cgroup(struct evlist *evlist, const char *cgroups, - struct rblist *metric_events); + struct rblist *metric_events, bool open_cgroup); void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup); -- 2.28.0.526.ge36021eeef-goog