perf stat: Fix handling of --for-each-cgroup with --bpf-counters to match non BPF...
authorNamhyung Kim <namhyung@kernel.org>
Wed, 4 Jan 2023 06:44:02 +0000 (22:44 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 4 Jan 2023 14:11:41 +0000 (11:11 -0300)
The --for-each-cgroup can have the same cgroup multiple times, but this
confuses BPF counters (since they have the same cgroup id), making only
the last cgroup events to be counted.

Let's check the cgroup name before adding a new entry to the cgroups
list.

Before:

  $ sudo ./perf stat -a --bpf-counters --for-each-cgroup /,/ sleep 1

   Performance counter stats for 'system wide':

       <not counted> msec cpu-clock                        /
       <not counted>      context-switches                 /
       <not counted>      cpu-migrations                   /
       <not counted>      page-faults                      /
       <not counted>      cycles                           /
       <not counted>      instructions                     /
       <not counted>      branches                         /
       <not counted>      branch-misses                    /
            8,016.04 msec cpu-clock                        /                #    7.998 CPUs utilized
               6,152      context-switches                 /                #  767.461 /sec
                 250      cpu-migrations                   /                #   31.187 /sec
                 442      page-faults                      /                #   55.139 /sec
         613,111,487      cycles                           /                #    0.076 GHz
         280,599,604      instructions                     /                #    0.46  insn per cycle
          57,692,724      branches                         /                #    7.197 M/sec
           3,385,168      branch-misses                    /                #    5.87% of all branches

         1.002220125 seconds time elapsed

After it becomes similar to the non-BPF mode:

  $ sudo ./perf stat -a --bpf-counters --for-each-cgroup /,/  sleep 1

   Performance counter stats for 'system wide':

            8,013.38 msec cpu-clock                        /                #    7.998 CPUs utilized
               6,859      context-switches                 /                #  855.944 /sec
                 334      cpu-migrations                   /                #   41.680 /sec
                 345      page-faults                      /                #   43.053 /sec
         782,326,119      cycles                           /                #    0.098 GHz
         471,645,724      instructions                     /                #    0.60  insn per cycle
          94,963,430      branches                         /                #   11.851 M/sec
           3,685,511      branch-misses                    /                #    3.88% of all branches

         1.001864539 seconds time elapsed

Committer notes:

As a reminder, to test with BPF counters one has to use BUILD_BPF_SKEL=1
in the make command line and have clang/llvm installed when building
perf, otherwise the --bpf-counters option will not be available:

  # perf stat -a --bpf-counters --for-each-cgroup /,/ sleep 1
  Error: unknown option `bpf-counters'

   Usage: perf stat [<options>] [<command>]

      -a, --all-cpus        system-wide collection from all CPUs
  <SNIP>
  #

Fixes: bb1c15b60b981d10 ("perf stat: Support regex pattern in --for-each-cgroup")
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: bpf@vger.kernel.org
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <songliubraving@fb.com>
Link: https://lore.kernel.org/r/20230104064402.1551516-5-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/cgroup.c

index e99b41f..cd978c2 100644 (file)
@@ -224,6 +224,19 @@ static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unus
        return 0;
 }
 
+static int check_and_add_cgroup_name(const char *fpath)
+{
+       struct cgroup_name *cn;
+
+       list_for_each_entry(cn, &cgroup_list, list) {
+               if (!strcmp(cn->name, fpath))
+                       return 0;
+       }
+
+       /* pretend if it's added by ftw() */
+       return add_cgroup_name(fpath, NULL, FTW_D, NULL);
+}
+
 static void release_cgroup_list(void)
 {
        struct cgroup_name *cn;
@@ -242,7 +255,7 @@ static int list_cgroups(const char *str)
        struct cgroup_name *cn;
        char *s;
 
-       /* use given name as is - for testing purpose */
+       /* use given name as is when no regex is given */
        for (;;) {
                p = strchr(str, ',');
                e = p ? p : eos;
@@ -253,13 +266,13 @@ static int list_cgroups(const char *str)
                        s = strndup(str, e - str);
                        if (!s)
                                return -1;
-                       /* pretend if it's added by ftw() */
-                       ret = add_cgroup_name(s, NULL, FTW_D, NULL);
+
+                       ret = check_and_add_cgroup_name(s);
                        free(s);
-                       if (ret)
+                       if (ret < 0)
                                return -1;
                } else {
-                       if (add_cgroup_name("", NULL, FTW_D, NULL) < 0)
+                       if (check_and_add_cgroup_name("/") < 0)
                                return -1;
                }