cgroup.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <subcmd/parse-options.h>
  3. #include "evsel.h"
  4. #include "cgroup.h"
  5. #include "evlist.h"
  6. #include "rblist.h"
  7. #include "metricgroup.h"
  8. #include "stat.h"
  9. #include <linux/zalloc.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <api/fs/fs.h>
  16. int nr_cgroups;
  17. static int open_cgroup(const char *name)
  18. {
  19. char path[PATH_MAX + 1];
  20. char mnt[PATH_MAX + 1];
  21. int fd;
  22. if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
  23. return -1;
  24. scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
  25. fd = open(path, O_RDONLY);
  26. if (fd == -1)
  27. fprintf(stderr, "no access to cgroup %s\n", path);
  28. return fd;
  29. }
  30. static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
  31. {
  32. struct evsel *counter;
  33. /*
  34. * check if cgrp is already defined, if so we reuse it
  35. */
  36. evlist__for_each_entry(evlist, counter) {
  37. if (!counter->cgrp)
  38. continue;
  39. if (!strcmp(counter->cgrp->name, str))
  40. return cgroup__get(counter->cgrp);
  41. }
  42. return NULL;
  43. }
  44. static struct cgroup *cgroup__new(const char *name, bool do_open)
  45. {
  46. struct cgroup *cgroup = zalloc(sizeof(*cgroup));
  47. if (cgroup != NULL) {
  48. refcount_set(&cgroup->refcnt, 1);
  49. cgroup->name = strdup(name);
  50. if (!cgroup->name)
  51. goto out_err;
  52. if (do_open) {
  53. cgroup->fd = open_cgroup(name);
  54. if (cgroup->fd == -1)
  55. goto out_free_name;
  56. } else {
  57. cgroup->fd = -1;
  58. }
  59. }
  60. return cgroup;
  61. out_free_name:
  62. zfree(&cgroup->name);
  63. out_err:
  64. free(cgroup);
  65. return NULL;
  66. }
  67. struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
  68. {
  69. struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
  70. return cgroup ?: cgroup__new(name, true);
  71. }
  72. static int add_cgroup(struct evlist *evlist, const char *str)
  73. {
  74. struct evsel *counter;
  75. struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
  76. int n;
  77. if (!cgrp)
  78. return -1;
  79. /*
  80. * find corresponding event
  81. * if add cgroup N, then need to find event N
  82. */
  83. n = 0;
  84. evlist__for_each_entry(evlist, counter) {
  85. if (n == nr_cgroups)
  86. goto found;
  87. n++;
  88. }
  89. cgroup__put(cgrp);
  90. return -1;
  91. found:
  92. counter->cgrp = cgrp;
  93. return 0;
  94. }
  95. static void cgroup__delete(struct cgroup *cgroup)
  96. {
  97. if (cgroup->fd >= 0)
  98. close(cgroup->fd);
  99. zfree(&cgroup->name);
  100. free(cgroup);
  101. }
  102. void cgroup__put(struct cgroup *cgrp)
  103. {
  104. if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
  105. cgroup__delete(cgrp);
  106. }
  107. }
  108. struct cgroup *cgroup__get(struct cgroup *cgroup)
  109. {
  110. if (cgroup)
  111. refcount_inc(&cgroup->refcnt);
  112. return cgroup;
  113. }
  114. static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
  115. {
  116. if (evsel->cgrp == NULL)
  117. evsel->cgrp = cgroup__get(cgroup);
  118. }
  119. void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
  120. {
  121. struct evsel *evsel;
  122. evlist__for_each_entry(evlist, evsel)
  123. evsel__set_default_cgroup(evsel, cgroup);
  124. }
  125. int parse_cgroups(const struct option *opt, const char *str,
  126. int unset __maybe_unused)
  127. {
  128. struct evlist *evlist = *(struct evlist **)opt->value;
  129. struct evsel *counter;
  130. struct cgroup *cgrp = NULL;
  131. const char *p, *e, *eos = str + strlen(str);
  132. char *s;
  133. int ret, i;
  134. if (list_empty(&evlist->core.entries)) {
  135. fprintf(stderr, "must define events before cgroups\n");
  136. return -1;
  137. }
  138. for (;;) {
  139. p = strchr(str, ',');
  140. e = p ? p : eos;
  141. /* allow empty cgroups, i.e., skip */
  142. if (e - str) {
  143. /* termination added */
  144. s = strndup(str, e - str);
  145. if (!s)
  146. return -1;
  147. ret = add_cgroup(evlist, s);
  148. free(s);
  149. if (ret)
  150. return -1;
  151. }
  152. /* nr_cgroups is increased een for empty cgroups */
  153. nr_cgroups++;
  154. if (!p)
  155. break;
  156. str = p+1;
  157. }
  158. /* for the case one cgroup combine to multiple events */
  159. i = 0;
  160. if (nr_cgroups == 1) {
  161. evlist__for_each_entry(evlist, counter) {
  162. if (i == 0)
  163. cgrp = counter->cgrp;
  164. else {
  165. counter->cgrp = cgrp;
  166. refcount_inc(&cgrp->refcnt);
  167. }
  168. i++;
  169. }
  170. }
  171. return 0;
  172. }
  173. int evlist__expand_cgroup(struct evlist *evlist, const char *str,
  174. struct rblist *metric_events, bool open_cgroup)
  175. {
  176. struct evlist *orig_list, *tmp_list;
  177. struct evsel *pos, *evsel, *leader;
  178. struct rblist orig_metric_events;
  179. struct cgroup *cgrp = NULL;
  180. const char *p, *e, *eos = str + strlen(str);
  181. int ret = -1;
  182. if (evlist->core.nr_entries == 0) {
  183. fprintf(stderr, "must define events before cgroups\n");
  184. return -EINVAL;
  185. }
  186. orig_list = evlist__new();
  187. tmp_list = evlist__new();
  188. if (orig_list == NULL || tmp_list == NULL) {
  189. fprintf(stderr, "memory allocation failed\n");
  190. return -ENOMEM;
  191. }
  192. /* save original events and init evlist */
  193. perf_evlist__splice_list_tail(orig_list, &evlist->core.entries);
  194. evlist->core.nr_entries = 0;
  195. if (metric_events) {
  196. orig_metric_events = *metric_events;
  197. rblist__init(metric_events);
  198. } else {
  199. rblist__init(&orig_metric_events);
  200. }
  201. for (;;) {
  202. p = strchr(str, ',');
  203. e = p ? p : eos;
  204. /* allow empty cgroups, i.e., skip */
  205. if (e - str) {
  206. /* termination added */
  207. char *name = strndup(str, e - str);
  208. if (!name)
  209. goto out_err;
  210. cgrp = cgroup__new(name, open_cgroup);
  211. free(name);
  212. if (cgrp == NULL)
  213. goto out_err;
  214. } else {
  215. cgrp = NULL;
  216. }
  217. leader = NULL;
  218. evlist__for_each_entry(orig_list, pos) {
  219. evsel = evsel__clone(pos);
  220. if (evsel == NULL)
  221. goto out_err;
  222. cgroup__put(evsel->cgrp);
  223. evsel->cgrp = cgroup__get(cgrp);
  224. if (evsel__is_group_leader(pos))
  225. leader = evsel;
  226. evsel->leader = leader;
  227. evlist__add(tmp_list, evsel);
  228. }
  229. /* cgroup__new() has a refcount, release it here */
  230. cgroup__put(cgrp);
  231. nr_cgroups++;
  232. if (metric_events) {
  233. perf_stat__collect_metric_expr(tmp_list);
  234. if (metricgroup__copy_metric_events(tmp_list, cgrp,
  235. metric_events,
  236. &orig_metric_events) < 0)
  237. break;
  238. }
  239. perf_evlist__splice_list_tail(evlist, &tmp_list->core.entries);
  240. tmp_list->core.nr_entries = 0;
  241. if (!p) {
  242. ret = 0;
  243. break;
  244. }
  245. str = p+1;
  246. }
  247. out_err:
  248. evlist__delete(orig_list);
  249. evlist__delete(tmp_list);
  250. rblist__exit(&orig_metric_events);
  251. return ret;
  252. }
  253. static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
  254. bool create, const char *path)
  255. {
  256. struct rb_node **p = &root->rb_node;
  257. struct rb_node *parent = NULL;
  258. struct cgroup *cgrp;
  259. while (*p != NULL) {
  260. parent = *p;
  261. cgrp = rb_entry(parent, struct cgroup, node);
  262. if (cgrp->id == id)
  263. return cgrp;
  264. if (cgrp->id < id)
  265. p = &(*p)->rb_left;
  266. else
  267. p = &(*p)->rb_right;
  268. }
  269. if (!create)
  270. return NULL;
  271. cgrp = malloc(sizeof(*cgrp));
  272. if (cgrp == NULL)
  273. return NULL;
  274. cgrp->name = strdup(path);
  275. if (cgrp->name == NULL) {
  276. free(cgrp);
  277. return NULL;
  278. }
  279. cgrp->fd = -1;
  280. cgrp->id = id;
  281. refcount_set(&cgrp->refcnt, 1);
  282. rb_link_node(&cgrp->node, parent, p);
  283. rb_insert_color(&cgrp->node, root);
  284. return cgrp;
  285. }
  286. struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
  287. const char *path)
  288. {
  289. struct cgroup *cgrp;
  290. down_write(&env->cgroups.lock);
  291. cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
  292. up_write(&env->cgroups.lock);
  293. return cgrp;
  294. }
  295. struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
  296. {
  297. struct cgroup *cgrp;
  298. down_read(&env->cgroups.lock);
  299. cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
  300. up_read(&env->cgroups.lock);
  301. return cgrp;
  302. }
  303. void perf_env__purge_cgroups(struct perf_env *env)
  304. {
  305. struct rb_node *node;
  306. struct cgroup *cgrp;
  307. down_write(&env->cgroups.lock);
  308. while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
  309. node = rb_first(&env->cgroups.tree);
  310. cgrp = rb_entry(node, struct cgroup, node);
  311. rb_erase(node, &env->cgroups.tree);
  312. cgroup__put(cgrp);
  313. }
  314. up_write(&env->cgroups.lock);
  315. }