xref: /openbmc/linux/tools/perf/util/cgroup.c (revision fc9ffb9c)
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 		if (!counter->cgrp)
103 			continue;
104 		if (!strcmp(counter->cgrp->name, str)) {
105 			cgrp = cgroup__get(counter->cgrp);
106 			break;
107 		}
108 	}
109 
110 	if (!cgrp) {
111 		cgrp = zalloc(sizeof(*cgrp));
112 		if (!cgrp)
113 			return -1;
114 
115 		cgrp->name = str;
116 		refcount_set(&cgrp->refcnt, 1);
117 
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;
130 	evlist__for_each_entry(evlist, counter) {
131 		if (n == nr_cgroups)
132 			goto found;
133 		n++;
134 	}
135 
136 	cgroup__put(cgrp);
137 	return -1;
138 found:
139 	counter->cgrp = cgrp;
140 	return 0;
141 }
142 
143 static void cgroup__delete(struct cgroup *cgroup)
144 {
145 	close(cgroup->fd);
146 	zfree(&cgroup->name);
147 	free(cgroup);
148 }
149 
150 void cgroup__put(struct cgroup *cgrp)
151 {
152 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
153 		cgroup__delete(cgrp);
154 	}
155 }
156 
157 struct cgroup *cgroup__get(struct cgroup *cgroup)
158 {
159        if (cgroup)
160 		refcount_inc(&cgroup->refcnt);
161        return cgroup;
162 }
163 
164 int parse_cgroups(const struct option *opt, const char *str,
165 		  int unset __maybe_unused)
166 {
167 	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
168 	struct perf_evsel *counter;
169 	struct cgroup *cgrp = NULL;
170 	const char *p, *e, *eos = str + strlen(str);
171 	char *s;
172 	int ret, i;
173 
174 	if (list_empty(&evlist->entries)) {
175 		fprintf(stderr, "must define events before cgroups\n");
176 		return -1;
177 	}
178 
179 	for (;;) {
180 		p = strchr(str, ',');
181 		e = p ? p : eos;
182 
183 		/* allow empty cgroups, i.e., skip */
184 		if (e - str) {
185 			/* termination added */
186 			s = strndup(str, e - str);
187 			if (!s)
188 				return -1;
189 			ret = add_cgroup(evlist, s);
190 			if (ret) {
191 				free(s);
192 				return -1;
193 			}
194 		}
195 		/* nr_cgroups is increased een for empty cgroups */
196 		nr_cgroups++;
197 		if (!p)
198 			break;
199 		str = p+1;
200 	}
201 	/* for the case one cgroup combine to multiple events */
202 	i = 0;
203 	if (nr_cgroups == 1) {
204 		evlist__for_each_entry(evlist, counter) {
205 			if (i == 0)
206 				cgrp = counter->cgrp;
207 			else {
208 				counter->cgrp = cgrp;
209 				refcount_inc(&cgrp->refcnt);
210 			}
211 			i++;
212 		}
213 	}
214 	return 0;
215 }
216