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