perf record: Support synthesizing cgroup events
authorNamhyung Kim <namhyung@kernel.org>
Wed, 25 Mar 2020 12:45:33 +0000 (21:45 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 3 Apr 2020 12:37:55 +0000 (09:37 -0300)
Synthesize cgroup events by iterating cgroup filesystem directories.
The cgroup event only saves the portion of cgroup path after the mount
point and the cgroup id (which actually is a file handle).

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/20200325124536.2800725-7-namhyung@kernel.org
Link: http://lore.kernel.org/lkml/20200402015249.3800462-1-namhyung@kernel.org
[ Extracted the HAVE_FILE_HANDLE from the followup patch, added missing __maybe_unused ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-record.c
tools/perf/util/synthetic-events.c
tools/perf/util/synthetic-events.h
tools/perf/util/tool.h

index 4c301466101baea9803f7a236fa1da3ed0396a80..2802de9538ff5fdcf544f0c75e588f92bac67ef7 100644 (file)
@@ -1397,6 +1397,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);
index f72d80999506420b8f5c02f1e9e81e0a693982cd..a661b122d9d8f25836bd12943e119c6da3c26c9f 100644 (file)
@@ -16,6 +16,7 @@
 #include "util/synthetic-events.h"
 #include "util/target.h"
 #include "util/time-utils.h"
+#include "util/cgroup.h"
 #include <linux/bitops.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -414,6 +415,127 @@ out:
        return rc;
 }
 
+#ifdef HAVE_FILE_HANDLE
+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 {
+               struct file_handle fh;
+               uint64_t cgroup_id;
+       } handle;
+       int mount_id;
+
+       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;
+
+       handle.fh.handle_bytes = sizeof(handle.cgroup_id);
+       if (name_to_handle_at(AT_FDCWD, path, &handle.fh, &mount_id, 0) < 0) {
+               pr_debug("stat failed: %s\n", path);
+               return -1;
+       }
+
+       event->cgroup.id = handle.cgroup_id;
+       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;
+
+               /* any sane path should be less than PATH_MAX */
+               if (strlen(path) + strlen(dent->d_name) + 1 >= PATH_MAX)
+                       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[PATH_MAX];
+       size_t mount_len;  /* length of mount point in the path */
+
+       if (cgroupfs_find_mountpoint(cgrp_root, PATH_MAX, "perf_event") < 0) {
+               pr_debug("cannot find cgroup mount point\n");
+               return -1;
+       }
+
+       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)
+               return -1;
+
+       return 0;
+}
+#else
+int perf_event__synthesize_cgroups(struct perf_tool *tool __maybe_unused,
+                                  perf_event__handler_t process __maybe_unused,
+                                  struct machine *machine __maybe_unused)
+{
+       return -1;
+}
+#endif
+
 int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process,
                                   struct machine *machine)
 {
index baead0cdc381033b2568b2f5098605c51bf2b417..e7a3e958973814102e302e6aa84a2bb3542e7a6e 100644 (file)
@@ -45,6 +45,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handl
 int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, bool mmap_data);
 int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine);
 int perf_event__synthesize_namespaces(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, 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_sample(union perf_event *event, u64 type, u64 read_format, const struct perf_sample *sample);
 int perf_event__synthesize_stat_config(struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine);
 int perf_event__synthesize_stat_events(struct perf_stat_config *config, struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs);
index 472ef5eb406861770e8490c5c93eb4962996d823..3fb67bd31e4a062993a0aecb0786d8cb17da9d6e 100644 (file)
@@ -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;
 };