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