0001
0002 #include <subcmd/parse-options.h>
0003 #include "evsel.h"
0004 #include "cgroup.h"
0005 #include "evlist.h"
0006 #include "rblist.h"
0007 #include "metricgroup.h"
0008 #include "stat.h"
0009 #include <linux/zalloc.h>
0010 #include <sys/types.h>
0011 #include <sys/stat.h>
0012 #include <sys/statfs.h>
0013 #include <fcntl.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <api/fs/fs.h>
0017 #include <ftw.h>
0018 #include <regex.h>
0019
0020 int nr_cgroups;
0021 bool cgrp_event_expanded;
0022
0023
0024 struct cgroup_name {
0025 struct list_head list;
0026 bool used;
0027 char name[];
0028 };
0029 static LIST_HEAD(cgroup_list);
0030
0031 static int open_cgroup(const char *name)
0032 {
0033 char path[PATH_MAX + 1];
0034 char mnt[PATH_MAX + 1];
0035 int fd;
0036
0037
0038 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
0039 return -1;
0040
0041 scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
0042
0043 fd = open(path, O_RDONLY);
0044 if (fd == -1)
0045 fprintf(stderr, "no access to cgroup %s\n", path);
0046
0047 return fd;
0048 }
0049
0050 #ifdef HAVE_FILE_HANDLE
0051 int read_cgroup_id(struct cgroup *cgrp)
0052 {
0053 char path[PATH_MAX + 1];
0054 char mnt[PATH_MAX + 1];
0055 struct {
0056 struct file_handle fh;
0057 uint64_t cgroup_id;
0058 } handle;
0059 int mount_id;
0060
0061 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
0062 return -1;
0063
0064 scnprintf(path, PATH_MAX, "%s/%s", mnt, cgrp->name);
0065
0066 handle.fh.handle_bytes = sizeof(handle.cgroup_id);
0067 if (name_to_handle_at(AT_FDCWD, path, &handle.fh, &mount_id, 0) < 0)
0068 return -1;
0069
0070 cgrp->id = handle.cgroup_id;
0071 return 0;
0072 }
0073 #endif
0074
0075 #ifndef CGROUP2_SUPER_MAGIC
0076 #define CGROUP2_SUPER_MAGIC 0x63677270
0077 #endif
0078
0079 int cgroup_is_v2(const char *subsys)
0080 {
0081 char mnt[PATH_MAX + 1];
0082 struct statfs stbuf;
0083
0084 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, subsys))
0085 return -1;
0086
0087 if (statfs(mnt, &stbuf) < 0)
0088 return -1;
0089
0090 return (stbuf.f_type == CGROUP2_SUPER_MAGIC);
0091 }
0092
0093 static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
0094 {
0095 struct evsel *counter;
0096
0097
0098
0099 evlist__for_each_entry(evlist, counter) {
0100 if (!counter->cgrp)
0101 continue;
0102 if (!strcmp(counter->cgrp->name, str))
0103 return cgroup__get(counter->cgrp);
0104 }
0105
0106 return NULL;
0107 }
0108
0109 static struct cgroup *cgroup__new(const char *name, bool do_open)
0110 {
0111 struct cgroup *cgroup = zalloc(sizeof(*cgroup));
0112
0113 if (cgroup != NULL) {
0114 refcount_set(&cgroup->refcnt, 1);
0115
0116 cgroup->name = strdup(name);
0117 if (!cgroup->name)
0118 goto out_err;
0119
0120 if (do_open) {
0121 cgroup->fd = open_cgroup(name);
0122 if (cgroup->fd == -1)
0123 goto out_free_name;
0124 } else {
0125 cgroup->fd = -1;
0126 }
0127 }
0128
0129 return cgroup;
0130
0131 out_free_name:
0132 zfree(&cgroup->name);
0133 out_err:
0134 free(cgroup);
0135 return NULL;
0136 }
0137
0138 struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
0139 {
0140 struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
0141
0142 return cgroup ?: cgroup__new(name, true);
0143 }
0144
0145 static int add_cgroup(struct evlist *evlist, const char *str)
0146 {
0147 struct evsel *counter;
0148 struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
0149 int n;
0150
0151 if (!cgrp)
0152 return -1;
0153
0154
0155
0156
0157 n = 0;
0158 evlist__for_each_entry(evlist, counter) {
0159 if (n == nr_cgroups)
0160 goto found;
0161 n++;
0162 }
0163
0164 cgroup__put(cgrp);
0165 return -1;
0166 found:
0167 counter->cgrp = cgrp;
0168 return 0;
0169 }
0170
0171 static void cgroup__delete(struct cgroup *cgroup)
0172 {
0173 if (cgroup->fd >= 0)
0174 close(cgroup->fd);
0175 zfree(&cgroup->name);
0176 free(cgroup);
0177 }
0178
0179 void cgroup__put(struct cgroup *cgrp)
0180 {
0181 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
0182 cgroup__delete(cgrp);
0183 }
0184 }
0185
0186 struct cgroup *cgroup__get(struct cgroup *cgroup)
0187 {
0188 if (cgroup)
0189 refcount_inc(&cgroup->refcnt);
0190 return cgroup;
0191 }
0192
0193 static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
0194 {
0195 if (evsel->cgrp == NULL)
0196 evsel->cgrp = cgroup__get(cgroup);
0197 }
0198
0199 void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
0200 {
0201 struct evsel *evsel;
0202
0203 evlist__for_each_entry(evlist, evsel)
0204 evsel__set_default_cgroup(evsel, cgroup);
0205 }
0206
0207
0208 static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unused,
0209 int typeflag, struct FTW *ftwbuf __maybe_unused)
0210 {
0211 struct cgroup_name *cn;
0212
0213 if (typeflag != FTW_D)
0214 return 0;
0215
0216 cn = malloc(sizeof(*cn) + strlen(fpath) + 1);
0217 if (cn == NULL)
0218 return -1;
0219
0220 cn->used = false;
0221 strcpy(cn->name, fpath);
0222
0223 list_add_tail(&cn->list, &cgroup_list);
0224 return 0;
0225 }
0226
0227 static void release_cgroup_list(void)
0228 {
0229 struct cgroup_name *cn;
0230
0231 while (!list_empty(&cgroup_list)) {
0232 cn = list_first_entry(&cgroup_list, struct cgroup_name, list);
0233 list_del(&cn->list);
0234 free(cn);
0235 }
0236 }
0237
0238
0239 static int list_cgroups(const char *str)
0240 {
0241 const char *p, *e, *eos = str + strlen(str);
0242 struct cgroup_name *cn;
0243 char *s;
0244
0245
0246 for (;;) {
0247 p = strchr(str, ',');
0248 e = p ? p : eos;
0249
0250 if (e - str) {
0251 int ret;
0252
0253 s = strndup(str, e - str);
0254 if (!s)
0255 return -1;
0256
0257 ret = add_cgroup_name(s, NULL, FTW_D, NULL);
0258 free(s);
0259 if (ret)
0260 return -1;
0261 } else {
0262 if (add_cgroup_name("", NULL, FTW_D, NULL) < 0)
0263 return -1;
0264 }
0265
0266 if (!p)
0267 break;
0268 str = p+1;
0269 }
0270
0271
0272 list_for_each_entry(cn, &cgroup_list, list)
0273 cn->used = true;
0274
0275 return 0;
0276 }
0277
0278
0279 static int match_cgroups(const char *str)
0280 {
0281 char mnt[PATH_MAX];
0282 const char *p, *e, *eos = str + strlen(str);
0283 struct cgroup_name *cn;
0284 regex_t reg;
0285 int prefix_len;
0286 char *s;
0287
0288 if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event"))
0289 return -1;
0290
0291
0292 prefix_len = strlen(mnt);
0293
0294
0295 if (nftw(mnt, add_cgroup_name, 20, 0) < 0)
0296 return -1;
0297
0298 for (;;) {
0299 p = strchr(str, ',');
0300 e = p ? p : eos;
0301
0302
0303 if (e - str) {
0304
0305 s = strndup(str, e - str);
0306 if (!s)
0307 return -1;
0308 if (regcomp(®, s, REG_NOSUB)) {
0309 free(s);
0310 return -1;
0311 }
0312
0313
0314 list_for_each_entry(cn, &cgroup_list, list) {
0315 char *name = cn->name + prefix_len;
0316
0317 if (name[0] == '/' && name[1])
0318 name++;
0319 if (!regexec(®, name, 0, NULL, 0))
0320 cn->used = true;
0321 }
0322 regfree(®);
0323 free(s);
0324 } else {
0325
0326 cn = list_first_entry(&cgroup_list, struct cgroup_name,
0327 list);
0328 cn->used = true;
0329 }
0330
0331 if (!p)
0332 break;
0333 str = p+1;
0334 }
0335 return prefix_len;
0336 }
0337
0338 int parse_cgroups(const struct option *opt, const char *str,
0339 int unset __maybe_unused)
0340 {
0341 struct evlist *evlist = *(struct evlist **)opt->value;
0342 struct evsel *counter;
0343 struct cgroup *cgrp = NULL;
0344 const char *p, *e, *eos = str + strlen(str);
0345 char *s;
0346 int ret, i;
0347
0348 if (list_empty(&evlist->core.entries)) {
0349 fprintf(stderr, "must define events before cgroups\n");
0350 return -1;
0351 }
0352
0353 for (;;) {
0354 p = strchr(str, ',');
0355 e = p ? p : eos;
0356
0357
0358 if (e - str) {
0359
0360 s = strndup(str, e - str);
0361 if (!s)
0362 return -1;
0363 ret = add_cgroup(evlist, s);
0364 free(s);
0365 if (ret)
0366 return -1;
0367 }
0368
0369 nr_cgroups++;
0370 if (!p)
0371 break;
0372 str = p+1;
0373 }
0374
0375 i = 0;
0376 if (nr_cgroups == 1) {
0377 evlist__for_each_entry(evlist, counter) {
0378 if (i == 0)
0379 cgrp = counter->cgrp;
0380 else {
0381 counter->cgrp = cgrp;
0382 refcount_inc(&cgrp->refcnt);
0383 }
0384 i++;
0385 }
0386 }
0387 return 0;
0388 }
0389
0390 static bool has_pattern_string(const char *str)
0391 {
0392 return !!strpbrk(str, "{}[]()|*+?^$");
0393 }
0394
0395 int evlist__expand_cgroup(struct evlist *evlist, const char *str,
0396 struct rblist *metric_events, bool open_cgroup)
0397 {
0398 struct evlist *orig_list, *tmp_list;
0399 struct evsel *pos, *evsel, *leader;
0400 struct rblist orig_metric_events;
0401 struct cgroup *cgrp = NULL;
0402 struct cgroup_name *cn;
0403 int ret = -1;
0404 int prefix_len;
0405
0406 if (evlist->core.nr_entries == 0) {
0407 fprintf(stderr, "must define events before cgroups\n");
0408 return -EINVAL;
0409 }
0410
0411 orig_list = evlist__new();
0412 tmp_list = evlist__new();
0413 if (orig_list == NULL || tmp_list == NULL) {
0414 fprintf(stderr, "memory allocation failed\n");
0415 return -ENOMEM;
0416 }
0417
0418
0419 evlist__splice_list_tail(orig_list, &evlist->core.entries);
0420 evlist->core.nr_entries = 0;
0421
0422 if (metric_events) {
0423 orig_metric_events = *metric_events;
0424 rblist__init(metric_events);
0425 } else {
0426 rblist__init(&orig_metric_events);
0427 }
0428
0429 if (has_pattern_string(str))
0430 prefix_len = match_cgroups(str);
0431 else
0432 prefix_len = list_cgroups(str);
0433
0434 if (prefix_len < 0)
0435 goto out_err;
0436
0437 list_for_each_entry(cn, &cgroup_list, list) {
0438 char *name;
0439
0440 if (!cn->used)
0441 continue;
0442
0443
0444 name = cn->name + prefix_len;
0445 if (name[0] == '/' && name[1])
0446 name++;
0447 cgrp = cgroup__new(name, open_cgroup);
0448 if (cgrp == NULL)
0449 goto out_err;
0450
0451 leader = NULL;
0452 evlist__for_each_entry(orig_list, pos) {
0453 evsel = evsel__clone(pos);
0454 if (evsel == NULL)
0455 goto out_err;
0456
0457 cgroup__put(evsel->cgrp);
0458 evsel->cgrp = cgroup__get(cgrp);
0459
0460 if (evsel__is_group_leader(pos))
0461 leader = evsel;
0462 evsel__set_leader(evsel, leader);
0463
0464 evlist__add(tmp_list, evsel);
0465 }
0466
0467 cgroup__put(cgrp);
0468 nr_cgroups++;
0469
0470 if (metric_events) {
0471 perf_stat__collect_metric_expr(tmp_list);
0472 if (metricgroup__copy_metric_events(tmp_list, cgrp,
0473 metric_events,
0474 &orig_metric_events) < 0)
0475 goto out_err;
0476 }
0477
0478 evlist__splice_list_tail(evlist, &tmp_list->core.entries);
0479 tmp_list->core.nr_entries = 0;
0480 }
0481
0482 if (list_empty(&evlist->core.entries)) {
0483 fprintf(stderr, "no cgroup matched: %s\n", str);
0484 goto out_err;
0485 }
0486
0487 ret = 0;
0488 cgrp_event_expanded = true;
0489
0490 out_err:
0491 evlist__delete(orig_list);
0492 evlist__delete(tmp_list);
0493 rblist__exit(&orig_metric_events);
0494 release_cgroup_list();
0495
0496 return ret;
0497 }
0498
0499 static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
0500 bool create, const char *path)
0501 {
0502 struct rb_node **p = &root->rb_node;
0503 struct rb_node *parent = NULL;
0504 struct cgroup *cgrp;
0505
0506 while (*p != NULL) {
0507 parent = *p;
0508 cgrp = rb_entry(parent, struct cgroup, node);
0509
0510 if (cgrp->id == id)
0511 return cgrp;
0512
0513 if (cgrp->id < id)
0514 p = &(*p)->rb_left;
0515 else
0516 p = &(*p)->rb_right;
0517 }
0518
0519 if (!create)
0520 return NULL;
0521
0522 cgrp = malloc(sizeof(*cgrp));
0523 if (cgrp == NULL)
0524 return NULL;
0525
0526 cgrp->name = strdup(path);
0527 if (cgrp->name == NULL) {
0528 free(cgrp);
0529 return NULL;
0530 }
0531
0532 cgrp->fd = -1;
0533 cgrp->id = id;
0534 refcount_set(&cgrp->refcnt, 1);
0535
0536 rb_link_node(&cgrp->node, parent, p);
0537 rb_insert_color(&cgrp->node, root);
0538
0539 return cgrp;
0540 }
0541
0542 struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
0543 const char *path)
0544 {
0545 struct cgroup *cgrp;
0546
0547 down_write(&env->cgroups.lock);
0548 cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
0549 up_write(&env->cgroups.lock);
0550 return cgrp;
0551 }
0552
0553 struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
0554 {
0555 struct cgroup *cgrp;
0556
0557 down_read(&env->cgroups.lock);
0558 cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
0559 up_read(&env->cgroups.lock);
0560 return cgrp;
0561 }
0562
0563 void perf_env__purge_cgroups(struct perf_env *env)
0564 {
0565 struct rb_node *node;
0566 struct cgroup *cgrp;
0567
0568 down_write(&env->cgroups.lock);
0569 while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
0570 node = rb_first(&env->cgroups.tree);
0571 cgrp = rb_entry(node, struct cgroup, node);
0572
0573 rb_erase(node, &env->cgroups.tree);
0574 cgroup__put(cgrp);
0575 }
0576 up_write(&env->cgroups.lock);
0577 }