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

perf stat: Add --for-each-cgroup option

The --for-each-cgroup option is a syntax sugar to monitor large number
of cgroups easily.  Current command line requires to list all the events
and cgroups even if users want to monitor same events for each cgroup.
This patch addresses that usage by copying given events for each cgroup
on user's behalf.

For instance, if they want to monitor 6 events for 200 cgroups each they
should write 1200 event names (with -e) AND 1200 cgroup names (with -G)
on the command line.  But with this change, they can just specify 6
events and 200 cgroups with a new option.

A simpler example below: It wants to measure 3 events for 2 cgroups ('A'
and 'B').  The result is that total 6 events are counted like below.

  $ perf stat -a -e cpu-clock,cycles,instructions --for-each-cgroup A,B sleep 1

   Performance counter stats for 'system wide':

              988.18 msec cpu-clock                 A #    0.987 CPUs utilized
       3,153,761,702      cycles                    A #    3.200 GHz                      (100.00%)
       8,067,769,847      instructions              A #    2.57  insn per cycle           (100.00%)
              982.71 msec cpu-clock                 B #    0.982 CPUs utilized
       3,136,093,298      cycles                    B #    3.182 GHz                      (99.99%)
       8,109,619,327      instructions              B #    2.58  insn per cycle           (99.99%)

         1.001228054 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/20200924124455.336326-3-namhyung@kernel.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 7fedd9b8
...@@ -166,6 +166,11 @@ use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'. ...@@ -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 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'. 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:: -o file::
--output file:: --output file::
Print the output into the designated file. Print the output into the designated file.
......
...@@ -1060,6 +1060,17 @@ static int parse_control_option(const struct option *opt, ...@@ -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); 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[] = { static struct option stat_options[] = {
OPT_BOOLEAN('T', "transaction", &transaction_run, OPT_BOOLEAN('T', "transaction", &transaction_run,
"hardware transaction statistics"), "hardware transaction statistics"),
...@@ -1103,7 +1114,9 @@ static struct option stat_options[] = { ...@@ -1103,7 +1114,9 @@ static struct option stat_options[] = {
OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator",
"print counts with custom separator"), "print counts with custom separator"),
OPT_CALLBACK('G', "cgroup", &evsel_list, "name", 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_STRING('o', "output", &output_name, "file", "output file name"),
OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), OPT_BOOLEAN(0, "append", &append_file, "append to the output file"),
OPT_INTEGER(0, "log-fd", &output_fd, OPT_INTEGER(0, "log-fd", &output_fd,
...@@ -2213,6 +2226,18 @@ int cmd_stat(int argc, const char **argv) ...@@ -2213,6 +2226,18 @@ int cmd_stat(int argc, const char **argv)
if (add_default_attributes()) if (add_default_attributes())
goto out; 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); target__validate(&target);
if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide)) if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide))
......
...@@ -193,6 +193,85 @@ int parse_cgroups(const struct option *opt, const char *str, ...@@ -193,6 +193,85 @@ int parse_cgroups(const struct option *opt, const char *str,
return 0; 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, static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
bool create, const char *path) bool create, const char *path)
{ {
......
...@@ -24,6 +24,7 @@ void cgroup__put(struct cgroup *cgroup); ...@@ -24,6 +24,7 @@ void cgroup__put(struct cgroup *cgroup);
struct evlist; struct evlist;
struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name); 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); void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup);
......
...@@ -145,6 +145,7 @@ struct perf_stat_config { ...@@ -145,6 +145,7 @@ struct perf_stat_config {
int ctl_fd; int ctl_fd;
int ctl_fd_ack; int ctl_fd_ack;
bool ctl_fd_close; bool ctl_fd_close;
const char *cgroup_list;
}; };
void perf_stat__set_big_num(int set); void perf_stat__set_big_num(int set);
......
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