blob: d9ffc1e6eb3997b6e57202599b0406fa2ce85c9e [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Stephane Eranian023695d2011-02-14 11:20:01 +02002#include "util.h"
3#include "../perf.h"
Josh Poimboeuf4b6ab942015-12-15 09:39:39 -06004#include <subcmd/parse-options.h>
Stephane Eranian023695d2011-02-14 11:20:01 +02005#include "evsel.h"
6#include "cgroup.h"
Stephane Eranian023695d2011-02-14 11:20:01 +02007#include "evlist.h"
Arnaldo Carvalho de Meloaa8cc2f2017-04-17 15:36:40 -03008#include <linux/stringify.h>
Stephane Eranian023695d2011-02-14 11:20:01 +02009
10int nr_cgroups;
11
12static int
13cgroupfs_find_mountpoint(char *buf, size_t maxlen)
14{
15 FILE *fp;
Arnaldo Carvalho de Meloc168fbf2011-11-16 12:55:59 -020016 char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
Tejun Heo968ebff2017-01-29 14:35:20 -050017 char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
Eric Dumazet621d2652011-04-08 12:11:06 +020018 char *token, *saved_ptr = NULL;
Stephane Eranian023695d2011-02-14 11:20:01 +020019
20 fp = fopen("/proc/mounts", "r");
21 if (!fp)
22 return -1;
23
24 /*
25 * in order to handle split hierarchy, we need to scan /proc/mounts
26 * and inspect every cgroupfs mount point to find one that has
27 * perf_event subsystem
28 */
Tejun Heo968ebff2017-01-29 14:35:20 -050029 path_v1[0] = '\0';
30 path_v2[0] = '\0';
31
Arnaldo Carvalho de Meloaa8cc2f2017-04-17 15:36:40 -030032 while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
33 __stringify(PATH_MAX)"s %*d %*d\n",
Stephane Eranian023695d2011-02-14 11:20:01 +020034 mountpoint, type, tokens) == 3) {
35
Tejun Heo968ebff2017-01-29 14:35:20 -050036 if (!path_v1[0] && !strcmp(type, "cgroup")) {
Stephane Eranian023695d2011-02-14 11:20:01 +020037
38 token = strtok_r(tokens, ",", &saved_ptr);
39
40 while (token != NULL) {
41 if (!strcmp(token, "perf_event")) {
Tejun Heo968ebff2017-01-29 14:35:20 -050042 strcpy(path_v1, mountpoint);
Stephane Eranian023695d2011-02-14 11:20:01 +020043 break;
44 }
45 token = strtok_r(NULL, ",", &saved_ptr);
46 }
47 }
Tejun Heo968ebff2017-01-29 14:35:20 -050048
49 if (!path_v2[0] && !strcmp(type, "cgroup2"))
50 strcpy(path_v2, mountpoint);
51
52 if (path_v1[0] && path_v2[0])
Stephane Eranian023695d2011-02-14 11:20:01 +020053 break;
54 }
55 fclose(fp);
Tejun Heo968ebff2017-01-29 14:35:20 -050056
57 if (path_v1[0])
58 path = path_v1;
59 else if (path_v2[0])
60 path = path_v2;
61 else
Stephane Eranian023695d2011-02-14 11:20:01 +020062 return -1;
63
Tejun Heo968ebff2017-01-29 14:35:20 -050064 if (strlen(path) < maxlen) {
65 strcpy(buf, path);
Stephane Eranian023695d2011-02-14 11:20:01 +020066 return 0;
67 }
68 return -1;
69}
70
71static int open_cgroup(char *name)
72{
Arnaldo Carvalho de Meloc168fbf2011-11-16 12:55:59 -020073 char path[PATH_MAX + 1];
74 char mnt[PATH_MAX + 1];
Stephane Eranian023695d2011-02-14 11:20:01 +020075 int fd;
76
77
Arnaldo Carvalho de Meloc168fbf2011-11-16 12:55:59 -020078 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
Stephane Eranian023695d2011-02-14 11:20:01 +020079 return -1;
80
Arnaldo Carvalho de Meloc168fbf2011-11-16 12:55:59 -020081 snprintf(path, PATH_MAX, "%s/%s", mnt, name);
Stephane Eranian023695d2011-02-14 11:20:01 +020082
83 fd = open(path, O_RDONLY);
84 if (fd == -1)
85 fprintf(stderr, "no access to cgroup %s\n", path);
86
87 return fd;
88}
89
90static int add_cgroup(struct perf_evlist *evlist, char *str)
91{
92 struct perf_evsel *counter;
93 struct cgroup_sel *cgrp = NULL;
94 int n;
95 /*
96 * check if cgrp is already defined, if so we reuse it
97 */
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -030098 evlist__for_each_entry(evlist, counter) {
Stephane Eranian023695d2011-02-14 11:20:01 +020099 cgrp = counter->cgrp;
100 if (!cgrp)
101 continue;
Arnaldo Carvalho de Melocd8dd032017-07-18 20:20:19 -0300102 if (!strcmp(cgrp->name, str)) {
103 refcount_inc(&cgrp->refcnt);
Stephane Eranian023695d2011-02-14 11:20:01 +0200104 break;
Arnaldo Carvalho de Melocd8dd032017-07-18 20:20:19 -0300105 }
Stephane Eranian023695d2011-02-14 11:20:01 +0200106
107 cgrp = NULL;
108 }
109
110 if (!cgrp) {
111 cgrp = zalloc(sizeof(*cgrp));
112 if (!cgrp)
113 return -1;
114
115 cgrp->name = str;
Arnaldo Carvalho de Melocd8dd032017-07-18 20:20:19 -0300116 refcount_set(&cgrp->refcnt, 1);
Stephane Eranian023695d2011-02-14 11:20:01 +0200117
118 cgrp->fd = open_cgroup(str);
119 if (cgrp->fd == -1) {
120 free(cgrp);
121 return -1;
122 }
123 }
124
125 /*
126 * find corresponding event
127 * if add cgroup N, then need to find event N
128 */
129 n = 0;
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -0300130 evlist__for_each_entry(evlist, counter) {
Stephane Eranian023695d2011-02-14 11:20:01 +0200131 if (n == nr_cgroups)
132 goto found;
133 n++;
134 }
Arnaldo Carvalho de Melocd8dd032017-07-18 20:20:19 -0300135 if (refcount_dec_and_test(&cgrp->refcnt))
Stephane Eranian023695d2011-02-14 11:20:01 +0200136 free(cgrp);
137
138 return -1;
139found:
Stephane Eranian023695d2011-02-14 11:20:01 +0200140 counter->cgrp = cgrp;
141 return 0;
142}
143
144void close_cgroup(struct cgroup_sel *cgrp)
145{
Elena Reshetova79c5fe62017-02-21 17:34:55 +0200146 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
Stephane Eranian023695d2011-02-14 11:20:01 +0200147 close(cgrp->fd);
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300148 zfree(&cgrp->name);
Stephane Eranian023695d2011-02-14 11:20:01 +0200149 free(cgrp);
150 }
151}
152
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300153int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
154 int unset __maybe_unused)
Stephane Eranian023695d2011-02-14 11:20:01 +0200155{
156 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
157 const char *p, *e, *eos = str + strlen(str);
158 char *s;
159 int ret;
160
161 if (list_empty(&evlist->entries)) {
162 fprintf(stderr, "must define events before cgroups\n");
163 return -1;
164 }
165
166 for (;;) {
167 p = strchr(str, ',');
168 e = p ? p : eos;
169
170 /* allow empty cgroups, i.e., skip */
171 if (e - str) {
172 /* termination added */
173 s = strndup(str, e - str);
174 if (!s)
175 return -1;
176 ret = add_cgroup(evlist, s);
177 if (ret) {
178 free(s);
179 return -1;
180 }
181 }
182 /* nr_cgroups is increased een for empty cgroups */
183 nr_cgroups++;
184 if (!p)
185 break;
186 str = p+1;
187 }
188 return 0;
189}