[PATCH 4/4] perf test: Add multiply cgroup event test

From: Namhyung Kim
Date: Tue Sep 08 2020 - 00:43:01 EST


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.garry@xxxxxxxxxx>
Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx>
---
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