diff options
-rw-r--r-- | tools/perf/Documentation/perf-stat.txt | 5 | ||||
-rw-r--r-- | tools/perf/builtin-stat.c | 27 | ||||
-rw-r--r-- | tools/perf/util/cgroup.c | 79 | ||||
-rw-r--r-- | tools/perf/util/cgroup.h | 1 | ||||
-rw-r--r-- | tools/perf/util/stat.h | 1 |
5 files changed, 112 insertions, 1 deletions
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 5bf3d7ae4660..9f9f29025e49 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -166,6 +166,11 @@ use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'. If wanting to monitor, say, 'cycles' for a cgroup and also for system wide, this command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'. +--for-each-cgroup name:: +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 +each event x name. This option cannot be used with -G/--cgroup option. + -o file:: --output file:: Print the output into the designated file. diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index cbb2d977eec7..0c9e21a29795 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1060,6 +1060,17 @@ static int parse_control_option(const struct option *opt, return evlist__parse_control(str, &config->ctl_fd, &config->ctl_fd_ack, &config->ctl_fd_close); } +static int parse_stat_cgroups(const struct option *opt, + const char *str, int unset) +{ + if (stat_config.cgroup_list) { + pr_err("--cgroup and --for-each-cgroup cannot be used together\n"); + return -1; + } + + return parse_cgroups(opt, str, unset); +} + static struct option stat_options[] = { OPT_BOOLEAN('T', "transaction", &transaction_run, "hardware transaction statistics"), @@ -1103,7 +1114,9 @@ static struct option stat_options[] = { OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", "print counts with custom separator"), OPT_CALLBACK('G', "cgroup", &evsel_list, "name", - "monitor event in cgroup name only", parse_cgroups), + "monitor event in cgroup name only", parse_stat_cgroups), + OPT_STRING(0, "for-each-cgroup", &stat_config.cgroup_list, "name", + "expand events for each cgroup"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), OPT_INTEGER(0, "log-fd", &output_fd, @@ -2213,6 +2226,18 @@ int cmd_stat(int argc, const char **argv) if (add_default_attributes()) goto out; + if (stat_config.cgroup_list) { + if (nr_cgroups > 0) { + pr_err("--cgroup and --for-each-cgroup cannot be used together\n"); + parse_options_usage(stat_usage, stat_options, "G", 1); + parse_options_usage(NULL, stat_options, "for-each-cgroup", 0); + goto out; + } + + if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list) < 0) + goto out; + } + target__validate(&target); if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide)) diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 050dea9f1e88..8b6a4fa49082 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -193,6 +193,85 @@ int parse_cgroups(const struct option *opt, const char *str, return 0; } +int evlist__expand_cgroup(struct evlist *evlist, const char *str) +{ + struct evlist *orig_list, *tmp_list; + struct evsel *pos, *evsel, *leader; + struct cgroup *cgrp = NULL; + const char *p, *e, *eos = str + strlen(str); + int ret = -1; + + if (evlist->core.nr_entries == 0) { + fprintf(stderr, "must define events before cgroups\n"); + return -EINVAL; + } + + orig_list = evlist__new(); + tmp_list = evlist__new(); + if (orig_list == NULL || tmp_list == NULL) { + fprintf(stderr, "memory allocation failed\n"); + return -ENOMEM; + } + + /* save original events and init evlist */ + perf_evlist__splice_list_tail(orig_list, &evlist->core.entries); + evlist->core.nr_entries = 0; + + for (;;) { + p = strchr(str, ','); + e = p ? p : eos; + + /* allow empty cgroups, i.e., skip */ + if (e - str) { + /* termination added */ + char *name = strndup(str, e - str); + if (!name) + goto out_err; + + cgrp = cgroup__new(name); + free(name); + if (cgrp == NULL) + goto out_err; + } else { + cgrp = NULL; + } + + leader = NULL; + evlist__for_each_entry(orig_list, pos) { + evsel = evsel__clone(pos); + if (evsel == NULL) + goto out_err; + + cgroup__put(evsel->cgrp); + evsel->cgrp = cgroup__get(cgrp); + + if (evsel__is_group_leader(pos)) + leader = evsel; + evsel->leader = leader; + + evlist__add(tmp_list, evsel); + } + /* cgroup__new() has a refcount, release it here */ + cgroup__put(cgrp); + nr_cgroups++; + + perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries); + tmp_list->core.nr_entries = 0; + + if (!p) { + ret = 0; + break; + } + str = p+1; + } + +out_err: + evlist__delete(orig_list); + evlist__delete(tmp_list); + + return ret; +} + static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id, bool create, const char *path) { diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h index e98d5975fe55..32893018296f 100644 --- a/tools/perf/util/cgroup.h +++ b/tools/perf/util/cgroup.h @@ -24,6 +24,7 @@ void cgroup__put(struct cgroup *cgroup); struct evlist; struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name); +int evlist__expand_cgroup(struct evlist *evlist, const char *cgroups); void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup); diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index f36c8c92efa0..487010c624be 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -145,6 +145,7 @@ struct perf_stat_config { int ctl_fd; int ctl_fd_ack; bool ctl_fd_close; + const char *cgroup_list; }; void perf_stat__set_big_num(int set); |