Synthesize cgroup events by iterating cgroup filesystem directories.
The cgroup event only saves the portion of cgroup path after the mount
point and the inode number.

Signed-off-by: Namhyung Kim <namhy...@kernel.org>
---
 tools/perf/builtin-record.c |   5 ++
 tools/perf/util/cgroup.c    |   3 +-
 tools/perf/util/cgroup.h    |   1 +
 tools/perf/util/event.c     | 115 ++++++++++++++++++++++++++++++++++++
 tools/perf/util/event.h     |   4 ++
 tools/perf/util/tool.h      |   1 +
 6 files changed, 127 insertions(+), 2 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 359bb8f33e57..a6e3c4413b39 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1318,6 +1318,11 @@ static int record__synthesize(struct record *rec, bool 
tail)
        if (err < 0)
                pr_warning("Couldn't synthesize bpf events.\n");
 
+       err = perf_event__synthesize_cgroups(tool, process_synthesized_event,
+                                            machine);
+       if (err < 0)
+               pr_warning("Couldn't synthesize cgroup events.\n");
+
        err = __machine__synthesize_threads(machine, tool, &opts->target, 
rec->evlist->core.threads,
                                            process_synthesized_event, 
opts->sample_address,
                                            1);
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index 8e4c26ea5078..274f0f29c72d 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -14,8 +14,7 @@ int nr_cgroups;
 
 static struct rb_root cgroup_tree = RB_ROOT;
 
-static int
-cgroupfs_find_mountpoint(char *buf, size_t maxlen)
+int cgroupfs_find_mountpoint(char *buf, size_t maxlen)
 {
        FILE *fp;
        char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h
index 11a8b187ec09..755f9712eda4 100644
--- a/tools/perf/util/cgroup.h
+++ b/tools/perf/util/cgroup.h
@@ -17,6 +17,7 @@ struct cgroup {
 
 extern int nr_cgroups; /* number of explicit cgroups defined */
 
+int cgroupfs_find_mountpoint(char *buf, size_t maxlen);
 struct cgroup *cgroup__get(struct cgroup *cgroup);
 void cgroup__put(struct cgroup *cgroup);
 
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index c19b00c1fc26..9e71b9561f72 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -29,6 +29,7 @@
 #include "stat.h"
 #include "session.h"
 #include "bpf-event.h"
+#include "cgroup.h"
 
 #define DEFAULT_PROC_MAP_PARSE_TIMEOUT 500
 
@@ -296,6 +297,120 @@ int perf_event__synthesize_namespaces(struct perf_tool 
*tool,
        return 0;
 }
 
+static int perf_event__synthesize_cgroup(struct perf_tool *tool,
+                                        union perf_event *event,
+                                        char *path, size_t mount_len,
+                                        perf_event__handler_t process,
+                                        struct machine *machine)
+{
+       size_t event_size = sizeof(event->cgroup) - sizeof(event->cgroup.path);
+       size_t path_len = strlen(path) - mount_len + 1;
+       struct stat64 stbuf;
+
+       while (path_len % sizeof(u64))
+               path[mount_len + path_len++] = '\0';
+
+       memset(&event->cgroup, 0, event_size);
+
+       event->cgroup.header.type = PERF_RECORD_CGROUP;
+       event->cgroup.header.size = event_size + path_len + 
machine->id_hdr_size;
+
+       if (stat64(path, &stbuf) < 0) {
+               pr_debug("stat failed: %s\n", path);
+               return -1;
+       }
+
+       event->cgroup.ino = stbuf.st_ino;
+       event->cgroup.path_len = path_len;
+       strncpy(event->cgroup.path, path + mount_len, path_len);
+       memset(event->cgroup.path + path_len, 0, machine->id_hdr_size);
+
+       if (perf_tool__process_synth_event(tool, event, machine, process) < 0) {
+               pr_debug("process synth event failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int perf_event__walk_cgroup_tree(struct perf_tool *tool,
+                                       union perf_event *event,
+                                       char *path, size_t mount_len,
+                                       perf_event__handler_t process,
+                                       struct machine *machine)
+{
+       size_t pos = strlen(path);
+       DIR *d;
+       struct dirent *dent;
+       int ret = 0;
+
+       if (perf_event__synthesize_cgroup(tool, event, path, mount_len,
+                                         process, machine) < 0)
+               return -1;
+
+       d = opendir(path);
+       if (d == NULL) {
+               pr_debug("failed to open directory: %s\n", path);
+               return -1;
+       }
+
+       while ((dent = readdir(d)) != NULL) {
+               if (dent->d_type != DT_DIR)
+                       continue;
+               if (!strcmp(dent->d_name, ".") ||
+                   !strcmp(dent->d_name, ".."))
+                       continue;
+
+               if (path[pos - 1] != '/')
+                       strcat(path, "/");
+               strcat(path, dent->d_name);
+
+               ret = perf_event__walk_cgroup_tree(tool, event, path,
+                                                  mount_len, process, machine);
+               if (ret < 0)
+                       break;
+
+               path[pos] = '\0';
+       }
+
+       closedir(d);
+       return ret;
+}
+
+int perf_event__synthesize_cgroups(struct perf_tool *tool,
+                                  perf_event__handler_t process,
+                                  struct machine *machine)
+{
+       union perf_event event;
+       char *cgrp_root;
+       size_t mount_len;  /* length of mount point in the path */
+       int ret = -1;
+
+       cgrp_root = malloc(PATH_MAX);
+       if (cgrp_root == NULL)
+               return -1;
+
+       if (cgroupfs_find_mountpoint(cgrp_root, PATH_MAX) < 0) {
+               pr_debug("cannot find cgroup mount point\n");
+               goto out;
+       }
+
+       mount_len = strlen(cgrp_root);
+       /* make sure the path starts with a slash (after mount point) */
+       strcat(cgrp_root, "/");
+
+       if (perf_event__walk_cgroup_tree(tool, &event, cgrp_root, mount_len,
+                                        process, machine) < 0)
+               goto out;
+
+       ret = 0;
+
+out:
+       free(cgrp_root);
+
+       return ret;
+}
+
 static int perf_event__synthesize_fork(struct perf_tool *tool,
                                       union perf_event *event,
                                       pid_t pid, pid_t tgid, pid_t ppid,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 0170435fd1e8..b4c4da69a771 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -735,6 +735,10 @@ int perf_event__synthesize_namespaces(struct perf_tool 
*tool,
                                      perf_event__handler_t process,
                                      struct machine *machine);
 
+int perf_event__synthesize_cgroups(struct perf_tool *tool,
+                                  perf_event__handler_t process,
+                                  struct machine *machine);
+
 int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       union perf_event *event,
                                       pid_t pid, pid_t tgid,
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index 472ef5eb4068..3fb67bd31e4a 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -79,6 +79,7 @@ struct perf_tool {
        bool            ordered_events;
        bool            ordering_requires_timestamps;
        bool            namespace_events;
+       bool            cgroup_events;
        bool            no_warn;
        enum show_feature_header show_feat_hdr;
 };
-- 
2.23.0.187.g17f5b7556c-goog

Reply via email to