cgroup.c 4.9 KB

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