|  | #include "util.h" | 
|  | #include "../perf.h" | 
|  | #include "parse-options.h" | 
|  | #include "evsel.h" | 
|  | #include "cgroup.h" | 
|  | #include "evlist.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 *token, *saved_ptr = NULL; | 
|  | int found = 0; | 
|  |  | 
|  | 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 | 
|  | */ | 
|  | while (fscanf(fp, "%*s %"STR(PATH_MAX)"s %"STR(PATH_MAX)"s %" | 
|  | STR(PATH_MAX)"s %*d %*d\n", | 
|  | mountpoint, type, tokens) == 3) { | 
|  |  | 
|  | if (!strcmp(type, "cgroup")) { | 
|  |  | 
|  | token = strtok_r(tokens, ",", &saved_ptr); | 
|  |  | 
|  | while (token != NULL) { | 
|  | if (!strcmp(token, "perf_event")) { | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | token = strtok_r(NULL, ",", &saved_ptr); | 
|  | } | 
|  | } | 
|  | if (found) | 
|  | break; | 
|  | } | 
|  | fclose(fp); | 
|  | if (!found) | 
|  | return -1; | 
|  |  | 
|  | if (strlen(mountpoint) < maxlen) { | 
|  | strcpy(buf, mountpoint); | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int open_cgroup(char *name) | 
|  | { | 
|  | char path[PATH_MAX + 1]; | 
|  | char mnt[PATH_MAX + 1]; | 
|  | int fd; | 
|  |  | 
|  |  | 
|  | if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1)) | 
|  | return -1; | 
|  |  | 
|  | snprintf(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 int add_cgroup(struct perf_evlist *evlist, char *str) | 
|  | { | 
|  | struct perf_evsel *counter; | 
|  | struct cgroup_sel *cgrp = NULL; | 
|  | int n; | 
|  | /* | 
|  | * check if cgrp is already defined, if so we reuse it | 
|  | */ | 
|  | list_for_each_entry(counter, &evlist->entries, node) { | 
|  | cgrp = counter->cgrp; | 
|  | if (!cgrp) | 
|  | continue; | 
|  | if (!strcmp(cgrp->name, str)) | 
|  | break; | 
|  |  | 
|  | cgrp = NULL; | 
|  | } | 
|  |  | 
|  | if (!cgrp) { | 
|  | cgrp = zalloc(sizeof(*cgrp)); | 
|  | if (!cgrp) | 
|  | return -1; | 
|  |  | 
|  | cgrp->name = str; | 
|  |  | 
|  | cgrp->fd = open_cgroup(str); | 
|  | if (cgrp->fd == -1) { | 
|  | free(cgrp); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * find corresponding event | 
|  | * if add cgroup N, then need to find event N | 
|  | */ | 
|  | n = 0; | 
|  | list_for_each_entry(counter, &evlist->entries, node) { | 
|  | if (n == nr_cgroups) | 
|  | goto found; | 
|  | n++; | 
|  | } | 
|  | if (cgrp->refcnt == 0) | 
|  | free(cgrp); | 
|  |  | 
|  | return -1; | 
|  | found: | 
|  | cgrp->refcnt++; | 
|  | counter->cgrp = cgrp; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void close_cgroup(struct cgroup_sel *cgrp) | 
|  | { | 
|  | if (!cgrp) | 
|  | return; | 
|  |  | 
|  | /* XXX: not reentrant */ | 
|  | if (--cgrp->refcnt == 0) { | 
|  | close(cgrp->fd); | 
|  | free(cgrp->name); | 
|  | free(cgrp); | 
|  | } | 
|  | } | 
|  |  | 
|  | int parse_cgroups(const struct option *opt __used, const char *str, | 
|  | int unset __used) | 
|  | { | 
|  | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 
|  | const char *p, *e, *eos = str + strlen(str); | 
|  | char *s; | 
|  | int ret; | 
|  |  | 
|  | 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); | 
|  | if (ret) { | 
|  | free(s); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | /* nr_cgroups is increased een for empty cgroups */ | 
|  | nr_cgroups++; | 
|  | if (!p) | 
|  | break; | 
|  | str = p+1; | 
|  | } | 
|  | return 0; | 
|  | } |