xref: /openbmc/linux/tools/perf/util/cgroup.c (revision fb960bd2)
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 
10 int nr_cgroups;
11 
12 static int
13 cgroupfs_find_mountpoint(char *buf, size_t maxlen)
14 {
15 	FILE *fp;
16 	char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
17 	char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
18 	char *token, *saved_ptr = NULL;
19 
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 	 */
29 	path_v1[0] = '\0';
30 	path_v2[0] = '\0';
31 
32 	while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
33 				__stringify(PATH_MAX)"s %*d %*d\n",
34 				mountpoint, type, tokens) == 3) {
35 
36 		if (!path_v1[0] && !strcmp(type, "cgroup")) {
37 
38 			token = strtok_r(tokens, ",", &saved_ptr);
39 
40 			while (token != NULL) {
41 				if (!strcmp(token, "perf_event")) {
42 					strcpy(path_v1, mountpoint);
43 					break;
44 				}
45 				token = strtok_r(NULL, ",", &saved_ptr);
46 			}
47 		}
48 
49 		if (!path_v2[0] && !strcmp(type, "cgroup2"))
50 			strcpy(path_v2, mountpoint);
51 
52 		if (path_v1[0] && path_v2[0])
53 			break;
54 	}
55 	fclose(fp);
56 
57 	if (path_v1[0])
58 		path = path_v1;
59 	else if (path_v2[0])
60 		path = path_v2;
61 	else
62 		return -1;
63 
64 	if (strlen(path) < maxlen) {
65 		strcpy(buf, path);
66 		return 0;
67 	}
68 	return -1;
69 }
70 
71 static int open_cgroup(char *name)
72 {
73 	char path[PATH_MAX + 1];
74 	char mnt[PATH_MAX + 1];
75 	int fd;
76 
77 
78 	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
79 		return -1;
80 
81 	snprintf(path, PATH_MAX, "%s/%s", mnt, name);
82 
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 
90 static 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 	 */
98 	evlist__for_each_entry(evlist, counter) {
99 		cgrp = counter->cgrp;
100 		if (!cgrp)
101 			continue;
102 		if (!strcmp(cgrp->name, str)) {
103 			refcount_inc(&cgrp->refcnt);
104 			break;
105 		}
106 
107 		cgrp = NULL;
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 	if (refcount_dec_and_test(&cgrp->refcnt))
136 		free(cgrp);
137 
138 	return -1;
139 found:
140 	counter->cgrp = cgrp;
141 	return 0;
142 }
143 
144 void close_cgroup(struct cgroup_sel *cgrp)
145 {
146 	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
147 		close(cgrp->fd);
148 		zfree(&cgrp->name);
149 		free(cgrp);
150 	}
151 }
152 
153 int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
154 		  int unset __maybe_unused)
155 {
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 }
190