Commit bb1c15b6 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo

perf stat: Support regex pattern in --for-each-cgroup

To make the command line even more compact with cgroups, support regex
pattern matching in cgroup names.

  $ perf stat -a -e cpu-clock,cycles --for-each-cgroup ^foo sleep 1

          3,000.73 msec cpu-clock                 foo #    2.998 CPUs utilized
    12,530,992,699      cycles                    foo #    7.517 GHz                      (100.00%)
          1,000.61 msec cpu-clock                 foo/bar #    1.000 CPUs utilized
     4,178,529,579      cycles                    foo/bar #    2.506 GHz                      (100.00%)
          1,000.03 msec cpu-clock                 foo/baz #    0.999 CPUs utilized
     4,176,104,315      cycles                    foo/baz #    2.505 GHz                      (100.00%)

       1.000892614 seconds time elapsed
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Acked-by: default avatarJiri Olsa <jolsa@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lore.kernel.org/lkml/20201027072855.655449-2-namhyung@kernel.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 9b0a7836
...@@ -168,8 +168,9 @@ command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'. ...@@ -168,8 +168,9 @@ command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'.
--for-each-cgroup name:: --for-each-cgroup name::
Expand event list for each cgroup in "name" (allow multiple cgroups separated Expand event list for each cgroup in "name" (allow multiple cgroups separated
by comma). This has same effect that repeating -e option and -G option for by comma). It also support regex patterns to match multiple groups. This has same
each event x name. This option cannot be used with -G/--cgroup option. effect that repeating -e option and -G option for each event x name. This option
cannot be used with -G/--cgroup option.
-o file:: -o file::
--output file:: --output file::
......
...@@ -2235,8 +2235,11 @@ int cmd_stat(int argc, const char **argv) ...@@ -2235,8 +2235,11 @@ int cmd_stat(int argc, const char **argv)
} }
if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list, if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list,
&stat_config.metric_events, true) < 0) &stat_config.metric_events, true) < 0) {
parse_options_usage(stat_usage, stat_options,
"for-each-cgroup", 0);
goto out; goto out;
}
} }
target__validate(&target); target__validate(&target);
......
...@@ -13,9 +13,19 @@ ...@@ -13,9 +13,19 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <api/fs/fs.h> #include <api/fs/fs.h>
#include <ftw.h>
#include <regex.h>
int nr_cgroups; int nr_cgroups;
/* used to match cgroup name with patterns */
struct cgroup_name {
struct list_head list;
bool used;
char name[];
};
static LIST_HEAD(cgroup_list);
static int open_cgroup(const char *name) static int open_cgroup(const char *name)
{ {
char path[PATH_MAX + 1]; char path[PATH_MAX + 1];
...@@ -149,6 +159,137 @@ void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup) ...@@ -149,6 +159,137 @@ void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
evsel__set_default_cgroup(evsel, cgroup); evsel__set_default_cgroup(evsel, cgroup);
} }
/* helper function for ftw() in match_cgroups and list_cgroups */
static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unused,
int typeflag)
{
struct cgroup_name *cn;
if (typeflag != FTW_D)
return 0;
cn = malloc(sizeof(*cn) + strlen(fpath) + 1);
if (cn == NULL)
return -1;
cn->used = false;
strcpy(cn->name, fpath);
list_add_tail(&cn->list, &cgroup_list);
return 0;
}
static void release_cgroup_list(void)
{
struct cgroup_name *cn;
while (!list_empty(&cgroup_list)) {
cn = list_first_entry(&cgroup_list, struct cgroup_name, list);
list_del(&cn->list);
free(cn);
}
}
/* collect given cgroups only */
static int list_cgroups(const char *str)
{
const char *p, *e, *eos = str + strlen(str);
struct cgroup_name *cn;
char *s;
/* use given name as is - for testing purpose */
for (;;) {
p = strchr(str, ',');
e = p ? p : eos;
if (e - str) {
int ret;
s = strndup(str, e - str);
if (!s)
return -1;
/* pretend if it's added by ftw() */
ret = add_cgroup_name(s, NULL, FTW_D);
free(s);
if (ret)
return -1;
} else {
if (add_cgroup_name("", NULL, FTW_D) < 0)
return -1;
}
if (!p)
break;
str = p+1;
}
/* these groups will be used */
list_for_each_entry(cn, &cgroup_list, list)
cn->used = true;
return 0;
}
/* collect all cgroups first and then match with the pattern */
static int match_cgroups(const char *str)
{
char mnt[PATH_MAX];
const char *p, *e, *eos = str + strlen(str);
struct cgroup_name *cn;
regex_t reg;
int prefix_len;
char *s;
if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event"))
return -1;
/* cgroup_name will have a full path, skip the root directory */
prefix_len = strlen(mnt);
/* collect all cgroups in the cgroup_list */
if (ftw(mnt, add_cgroup_name, 20) < 0)
return -1;
for (;;) {
p = strchr(str, ',');
e = p ? p : eos;
/* allow empty cgroups, i.e., skip */
if (e - str) {
/* termination added */
s = strndup(str, e - str);
if (!s)
return -1;
if (regcomp(&reg, s, REG_NOSUB)) {
free(s);
return -1;
}
/* check cgroup name with the pattern */
list_for_each_entry(cn, &cgroup_list, list) {
char *name = cn->name + prefix_len;
if (name[0] == '/' && name[1])
name++;
if (!regexec(&reg, name, 0, NULL, 0))
cn->used = true;
}
regfree(&reg);
free(s);
} else {
/* first entry to root cgroup */
cn = list_first_entry(&cgroup_list, struct cgroup_name,
list);
cn->used = true;
}
if (!p)
break;
str = p+1;
}
return prefix_len;
}
int parse_cgroups(const struct option *opt, const char *str, int parse_cgroups(const struct option *opt, const char *str,
int unset __maybe_unused) int unset __maybe_unused)
{ {
...@@ -201,6 +342,11 @@ int parse_cgroups(const struct option *opt, const char *str, ...@@ -201,6 +342,11 @@ int parse_cgroups(const struct option *opt, const char *str,
return 0; return 0;
} }
static bool has_pattern_string(const char *str)
{
return !!strpbrk(str, "{}[]()|*+?^$");
}
int evlist__expand_cgroup(struct evlist *evlist, const char *str, int evlist__expand_cgroup(struct evlist *evlist, const char *str,
struct rblist *metric_events, bool open_cgroup) struct rblist *metric_events, bool open_cgroup)
{ {
...@@ -208,8 +354,9 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, ...@@ -208,8 +354,9 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
struct evsel *pos, *evsel, *leader; struct evsel *pos, *evsel, *leader;
struct rblist orig_metric_events; struct rblist orig_metric_events;
struct cgroup *cgrp = NULL; struct cgroup *cgrp = NULL;
const char *p, *e, *eos = str + strlen(str); struct cgroup_name *cn;
int ret = -1; int ret = -1;
int prefix_len;
if (evlist->core.nr_entries == 0) { if (evlist->core.nr_entries == 0) {
fprintf(stderr, "must define events before cgroups\n"); fprintf(stderr, "must define events before cgroups\n");
...@@ -234,24 +381,27 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, ...@@ -234,24 +381,27 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
rblist__init(&orig_metric_events); rblist__init(&orig_metric_events);
} }
for (;;) { if (has_pattern_string(str))
p = strchr(str, ','); prefix_len = match_cgroups(str);
e = p ? p : eos; else
prefix_len = list_cgroups(str);
/* allow empty cgroups, i.e., skip */ if (prefix_len < 0)
if (e - str) { goto out_err;
/* termination added */
char *name = strndup(str, e - str);
if (!name)
goto out_err;
cgrp = cgroup__new(name, open_cgroup); list_for_each_entry(cn, &cgroup_list, list) {
free(name); char *name;
if (cgrp == NULL)
goto out_err; if (!cn->used)
} else { continue;
cgrp = NULL;
} /* cgroup_name might have a full path, skip the prefix */
name = cn->name + prefix_len;
if (name[0] == '/' && name[1])
name++;
cgrp = cgroup__new(name, open_cgroup);
if (cgrp == NULL)
goto out_err;
leader = NULL; leader = NULL;
evlist__for_each_entry(orig_list, pos) { evlist__for_each_entry(orig_list, pos) {
...@@ -277,23 +427,25 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, ...@@ -277,23 +427,25 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
if (metricgroup__copy_metric_events(tmp_list, cgrp, if (metricgroup__copy_metric_events(tmp_list, cgrp,
metric_events, metric_events,
&orig_metric_events) < 0) &orig_metric_events) < 0)
break; goto out_err;
} }
perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries); perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
tmp_list->core.nr_entries = 0; tmp_list->core.nr_entries = 0;
}
if (!p) { if (list_empty(&evlist->core.entries)) {
ret = 0; fprintf(stderr, "no cgroup matched: %s\n", str);
break; goto out_err;
}
str = p+1;
} }
ret = 0;
out_err: out_err:
evlist__delete(orig_list); evlist__delete(orig_list);
evlist__delete(tmp_list); evlist__delete(tmp_list);
rblist__exit(&orig_metric_events); rblist__exit(&orig_metric_events);
release_cgroup_list();
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment