Linux Perf
cgroup.c
Go to the documentation of this file.
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 
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(const 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  scnprintf(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 struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str)
94 {
95  struct perf_evsel *counter;
96  /*
97  * check if cgrp is already defined, if so we reuse it
98  */
99  evlist__for_each_entry(evlist, counter) {
100  if (!counter->cgrp)
101  continue;
102  if (!strcmp(counter->cgrp->name, str))
103  return cgroup__get(counter->cgrp);
104  }
105 
106  return NULL;
107 }
108 
109 static struct cgroup *cgroup__new(const char *name)
110 {
111  struct cgroup *cgroup = zalloc(sizeof(*cgroup));
112 
113  if (cgroup != NULL) {
114  refcount_set(&cgroup->refcnt, 1);
115 
116  cgroup->name = strdup(name);
117  if (!cgroup->name)
118  goto out_err;
119  cgroup->fd = open_cgroup(name);
120  if (cgroup->fd == -1)
121  goto out_free_name;
122  }
123 
124  return cgroup;
125 
126 out_free_name:
127  free(cgroup->name);
128 out_err:
129  free(cgroup);
130  return NULL;
131 }
132 
133 struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name)
134 {
135  struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
136 
137  return cgroup ?: cgroup__new(name);
138 }
139 
140 static int add_cgroup(struct perf_evlist *evlist, const char *str)
141 {
142  struct perf_evsel *counter;
143  struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
144  int n;
145 
146  if (!cgrp)
147  return -1;
148  /*
149  * find corresponding event
150  * if add cgroup N, then need to find event N
151  */
152  n = 0;
153  evlist__for_each_entry(evlist, counter) {
154  if (n == nr_cgroups)
155  goto found;
156  n++;
157  }
158 
159  cgroup__put(cgrp);
160  return -1;
161 found:
162  counter->cgrp = cgrp;
163  return 0;
164 }
165 
166 static void cgroup__delete(struct cgroup *cgroup)
167 {
168  close(cgroup->fd);
169  zfree(&cgroup->name);
170  free(cgroup);
171 }
172 
173 void cgroup__put(struct cgroup *cgrp)
174 {
175  if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
176  cgroup__delete(cgrp);
177  }
178 }
179 
180 struct cgroup *cgroup__get(struct cgroup *cgroup)
181 {
182  if (cgroup)
183  refcount_inc(&cgroup->refcnt);
184  return cgroup;
185 }
186 
187 static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup)
188 {
189  if (evsel->cgrp == NULL)
190  evsel->cgrp = cgroup__get(cgroup);
191 }
192 
194 {
195  struct perf_evsel *evsel;
196 
197  evlist__for_each_entry(evlist, evsel)
198  evsel__set_default_cgroup(evsel, cgroup);
199 }
200 
201 int parse_cgroups(const struct option *opt, const char *str,
202  int unset __maybe_unused)
203 {
204  struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
205  struct perf_evsel *counter;
206  struct cgroup *cgrp = NULL;
207  const char *p, *e, *eos = str + strlen(str);
208  char *s;
209  int ret, i;
210 
211  if (list_empty(&evlist->entries)) {
212  fprintf(stderr, "must define events before cgroups\n");
213  return -1;
214  }
215 
216  for (;;) {
217  p = strchr(str, ',');
218  e = p ? p : eos;
219 
220  /* allow empty cgroups, i.e., skip */
221  if (e - str) {
222  /* termination added */
223  s = strndup(str, e - str);
224  if (!s)
225  return -1;
226  ret = add_cgroup(evlist, s);
227  free(s);
228  if (ret)
229  return -1;
230  }
231  /* nr_cgroups is increased een for empty cgroups */
232  nr_cgroups++;
233  if (!p)
234  break;
235  str = p+1;
236  }
237  /* for the case one cgroup combine to multiple events */
238  i = 0;
239  if (nr_cgroups == 1) {
240  evlist__for_each_entry(evlist, counter) {
241  if (i == 0)
242  cgrp = counter->cgrp;
243  else {
244  counter->cgrp = cgrp;
245  refcount_inc(&cgrp->refcnt);
246  }
247  i++;
248  }
249  }
250  return 0;
251 }
refcount_t refcnt
Definition: cgroup.h:12
struct cgroup * cgrp
Definition: evsel.h:110
struct cgroup * cgroup__get(struct cgroup *cgroup)
Definition: cgroup.c:180
int parse_cgroups(const struct option *opt, const char *str, int unset __maybe_unused)
Definition: cgroup.c:201
static void cgroup__delete(struct cgroup *cgroup)
Definition: cgroup.c:166
const char * name
#define evlist__for_each_entry(evlist, evsel)
Definition: evlist.h:247
static int cgroupfs_find_mountpoint(char *buf, size_t maxlen)
Definition: cgroup.c:16
static struct cgroup * cgroup__new(const char *name)
Definition: cgroup.c:109
static struct cgroup * evlist__find_cgroup(struct perf_evlist *evlist, const char *str)
Definition: cgroup.c:93
static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup)
Definition: cgroup.c:187
#define PATH_MAX
Definition: jevents.c:1042
static int str(yyscan_t scanner, int token)
struct cgroup * evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name)
Definition: cgroup.c:133
int fd
Definition: cgroup.h:11
#define zfree(ptr)
Definition: util.h:25
char * name
Definition: cgroup.h:10
static int open_cgroup(const char *name)
Definition: cgroup.c:74
static int add_cgroup(struct perf_evlist *evlist, const char *str)
Definition: cgroup.c:140
void cgroup__put(struct cgroup *cgrp)
Definition: cgroup.c:173
void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup)
Definition: cgroup.c:193
void free(void *)
int nr_cgroups
Definition: cgroup.c:13
struct list_head entries
Definition: evlist.h:28
Definition: cgroup.h:9
void static void * zalloc(size_t size)
Definition: util.h:20