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