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