[PATCH 4/9] perf tools: Maintain cgroup hierarchy

From: Namhyung Kim
Date: Mon Dec 23 2019 - 01:08:31 EST


Each cgroup is kept in the global cgroup_tree sorted by the cgroup id.
Hist entries have cgroup id can compare it directly and later it can
be used to find a group name using this tree.

Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx>
---
tools/perf/util/cgroup.c | 72 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/cgroup.h | 15 +++++---
tools/perf/util/machine.c | 7 ++++
tools/perf/util/session.c | 4 +++
4 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index 4881d4af3381..4e8ef1db0c94 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -13,6 +13,8 @@

int nr_cgroups;

+static struct rb_root cgroup_tree = RB_ROOT;
+
static int
cgroupfs_find_mountpoint(char *buf, size_t maxlen)
{
@@ -250,3 +252,73 @@ int parse_cgroups(const struct option *opt, const char *str,
}
return 0;
}
+
+struct cgroup *cgroup__findnew(uint64_t id, const char *path)
+{
+ struct rb_node **p = &cgroup_tree.rb_node;
+ struct rb_node *parent = NULL;
+ struct cgroup *cgrp;
+
+ while (*p != NULL) {
+ parent = *p;
+ cgrp = rb_entry(parent, struct cgroup, node);
+
+ if (cgrp->id == id)
+ return cgrp;
+
+ if (cgrp->id < id)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ cgrp = malloc(sizeof(*cgrp));
+ if (cgrp == NULL)
+ return NULL;
+
+ cgrp->name = strdup(path);
+ if (cgrp->name == NULL) {
+ free(cgrp);
+ return NULL;
+ }
+
+ cgrp->fd = -1;
+ cgrp->id = id;
+ refcount_set(&cgrp->refcnt, 1);
+
+ rb_link_node(&cgrp->node, parent, p);
+ rb_insert_color(&cgrp->node, &cgroup_tree);
+
+ return cgrp;
+}
+
+struct cgroup *cgroup__find_by_path(const char *path)
+{
+ struct rb_node *node;
+
+ node = rb_first(&cgroup_tree);
+ while (node) {
+ struct cgroup *cgrp = rb_entry(node, struct cgroup, node);
+
+ if (!strcmp(cgrp->name, path))
+ return cgrp;
+
+ node = rb_next(&cgrp->node);
+ }
+
+ return NULL;
+}
+
+void destroy_cgroups(void)
+{
+ struct rb_node *node;
+ struct cgroup *cgrp;
+
+ while (!RB_EMPTY_ROOT(&cgroup_tree)) {
+ node = rb_first(&cgroup_tree);
+ cgrp = rb_entry(node, struct cgroup, node);
+
+ rb_erase(node, &cgroup_tree);
+ cgroup__put(cgrp);
+ }
+}
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h
index 2ec11f01090d..381583df27c7 100644
--- a/tools/perf/util/cgroup.h
+++ b/tools/perf/util/cgroup.h
@@ -3,16 +3,18 @@
#define __CGROUP_H__

#include <linux/refcount.h>
+#include <linux/rbtree.h>

struct option;

struct cgroup {
- char *name;
- int fd;
- refcount_t refcnt;
+ struct rb_node node;
+ u64 id;
+ char *name;
+ int fd;
+ refcount_t refcnt;
};

-
extern int nr_cgroups; /* number of explicit cgroups defined */

struct cgroup *cgroup__get(struct cgroup *cgroup);
@@ -26,4 +28,9 @@ void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup);

int parse_cgroups(const struct option *opt, const char *str, int unset);

+struct cgroup *cgroup__findnew(uint64_t id, const char *path);
+struct cgroup *cgroup__find_by_path(const char *path);
+
+void destroy_cgroups(void);
+
#endif /* __CGROUP_H__ */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 2c3223bec561..19d40a2016d7 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -33,6 +33,7 @@
#include "asm/bug.h"
#include "bpf-event.h"
#include <internal/lib.h> // page_size
+#include "cgroup.h"

#include <linux/ctype.h>
#include <symbol/kallsyms.h>
@@ -658,9 +659,15 @@ int machine__process_cgroup_event(struct machine *machine __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused)
{
+ struct cgroup *cgrp;
+
if (dump_trace)
perf_event__fprintf_cgroup(event, stdout);

+ cgrp = cgroup__findnew(event->cgroup.id, event->cgroup.path);
+ if (cgrp == NULL)
+ return -ENOMEM;
+
return 0;
}

diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 6b4c12d48c3f..5e2c9f504184 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -289,6 +289,8 @@ static void perf_session__release_decomp_events(struct perf_session *session)
} while (1);
}

+extern void destroy_cgroups(void);
+
void perf_session__delete(struct perf_session *session)
{
if (session == NULL)
@@ -303,6 +305,8 @@ void perf_session__delete(struct perf_session *session)
if (session->data)
perf_data__close(session->data);
free(session);
+
+ destroy_cgroups();
}

static int process_event_synth_tracing_data_stub(struct perf_session *session
--
2.24.1.735.g03f4e72817-goog