162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <subcmd/parse-options.h>
362306a36Sopenharmony_ci#include "evsel.h"
462306a36Sopenharmony_ci#include "cgroup.h"
562306a36Sopenharmony_ci#include "evlist.h"
662306a36Sopenharmony_ci#include "rblist.h"
762306a36Sopenharmony_ci#include "metricgroup.h"
862306a36Sopenharmony_ci#include "stat.h"
962306a36Sopenharmony_ci#include <linux/zalloc.h>
1062306a36Sopenharmony_ci#include <sys/types.h>
1162306a36Sopenharmony_ci#include <sys/stat.h>
1262306a36Sopenharmony_ci#include <sys/statfs.h>
1362306a36Sopenharmony_ci#include <fcntl.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci#include <api/fs/fs.h>
1762306a36Sopenharmony_ci#include <ftw.h>
1862306a36Sopenharmony_ci#include <regex.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint nr_cgroups;
2162306a36Sopenharmony_cibool cgrp_event_expanded;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* used to match cgroup name with patterns */
2462306a36Sopenharmony_cistruct cgroup_name {
2562306a36Sopenharmony_ci	struct list_head list;
2662306a36Sopenharmony_ci	bool used;
2762306a36Sopenharmony_ci	char name[];
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_cistatic LIST_HEAD(cgroup_list);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int open_cgroup(const char *name)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	char path[PATH_MAX + 1];
3462306a36Sopenharmony_ci	char mnt[PATH_MAX + 1];
3562306a36Sopenharmony_ci	int fd;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
3962306a36Sopenharmony_ci		return -1;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	fd = open(path, O_RDONLY);
4462306a36Sopenharmony_ci	if (fd == -1)
4562306a36Sopenharmony_ci		fprintf(stderr, "no access to cgroup %s\n", path);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return fd;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#ifdef HAVE_FILE_HANDLE
5162306a36Sopenharmony_ciint read_cgroup_id(struct cgroup *cgrp)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	char path[PATH_MAX + 1];
5462306a36Sopenharmony_ci	char mnt[PATH_MAX + 1];
5562306a36Sopenharmony_ci	struct {
5662306a36Sopenharmony_ci		struct file_handle fh;
5762306a36Sopenharmony_ci		uint64_t cgroup_id;
5862306a36Sopenharmony_ci	} handle;
5962306a36Sopenharmony_ci	int mount_id;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
6262306a36Sopenharmony_ci		return -1;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	scnprintf(path, PATH_MAX, "%s/%s", mnt, cgrp->name);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	handle.fh.handle_bytes = sizeof(handle.cgroup_id);
6762306a36Sopenharmony_ci	if (name_to_handle_at(AT_FDCWD, path, &handle.fh, &mount_id, 0) < 0)
6862306a36Sopenharmony_ci		return -1;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	cgrp->id = handle.cgroup_id;
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci#endif  /* HAVE_FILE_HANDLE */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#ifndef CGROUP2_SUPER_MAGIC
7662306a36Sopenharmony_ci#define CGROUP2_SUPER_MAGIC  0x63677270
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint cgroup_is_v2(const char *subsys)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	char mnt[PATH_MAX + 1];
8262306a36Sopenharmony_ci	struct statfs stbuf;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, subsys))
8562306a36Sopenharmony_ci		return -1;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (statfs(mnt, &stbuf) < 0)
8862306a36Sopenharmony_ci		return -1;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return (stbuf.f_type == CGROUP2_SUPER_MAGIC);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct evsel *counter;
9662306a36Sopenharmony_ci	/*
9762306a36Sopenharmony_ci	 * check if cgrp is already defined, if so we reuse it
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	evlist__for_each_entry(evlist, counter) {
10062306a36Sopenharmony_ci		if (!counter->cgrp)
10162306a36Sopenharmony_ci			continue;
10262306a36Sopenharmony_ci		if (!strcmp(counter->cgrp->name, str))
10362306a36Sopenharmony_ci			return cgroup__get(counter->cgrp);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return NULL;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic struct cgroup *cgroup__new(const char *name, bool do_open)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (cgroup != NULL) {
11462306a36Sopenharmony_ci		refcount_set(&cgroup->refcnt, 1);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		cgroup->name = strdup(name);
11762306a36Sopenharmony_ci		if (!cgroup->name)
11862306a36Sopenharmony_ci			goto out_err;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (do_open) {
12162306a36Sopenharmony_ci			cgroup->fd = open_cgroup(name);
12262306a36Sopenharmony_ci			if (cgroup->fd == -1)
12362306a36Sopenharmony_ci				goto out_free_name;
12462306a36Sopenharmony_ci		} else {
12562306a36Sopenharmony_ci			cgroup->fd = -1;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return cgroup;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciout_free_name:
13262306a36Sopenharmony_ci	zfree(&cgroup->name);
13362306a36Sopenharmony_ciout_err:
13462306a36Sopenharmony_ci	free(cgroup);
13562306a36Sopenharmony_ci	return NULL;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistruct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return cgroup ?: cgroup__new(name, true);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int add_cgroup(struct evlist *evlist, const char *str)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct evsel *counter;
14862306a36Sopenharmony_ci	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
14962306a36Sopenharmony_ci	int n;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!cgrp)
15262306a36Sopenharmony_ci		return -1;
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * find corresponding event
15562306a36Sopenharmony_ci	 * if add cgroup N, then need to find event N
15662306a36Sopenharmony_ci	 */
15762306a36Sopenharmony_ci	n = 0;
15862306a36Sopenharmony_ci	evlist__for_each_entry(evlist, counter) {
15962306a36Sopenharmony_ci		if (n == nr_cgroups)
16062306a36Sopenharmony_ci			goto found;
16162306a36Sopenharmony_ci		n++;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	cgroup__put(cgrp);
16562306a36Sopenharmony_ci	return -1;
16662306a36Sopenharmony_cifound:
16762306a36Sopenharmony_ci	counter->cgrp = cgrp;
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void cgroup__delete(struct cgroup *cgroup)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	if (cgroup->fd >= 0)
17462306a36Sopenharmony_ci		close(cgroup->fd);
17562306a36Sopenharmony_ci	zfree(&cgroup->name);
17662306a36Sopenharmony_ci	free(cgroup);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_civoid cgroup__put(struct cgroup *cgrp)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
18262306a36Sopenharmony_ci		cgroup__delete(cgrp);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistruct cgroup *cgroup__get(struct cgroup *cgroup)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci       if (cgroup)
18962306a36Sopenharmony_ci		refcount_inc(&cgroup->refcnt);
19062306a36Sopenharmony_ci       return cgroup;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	if (evsel->cgrp == NULL)
19662306a36Sopenharmony_ci		evsel->cgrp = cgroup__get(cgroup);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_civoid evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct evsel *evsel;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	evlist__for_each_entry(evlist, evsel)
20462306a36Sopenharmony_ci		evsel__set_default_cgroup(evsel, cgroup);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/* helper function for ftw() in match_cgroups and list_cgroups */
20862306a36Sopenharmony_cistatic int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unused,
20962306a36Sopenharmony_ci			   int typeflag, struct FTW *ftwbuf __maybe_unused)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct cgroup_name *cn;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (typeflag != FTW_D)
21462306a36Sopenharmony_ci		return 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	cn = malloc(sizeof(*cn) + strlen(fpath) + 1);
21762306a36Sopenharmony_ci	if (cn == NULL)
21862306a36Sopenharmony_ci		return -1;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	cn->used = false;
22162306a36Sopenharmony_ci	strcpy(cn->name, fpath);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	list_add_tail(&cn->list, &cgroup_list);
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int check_and_add_cgroup_name(const char *fpath)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct cgroup_name *cn;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	list_for_each_entry(cn, &cgroup_list, list) {
23262306a36Sopenharmony_ci		if (!strcmp(cn->name, fpath))
23362306a36Sopenharmony_ci			return 0;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* pretend if it's added by ftw() */
23762306a36Sopenharmony_ci	return add_cgroup_name(fpath, NULL, FTW_D, NULL);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void release_cgroup_list(void)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct cgroup_name *cn;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	while (!list_empty(&cgroup_list)) {
24562306a36Sopenharmony_ci		cn = list_first_entry(&cgroup_list, struct cgroup_name, list);
24662306a36Sopenharmony_ci		list_del(&cn->list);
24762306a36Sopenharmony_ci		free(cn);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* collect given cgroups only */
25262306a36Sopenharmony_cistatic int list_cgroups(const char *str)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	const char *p, *e, *eos = str + strlen(str);
25562306a36Sopenharmony_ci	struct cgroup_name *cn;
25662306a36Sopenharmony_ci	char *s;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* use given name as is when no regex is given */
25962306a36Sopenharmony_ci	for (;;) {
26062306a36Sopenharmony_ci		p = strchr(str, ',');
26162306a36Sopenharmony_ci		e = p ? p : eos;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (e - str) {
26462306a36Sopenharmony_ci			int ret;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci			s = strndup(str, e - str);
26762306a36Sopenharmony_ci			if (!s)
26862306a36Sopenharmony_ci				return -1;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			ret = check_and_add_cgroup_name(s);
27162306a36Sopenharmony_ci			free(s);
27262306a36Sopenharmony_ci			if (ret < 0)
27362306a36Sopenharmony_ci				return -1;
27462306a36Sopenharmony_ci		} else {
27562306a36Sopenharmony_ci			if (check_and_add_cgroup_name("/") < 0)
27662306a36Sopenharmony_ci				return -1;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		if (!p)
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci		str = p+1;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* these groups will be used */
28562306a36Sopenharmony_ci	list_for_each_entry(cn, &cgroup_list, list)
28662306a36Sopenharmony_ci		cn->used = true;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/* collect all cgroups first and then match with the pattern */
29262306a36Sopenharmony_cistatic int match_cgroups(const char *str)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	char mnt[PATH_MAX];
29562306a36Sopenharmony_ci	const char *p, *e, *eos = str + strlen(str);
29662306a36Sopenharmony_ci	struct cgroup_name *cn;
29762306a36Sopenharmony_ci	regex_t reg;
29862306a36Sopenharmony_ci	int prefix_len;
29962306a36Sopenharmony_ci	char *s;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event"))
30262306a36Sopenharmony_ci		return -1;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* cgroup_name will have a full path, skip the root directory */
30562306a36Sopenharmony_ci	prefix_len = strlen(mnt);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* collect all cgroups in the cgroup_list */
30862306a36Sopenharmony_ci	if (nftw(mnt, add_cgroup_name, 20, 0) < 0)
30962306a36Sopenharmony_ci		return -1;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	for (;;) {
31262306a36Sopenharmony_ci		p = strchr(str, ',');
31362306a36Sopenharmony_ci		e = p ? p : eos;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		/* allow empty cgroups, i.e., skip */
31662306a36Sopenharmony_ci		if (e - str) {
31762306a36Sopenharmony_ci			/* termination added */
31862306a36Sopenharmony_ci			s = strndup(str, e - str);
31962306a36Sopenharmony_ci			if (!s)
32062306a36Sopenharmony_ci				return -1;
32162306a36Sopenharmony_ci			if (regcomp(&reg, s, REG_NOSUB)) {
32262306a36Sopenharmony_ci				free(s);
32362306a36Sopenharmony_ci				return -1;
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci			/* check cgroup name with the pattern */
32762306a36Sopenharmony_ci			list_for_each_entry(cn, &cgroup_list, list) {
32862306a36Sopenharmony_ci				char *name = cn->name + prefix_len;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci				if (name[0] == '/' && name[1])
33162306a36Sopenharmony_ci					name++;
33262306a36Sopenharmony_ci				if (!regexec(&reg, name, 0, NULL, 0))
33362306a36Sopenharmony_ci					cn->used = true;
33462306a36Sopenharmony_ci			}
33562306a36Sopenharmony_ci			regfree(&reg);
33662306a36Sopenharmony_ci			free(s);
33762306a36Sopenharmony_ci		} else {
33862306a36Sopenharmony_ci			/* first entry to root cgroup */
33962306a36Sopenharmony_ci			cn = list_first_entry(&cgroup_list, struct cgroup_name,
34062306a36Sopenharmony_ci					      list);
34162306a36Sopenharmony_ci			cn->used = true;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (!p)
34562306a36Sopenharmony_ci			break;
34662306a36Sopenharmony_ci		str = p+1;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	return prefix_len;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciint parse_cgroups(const struct option *opt, const char *str,
35262306a36Sopenharmony_ci		  int unset __maybe_unused)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct evlist *evlist = *(struct evlist **)opt->value;
35562306a36Sopenharmony_ci	struct evsel *counter;
35662306a36Sopenharmony_ci	struct cgroup *cgrp = NULL;
35762306a36Sopenharmony_ci	const char *p, *e, *eos = str + strlen(str);
35862306a36Sopenharmony_ci	char *s;
35962306a36Sopenharmony_ci	int ret, i;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (list_empty(&evlist->core.entries)) {
36262306a36Sopenharmony_ci		fprintf(stderr, "must define events before cgroups\n");
36362306a36Sopenharmony_ci		return -1;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	for (;;) {
36762306a36Sopenharmony_ci		p = strchr(str, ',');
36862306a36Sopenharmony_ci		e = p ? p : eos;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* allow empty cgroups, i.e., skip */
37162306a36Sopenharmony_ci		if (e - str) {
37262306a36Sopenharmony_ci			/* termination added */
37362306a36Sopenharmony_ci			s = strndup(str, e - str);
37462306a36Sopenharmony_ci			if (!s)
37562306a36Sopenharmony_ci				return -1;
37662306a36Sopenharmony_ci			ret = add_cgroup(evlist, s);
37762306a36Sopenharmony_ci			free(s);
37862306a36Sopenharmony_ci			if (ret)
37962306a36Sopenharmony_ci				return -1;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci		/* nr_cgroups is increased een for empty cgroups */
38262306a36Sopenharmony_ci		nr_cgroups++;
38362306a36Sopenharmony_ci		if (!p)
38462306a36Sopenharmony_ci			break;
38562306a36Sopenharmony_ci		str = p+1;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci	/* for the case one cgroup combine to multiple events */
38862306a36Sopenharmony_ci	i = 0;
38962306a36Sopenharmony_ci	if (nr_cgroups == 1) {
39062306a36Sopenharmony_ci		evlist__for_each_entry(evlist, counter) {
39162306a36Sopenharmony_ci			if (i == 0)
39262306a36Sopenharmony_ci				cgrp = counter->cgrp;
39362306a36Sopenharmony_ci			else {
39462306a36Sopenharmony_ci				counter->cgrp = cgrp;
39562306a36Sopenharmony_ci				refcount_inc(&cgrp->refcnt);
39662306a36Sopenharmony_ci			}
39762306a36Sopenharmony_ci			i++;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic bool has_pattern_string(const char *str)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	return !!strpbrk(str, "{}[]()|*+?^$");
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ciint evlist__expand_cgroup(struct evlist *evlist, const char *str,
40962306a36Sopenharmony_ci			  struct rblist *metric_events, bool open_cgroup)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct evlist *orig_list, *tmp_list;
41262306a36Sopenharmony_ci	struct evsel *pos, *evsel, *leader;
41362306a36Sopenharmony_ci	struct rblist orig_metric_events;
41462306a36Sopenharmony_ci	struct cgroup *cgrp = NULL;
41562306a36Sopenharmony_ci	struct cgroup_name *cn;
41662306a36Sopenharmony_ci	int ret = -1;
41762306a36Sopenharmony_ci	int prefix_len;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (evlist->core.nr_entries == 0) {
42062306a36Sopenharmony_ci		fprintf(stderr, "must define events before cgroups\n");
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	orig_list = evlist__new();
42562306a36Sopenharmony_ci	tmp_list = evlist__new();
42662306a36Sopenharmony_ci	if (orig_list == NULL || tmp_list == NULL) {
42762306a36Sopenharmony_ci		fprintf(stderr, "memory allocation failed\n");
42862306a36Sopenharmony_ci		return -ENOMEM;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* save original events and init evlist */
43262306a36Sopenharmony_ci	evlist__splice_list_tail(orig_list, &evlist->core.entries);
43362306a36Sopenharmony_ci	evlist->core.nr_entries = 0;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (metric_events) {
43662306a36Sopenharmony_ci		orig_metric_events = *metric_events;
43762306a36Sopenharmony_ci		rblist__init(metric_events);
43862306a36Sopenharmony_ci	} else {
43962306a36Sopenharmony_ci		rblist__init(&orig_metric_events);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (has_pattern_string(str))
44362306a36Sopenharmony_ci		prefix_len = match_cgroups(str);
44462306a36Sopenharmony_ci	else
44562306a36Sopenharmony_ci		prefix_len = list_cgroups(str);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (prefix_len < 0)
44862306a36Sopenharmony_ci		goto out_err;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	list_for_each_entry(cn, &cgroup_list, list) {
45162306a36Sopenharmony_ci		char *name;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		if (!cn->used)
45462306a36Sopenharmony_ci			continue;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* cgroup_name might have a full path, skip the prefix */
45762306a36Sopenharmony_ci		name = cn->name + prefix_len;
45862306a36Sopenharmony_ci		if (name[0] == '/' && name[1])
45962306a36Sopenharmony_ci			name++;
46062306a36Sopenharmony_ci		cgrp = cgroup__new(name, open_cgroup);
46162306a36Sopenharmony_ci		if (cgrp == NULL)
46262306a36Sopenharmony_ci			goto out_err;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		leader = NULL;
46562306a36Sopenharmony_ci		evlist__for_each_entry(orig_list, pos) {
46662306a36Sopenharmony_ci			evsel = evsel__clone(pos);
46762306a36Sopenharmony_ci			if (evsel == NULL)
46862306a36Sopenharmony_ci				goto out_err;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci			cgroup__put(evsel->cgrp);
47162306a36Sopenharmony_ci			evsel->cgrp = cgroup__get(cgrp);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			if (evsel__is_group_leader(pos))
47462306a36Sopenharmony_ci				leader = evsel;
47562306a36Sopenharmony_ci			evsel__set_leader(evsel, leader);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci			evlist__add(tmp_list, evsel);
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci		/* cgroup__new() has a refcount, release it here */
48062306a36Sopenharmony_ci		cgroup__put(cgrp);
48162306a36Sopenharmony_ci		nr_cgroups++;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		if (metric_events) {
48462306a36Sopenharmony_ci			if (metricgroup__copy_metric_events(tmp_list, cgrp,
48562306a36Sopenharmony_ci							    metric_events,
48662306a36Sopenharmony_ci							    &orig_metric_events) < 0)
48762306a36Sopenharmony_ci				goto out_err;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		evlist__splice_list_tail(evlist, &tmp_list->core.entries);
49162306a36Sopenharmony_ci		tmp_list->core.nr_entries = 0;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (list_empty(&evlist->core.entries)) {
49562306a36Sopenharmony_ci		fprintf(stderr, "no cgroup matched: %s\n", str);
49662306a36Sopenharmony_ci		goto out_err;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = 0;
50062306a36Sopenharmony_ci	cgrp_event_expanded = true;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciout_err:
50362306a36Sopenharmony_ci	evlist__delete(orig_list);
50462306a36Sopenharmony_ci	evlist__delete(tmp_list);
50562306a36Sopenharmony_ci	rblist__exit(&orig_metric_events);
50662306a36Sopenharmony_ci	release_cgroup_list();
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return ret;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
51262306a36Sopenharmony_ci					bool create, const char *path)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct rb_node **p = &root->rb_node;
51562306a36Sopenharmony_ci	struct rb_node *parent = NULL;
51662306a36Sopenharmony_ci	struct cgroup *cgrp;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	while (*p != NULL) {
51962306a36Sopenharmony_ci		parent = *p;
52062306a36Sopenharmony_ci		cgrp = rb_entry(parent, struct cgroup, node);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		if (cgrp->id == id)
52362306a36Sopenharmony_ci			return cgrp;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		if (cgrp->id < id)
52662306a36Sopenharmony_ci			p = &(*p)->rb_left;
52762306a36Sopenharmony_ci		else
52862306a36Sopenharmony_ci			p = &(*p)->rb_right;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!create)
53262306a36Sopenharmony_ci		return NULL;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	cgrp = malloc(sizeof(*cgrp));
53562306a36Sopenharmony_ci	if (cgrp == NULL)
53662306a36Sopenharmony_ci		return NULL;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	cgrp->name = strdup(path);
53962306a36Sopenharmony_ci	if (cgrp->name == NULL) {
54062306a36Sopenharmony_ci		free(cgrp);
54162306a36Sopenharmony_ci		return NULL;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	cgrp->fd = -1;
54562306a36Sopenharmony_ci	cgrp->id = id;
54662306a36Sopenharmony_ci	refcount_set(&cgrp->refcnt, 1);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	rb_link_node(&cgrp->node, parent, p);
54962306a36Sopenharmony_ci	rb_insert_color(&cgrp->node, root);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return cgrp;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistruct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
55562306a36Sopenharmony_ci			       const char *path)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct cgroup *cgrp;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	down_write(&env->cgroups.lock);
56062306a36Sopenharmony_ci	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
56162306a36Sopenharmony_ci	up_write(&env->cgroups.lock);
56262306a36Sopenharmony_ci	return cgrp;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistruct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct cgroup *cgrp;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	down_read(&env->cgroups.lock);
57062306a36Sopenharmony_ci	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
57162306a36Sopenharmony_ci	up_read(&env->cgroups.lock);
57262306a36Sopenharmony_ci	return cgrp;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_civoid perf_env__purge_cgroups(struct perf_env *env)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct rb_node *node;
57862306a36Sopenharmony_ci	struct cgroup *cgrp;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	down_write(&env->cgroups.lock);
58162306a36Sopenharmony_ci	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
58262306a36Sopenharmony_ci		node = rb_first(&env->cgroups.tree);
58362306a36Sopenharmony_ci		cgrp = rb_entry(node, struct cgroup, node);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		rb_erase(node, &env->cgroups.tree);
58662306a36Sopenharmony_ci		cgroup__put(cgrp);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	up_write(&env->cgroups.lock);
58962306a36Sopenharmony_ci}
590