It'll expand given events for cgroups A, B and C.

  $ ./perf test -v expansion
  69: Event expansion 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 expansion 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/expand-cgroup.c | 241 +++++++++++++++++++++++++++++++
 tools/perf/tests/tests.h         |   1 +
 tools/perf/util/cgroup.c         |  19 ++-
 tools/perf/util/cgroup.h         |   2 +-
 7 files changed, 261 insertions(+), 9 deletions(-)
 create mode 100644 tools/perf/tests/expand-cgroup.c

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 8b81d62ab18b..f00600d9903e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -2249,7 +2249,7 @@ int cmd_stat(int argc, const char **argv)
 
        if (stat_config.cgroup_list) {
                if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list,
-                                         &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 69bea7996f18..4d15bf6041fb 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -61,6 +61,7 @@ perf-y += demangle-java-test.o
 perf-y += pfm.o
 perf-y += parse-metric.o
 perf-y += pe-file-parsing.o
+perf-y += expand-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 651b8ea3354a..132bdb3e6c31 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -345,6 +345,10 @@ static struct test generic_tests[] = {
                .desc = "PE file support",
                .func = test__pe_file_parsing,
        },
+       {
+               .desc = "Event expansion for cgroups",
+               .func = test__expand_cgroup_events,
+       },
        {
                .func = NULL,
        },
diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
new file mode 100644
index 000000000000..d5771e4d094f
--- /dev/null
+++ b/tools/perf/tests/expand-cgroup.c
@@ -0,0 +1,241 @@
+// 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 "pfm.h"
+#include <subcmd/parse-options.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int test_expand_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__expand_cgroup(evlist, cgrp_str, metric_events, false);
+       if (ret < 0) {
+               pr_debug("failed to expand events for cgroups\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 expand_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_expand_events(evlist, &metric_events);
+       evlist__delete(evlist);
+       return ret;
+}
+
+static int expand_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);
+               goto out;
+       }
+
+       rblist__init(&metric_events);
+       ret = test_expand_events(evlist, &metric_events);
+out:
+       evlist__delete(evlist);
+       return ret;
+}
+
+static int expand_libpfm_events(void)
+{
+       int ret;
+       struct evlist *evlist;
+       struct rblist metric_events;
+       const char event_str[] = "UNHALTED_CORE_CYCLES";
+       struct option opt = {
+               .value = &evlist,
+       };
+
+       symbol_conf.event_group = true;
+
+       evlist = evlist__new();
+       TEST_ASSERT_VAL("failed to get evlist", evlist);
+
+       ret = parse_libpfm_events_option(&opt, event_str, 0);
+       if (ret < 0) {
+               pr_debug("failed to parse libpfm event '%s', err %d\n",
+                        event_str, ret);
+               goto out;
+       }
+       if (perf_evlist__empty(evlist)) {
+               pr_debug("libpfm was not enabled\n");
+               goto out;
+       }
+
+       rblist__init(&metric_events);
+       ret = test_expand_events(evlist, &metric_events);
+out:
+       evlist__delete(evlist);
+       return ret;
+}
+
+static int expand_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_expand_events(evlist, &metric_events);
+
+out:
+       metricgroup__rblist_exit(&metric_events);
+       evlist__delete(evlist);
+       return ret;
+}
+
+int test__expand_cgroup_events(struct test *test __maybe_unused,
+                              int subtest __maybe_unused)
+{
+       int ret;
+
+       ret = expand_default_events();
+       TEST_ASSERT_EQUAL("failed to expand default events", ret, 0);
+
+       ret = expand_group_events();
+       TEST_ASSERT_EQUAL("failed to expand event group", ret, 0);
+
+       ret = expand_libpfm_events();
+       TEST_ASSERT_EQUAL("failed to expand event group", ret, 0);
+
+       ret = expand_metric_events();
+       TEST_ASSERT_EQUAL("failed to expand metric events", ret, 0);
+
+       return ret;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ef0f33c6ba23..c85a2c08e407 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -123,6 +123,7 @@ 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__pe_file_parsing(struct test *test, int subtest);
+int test__expand_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 a1bf345a770b..eeffa08251b5 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__expand_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__expand_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 eea6df8ee373..162906f3412a 100644
--- a/tools/perf/util/cgroup.h
+++ b/tools/perf/util/cgroup.h
@@ -26,7 +26,7 @@ struct rblist;
 
 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name);
 int evlist__expand_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.618.gf4bc123cb7-goog

Reply via email to