xref: /linux/tools/perf/util/cgroup.c (revision 60e13231561b3a4c5269bfa1ef6c0569ad6f28ec)
1 #include "util.h"
2 #include "../perf.h"
3 #include "parse-options.h"
4 #include "evsel.h"
5 #include "cgroup.h"
6 #include "debugfs.h" /* MAX_PATH, STR() */
7 #include "evlist.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[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1];
16 	char *token, *saved_ptr = NULL;
17 	int found = 0;
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 	while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %"
29 				STR(MAX_PATH)"s %*d %*d\n",
30 				mountpoint, type, tokens) == 3) {
31 
32 		if (!strcmp(type, "cgroup")) {
33 
34 			token = strtok_r(tokens, ",", &saved_ptr);
35 
36 			while (token != NULL) {
37 				if (!strcmp(token, "perf_event")) {
38 					found = 1;
39 					break;
40 				}
41 				token = strtok_r(NULL, ",", &saved_ptr);
42 			}
43 		}
44 		if (found)
45 			break;
46 	}
47 	fclose(fp);
48 	if (!found)
49 		return -1;
50 
51 	if (strlen(mountpoint) < maxlen) {
52 		strcpy(buf, mountpoint);
53 		return 0;
54 	}
55 	return -1;
56 }
57 
58 static int open_cgroup(char *name)
59 {
60 	char path[MAX_PATH+1];
61 	char mnt[MAX_PATH+1];
62 	int fd;
63 
64 
65 	if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1))
66 		return -1;
67 
68 	snprintf(path, MAX_PATH, "%s/%s", mnt, name);
69 
70 	fd = open(path, O_RDONLY);
71 	if (fd == -1)
72 		fprintf(stderr, "no access to cgroup %s\n", path);
73 
74 	return fd;
75 }
76 
77 static int add_cgroup(struct perf_evlist *evlist, char *str)
78 {
79 	struct perf_evsel *counter;
80 	struct cgroup_sel *cgrp = NULL;
81 	int n;
82 	/*
83 	 * check if cgrp is already defined, if so we reuse it
84 	 */
85 	list_for_each_entry(counter, &evlist->entries, node) {
86 		cgrp = counter->cgrp;
87 		if (!cgrp)
88 			continue;
89 		if (!strcmp(cgrp->name, str))
90 			break;
91 
92 		cgrp = NULL;
93 	}
94 
95 	if (!cgrp) {
96 		cgrp = zalloc(sizeof(*cgrp));
97 		if (!cgrp)
98 			return -1;
99 
100 		cgrp->name = str;
101 
102 		cgrp->fd = open_cgroup(str);
103 		if (cgrp->fd == -1) {
104 			free(cgrp);
105 			return -1;
106 		}
107 	}
108 
109 	/*
110 	 * find corresponding event
111 	 * if add cgroup N, then need to find event N
112 	 */
113 	n = 0;
114 	list_for_each_entry(counter, &evlist->entries, node) {
115 		if (n == nr_cgroups)
116 			goto found;
117 		n++;
118 	}
119 	if (cgrp->refcnt == 0)
120 		free(cgrp);
121 
122 	return -1;
123 found:
124 	cgrp->refcnt++;
125 	counter->cgrp = cgrp;
126 	return 0;
127 }
128 
129 void close_cgroup(struct cgroup_sel *cgrp)
130 {
131 	if (!cgrp)
132 		return;
133 
134 	/* XXX: not reentrant */
135 	if (--cgrp->refcnt == 0) {
136 		close(cgrp->fd);
137 		free(cgrp->name);
138 		free(cgrp);
139 	}
140 }
141 
142 int parse_cgroups(const struct option *opt __used, const char *str,
143 		  int unset __used)
144 {
145 	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
146 	const char *p, *e, *eos = str + strlen(str);
147 	char *s;
148 	int ret;
149 
150 	if (list_empty(&evlist->entries)) {
151 		fprintf(stderr, "must define events before cgroups\n");
152 		return -1;
153 	}
154 
155 	for (;;) {
156 		p = strchr(str, ',');
157 		e = p ? p : eos;
158 
159 		/* allow empty cgroups, i.e., skip */
160 		if (e - str) {
161 			/* termination added */
162 			s = strndup(str, e - str);
163 			if (!s)
164 				return -1;
165 			ret = add_cgroup(evlist, s);
166 			if (ret) {
167 				free(s);
168 				return -1;
169 			}
170 		}
171 		/* nr_cgroups is increased een for empty cgroups */
172 		nr_cgroups++;
173 		if (!p)
174 			break;
175 		str = p+1;
176 	}
177 	return 0;
178 }
179