]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
023695d9 SE |
2 | #include "util.h" |
3 | #include "../perf.h" | |
4b6ab94e | 4 | #include <subcmd/parse-options.h> |
023695d9 SE |
5 | #include "evsel.h" |
6 | #include "cgroup.h" | |
023695d9 | 7 | #include "evlist.h" |
aa8cc2f6 | 8 | #include <linux/stringify.h> |
023695d9 SE |
9 | |
10 | int nr_cgroups; | |
11 | ||
12 | static int | |
13 | cgroupfs_find_mountpoint(char *buf, size_t maxlen) | |
14 | { | |
15 | FILE *fp; | |
c168fbfb | 16 | char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1]; |
968ebff1 | 17 | char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path; |
621d2656 | 18 | char *token, *saved_ptr = NULL; |
023695d9 SE |
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 | */ | |
968ebff1 TH |
29 | path_v1[0] = '\0'; |
30 | path_v2[0] = '\0'; | |
31 | ||
aa8cc2f6 ACM |
32 | while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %" |
33 | __stringify(PATH_MAX)"s %*d %*d\n", | |
023695d9 SE |
34 | mountpoint, type, tokens) == 3) { |
35 | ||
968ebff1 | 36 | if (!path_v1[0] && !strcmp(type, "cgroup")) { |
023695d9 SE |
37 | |
38 | token = strtok_r(tokens, ",", &saved_ptr); | |
39 | ||
40 | while (token != NULL) { | |
41 | if (!strcmp(token, "perf_event")) { | |
968ebff1 | 42 | strcpy(path_v1, mountpoint); |
023695d9 SE |
43 | break; |
44 | } | |
45 | token = strtok_r(NULL, ",", &saved_ptr); | |
46 | } | |
47 | } | |
968ebff1 TH |
48 | |
49 | if (!path_v2[0] && !strcmp(type, "cgroup2")) | |
50 | strcpy(path_v2, mountpoint); | |
51 | ||
52 | if (path_v1[0] && path_v2[0]) | |
023695d9 SE |
53 | break; |
54 | } | |
55 | fclose(fp); | |
968ebff1 TH |
56 | |
57 | if (path_v1[0]) | |
58 | path = path_v1; | |
59 | else if (path_v2[0]) | |
60 | path = path_v2; | |
61 | else | |
023695d9 SE |
62 | return -1; |
63 | ||
968ebff1 TH |
64 | if (strlen(path) < maxlen) { |
65 | strcpy(buf, path); | |
023695d9 SE |
66 | return 0; |
67 | } | |
68 | return -1; | |
69 | } | |
70 | ||
71 | static int open_cgroup(char *name) | |
72 | { | |
c168fbfb ACM |
73 | char path[PATH_MAX + 1]; |
74 | char mnt[PATH_MAX + 1]; | |
023695d9 SE |
75 | int fd; |
76 | ||
77 | ||
c168fbfb | 78 | if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1)) |
023695d9 SE |
79 | return -1; |
80 | ||
c168fbfb | 81 | snprintf(path, PATH_MAX, "%s/%s", mnt, name); |
023695d9 SE |
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 | */ | |
e5cadb93 | 98 | evlist__for_each_entry(evlist, counter) { |
023695d9 SE |
99 | cgrp = counter->cgrp; |
100 | if (!cgrp) | |
101 | continue; | |
cd8dd032 ACM |
102 | if (!strcmp(cgrp->name, str)) { |
103 | refcount_inc(&cgrp->refcnt); | |
023695d9 | 104 | break; |
cd8dd032 | 105 | } |
023695d9 SE |
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; | |
cd8dd032 | 116 | refcount_set(&cgrp->refcnt, 1); |
023695d9 SE |
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; | |
e5cadb93 | 130 | evlist__for_each_entry(evlist, counter) { |
023695d9 SE |
131 | if (n == nr_cgroups) |
132 | goto found; | |
133 | n++; | |
134 | } | |
cd8dd032 | 135 | if (refcount_dec_and_test(&cgrp->refcnt)) |
023695d9 SE |
136 | free(cgrp); |
137 | ||
138 | return -1; | |
139 | found: | |
023695d9 SE |
140 | counter->cgrp = cgrp; |
141 | return 0; | |
142 | } | |
143 | ||
144 | void close_cgroup(struct cgroup_sel *cgrp) | |
145 | { | |
79c5fe6d | 146 | if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { |
023695d9 | 147 | close(cgrp->fd); |
74cf249d | 148 | zfree(&cgrp->name); |
023695d9 SE |
149 | free(cgrp); |
150 | } | |
151 | } | |
152 | ||
1d037ca1 IT |
153 | int parse_cgroups(const struct option *opt __maybe_unused, const char *str, |
154 | int unset __maybe_unused) | |
023695d9 SE |
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 | } |