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