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 break; 103 104 cgrp = NULL; 105 } 106 107 if (!cgrp) { 108 cgrp = zalloc(sizeof(*cgrp)); 109 if (!cgrp) 110 return -1; 111 112 cgrp->name = str; 113 114 cgrp->fd = open_cgroup(str); 115 if (cgrp->fd == -1) { 116 free(cgrp); 117 return -1; 118 } 119 } 120 121 /* 122 * find corresponding event 123 * if add cgroup N, then need to find event N 124 */ 125 n = 0; 126 evlist__for_each_entry(evlist, counter) { 127 if (n == nr_cgroups) 128 goto found; 129 n++; 130 } 131 if (refcount_read(&cgrp->refcnt) == 0) 132 free(cgrp); 133 134 return -1; 135 found: 136 refcount_inc(&cgrp->refcnt); 137 counter->cgrp = cgrp; 138 return 0; 139 } 140 141 void close_cgroup(struct cgroup_sel *cgrp) 142 { 143 if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { 144 close(cgrp->fd); 145 zfree(&cgrp->name); 146 free(cgrp); 147 } 148 } 149 150 int parse_cgroups(const struct option *opt __maybe_unused, const char *str, 151 int unset __maybe_unused) 152 { 153 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; 154 const char *p, *e, *eos = str + strlen(str); 155 char *s; 156 int ret; 157 158 if (list_empty(&evlist->entries)) { 159 fprintf(stderr, "must define events before cgroups\n"); 160 return -1; 161 } 162 163 for (;;) { 164 p = strchr(str, ','); 165 e = p ? p : eos; 166 167 /* allow empty cgroups, i.e., skip */ 168 if (e - str) { 169 /* termination added */ 170 s = strndup(str, e - str); 171 if (!s) 172 return -1; 173 ret = add_cgroup(evlist, s); 174 if (ret) { 175 free(s); 176 return -1; 177 } 178 } 179 /* nr_cgroups is increased een for empty cgroups */ 180 nr_cgroups++; 181 if (!p) 182 break; 183 str = p+1; 184 } 185 return 0; 186 } 187