xref: /openbmc/linux/tools/perf/util/cgroup.c (revision 8ee90c5c)
1 #include "util.h"
2 #include "../perf.h"
3 #include <subcmd/parse-options.h>
4 #include "evsel.h"
5 #include "cgroup.h"
6 #include "evlist.h"
7 #include <linux/stringify.h>
8 
9 int nr_cgroups;
10 
11 static int
12 cgroupfs_find_mountpoint(char *buf, size_t maxlen)
13 {
14 	FILE *fp;
15 	char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
16 	char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
17 	char *token, *saved_ptr = NULL;
18 
19 	fp = fopen("/proc/mounts", "r");
20 	if (!fp)
21 		return -1;
22 
23 	/*
24 	 * in order to handle split hierarchy, we need to scan /proc/mounts
25 	 * and inspect every cgroupfs mount point to find one that has
26 	 * perf_event subsystem
27 	 */
28 	path_v1[0] = '\0';
29 	path_v2[0] = '\0';
30 
31 	while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
32 				__stringify(PATH_MAX)"s %*d %*d\n",
33 				mountpoint, type, tokens) == 3) {
34 
35 		if (!path_v1[0] && !strcmp(type, "cgroup")) {
36 
37 			token = strtok_r(tokens, ",", &saved_ptr);
38 
39 			while (token != NULL) {
40 				if (!strcmp(token, "perf_event")) {
41 					strcpy(path_v1, mountpoint);
42 					break;
43 				}
44 				token = strtok_r(NULL, ",", &saved_ptr);
45 			}
46 		}
47 
48 		if (!path_v2[0] && !strcmp(type, "cgroup2"))
49 			strcpy(path_v2, mountpoint);
50 
51 		if (path_v1[0] && path_v2[0])
52 			break;
53 	}
54 	fclose(fp);
55 
56 	if (path_v1[0])
57 		path = path_v1;
58 	else if (path_v2[0])
59 		path = path_v2;
60 	else
61 		return -1;
62 
63 	if (strlen(path) < maxlen) {
64 		strcpy(buf, path);
65 		return 0;
66 	}
67 	return -1;
68 }
69 
70 static int open_cgroup(char *name)
71 {
72 	char path[PATH_MAX + 1];
73 	char mnt[PATH_MAX + 1];
74 	int fd;
75 
76 
77 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
78 		return -1;
79 
80 	snprintf(path, PATH_MAX, "%s/%s", mnt, name);
81 
82 	fd = open(path, O_RDONLY);
83 	if (fd == -1)
84 		fprintf(stderr, "no access to cgroup %s\n", path);
85 
86 	return fd;
87 }
88 
89 static int add_cgroup(struct perf_evlist *evlist, char *str)
90 {
91 	struct perf_evsel *counter;
92 	struct cgroup_sel *cgrp = NULL;
93 	int n;
94 	/*
95 	 * check if cgrp is already defined, if so we reuse it
96 	 */
97 	evlist__for_each_entry(evlist, counter) {
98 		cgrp = counter->cgrp;
99 		if (!cgrp)
100 			continue;
101 		if (!strcmp(cgrp->name, str)) {
102 			refcount_inc(&cgrp->refcnt);
103 			break;
104 		}
105 
106 		cgrp = NULL;
107 	}
108 
109 	if (!cgrp) {
110 		cgrp = zalloc(sizeof(*cgrp));
111 		if (!cgrp)
112 			return -1;
113 
114 		cgrp->name = str;
115 		refcount_set(&cgrp->refcnt, 1);
116 
117 		cgrp->fd = open_cgroup(str);
118 		if (cgrp->fd == -1) {
119 			free(cgrp);
120 			return -1;
121 		}
122 	}
123 
124 	/*
125 	 * find corresponding event
126 	 * if add cgroup N, then need to find event N
127 	 */
128 	n = 0;
129 	evlist__for_each_entry(evlist, counter) {
130 		if (n == nr_cgroups)
131 			goto found;
132 		n++;
133 	}
134 	if (refcount_dec_and_test(&cgrp->refcnt))
135 		free(cgrp);
136 
137 	return -1;
138 found:
139 	counter->cgrp = cgrp;
140 	return 0;
141 }
142 
143 void close_cgroup(struct cgroup_sel *cgrp)
144 {
145 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
146 		close(cgrp->fd);
147 		zfree(&cgrp->name);
148 		free(cgrp);
149 	}
150 }
151 
152 int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
153 		  int unset __maybe_unused)
154 {
155 	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
156 	const char *p, *e, *eos = str + strlen(str);
157 	char *s;
158 	int ret;
159 
160 	if (list_empty(&evlist->entries)) {
161 		fprintf(stderr, "must define events before cgroups\n");
162 		return -1;
163 	}
164 
165 	for (;;) {
166 		p = strchr(str, ',');
167 		e = p ? p : eos;
168 
169 		/* allow empty cgroups, i.e., skip */
170 		if (e - str) {
171 			/* termination added */
172 			s = strndup(str, e - str);
173 			if (!s)
174 				return -1;
175 			ret = add_cgroup(evlist, s);
176 			if (ret) {
177 				free(s);
178 				return -1;
179 			}
180 		}
181 		/* nr_cgroups is increased een for empty cgroups */
182 		nr_cgroups++;
183 		if (!p)
184 			break;
185 		str = p+1;
186 	}
187 	return 0;
188 }
189