123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // SPDX-License-Identifier: GPL-2.0
- #include "util.h"
- #include "../perf.h"
- #include <subcmd/parse-options.h>
- #include "evsel.h"
- #include "cgroup.h"
- #include "evlist.h"
- #include <linux/stringify.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- int nr_cgroups;
- static int
- cgroupfs_find_mountpoint(char *buf, size_t maxlen)
- {
- FILE *fp;
- char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
- char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
- char *token, *saved_ptr = NULL;
- fp = fopen("/proc/mounts", "r");
- if (!fp)
- return -1;
- /*
- * in order to handle split hierarchy, we need to scan /proc/mounts
- * and inspect every cgroupfs mount point to find one that has
- * perf_event subsystem
- */
- path_v1[0] = '\0';
- path_v2[0] = '\0';
- while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
- __stringify(PATH_MAX)"s %*d %*d\n",
- mountpoint, type, tokens) == 3) {
- if (!path_v1[0] && !strcmp(type, "cgroup")) {
- token = strtok_r(tokens, ",", &saved_ptr);
- while (token != NULL) {
- if (!strcmp(token, "perf_event")) {
- strcpy(path_v1, mountpoint);
- break;
- }
- token = strtok_r(NULL, ",", &saved_ptr);
- }
- }
- if (!path_v2[0] && !strcmp(type, "cgroup2"))
- strcpy(path_v2, mountpoint);
- if (path_v1[0] && path_v2[0])
- break;
- }
- fclose(fp);
- if (path_v1[0])
- path = path_v1;
- else if (path_v2[0])
- path = path_v2;
- else
- return -1;
- if (strlen(path) < maxlen) {
- strcpy(buf, path);
- return 0;
- }
- return -1;
- }
- static int open_cgroup(const char *name)
- {
- char path[PATH_MAX + 1];
- char mnt[PATH_MAX + 1];
- int fd;
- if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
- return -1;
- scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
- fd = open(path, O_RDONLY);
- if (fd == -1)
- fprintf(stderr, "no access to cgroup %s\n", path);
- return fd;
- }
- static struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str)
- {
- struct perf_evsel *counter;
- /*
- * check if cgrp is already defined, if so we reuse it
- */
- evlist__for_each_entry(evlist, counter) {
- if (!counter->cgrp)
- continue;
- if (!strcmp(counter->cgrp->name, str))
- return cgroup__get(counter->cgrp);
- }
- return NULL;
- }
- static struct cgroup *cgroup__new(const char *name)
- {
- struct cgroup *cgroup = zalloc(sizeof(*cgroup));
- if (cgroup != NULL) {
- refcount_set(&cgroup->refcnt, 1);
- cgroup->name = strdup(name);
- if (!cgroup->name)
- goto out_err;
- cgroup->fd = open_cgroup(name);
- if (cgroup->fd == -1)
- goto out_free_name;
- }
- return cgroup;
- out_free_name:
- free(cgroup->name);
- out_err:
- free(cgroup);
- return NULL;
- }
- struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name)
- {
- struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
- return cgroup ?: cgroup__new(name);
- }
- static int add_cgroup(struct perf_evlist *evlist, const char *str)
- {
- struct perf_evsel *counter;
- struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
- int n;
- if (!cgrp)
- return -1;
- /*
- * find corresponding event
- * if add cgroup N, then need to find event N
- */
- n = 0;
- evlist__for_each_entry(evlist, counter) {
- if (n == nr_cgroups)
- goto found;
- n++;
- }
- cgroup__put(cgrp);
- return -1;
- found:
- counter->cgrp = cgrp;
- return 0;
- }
- static void cgroup__delete(struct cgroup *cgroup)
- {
- close(cgroup->fd);
- zfree(&cgroup->name);
- free(cgroup);
- }
- void cgroup__put(struct cgroup *cgrp)
- {
- if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
- cgroup__delete(cgrp);
- }
- }
- struct cgroup *cgroup__get(struct cgroup *cgroup)
- {
- if (cgroup)
- refcount_inc(&cgroup->refcnt);
- return cgroup;
- }
- static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup)
- {
- if (evsel->cgrp == NULL)
- evsel->cgrp = cgroup__get(cgroup);
- }
- void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup)
- {
- struct perf_evsel *evsel;
- evlist__for_each_entry(evlist, evsel)
- evsel__set_default_cgroup(evsel, cgroup);
- }
- int parse_cgroups(const struct option *opt, const char *str,
- int unset __maybe_unused)
- {
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
- struct perf_evsel *counter;
- struct cgroup *cgrp = NULL;
- const char *p, *e, *eos = str + strlen(str);
- char *s;
- int ret, i;
- if (list_empty(&evlist->entries)) {
- fprintf(stderr, "must define events before cgroups\n");
- return -1;
- }
- for (;;) {
- p = strchr(str, ',');
- e = p ? p : eos;
- /* allow empty cgroups, i.e., skip */
- if (e - str) {
- /* termination added */
- s = strndup(str, e - str);
- if (!s)
- return -1;
- ret = add_cgroup(evlist, s);
- free(s);
- if (ret)
- return -1;
- }
- /* nr_cgroups is increased een for empty cgroups */
- nr_cgroups++;
- if (!p)
- break;
- str = p+1;
- }
- /* for the case one cgroup combine to multiple events */
- i = 0;
- if (nr_cgroups == 1) {
- evlist__for_each_entry(evlist, counter) {
- if (i == 0)
- cgrp = counter->cgrp;
- else {
- counter->cgrp = cgrp;
- refcount_inc(&cgrp->refcnt);
- }
- i++;
- }
- }
- return 0;
- }
|