linux/tools/perf/util/cgroup.c
<<
>>
Prefs
   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 <linux/stringify.h>
   7#include <linux/zalloc.h>
   8#include <sys/types.h>
   9#include <sys/stat.h>
  10#include <fcntl.h>
  11#include <stdlib.h>
  12#include <string.h>
  13
  14int nr_cgroups;
  15
  16static int
  17cgroupfs_find_mountpoint(char *buf, size_t maxlen)
  18{
  19        FILE *fp;
  20        char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
  21        char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
  22        char *token, *saved_ptr = NULL;
  23
  24        fp = fopen("/proc/mounts", "r");
  25        if (!fp)
  26                return -1;
  27
  28        /*
  29         * in order to handle split hierarchy, we need to scan /proc/mounts
  30         * and inspect every cgroupfs mount point to find one that has
  31         * perf_event subsystem
  32         */
  33        path_v1[0] = '\0';
  34        path_v2[0] = '\0';
  35
  36        while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
  37                                __stringify(PATH_MAX)"s %*d %*d\n",
  38                                mountpoint, type, tokens) == 3) {
  39
  40                if (!path_v1[0] && !strcmp(type, "cgroup")) {
  41
  42                        token = strtok_r(tokens, ",", &saved_ptr);
  43
  44                        while (token != NULL) {
  45                                if (!strcmp(token, "perf_event")) {
  46                                        strcpy(path_v1, mountpoint);
  47                                        break;
  48                                }
  49                                token = strtok_r(NULL, ",", &saved_ptr);
  50                        }
  51                }
  52
  53                if (!path_v2[0] && !strcmp(type, "cgroup2"))
  54                        strcpy(path_v2, mountpoint);
  55
  56                if (path_v1[0] && path_v2[0])
  57                        break;
  58        }
  59        fclose(fp);
  60
  61        if (path_v1[0])
  62                path = path_v1;
  63        else if (path_v2[0])
  64                path = path_v2;
  65        else
  66                return -1;
  67
  68        if (strlen(path) < maxlen) {
  69                strcpy(buf, path);
  70                return 0;
  71        }
  72        return -1;
  73}
  74
  75static int open_cgroup(const char *name)
  76{
  77        char path[PATH_MAX + 1];
  78        char mnt[PATH_MAX + 1];
  79        int fd;
  80
  81
  82        if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
  83                return -1;
  84
  85        scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
  86
  87        fd = open(path, O_RDONLY);
  88        if (fd == -1)
  89                fprintf(stderr, "no access to cgroup %s\n", path);
  90
  91        return fd;
  92}
  93
  94static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
  95{
  96        struct evsel *counter;
  97        /*
  98         * check if cgrp is already defined, if so we reuse it
  99         */
 100        evlist__for_each_entry(evlist, counter) {
 101                if (!counter->cgrp)
 102                        continue;
 103                if (!strcmp(counter->cgrp->name, str))
 104                        return cgroup__get(counter->cgrp);
 105        }
 106
 107        return NULL;
 108}
 109
 110static struct cgroup *cgroup__new(const char *name)
 111{
 112        struct cgroup *cgroup = zalloc(sizeof(*cgroup));
 113
 114        if (cgroup != NULL) {
 115                refcount_set(&cgroup->refcnt, 1);
 116
 117                cgroup->name = strdup(name);
 118                if (!cgroup->name)
 119                        goto out_err;
 120                cgroup->fd = open_cgroup(name);
 121                if (cgroup->fd == -1)
 122                        goto out_free_name;
 123        }
 124
 125        return cgroup;
 126
 127out_free_name:
 128        zfree(&cgroup->name);
 129out_err:
 130        free(cgroup);
 131        return NULL;
 132}
 133
 134struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
 135{
 136        struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
 137
 138        return cgroup ?: cgroup__new(name);
 139}
 140
 141static int add_cgroup(struct evlist *evlist, const char *str)
 142{
 143        struct evsel *counter;
 144        struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
 145        int n;
 146
 147        if (!cgrp)
 148                return -1;
 149        /*
 150         * find corresponding event
 151         * if add cgroup N, then need to find event N
 152         */
 153        n = 0;
 154        evlist__for_each_entry(evlist, counter) {
 155                if (n == nr_cgroups)
 156                        goto found;
 157                n++;
 158        }
 159
 160        cgroup__put(cgrp);
 161        return -1;
 162found:
 163        counter->cgrp = cgrp;
 164        return 0;
 165}
 166
 167static void cgroup__delete(struct cgroup *cgroup)
 168{
 169        close(cgroup->fd);
 170        zfree(&cgroup->name);
 171        free(cgroup);
 172}
 173
 174void cgroup__put(struct cgroup *cgrp)
 175{
 176        if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
 177                cgroup__delete(cgrp);
 178        }
 179}
 180
 181struct cgroup *cgroup__get(struct cgroup *cgroup)
 182{
 183       if (cgroup)
 184                refcount_inc(&cgroup->refcnt);
 185       return cgroup;
 186}
 187
 188static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
 189{
 190        if (evsel->cgrp == NULL)
 191                evsel->cgrp = cgroup__get(cgroup);
 192}
 193
 194void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
 195{
 196        struct evsel *evsel;
 197
 198        evlist__for_each_entry(evlist, evsel)
 199                evsel__set_default_cgroup(evsel, cgroup);
 200}
 201
 202int parse_cgroups(const struct option *opt, const char *str,
 203                  int unset __maybe_unused)
 204{
 205        struct evlist *evlist = *(struct evlist **)opt->value;
 206        struct evsel *counter;
 207        struct cgroup *cgrp = NULL;
 208        const char *p, *e, *eos = str + strlen(str);
 209        char *s;
 210        int ret, i;
 211
 212        if (list_empty(&evlist->core.entries)) {
 213                fprintf(stderr, "must define events before cgroups\n");
 214                return -1;
 215        }
 216
 217        for (;;) {
 218                p = strchr(str, ',');
 219                e = p ? p : eos;
 220
 221                /* allow empty cgroups, i.e., skip */
 222                if (e - str) {
 223                        /* termination added */
 224                        s = strndup(str, e - str);
 225                        if (!s)
 226                                return -1;
 227                        ret = add_cgroup(evlist, s);
 228                        free(s);
 229                        if (ret)
 230                                return -1;
 231                }
 232                /* nr_cgroups is increased een for empty cgroups */
 233                nr_cgroups++;
 234                if (!p)
 235                        break;
 236                str = p+1;
 237        }
 238        /* for the case one cgroup combine to multiple events */
 239        i = 0;
 240        if (nr_cgroups == 1) {
 241                evlist__for_each_entry(evlist, counter) {
 242                        if (i == 0)
 243                                cgrp = counter->cgrp;
 244                        else {
 245                                counter->cgrp = cgrp;
 246                                refcount_inc(&cgrp->refcnt);
 247                        }
 248                        i++;
 249                }
 250        }
 251        return 0;
 252}
 253