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>
21 /* used to match cgroup name with patterns */
23 struct list_head list
;
27 static LIST_HEAD(cgroup_list
);
29 static int open_cgroup(const char *name
)
31 char path
[PATH_MAX
+ 1];
32 char mnt
[PATH_MAX
+ 1];
36 if (cgroupfs_find_mountpoint(mnt
, PATH_MAX
+ 1, "perf_event"))
39 scnprintf(path
, PATH_MAX
, "%s/%s", mnt
, name
);
41 fd
= open(path
, O_RDONLY
);
43 fprintf(stderr
, "no access to cgroup %s\n", path
);
48 static struct cgroup
*evlist__find_cgroup(struct evlist
*evlist
, const char *str
)
50 struct evsel
*counter
;
52 * check if cgrp is already defined, if so we reuse it
54 evlist__for_each_entry(evlist
, counter
) {
57 if (!strcmp(counter
->cgrp
->name
, str
))
58 return cgroup__get(counter
->cgrp
);
64 static struct cgroup
*cgroup__new(const char *name
, bool do_open
)
66 struct cgroup
*cgroup
= zalloc(sizeof(*cgroup
));
69 refcount_set(&cgroup
->refcnt
, 1);
71 cgroup
->name
= strdup(name
);
76 cgroup
->fd
= open_cgroup(name
);
93 struct cgroup
*evlist__findnew_cgroup(struct evlist
*evlist
, const char *name
)
95 struct cgroup
*cgroup
= evlist__find_cgroup(evlist
, name
);
97 return cgroup
?: cgroup__new(name
, true);
100 static int add_cgroup(struct evlist
*evlist
, const char *str
)
102 struct evsel
*counter
;
103 struct cgroup
*cgrp
= evlist__findnew_cgroup(evlist
, str
);
109 * find corresponding event
110 * if add cgroup N, then need to find event N
113 evlist__for_each_entry(evlist
, counter
) {
122 counter
->cgrp
= cgrp
;
126 static void cgroup__delete(struct cgroup
*cgroup
)
130 zfree(&cgroup
->name
);
134 void cgroup__put(struct cgroup
*cgrp
)
136 if (cgrp
&& refcount_dec_and_test(&cgrp
->refcnt
)) {
137 cgroup__delete(cgrp
);
141 struct cgroup
*cgroup__get(struct cgroup
*cgroup
)
144 refcount_inc(&cgroup
->refcnt
);
148 static void evsel__set_default_cgroup(struct evsel
*evsel
, struct cgroup
*cgroup
)
150 if (evsel
->cgrp
== NULL
)
151 evsel
->cgrp
= cgroup__get(cgroup
);
154 void evlist__set_default_cgroup(struct evlist
*evlist
, struct cgroup
*cgroup
)
158 evlist__for_each_entry(evlist
, evsel
)
159 evsel__set_default_cgroup(evsel
, cgroup
);
162 /* helper function for ftw() in match_cgroups and list_cgroups */
163 static int add_cgroup_name(const char *fpath
, const struct stat
*sb __maybe_unused
,
166 struct cgroup_name
*cn
;
168 if (typeflag
!= FTW_D
)
171 cn
= malloc(sizeof(*cn
) + strlen(fpath
) + 1);
176 strcpy(cn
->name
, fpath
);
178 list_add_tail(&cn
->list
, &cgroup_list
);
182 static void release_cgroup_list(void)
184 struct cgroup_name
*cn
;
186 while (!list_empty(&cgroup_list
)) {
187 cn
= list_first_entry(&cgroup_list
, struct cgroup_name
, list
);
193 /* collect given cgroups only */
194 static int list_cgroups(const char *str
)
196 const char *p
, *e
, *eos
= str
+ strlen(str
);
197 struct cgroup_name
*cn
;
200 /* use given name as is - for testing purpose */
202 p
= strchr(str
, ',');
208 s
= strndup(str
, e
- str
);
211 /* pretend if it's added by ftw() */
212 ret
= add_cgroup_name(s
, NULL
, FTW_D
);
217 if (add_cgroup_name("", NULL
, FTW_D
) < 0)
226 /* these groups will be used */
227 list_for_each_entry(cn
, &cgroup_list
, list
)
233 /* collect all cgroups first and then match with the pattern */
234 static int match_cgroups(const char *str
)
237 const char *p
, *e
, *eos
= str
+ strlen(str
);
238 struct cgroup_name
*cn
;
243 if (cgroupfs_find_mountpoint(mnt
, sizeof(mnt
), "perf_event"))
246 /* cgroup_name will have a full path, skip the root directory */
247 prefix_len
= strlen(mnt
);
249 /* collect all cgroups in the cgroup_list */
250 if (ftw(mnt
, add_cgroup_name
, 20) < 0)
254 p
= strchr(str
, ',');
257 /* allow empty cgroups, i.e., skip */
259 /* termination added */
260 s
= strndup(str
, e
- str
);
263 if (regcomp(®
, s
, REG_NOSUB
)) {
268 /* check cgroup name with the pattern */
269 list_for_each_entry(cn
, &cgroup_list
, list
) {
270 char *name
= cn
->name
+ prefix_len
;
272 if (name
[0] == '/' && name
[1])
274 if (!regexec(®
, name
, 0, NULL
, 0))
280 /* first entry to root cgroup */
281 cn
= list_first_entry(&cgroup_list
, struct cgroup_name
,
293 int parse_cgroups(const struct option
*opt
, const char *str
,
294 int unset __maybe_unused
)
296 struct evlist
*evlist
= *(struct evlist
**)opt
->value
;
297 struct evsel
*counter
;
298 struct cgroup
*cgrp
= NULL
;
299 const char *p
, *e
, *eos
= str
+ strlen(str
);
303 if (list_empty(&evlist
->core
.entries
)) {
304 fprintf(stderr
, "must define events before cgroups\n");
309 p
= strchr(str
, ',');
312 /* allow empty cgroups, i.e., skip */
314 /* termination added */
315 s
= strndup(str
, e
- str
);
318 ret
= add_cgroup(evlist
, s
);
323 /* nr_cgroups is increased een for empty cgroups */
329 /* for the case one cgroup combine to multiple events */
331 if (nr_cgroups
== 1) {
332 evlist__for_each_entry(evlist
, counter
) {
334 cgrp
= counter
->cgrp
;
336 counter
->cgrp
= cgrp
;
337 refcount_inc(&cgrp
->refcnt
);
345 static bool has_pattern_string(const char *str
)
347 return !!strpbrk(str
, "{}[]()|*+?^$");
350 int evlist__expand_cgroup(struct evlist
*evlist
, const char *str
,
351 struct rblist
*metric_events
, bool open_cgroup
)
353 struct evlist
*orig_list
, *tmp_list
;
354 struct evsel
*pos
, *evsel
, *leader
;
355 struct rblist orig_metric_events
;
356 struct cgroup
*cgrp
= NULL
;
357 struct cgroup_name
*cn
;
361 if (evlist
->core
.nr_entries
== 0) {
362 fprintf(stderr
, "must define events before cgroups\n");
366 orig_list
= evlist__new();
367 tmp_list
= evlist__new();
368 if (orig_list
== NULL
|| tmp_list
== NULL
) {
369 fprintf(stderr
, "memory allocation failed\n");
373 /* save original events and init evlist */
374 evlist__splice_list_tail(orig_list
, &evlist
->core
.entries
);
375 evlist
->core
.nr_entries
= 0;
378 orig_metric_events
= *metric_events
;
379 rblist__init(metric_events
);
381 rblist__init(&orig_metric_events
);
384 if (has_pattern_string(str
))
385 prefix_len
= match_cgroups(str
);
387 prefix_len
= list_cgroups(str
);
392 list_for_each_entry(cn
, &cgroup_list
, list
) {
398 /* cgroup_name might have a full path, skip the prefix */
399 name
= cn
->name
+ prefix_len
;
400 if (name
[0] == '/' && name
[1])
402 cgrp
= cgroup__new(name
, open_cgroup
);
407 evlist__for_each_entry(orig_list
, pos
) {
408 evsel
= evsel__clone(pos
);
412 cgroup__put(evsel
->cgrp
);
413 evsel
->cgrp
= cgroup__get(cgrp
);
415 if (evsel__is_group_leader(pos
))
417 evsel
->leader
= leader
;
419 evlist__add(tmp_list
, evsel
);
421 /* cgroup__new() has a refcount, release it here */
426 perf_stat__collect_metric_expr(tmp_list
);
427 if (metricgroup__copy_metric_events(tmp_list
, cgrp
,
429 &orig_metric_events
) < 0)
433 evlist__splice_list_tail(evlist
, &tmp_list
->core
.entries
);
434 tmp_list
->core
.nr_entries
= 0;
437 if (list_empty(&evlist
->core
.entries
)) {
438 fprintf(stderr
, "no cgroup matched: %s\n", str
);
445 evlist__delete(orig_list
);
446 evlist__delete(tmp_list
);
447 rblist__exit(&orig_metric_events
);
448 release_cgroup_list();
453 static struct cgroup
*__cgroup__findnew(struct rb_root
*root
, uint64_t id
,
454 bool create
, const char *path
)
456 struct rb_node
**p
= &root
->rb_node
;
457 struct rb_node
*parent
= NULL
;
462 cgrp
= rb_entry(parent
, struct cgroup
, node
);
476 cgrp
= malloc(sizeof(*cgrp
));
480 cgrp
->name
= strdup(path
);
481 if (cgrp
->name
== NULL
) {
488 refcount_set(&cgrp
->refcnt
, 1);
490 rb_link_node(&cgrp
->node
, parent
, p
);
491 rb_insert_color(&cgrp
->node
, root
);
496 struct cgroup
*cgroup__findnew(struct perf_env
*env
, uint64_t id
,
501 down_write(&env
->cgroups
.lock
);
502 cgrp
= __cgroup__findnew(&env
->cgroups
.tree
, id
, true, path
);
503 up_write(&env
->cgroups
.lock
);
507 struct cgroup
*cgroup__find(struct perf_env
*env
, uint64_t id
)
511 down_read(&env
->cgroups
.lock
);
512 cgrp
= __cgroup__findnew(&env
->cgroups
.tree
, id
, false, NULL
);
513 up_read(&env
->cgroups
.lock
);
517 void perf_env__purge_cgroups(struct perf_env
*env
)
519 struct rb_node
*node
;
522 down_write(&env
->cgroups
.lock
);
523 while (!RB_EMPTY_ROOT(&env
->cgroups
.tree
)) {
524 node
= rb_first(&env
->cgroups
.tree
);
525 cgrp
= rb_entry(node
, struct cgroup
, node
);
527 rb_erase(node
, &env
->cgroups
.tree
);
530 up_write(&env
->cgroups
.lock
);