Re: [PATCH] perf stat: Support regex pattern in --for-each-cgroup

2020-10-23 Thread Namhyung Kim
Hi Andi,

On Sat, Oct 24, 2020 at 2:04 AM Andi Kleen  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


Re: [PATCH] perf stat: Support regex pattern in --for-each-cgroup

2020-10-23 Thread Andi Kleen
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



[PATCH] perf stat: Support regex pattern in --for-each-cgroup

2020-10-23 Thread Namhyung Kim
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  cyclesA #4.204 GHz  
(100.00%)
988.53 msec cpu-clock B #0.988 CPUs utilized
 4,137,114,788  cyclesB #4.185 GHz  
(100.00%)
983.94 msec cpu-clock C #0.983 CPUs utilized
 4,117,862,551  cyclesC #4.166 GHz  
(99.99%)

   1.000975519 seconds time elapsed

Signed-off-by: Namhyung Kim 
---
 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 
 #include 
 #include 
+#include 
+#include 
 
 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] == '/' && n