1 // SPDX-License-Identifier: GPL-2.0
2 #include <subcmd/parse-options.h>
7 #include "metricgroup.h"
9 #include <linux/zalloc.h>
10 #include <sys/types.h>
15 #include <api/fs/fs.h>
19 static int open_cgroup(const char *name)
21 char path[PATH_MAX + 1];
22 char mnt[PATH_MAX + 1];
26 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
29 scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
31 fd = open(path, O_RDONLY);
33 fprintf(stderr, "no access to cgroup %s\n", path);
38 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
40 struct evsel *counter;
42 * check if cgrp is already defined, if so we reuse it
44 evlist__for_each_entry(evlist, counter) {
47 if (!strcmp(counter->cgrp->name, str))
48 return cgroup__get(counter->cgrp);
54 static struct cgroup *cgroup__new(const char *name, bool do_open)
56 struct cgroup *cgroup = zalloc(sizeof(*cgroup));
59 refcount_set(&cgroup->refcnt, 1);
61 cgroup->name = strdup(name);
66 cgroup->fd = open_cgroup(name);
83 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
85 struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
87 return cgroup ?: cgroup__new(name, true);
90 static int add_cgroup(struct evlist *evlist, const char *str)
92 struct evsel *counter;
93 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
99 * find corresponding event
100 * if add cgroup N, then need to find event N
103 evlist__for_each_entry(evlist, counter) {
112 counter->cgrp = cgrp;
116 static void cgroup__delete(struct cgroup *cgroup)
120 zfree(&cgroup->name);
124 void cgroup__put(struct cgroup *cgrp)
126 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
127 cgroup__delete(cgrp);
131 struct cgroup *cgroup__get(struct cgroup *cgroup)
134 refcount_inc(&cgroup->refcnt);
138 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
140 if (evsel->cgrp == NULL)
141 evsel->cgrp = cgroup__get(cgroup);
144 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
148 evlist__for_each_entry(evlist, evsel)
149 evsel__set_default_cgroup(evsel, cgroup);
152 int parse_cgroups(const struct option *opt, const char *str,
153 int unset __maybe_unused)
155 struct evlist *evlist = *(struct evlist **)opt->value;
156 struct evsel *counter;
157 struct cgroup *cgrp = NULL;
158 const char *p, *e, *eos = str + strlen(str);
162 if (list_empty(&evlist->core.entries)) {
163 fprintf(stderr, "must define events before cgroups\n");
168 p = strchr(str, ',');
171 /* allow empty cgroups, i.e., skip */
173 /* termination added */
174 s = strndup(str, e - str);
177 ret = add_cgroup(evlist, s);
182 /* nr_cgroups is increased een for empty cgroups */
188 /* for the case one cgroup combine to multiple events */
190 if (nr_cgroups == 1) {
191 evlist__for_each_entry(evlist, counter) {
193 cgrp = counter->cgrp;
195 counter->cgrp = cgrp;
196 refcount_inc(&cgrp->refcnt);
204 int evlist__expand_cgroup(struct evlist *evlist, const char *str,
205 struct rblist *metric_events, bool open_cgroup)
207 struct evlist *orig_list, *tmp_list;
208 struct evsel *pos, *evsel, *leader;
209 struct rblist orig_metric_events;
210 struct cgroup *cgrp = NULL;
211 const char *p, *e, *eos = str + strlen(str);
214 if (evlist->core.nr_entries == 0) {
215 fprintf(stderr, "must define events before cgroups\n");
219 orig_list = evlist__new();
220 tmp_list = evlist__new();
221 if (orig_list == NULL || tmp_list == NULL) {
222 fprintf(stderr, "memory allocation failed\n");
226 /* save original events and init evlist */
227 perf_evlist__splice_list_tail(orig_list, &evlist->core.entries);
228 evlist->core.nr_entries = 0;
231 orig_metric_events = *metric_events;
232 rblist__init(metric_events);
234 rblist__init(&orig_metric_events);
238 p = strchr(str, ',');
241 /* allow empty cgroups, i.e., skip */
243 /* termination added */
244 char *name = strndup(str, e - str);
248 cgrp = cgroup__new(name, open_cgroup);
257 evlist__for_each_entry(orig_list, pos) {
258 evsel = evsel__clone(pos);
262 cgroup__put(evsel->cgrp);
263 evsel->cgrp = cgroup__get(cgrp);
265 if (evsel__is_group_leader(pos))
267 evsel->leader = leader;
269 evlist__add(tmp_list, evsel);
271 /* cgroup__new() has a refcount, release it here */
276 perf_stat__collect_metric_expr(tmp_list);
277 if (metricgroup__copy_metric_events(tmp_list, cgrp,
279 &orig_metric_events) < 0)
283 perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
284 tmp_list->core.nr_entries = 0;
294 evlist__delete(orig_list);
295 evlist__delete(tmp_list);
296 rblist__exit(&orig_metric_events);
301 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
302 bool create, const char *path)
304 struct rb_node **p = &root->rb_node;
305 struct rb_node *parent = NULL;
310 cgrp = rb_entry(parent, struct cgroup, node);
324 cgrp = malloc(sizeof(*cgrp));
328 cgrp->name = strdup(path);
329 if (cgrp->name == NULL) {
336 refcount_set(&cgrp->refcnt, 1);
338 rb_link_node(&cgrp->node, parent, p);
339 rb_insert_color(&cgrp->node, root);
344 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
349 down_write(&env->cgroups.lock);
350 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
351 up_write(&env->cgroups.lock);
355 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
359 down_read(&env->cgroups.lock);
360 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
361 up_read(&env->cgroups.lock);
365 void perf_env__purge_cgroups(struct perf_env *env)
367 struct rb_node *node;
370 down_write(&env->cgroups.lock);
371 while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
372 node = rb_first(&env->cgroups.tree);
373 cgrp = rb_entry(node, struct cgroup, node);
375 rb_erase(node, &env->cgroups.tree);
378 up_write(&env->cgroups.lock);