Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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 /* used to match cgroup name with patterns */
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  /* HAVE_FILE_HANDLE */
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      * check if cgrp is already defined, if so we reuse it
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      * find corresponding event
0155      * if add cgroup N, then need to find event N
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 /* helper function for ftw() in match_cgroups and list_cgroups */
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 /* collect given cgroups only */
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     /* use given name as is - for testing purpose */
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             /* pretend if it's added by ftw() */
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     /* these groups will be used */
0272     list_for_each_entry(cn, &cgroup_list, list)
0273         cn->used = true;
0274 
0275     return 0;
0276 }
0277 
0278 /* collect all cgroups first and then match with the pattern */
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     /* cgroup_name will have a full path, skip the root directory */
0292     prefix_len = strlen(mnt);
0293 
0294     /* collect all cgroups in the cgroup_list */
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         /* allow empty cgroups, i.e., skip */
0303         if (e - str) {
0304             /* termination added */
0305             s = strndup(str, e - str);
0306             if (!s)
0307                 return -1;
0308             if (regcomp(&reg, s, REG_NOSUB)) {
0309                 free(s);
0310                 return -1;
0311             }
0312 
0313             /* check cgroup name with the pattern */
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(&reg, name, 0, NULL, 0))
0320                     cn->used = true;
0321             }
0322             regfree(&reg);
0323             free(s);
0324         } else {
0325             /* first entry to root cgroup */
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         /* allow empty cgroups, i.e., skip */
0358         if (e - str) {
0359             /* termination added */
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         /* nr_cgroups is increased een for empty cgroups */
0369         nr_cgroups++;
0370         if (!p)
0371             break;
0372         str = p+1;
0373     }
0374     /* for the case one cgroup combine to multiple events */
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     /* save original events and init evlist */
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         /* cgroup_name might have a full path, skip the prefix */
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         /* cgroup__new() has a refcount, release it here */
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 }