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>
12 #include <sys/statfs.h>
16 #include <api/fs/fs.h>
21 bool cgrp_event_expanded
;
23 /* used to match cgroup name with patterns */
25 struct list_head list
;
29 static LIST_HEAD(cgroup_list
);
31 static int open_cgroup(const char *name
)
33 char path
[PATH_MAX
+ 1];
34 char mnt
[PATH_MAX
+ 1];
38 if (cgroupfs_find_mountpoint(mnt
, PATH_MAX
+ 1, "perf_event"))
41 scnprintf(path
, PATH_MAX
, "%s/%s", mnt
, name
);
43 fd
= open(path
, O_RDONLY
);
45 fprintf(stderr
, "no access to cgroup %s\n", path
);
50 #ifdef HAVE_FILE_HANDLE
51 static u64
__read_cgroup_id(const char *path
)
54 struct file_handle fh
;
59 handle
.fh
.handle_bytes
= sizeof(handle
.cgroup_id
);
60 if (name_to_handle_at(AT_FDCWD
, path
, &handle
.fh
, &mount_id
, 0) < 0)
63 return handle
.cgroup_id
;
66 int read_cgroup_id(struct cgroup
*cgrp
)
68 char path
[PATH_MAX
+ 1];
69 char mnt
[PATH_MAX
+ 1];
71 if (cgroupfs_find_mountpoint(mnt
, PATH_MAX
+ 1, "perf_event"))
74 scnprintf(path
, PATH_MAX
, "%s/%s", mnt
, cgrp
->name
);
76 cgrp
->id
= __read_cgroup_id(path
);
80 static inline u64
__read_cgroup_id(const char *path __maybe_unused
) { return -1ULL; }
81 #endif /* HAVE_FILE_HANDLE */
83 #ifndef CGROUP2_SUPER_MAGIC
84 #define CGROUP2_SUPER_MAGIC 0x63677270
87 int cgroup_is_v2(const char *subsys
)
89 char mnt
[PATH_MAX
+ 1];
92 if (cgroupfs_find_mountpoint(mnt
, PATH_MAX
+ 1, subsys
))
95 if (statfs(mnt
, &stbuf
) < 0)
98 return (stbuf
.f_type
== CGROUP2_SUPER_MAGIC
);
101 static struct cgroup
*evlist__find_cgroup(struct evlist
*evlist
, const char *str
)
103 struct evsel
*counter
;
105 * check if cgrp is already defined, if so we reuse it
107 evlist__for_each_entry(evlist
, counter
) {
110 if (!strcmp(counter
->cgrp
->name
, str
))
111 return cgroup__get(counter
->cgrp
);
117 struct cgroup
*cgroup__new(const char *name
, bool do_open
)
119 struct cgroup
*cgroup
= zalloc(sizeof(*cgroup
));
121 if (cgroup
!= NULL
) {
122 refcount_set(&cgroup
->refcnt
, 1);
124 cgroup
->name
= strdup(name
);
129 cgroup
->fd
= open_cgroup(name
);
130 if (cgroup
->fd
== -1)
140 zfree(&cgroup
->name
);
146 struct cgroup
*evlist__findnew_cgroup(struct evlist
*evlist
, const char *name
)
148 struct cgroup
*cgroup
= evlist__find_cgroup(evlist
, name
);
150 return cgroup
?: cgroup__new(name
, true);
153 static int add_cgroup(struct evlist
*evlist
, const char *str
)
155 struct evsel
*counter
;
156 struct cgroup
*cgrp
= evlist__findnew_cgroup(evlist
, str
);
162 * find corresponding event
163 * if add cgroup N, then need to find event N
166 evlist__for_each_entry(evlist
, counter
) {
175 counter
->cgrp
= cgrp
;
179 static void cgroup__delete(struct cgroup
*cgroup
)
183 zfree(&cgroup
->name
);
187 void cgroup__put(struct cgroup
*cgrp
)
189 if (cgrp
&& refcount_dec_and_test(&cgrp
->refcnt
)) {
190 cgroup__delete(cgrp
);
194 struct cgroup
*cgroup__get(struct cgroup
*cgroup
)
197 refcount_inc(&cgroup
->refcnt
);
201 static void evsel__set_default_cgroup(struct evsel
*evsel
, struct cgroup
*cgroup
)
203 if (evsel
->cgrp
== NULL
)
204 evsel
->cgrp
= cgroup__get(cgroup
);
207 void evlist__set_default_cgroup(struct evlist
*evlist
, struct cgroup
*cgroup
)
211 evlist__for_each_entry(evlist
, evsel
)
212 evsel__set_default_cgroup(evsel
, cgroup
);
215 /* helper function for ftw() in match_cgroups and list_cgroups */
216 static int add_cgroup_name(const char *fpath
, const struct stat
*sb __maybe_unused
,
217 int typeflag
, struct FTW
*ftwbuf __maybe_unused
)
219 struct cgroup_name
*cn
;
221 if (typeflag
!= FTW_D
)
224 cn
= malloc(sizeof(*cn
) + strlen(fpath
) + 1);
229 strcpy(cn
->name
, fpath
);
231 list_add_tail(&cn
->list
, &cgroup_list
);
235 static int check_and_add_cgroup_name(const char *fpath
)
237 struct cgroup_name
*cn
;
239 list_for_each_entry(cn
, &cgroup_list
, list
) {
240 if (!strcmp(cn
->name
, fpath
))
244 /* pretend if it's added by ftw() */
245 return add_cgroup_name(fpath
, NULL
, FTW_D
, NULL
);
248 static void release_cgroup_list(void)
250 struct cgroup_name
*cn
;
252 while (!list_empty(&cgroup_list
)) {
253 cn
= list_first_entry(&cgroup_list
, struct cgroup_name
, list
);
259 /* collect given cgroups only */
260 static int list_cgroups(const char *str
)
262 const char *p
, *e
, *eos
= str
+ strlen(str
);
263 struct cgroup_name
*cn
;
266 /* use given name as is when no regex is given */
268 p
= strchr(str
, ',');
274 s
= strndup(str
, e
- str
);
278 ret
= check_and_add_cgroup_name(s
);
283 if (check_and_add_cgroup_name("/") < 0)
292 /* these groups will be used */
293 list_for_each_entry(cn
, &cgroup_list
, list
)
299 /* collect all cgroups first and then match with the pattern */
300 static int match_cgroups(const char *str
)
303 const char *p
, *e
, *eos
= str
+ strlen(str
);
304 struct cgroup_name
*cn
;
309 if (cgroupfs_find_mountpoint(mnt
, sizeof(mnt
), "perf_event"))
312 /* cgroup_name will have a full path, skip the root directory */
313 prefix_len
= strlen(mnt
);
315 /* collect all cgroups in the cgroup_list */
316 if (nftw(mnt
, add_cgroup_name
, 20, 0) < 0)
320 p
= strchr(str
, ',');
323 /* allow empty cgroups, i.e., skip */
325 /* termination added */
326 s
= strndup(str
, e
- str
);
329 if (regcomp(®
, s
, REG_NOSUB
)) {
334 /* check cgroup name with the pattern */
335 list_for_each_entry(cn
, &cgroup_list
, list
) {
336 char *name
= cn
->name
+ prefix_len
;
338 if (name
[0] == '/' && name
[1])
340 if (!regexec(®
, name
, 0, NULL
, 0))
346 /* first entry to root cgroup */
347 cn
= list_first_entry(&cgroup_list
, struct cgroup_name
,
359 int parse_cgroups(const struct option
*opt
, const char *str
,
360 int unset __maybe_unused
)
362 struct evlist
*evlist
= *(struct evlist
**)opt
->value
;
363 struct evsel
*counter
;
364 struct cgroup
*cgrp
= NULL
;
365 const char *p
, *e
, *eos
= str
+ strlen(str
);
369 if (list_empty(&evlist
->core
.entries
)) {
370 fprintf(stderr
, "must define events before cgroups\n");
375 p
= strchr(str
, ',');
378 /* allow empty cgroups, i.e., skip */
380 /* termination added */
381 s
= strndup(str
, e
- str
);
384 ret
= add_cgroup(evlist
, s
);
389 /* nr_cgroups is increased een for empty cgroups */
395 /* for the case one cgroup combine to multiple events */
397 if (nr_cgroups
== 1) {
398 evlist__for_each_entry(evlist
, counter
) {
400 cgrp
= counter
->cgrp
;
402 counter
->cgrp
= cgrp
;
403 refcount_inc(&cgrp
->refcnt
);
411 static bool has_pattern_string(const char *str
)
413 return !!strpbrk(str
, "{}[]()|*+?^$");
416 int evlist__expand_cgroup(struct evlist
*evlist
, const char *str
,
417 struct rblist
*metric_events
, bool open_cgroup
)
419 struct evlist
*orig_list
, *tmp_list
;
420 struct evsel
*pos
, *evsel
, *leader
;
421 struct rblist orig_metric_events
;
422 struct cgroup
*cgrp
= NULL
;
423 struct cgroup_name
*cn
;
427 if (evlist
->core
.nr_entries
== 0) {
428 fprintf(stderr
, "must define events before cgroups\n");
432 orig_list
= evlist__new();
433 tmp_list
= evlist__new();
434 if (orig_list
== NULL
|| tmp_list
== NULL
) {
435 fprintf(stderr
, "memory allocation failed\n");
439 /* save original events and init evlist */
440 evlist__splice_list_tail(orig_list
, &evlist
->core
.entries
);
441 evlist
->core
.nr_entries
= 0;
444 orig_metric_events
= *metric_events
;
445 rblist__init(metric_events
);
447 rblist__init(&orig_metric_events
);
450 if (has_pattern_string(str
))
451 prefix_len
= match_cgroups(str
);
453 prefix_len
= list_cgroups(str
);
458 list_for_each_entry(cn
, &cgroup_list
, list
) {
464 /* cgroup_name might have a full path, skip the prefix */
465 name
= cn
->name
+ prefix_len
;
466 if (name
[0] == '/' && name
[1])
469 /* the cgroup can go away in the meantime */
470 cgrp
= cgroup__new(name
, open_cgroup
);
475 evlist__for_each_entry(orig_list
, pos
) {
476 evsel
= evsel__clone(pos
);
480 cgroup__put(evsel
->cgrp
);
481 evsel
->cgrp
= cgroup__get(cgrp
);
483 if (evsel__is_group_leader(pos
))
485 evsel__set_leader(evsel
, leader
);
487 evlist__add(tmp_list
, evsel
);
489 /* cgroup__new() has a refcount, release it here */
494 if (metricgroup__copy_metric_events(tmp_list
, cgrp
,
496 &orig_metric_events
) < 0)
500 evlist__splice_list_tail(evlist
, &tmp_list
->core
.entries
);
501 tmp_list
->core
.nr_entries
= 0;
504 if (list_empty(&evlist
->core
.entries
)) {
505 fprintf(stderr
, "no cgroup matched: %s\n", str
);
510 cgrp_event_expanded
= true;
513 evlist__delete(orig_list
);
514 evlist__delete(tmp_list
);
515 rblist__exit(&orig_metric_events
);
516 release_cgroup_list();
521 static struct cgroup
*__cgroup__findnew(struct rb_root
*root
, uint64_t id
,
522 bool create
, const char *path
)
524 struct rb_node
**p
= &root
->rb_node
;
525 struct rb_node
*parent
= NULL
;
530 cgrp
= rb_entry(parent
, struct cgroup
, node
);
544 cgrp
= malloc(sizeof(*cgrp
));
548 cgrp
->name
= strdup(path
);
549 if (cgrp
->name
== NULL
) {
556 refcount_set(&cgrp
->refcnt
, 1);
558 rb_link_node(&cgrp
->node
, parent
, p
);
559 rb_insert_color(&cgrp
->node
, root
);
564 struct cgroup
*cgroup__findnew(struct perf_env
*env
, uint64_t id
,
569 down_write(&env
->cgroups
.lock
);
570 cgrp
= __cgroup__findnew(&env
->cgroups
.tree
, id
, true, path
);
571 up_write(&env
->cgroups
.lock
);
575 struct cgroup
*__cgroup__find(struct rb_root
*root
, uint64_t id
)
577 return __cgroup__findnew(root
, id
, /*create=*/false, /*path=*/NULL
);
580 struct cgroup
*cgroup__find(struct perf_env
*env
, uint64_t id
)
584 down_read(&env
->cgroups
.lock
);
585 cgrp
= __cgroup__findnew(&env
->cgroups
.tree
, id
, false, NULL
);
586 up_read(&env
->cgroups
.lock
);
590 void perf_env__purge_cgroups(struct perf_env
*env
)
592 struct rb_node
*node
;
595 down_write(&env
->cgroups
.lock
);
596 while (!RB_EMPTY_ROOT(&env
->cgroups
.tree
)) {
597 node
= rb_first(&env
->cgroups
.tree
);
598 cgrp
= rb_entry(node
, struct cgroup
, node
);
600 rb_erase(node
, &env
->cgroups
.tree
);
603 up_write(&env
->cgroups
.lock
);
606 void read_all_cgroups(struct rb_root
*root
)
609 struct cgroup_name
*cn
;
612 if (cgroupfs_find_mountpoint(mnt
, sizeof(mnt
), "perf_event"))
615 /* cgroup_name will have a full path, skip the root directory */
616 prefix_len
= strlen(mnt
);
618 /* collect all cgroups in the cgroup_list */
619 if (nftw(mnt
, add_cgroup_name
, 20, 0) < 0)
622 list_for_each_entry(cn
, &cgroup_list
, list
) {
626 /* cgroup_name might have a full path, skip the prefix */
627 name
= cn
->name
+ prefix_len
;
631 cgrp_id
= __read_cgroup_id(cn
->name
);
632 __cgroup__findnew(root
, cgrp_id
, /*create=*/true, name
);
635 release_cgroup_list();