1#include "util.h" 2#include "../perf.h" 3#include "parse-options.h" 4#include "evsel.h" 5#include "cgroup.h" 6#include "evlist.h" 7 8int nr_cgroups; 9 10static int 11cgroupfs_find_mountpoint(char *buf, size_t maxlen) 12{ 13 FILE *fp; 14 char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1]; 15 char *token, *saved_ptr = NULL; 16 int found = 0; 17 18 fp = fopen("/proc/mounts", "r"); 19 if (!fp) 20 return -1; 21 22 /* 23 * in order to handle split hierarchy, we need to scan /proc/mounts 24 * and inspect every cgroupfs mount point to find one that has 25 * perf_event subsystem 26 */ 27 while (fscanf(fp, "%*s %"STR(PATH_MAX)"s %"STR(PATH_MAX)"s %" 28 STR(PATH_MAX)"s %*d %*d\n", 29 mountpoint, type, tokens) == 3) { 30 31 if (!strcmp(type, "cgroup")) { 32 33 token = strtok_r(tokens, ",", &saved_ptr); 34 35 while (token != NULL) { 36 if (!strcmp(token, "perf_event")) { 37 found = 1; 38 break; 39 } 40 token = strtok_r(NULL, ",", &saved_ptr); 41 } 42 } 43 if (found) 44 break; 45 } 46 fclose(fp); 47 if (!found) 48 return -1; 49 50 if (strlen(mountpoint) < maxlen) { 51 strcpy(buf, mountpoint); 52 return 0; 53 } 54 return -1; 55} 56 57static int open_cgroup(char *name) 58{ 59 char path[PATH_MAX + 1]; 60 char mnt[PATH_MAX + 1]; 61 int fd; 62 63 64 if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1)) 65 return -1; 66 67 snprintf(path, PATH_MAX, "%s/%s", mnt, name); 68 69 fd = open(path, O_RDONLY); 70 if (fd == -1) 71 fprintf(stderr, "no access to cgroup %s\n", path); 72 73 return fd; 74} 75 76static int add_cgroup(struct perf_evlist *evlist, char *str) 77{ 78 struct perf_evsel *counter; 79 struct cgroup_sel *cgrp = NULL; 80 int n; 81 /* 82 * check if cgrp is already defined, if so we reuse it 83 */ 84 evlist__for_each(evlist, counter) { 85 cgrp = counter->cgrp; 86 if (!cgrp) 87 continue; 88 if (!strcmp(cgrp->name, str)) 89 break; 90 91 cgrp = NULL; 92 } 93 94 if (!cgrp) { 95 cgrp = zalloc(sizeof(*cgrp)); 96 if (!cgrp) 97 return -1; 98 99 cgrp->name = str; 100 101 cgrp->fd = open_cgroup(str); 102 if (cgrp->fd == -1) { 103 free(cgrp); 104 return -1; 105 } 106 } 107 108 /* 109 * find corresponding event 110 * if add cgroup N, then need to find event N 111 */ 112 n = 0; 113 evlist__for_each(evlist, counter) { 114 if (n == nr_cgroups) 115 goto found; 116 n++; 117 } 118 if (cgrp->refcnt == 0) 119 free(cgrp); 120 121 return -1; 122found: 123 cgrp->refcnt++; 124 counter->cgrp = cgrp; 125 return 0; 126} 127 128void close_cgroup(struct cgroup_sel *cgrp) 129{ 130 if (!cgrp) 131 return; 132 133 /* XXX: not reentrant */ 134 if (--cgrp->refcnt == 0) { 135 close(cgrp->fd); 136 zfree(&cgrp->name); 137 free(cgrp); 138 } 139} 140 141int parse_cgroups(const struct option *opt __maybe_unused, const char *str, 142 int unset __maybe_unused) 143{ 144 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; 145 const char *p, *e, *eos = str + strlen(str); 146 char *s; 147 int ret; 148 149 if (list_empty(&evlist->entries)) { 150 fprintf(stderr, "must define events before cgroups\n"); 151 return -1; 152 } 153 154 for (;;) { 155 p = strchr(str, ','); 156 e = p ? p : eos; 157 158 /* allow empty cgroups, i.e., skip */ 159 if (e - str) { 160 /* termination added */ 161 s = strndup(str, e - str); 162 if (!s) 163 return -1; 164 ret = add_cgroup(evlist, s); 165 if (ret) { 166 free(s); 167 return -1; 168 } 169 } 170 /* nr_cgroups is increased een for empty cgroups */ 171 nr_cgroups++; 172 if (!p) 173 break; 174 str = p+1; 175 } 176 return 0; 177} 178