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 17int nr_cgroups; 18 19static 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 38static 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 54static 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 76out_free_name: 77 zfree(&cgroup->name); 78out_err: 79 free(cgroup); 80 return NULL; 81} 82 83struct 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 90static 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; 111found: 112 counter->cgrp = cgrp; 113 return 0; 114} 115 116static 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 124void cgroup__put(struct cgroup *cgrp) 125{ 126 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 127 cgroup__delete(cgrp); 128 } 129} 130 131struct cgroup *cgroup__get(struct cgroup *cgroup) 132{ 133 if (cgroup) 134 refcount_inc(&cgroup->refcnt); 135 return cgroup; 136} 137 138static 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 144void 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 152int 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 204int 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 293out_err: 294 evlist__delete(orig_list); 295 evlist__delete(tmp_list); 296 rblist__exit(&orig_metric_events); 297 298 return ret; 299} 300 301static 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 344struct 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 355struct 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 365void 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