1 // SPDX-License-Identifier: GPL-2.0 2 #include <subcmd/parse-options.h> 3 #include "evsel.h" 4 #include "cgroup.h" 5 #include "evlist.h" 6 #include "rblist.h" 7 #include "metricgroup.h" 8 #include "stat.h" 9 #include <linux/zalloc.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <api/fs/fs.h> 16 17 int nr_cgroups; 18 19 static int open_cgroup(const char *name) 20 { 21 char path[PATH_MAX + 1]; 22 char mnt[PATH_MAX + 1]; 23 int fd; 24 25 26 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event")) 27 return -1; 28 29 scnprintf(path, PATH_MAX, "%s/%s", mnt, name); 30 31 fd = open(path, O_RDONLY); 32 if (fd == -1) 33 fprintf(stderr, "no access to cgroup %s\n", path); 34 35 return fd; 36 } 37 38 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str) 39 { 40 struct evsel *counter; 41 /* 42 * check if cgrp is already defined, if so we reuse it 43 */ 44 evlist__for_each_entry(evlist, counter) { 45 if (!counter->cgrp) 46 continue; 47 if (!strcmp(counter->cgrp->name, str)) 48 return cgroup__get(counter->cgrp); 49 } 50 51 return NULL; 52 } 53 54 static struct cgroup *cgroup__new(const char *name, bool do_open) 55 { 56 struct cgroup *cgroup = zalloc(sizeof(*cgroup)); 57 58 if (cgroup != NULL) { 59 refcount_set(&cgroup->refcnt, 1); 60 61 cgroup->name = strdup(name); 62 if (!cgroup->name) 63 goto out_err; 64 65 if (do_open) { 66 cgroup->fd = open_cgroup(name); 67 if (cgroup->fd == -1) 68 goto out_free_name; 69 } else { 70 cgroup->fd = -1; 71 } 72 } 73 74 return cgroup; 75 76 out_free_name: 77 zfree(&cgroup->name); 78 out_err: 79 free(cgroup); 80 return NULL; 81 } 82 83 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name) 84 { 85 struct cgroup *cgroup = evlist__find_cgroup(evlist, name); 86 87 return cgroup ?: cgroup__new(name, true); 88 } 89 90 static int add_cgroup(struct evlist *evlist, const char *str) 91 { 92 struct evsel *counter; 93 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str); 94 int n; 95 96 if (!cgrp) 97 return -1; 98 /* 99 * find corresponding event 100 * if add cgroup N, then need to find event N 101 */ 102 n = 0; 103 evlist__for_each_entry(evlist, counter) { 104 if (n == nr_cgroups) 105 goto found; 106 n++; 107 } 108 109 cgroup__put(cgrp); 110 return -1; 111 found: 112 counter->cgrp = cgrp; 113 return 0; 114 } 115 116 static void cgroup__delete(struct cgroup *cgroup) 117 { 118 if (cgroup->fd >= 0) 119 close(cgroup->fd); 120 zfree(&cgroup->name); 121 free(cgroup); 122 } 123 124 void cgroup__put(struct cgroup *cgrp) 125 { 126 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 127 cgroup__delete(cgrp); 128 } 129 } 130 131 struct cgroup *cgroup__get(struct cgroup *cgroup) 132 { 133 if (cgroup) 134 refcount_inc(&cgroup->refcnt); 135 return cgroup; 136 } 137 138 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup) 139 { 140 if (evsel->cgrp == NULL) 141 evsel->cgrp = cgroup__get(cgroup); 142 } 143 144 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup) 145 { 146 struct evsel *evsel; 147 148 evlist__for_each_entry(evlist, evsel) 149 evsel__set_default_cgroup(evsel, cgroup); 150 } 151 152 int parse_cgroups(const struct option *opt, const char *str, 153 int unset __maybe_unused) 154 { 155 struct evlist *evlist = *(struct evlist **)opt->value; 156 struct evsel *counter; 157 struct cgroup *cgrp = NULL; 158 const char *p, *e, *eos = str + strlen(str); 159 char *s; 160 int ret, i; 161 162 if (list_empty(&evlist->core.entries)) { 163 fprintf(stderr, "must define events before cgroups\n"); 164 return -1; 165 } 166 167 for (;;) { 168 p = strchr(str, ','); 169 e = p ? p : eos; 170 171 /* allow empty cgroups, i.e., skip */ 172 if (e - str) { 173 /* termination added */ 174 s = strndup(str, e - str); 175 if (!s) 176 return -1; 177 ret = add_cgroup(evlist, s); 178 free(s); 179 if (ret) 180 return -1; 181 } 182 /* nr_cgroups is increased een for empty cgroups */ 183 nr_cgroups++; 184 if (!p) 185 break; 186 str = p+1; 187 } 188 /* for the case one cgroup combine to multiple events */ 189 i = 0; 190 if (nr_cgroups == 1) { 191 evlist__for_each_entry(evlist, counter) { 192 if (i == 0) 193 cgrp = counter->cgrp; 194 else { 195 counter->cgrp = cgrp; 196 refcount_inc(&cgrp->refcnt); 197 } 198 i++; 199 } 200 } 201 return 0; 202 } 203 204 int evlist__expand_cgroup(struct evlist *evlist, const char *str, 205 struct rblist *metric_events, bool open_cgroup) 206 { 207 struct evlist *orig_list, *tmp_list; 208 struct evsel *pos, *evsel, *leader; 209 struct rblist orig_metric_events; 210 struct cgroup *cgrp = NULL; 211 const char *p, *e, *eos = str + strlen(str); 212 int ret = -1; 213 214 if (evlist->core.nr_entries == 0) { 215 fprintf(stderr, "must define events before cgroups\n"); 216 return -EINVAL; 217 } 218 219 orig_list = evlist__new(); 220 tmp_list = evlist__new(); 221 if (orig_list == NULL || tmp_list == NULL) { 222 fprintf(stderr, "memory allocation failed\n"); 223 return -ENOMEM; 224 } 225 226 /* save original events and init evlist */ 227 perf_evlist__splice_list_tail(orig_list, &evlist->core.entries); 228 evlist->core.nr_entries = 0; 229 230 if (metric_events) { 231 orig_metric_events = *metric_events; 232 rblist__init(metric_events); 233 } else { 234 rblist__init(&orig_metric_events); 235 } 236 237 for (;;) { 238 p = strchr(str, ','); 239 e = p ? p : eos; 240 241 /* allow empty cgroups, i.e., skip */ 242 if (e - str) { 243 /* termination added */ 244 char *name = strndup(str, e - str); 245 if (!name) 246 goto out_err; 247 248 cgrp = cgroup__new(name, open_cgroup); 249 free(name); 250 if (cgrp == NULL) 251 goto out_err; 252 } else { 253 cgrp = NULL; 254 } 255 256 leader = NULL; 257 evlist__for_each_entry(orig_list, pos) { 258 evsel = evsel__clone(pos); 259 if (evsel == NULL) 260 goto out_err; 261 262 cgroup__put(evsel->cgrp); 263 evsel->cgrp = cgroup__get(cgrp); 264 265 if (evsel__is_group_leader(pos)) 266 leader = evsel; 267 evsel->leader = leader; 268 269 evlist__add(tmp_list, evsel); 270 } 271 /* cgroup__new() has a refcount, release it here */ 272 cgroup__put(cgrp); 273 nr_cgroups++; 274 275 if (metric_events) { 276 perf_stat__collect_metric_expr(tmp_list); 277 if (metricgroup__copy_metric_events(tmp_list, cgrp, 278 metric_events, 279 &orig_metric_events) < 0) 280 break; 281 } 282 283 perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries); 284 tmp_list->core.nr_entries = 0; 285 286 if (!p) { 287 ret = 0; 288 break; 289 } 290 str = p+1; 291 } 292 293 out_err: 294 evlist__delete(orig_list); 295 evlist__delete(tmp_list); 296 rblist__exit(&orig_metric_events); 297 298 return ret; 299 } 300 301 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id, 302 bool create, const char *path) 303 { 304 struct rb_node **p = &root->rb_node; 305 struct rb_node *parent = NULL; 306 struct cgroup *cgrp; 307 308 while (*p != NULL) { 309 parent = *p; 310 cgrp = rb_entry(parent, struct cgroup, node); 311 312 if (cgrp->id == id) 313 return cgrp; 314 315 if (cgrp->id < id) 316 p = &(*p)->rb_left; 317 else 318 p = &(*p)->rb_right; 319 } 320 321 if (!create) 322 return NULL; 323 324 cgrp = malloc(sizeof(*cgrp)); 325 if (cgrp == NULL) 326 return NULL; 327 328 cgrp->name = strdup(path); 329 if (cgrp->name == NULL) { 330 free(cgrp); 331 return NULL; 332 } 333 334 cgrp->fd = -1; 335 cgrp->id = id; 336 refcount_set(&cgrp->refcnt, 1); 337 338 rb_link_node(&cgrp->node, parent, p); 339 rb_insert_color(&cgrp->node, root); 340 341 return cgrp; 342 } 343 344 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id, 345 const char *path) 346 { 347 struct cgroup *cgrp; 348 349 down_write(&env->cgroups.lock); 350 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path); 351 up_write(&env->cgroups.lock); 352 return cgrp; 353 } 354 355 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id) 356 { 357 struct cgroup *cgrp; 358 359 down_read(&env->cgroups.lock); 360 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL); 361 up_read(&env->cgroups.lock); 362 return cgrp; 363 } 364 365 void perf_env__purge_cgroups(struct perf_env *env) 366 { 367 struct rb_node *node; 368 struct cgroup *cgrp; 369 370 down_write(&env->cgroups.lock); 371 while (!RB_EMPTY_ROOT(&env->cgroups.tree)) { 372 node = rb_first(&env->cgroups.tree); 373 cgrp = rb_entry(node, struct cgroup, node); 374 375 rb_erase(node, &env->cgroups.tree); 376 cgroup__put(cgrp); 377 } 378 up_write(&env->cgroups.lock); 379 } 380