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 8 int nr_cgroups; 9 10 static int 11 cgroupfs_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 57 static 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 76 static 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_entry(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_entry(evlist, counter) { 114 if (n == nr_cgroups) 115 goto found; 116 n++; 117 } 118 if (atomic_read(&cgrp->refcnt) == 0) 119 free(cgrp); 120 121 return -1; 122 found: 123 atomic_inc(&cgrp->refcnt); 124 counter->cgrp = cgrp; 125 return 0; 126 } 127 128 void close_cgroup(struct cgroup_sel *cgrp) 129 { 130 if (cgrp && atomic_dec_and_test(&cgrp->refcnt)) { 131 close(cgrp->fd); 132 zfree(&cgrp->name); 133 free(cgrp); 134 } 135 } 136 137 int parse_cgroups(const struct option *opt __maybe_unused, const char *str, 138 int unset __maybe_unused) 139 { 140 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; 141 const char *p, *e, *eos = str + strlen(str); 142 char *s; 143 int ret; 144 145 if (list_empty(&evlist->entries)) { 146 fprintf(stderr, "must define events before cgroups\n"); 147 return -1; 148 } 149 150 for (;;) { 151 p = strchr(str, ','); 152 e = p ? p : eos; 153 154 /* allow empty cgroups, i.e., skip */ 155 if (e - str) { 156 /* termination added */ 157 s = strndup(str, e - str); 158 if (!s) 159 return -1; 160 ret = add_cgroup(evlist, s); 161 if (ret) { 162 free(s); 163 return -1; 164 } 165 } 166 /* nr_cgroups is increased een for empty cgroups */ 167 nr_cgroups++; 168 if (!p) 169 break; 170 str = p+1; 171 } 172 return 0; 173 } 174