| Stephane Eranian | 023695d | 2011-02-14 11:20:01 +0200 | [diff] [blame] | 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 | } |