* [PATCH] perf stat: Support regex pattern in --for-each-cgroup
@ 2020-10-23 7:42 Namhyung Kim
2020-10-23 17:04 ` Andi Kleen
0 siblings, 1 reply; 3+ messages in thread
From: Namhyung Kim @ 2020-10-23 7:42 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa
Cc: Ingo Molnar, Peter Zijlstra, Mark Rutland, Alexander Shishkin,
LKML, Stephane Eranian, Andi Kleen, Ian Rogers
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 '^.$' sleep 1
Performance counter stats for 'system wide':
992.90 msec cpu-clock A # 0.992 CPUs utilized
4,155,444,322 cycles A # 4.204 GHz (100.00%)
988.53 msec cpu-clock B # 0.988 CPUs utilized
4,137,114,788 cycles B # 4.185 GHz (100.00%)
983.94 msec cpu-clock C # 0.983 CPUs utilized
4,117,862,551 cycles C # 4.166 GHz (99.99%)
1.000975519 seconds time elapsed
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/cgroup.c | 192 ++++++++++++++++++++++++++++++++++-----
1 file changed, 168 insertions(+), 24 deletions(-)
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index b81324a13a2b..127c2411fb9f 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,25 @@ 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);
leader = NULL;
evlist__for_each_entry(orig_list, pos) {
@@ -277,23 +425,19 @@ 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;
}
+ ret = 0;
out_err:
evlist__delete(orig_list);
evlist__delete(tmp_list);
rblist__exit(&orig_metric_events);
+ release_cgroup_list();
return ret;
}
--
2.29.0.rc1.297.gfa9743e501-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] perf stat: Support regex pattern in --for-each-cgroup
2020-10-23 7:42 [PATCH] perf stat: Support regex pattern in --for-each-cgroup Namhyung Kim
@ 2020-10-23 17:04 ` Andi Kleen
2020-10-24 2:25 ` Namhyung Kim
0 siblings, 1 reply; 3+ messages in thread
From: Andi Kleen @ 2020-10-23 17:04 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ingo Molnar, Peter Zijlstra,
Mark Rutland, Alexander Shishkin, LKML, Stephane Eranian,
Ian Rogers
On Fri, Oct 23, 2020 at 04:42:34PM +0900, Namhyung Kim wrote:
> 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 '^.$' sleep 1
The example doesn't exactly show the benefit. So ^.$ would be only
for one character cgroups?
Missing documentation updates.
-Andi
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] perf stat: Support regex pattern in --for-each-cgroup
2020-10-23 17:04 ` Andi Kleen
@ 2020-10-24 2:25 ` Namhyung Kim
0 siblings, 0 replies; 3+ messages in thread
From: Namhyung Kim @ 2020-10-24 2:25 UTC (permalink / raw)
To: Andi Kleen
Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ingo Molnar, Peter Zijlstra,
Mark Rutland, Alexander Shishkin, LKML, Stephane Eranian,
Ian Rogers
Hi Andi,
On Sat, Oct 24, 2020 at 2:04 AM Andi Kleen <ak@linux.intel.com> wrote:
>
> On Fri, Oct 23, 2020 at 04:42:34PM +0900, Namhyung Kim wrote:
> > 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 '^.$' sleep 1
>
> The example doesn't exactly show the benefit. So ^.$ would be only
> for one character cgroups?
Right, I know it's not a good example but just wanted to show the
possibility of regex patterns. Let me come up with a better one.
>
> Missing documentation updates.
Will add.
Thanks
Namhyung
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2020-10-24 2:26 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-10-23 7:42 [PATCH] perf stat: Support regex pattern in --for-each-cgroup Namhyung Kim
2020-10-23 17:04 ` Andi Kleen
2020-10-24 2:25 ` Namhyung Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox