1 // SPDX-License-Identifier: GPL-2.0
2 #include <subcmd/parse-options.h>
3 #include "evsel.h"
4 #include "cgroup.h"
5 #include "evlist.h"
6 #include "rblist.h"
7 #include "metricgroup.h"
8 #include "stat.h"
9 #include <linux/zalloc.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <api/fs/fs.h>
16
17 int nr_cgroups;
18
open_cgroup(const char * name)19 static int open_cgroup(const char *name)
20 {
21 char path[PATH_MAX + 1];
22 char mnt[PATH_MAX + 1];
23 int fd;
24
25
26 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
27 return -1;
28
29 scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
30
31 fd = open(path, O_RDONLY);
32 if (fd == -1)
33 fprintf(stderr, "no access to cgroup %s\n", path);
34
35 return fd;
36 }
37
evlist__find_cgroup(struct evlist * evlist,const char * str)38 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
39 {
40 struct evsel *counter;
41 /*
42 * check if cgrp is already defined, if so we reuse it
43 */
44 evlist__for_each_entry(evlist, counter) {
45 if (!counter->cgrp)
46 continue;
47 if (!strcmp(counter->cgrp->name, str))
48 return cgroup__get(counter->cgrp);
49 }
50
51 return NULL;
52 }
53
cgroup__new(const char * name,bool do_open)54 static struct cgroup *cgroup__new(const char *name, bool do_open)
55 {
56 struct cgroup *cgroup = zalloc(sizeof(*cgroup));
57
58 if (cgroup != NULL) {
59 refcount_set(&cgroup->refcnt, 1);
60
61 cgroup->name = strdup(name);
62 if (!cgroup->name)
63 goto out_err;
64
65 if (do_open) {
66 cgroup->fd = open_cgroup(name);
67 if (cgroup->fd == -1)
68 goto out_free_name;
69 } else {
70 cgroup->fd = -1;
71 }
72 }
73
74 return cgroup;
75
76 out_free_name:
77 zfree(&cgroup->name);
78 out_err:
79 free(cgroup);
80 return NULL;
81 }
82
evlist__findnew_cgroup(struct evlist * evlist,const char * name)83 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
84 {
85 struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
86
87 return cgroup ?: cgroup__new(name, true);
88 }
89
add_cgroup(struct evlist * evlist,const char * str)90 static int add_cgroup(struct evlist *evlist, const char *str)
91 {
92 struct evsel *counter;
93 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
94 int n;
95
96 if (!cgrp)
97 return -1;
98 /*
99 * find corresponding event
100 * if add cgroup N, then need to find event N
101 */
102 n = 0;
103 evlist__for_each_entry(evlist, counter) {
104 if (n == nr_cgroups)
105 goto found;
106 n++;
107 }
108
109 cgroup__put(cgrp);
110 return -1;
111 found:
112 counter->cgrp = cgrp;
113 return 0;
114 }
115
cgroup__delete(struct cgroup * cgroup)116 static void cgroup__delete(struct cgroup *cgroup)
117 {
118 if (cgroup->fd >= 0)
119 close(cgroup->fd);
120 zfree(&cgroup->name);
121 free(cgroup);
122 }
123
cgroup__put(struct cgroup * cgrp)124 void cgroup__put(struct cgroup *cgrp)
125 {
126 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
127 cgroup__delete(cgrp);
128 }
129 }
130
cgroup__get(struct cgroup * cgroup)131 struct cgroup *cgroup__get(struct cgroup *cgroup)
132 {
133 if (cgroup)
134 refcount_inc(&cgroup->refcnt);
135 return cgroup;
136 }
137
evsel__set_default_cgroup(struct evsel * evsel,struct cgroup * cgroup)138 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
139 {
140 if (evsel->cgrp == NULL)
141 evsel->cgrp = cgroup__get(cgroup);
142 }
143
evlist__set_default_cgroup(struct evlist * evlist,struct cgroup * cgroup)144 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
145 {
146 struct evsel *evsel;
147
148 evlist__for_each_entry(evlist, evsel)
149 evsel__set_default_cgroup(evsel, cgroup);
150 }
151
parse_cgroups(const struct option * opt,const char * str,int unset __maybe_unused)152 int parse_cgroups(const struct option *opt, const char *str,
153 int unset __maybe_unused)
154 {
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);
159 char *s;
160 int ret, i;
161
162 if (list_empty(&evlist->core.entries)) {
163 fprintf(stderr, "must define events before cgroups\n");
164 return -1;
165 }
166
167 for (;;) {
168 p = strchr(str, ',');
169 e = p ? p : eos;
170
171 /* allow empty cgroups, i.e., skip */
172 if (e - str) {
173 /* termination added */
174 s = strndup(str, e - str);
175 if (!s)
176 return -1;
177 ret = add_cgroup(evlist, s);
178 free(s);
179 if (ret)
180 return -1;
181 }
182 /* nr_cgroups is increased een for empty cgroups */
183 nr_cgroups++;
184 if (!p)
185 break;
186 str = p+1;
187 }
188 /* for the case one cgroup combine to multiple events */
189 i = 0;
190 if (nr_cgroups == 1) {
191 evlist__for_each_entry(evlist, counter) {
192 if (i == 0)
193 cgrp = counter->cgrp;
194 else {
195 counter->cgrp = cgrp;
196 refcount_inc(&cgrp->refcnt);
197 }
198 i++;
199 }
200 }
201 return 0;
202 }
203
evlist__expand_cgroup(struct evlist * evlist,const char * str,struct rblist * metric_events,bool open_cgroup)204 int evlist__expand_cgroup(struct evlist *evlist, const char *str,
205 struct rblist *metric_events, bool open_cgroup)
206 {
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);
212 int ret = -1;
213
214 if (evlist->core.nr_entries == 0) {
215 fprintf(stderr, "must define events before cgroups\n");
216 return -EINVAL;
217 }
218
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");
223 return -ENOMEM;
224 }
225
226 /* save original events and init evlist */
227 perf_evlist__splice_list_tail(orig_list, &evlist->core.entries);
228 evlist->core.nr_entries = 0;
229
230 if (metric_events) {
231 orig_metric_events = *metric_events;
232 rblist__init(metric_events);
233 } else {
234 rblist__init(&orig_metric_events);
235 }
236
237 for (;;) {
238 p = strchr(str, ',');
239 e = p ? p : eos;
240
241 /* allow empty cgroups, i.e., skip */
242 if (e - str) {
243 /* termination added */
244 char *name = strndup(str, e - str);
245 if (!name)
246 goto out_err;
247
248 cgrp = cgroup__new(name, open_cgroup);
249 free(name);
250 if (cgrp == NULL)
251 goto out_err;
252 } else {
253 cgrp = NULL;
254 }
255
256 leader = NULL;
257 evlist__for_each_entry(orig_list, pos) {
258 evsel = evsel__clone(pos);
259 if (evsel == NULL)
260 goto out_err;
261
262 cgroup__put(evsel->cgrp);
263 evsel->cgrp = cgroup__get(cgrp);
264
265 if (evsel__is_group_leader(pos))
266 leader = evsel;
267 evsel->leader = leader;
268
269 evlist__add(tmp_list, evsel);
270 }
271 /* cgroup__new() has a refcount, release it here */
272 cgroup__put(cgrp);
273 nr_cgroups++;
274
275 if (metric_events) {
276 perf_stat__collect_metric_expr(tmp_list);
277 if (metricgroup__copy_metric_events(tmp_list, cgrp,
278 metric_events,
279 &orig_metric_events) < 0)
280 break;
281 }
282
283 perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
284 tmp_list->core.nr_entries = 0;
285
286 if (!p) {
287 ret = 0;
288 break;
289 }
290 str = p+1;
291 }
292
293 out_err:
294 evlist__delete(orig_list);
295 evlist__delete(tmp_list);
296 rblist__exit(&orig_metric_events);
297
298 return ret;
299 }
300
__cgroup__findnew(struct rb_root * root,uint64_t id,bool create,const char * path)301 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
302 bool create, const char *path)
303 {
304 struct rb_node **p = &root->rb_node;
305 struct rb_node *parent = NULL;
306 struct cgroup *cgrp;
307
308 while (*p != NULL) {
309 parent = *p;
310 cgrp = rb_entry(parent, struct cgroup, node);
311
312 if (cgrp->id == id)
313 return cgrp;
314
315 if (cgrp->id < id)
316 p = &(*p)->rb_left;
317 else
318 p = &(*p)->rb_right;
319 }
320
321 if (!create)
322 return NULL;
323
324 cgrp = malloc(sizeof(*cgrp));
325 if (cgrp == NULL)
326 return NULL;
327
328 cgrp->name = strdup(path);
329 if (cgrp->name == NULL) {
330 free(cgrp);
331 return NULL;
332 }
333
334 cgrp->fd = -1;
335 cgrp->id = id;
336 refcount_set(&cgrp->refcnt, 1);
337
338 rb_link_node(&cgrp->node, parent, p);
339 rb_insert_color(&cgrp->node, root);
340
341 return cgrp;
342 }
343
cgroup__findnew(struct perf_env * env,uint64_t id,const char * path)344 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
345 const char *path)
346 {
347 struct cgroup *cgrp;
348
349 down_write(&env->cgroups.lock);
350 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
351 up_write(&env->cgroups.lock);
352 return cgrp;
353 }
354
cgroup__find(struct perf_env * env,uint64_t id)355 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
356 {
357 struct cgroup *cgrp;
358
359 down_read(&env->cgroups.lock);
360 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
361 up_read(&env->cgroups.lock);
362 return cgrp;
363 }
364
perf_env__purge_cgroups(struct perf_env * env)365 void perf_env__purge_cgroups(struct perf_env *env)
366 {
367 struct rb_node *node;
368 struct cgroup *cgrp;
369
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);
374
375 rb_erase(node, &env->cgroups.tree);
376 cgroup__put(cgrp);
377 }
378 up_write(&env->cgroups.lock);
379 }
380