xref: /openbmc/linux/tools/perf/util/cgroup.c (revision 550987be)
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_sel *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 void close_cgroup(struct cgroup_sel *cgrp)
148 {
149 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
150 		close(cgrp->fd);
151 		zfree(&cgrp->name);
152 		free(cgrp);
153 	}
154 }
155 
156 int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
157 		  int unset __maybe_unused)
158 {
159 	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
160 	const char *p, *e, *eos = str + strlen(str);
161 	char *s;
162 	int ret;
163 
164 	if (list_empty(&evlist->entries)) {
165 		fprintf(stderr, "must define events before cgroups\n");
166 		return -1;
167 	}
168 
169 	for (;;) {
170 		p = strchr(str, ',');
171 		e = p ? p : eos;
172 
173 		/* allow empty cgroups, i.e., skip */
174 		if (e - str) {
175 			/* termination added */
176 			s = strndup(str, e - str);
177 			if (!s)
178 				return -1;
179 			ret = add_cgroup(evlist, s);
180 			if (ret) {
181 				free(s);
182 				return -1;
183 			}
184 		}
185 		/* nr_cgroups is increased een for empty cgroups */
186 		nr_cgroups++;
187 		if (!p)
188 			break;
189 		str = p+1;
190 	}
191 	return 0;
192 }
193