linux/tools/perf/util/cgroup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "../perf.h"
   3#include <subcmd/parse-options.h>
   4#include "evsel.h"
   5#include "cgroup.h"
   6#include "evlist.h"
   7#include <linux/stringify.h>
   8#include <linux/zalloc.h>
   9#include <sys/types.h>
  10#include <sys/stat.h>
  11#include <fcntl.h>
  12
  13int nr_cgroups;
  14
  15static int
  16cgroupfs_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
  74static 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
  93static 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
 109static 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
 126out_free_name:
 127        zfree(&cgroup->name);
 128out_err:
 129        free(cgroup);
 130        return NULL;
 131}
 132
 133struct 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
 140static 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;
 161found:
 162        counter->cgrp = cgrp;
 163        return 0;
 164}
 165
 166static void cgroup__delete(struct cgroup *cgroup)
 167{
 168        close(cgroup->fd);
 169        zfree(&cgroup->name);
 170        free(cgroup);
 171}
 172
 173void cgroup__put(struct cgroup *cgrp)
 174{
 175        if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
 176                cgroup__delete(cgrp);
 177        }
 178}
 179
 180struct cgroup *cgroup__get(struct cgroup *cgroup)
 181{
 182       if (cgroup)
 183                refcount_inc(&cgroup->refcnt);
 184       return cgroup;
 185}
 186
 187static 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
 193void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup)
 194{
 195        struct perf_evsel *evsel;
 196
 197        evlist__for_each_entry(evlist, evsel)
 198                evsel__set_default_cgroup(evsel, cgroup);
 199}
 200
 201int 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}
 252