diff options
author | Namhyung Kim <namhyung@kernel.org> | 2020-10-27 16:28:55 +0900 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2020-11-04 09:42:41 -0300 |
commit | bb1c15b60b981d1065d7766ccf9de6c32beedfa3 (patch) | |
tree | 48e6442022790b2de53ff508f1fd424449be74b9 /tools/perf/util | |
parent | 9b0a7836359443227c9af101f7aea8412e739458 (diff) |
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: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri 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.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/cgroup.c | 198 |
1 files changed, 175 insertions, 23 deletions
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index b81324a13a2b..704333748549 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -13,9 +13,19 @@ #include <stdlib.h> #include <string.h> #include <api/fs/fs.h> +#include <ftw.h> +#include <regex.h> 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) { char path[PATH_MAX + 1]; @@ -149,6 +159,137 @@ void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *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(®, 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(®, name, 0, NULL, 0)) + cn->used = true; + } + regfree(®); + 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 unset __maybe_unused) { @@ -201,6 +342,11 @@ int parse_cgroups(const struct option *opt, const char *str, return 0; } +static bool has_pattern_string(const char *str) +{ + return !!strpbrk(str, "{}[]()|*+?^$"); +} + int evlist__expand_cgroup(struct evlist *evlist, const char *str, struct rblist *metric_events, bool open_cgroup) { @@ -208,8 +354,9 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, struct evsel *pos, *evsel, *leader; struct rblist orig_metric_events; struct cgroup *cgrp = NULL; - const char *p, *e, *eos = str + strlen(str); + struct cgroup_name *cn; int ret = -1; + int prefix_len; if (evlist->core.nr_entries == 0) { fprintf(stderr, "must define events before cgroups\n"); @@ -234,24 +381,27 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, rblist__init(&orig_metric_events); } - for (;;) { - p = strchr(str, ','); - e = p ? p : eos; + if (has_pattern_string(str)) + prefix_len = match_cgroups(str); + else + prefix_len = list_cgroups(str); - /* allow empty cgroups, i.e., skip */ - if (e - str) { - /* termination added */ - char *name = strndup(str, e - str); - if (!name) - goto out_err; + if (prefix_len < 0) + goto out_err; - cgrp = cgroup__new(name, open_cgroup); - free(name); - if (cgrp == NULL) - goto out_err; - } else { - cgrp = NULL; - } + list_for_each_entry(cn, &cgroup_list, list) { + char *name; + + if (!cn->used) + continue; + + /* 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; evlist__for_each_entry(orig_list, pos) { @@ -277,23 +427,25 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, if (metricgroup__copy_metric_events(tmp_list, cgrp, metric_events, &orig_metric_events) < 0) - break; + goto out_err; } perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries); tmp_list->core.nr_entries = 0; + } - if (!p) { - ret = 0; - break; - } - str = p+1; + if (list_empty(&evlist->core.entries)) { + fprintf(stderr, "no cgroup matched: %s\n", str); + goto out_err; } + ret = 0; + out_err: evlist__delete(orig_list); evlist__delete(tmp_list); rblist__exit(&orig_metric_events); + release_cgroup_list(); return ret; } |