PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/tools/perf/util/cgroup.c

https://github.com/kvaneesh/linux
C | 577 lines | 447 code | 102 blank | 28 comment | 94 complexity | 5411ff0c9cf9727f7e02a2f722877260 MD5 | raw file
  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 <sys/statfs.h>
  13. #include <fcntl.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <api/fs/fs.h>
  17. #include <ftw.h>
  18. #include <regex.h>
  19. int nr_cgroups;
  20. bool cgrp_event_expanded;
  21. /* used to match cgroup name with patterns */
  22. struct cgroup_name {
  23. struct list_head list;
  24. bool used;
  25. char name[];
  26. };
  27. static LIST_HEAD(cgroup_list);
  28. static int open_cgroup(const char *name)
  29. {
  30. char path[PATH_MAX + 1];
  31. char mnt[PATH_MAX + 1];
  32. int fd;
  33. if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
  34. return -1;
  35. scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
  36. fd = open(path, O_RDONLY);
  37. if (fd == -1)
  38. fprintf(stderr, "no access to cgroup %s\n", path);
  39. return fd;
  40. }
  41. #ifdef HAVE_FILE_HANDLE
  42. int read_cgroup_id(struct cgroup *cgrp)
  43. {
  44. char path[PATH_MAX + 1];
  45. char mnt[PATH_MAX + 1];
  46. struct {
  47. struct file_handle fh;
  48. uint64_t cgroup_id;
  49. } handle;
  50. int mount_id;
  51. if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
  52. return -1;
  53. scnprintf(path, PATH_MAX, "%s/%s", mnt, cgrp->name);
  54. handle.fh.handle_bytes = sizeof(handle.cgroup_id);
  55. if (name_to_handle_at(AT_FDCWD, path, &handle.fh, &mount_id, 0) < 0)
  56. return -1;
  57. cgrp->id = handle.cgroup_id;
  58. return 0;
  59. }
  60. #endif /* HAVE_FILE_HANDLE */
  61. #ifndef CGROUP2_SUPER_MAGIC
  62. #define CGROUP2_SUPER_MAGIC 0x63677270
  63. #endif
  64. int cgroup_is_v2(const char *subsys)
  65. {
  66. char mnt[PATH_MAX + 1];
  67. struct statfs stbuf;
  68. if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, subsys))
  69. return -1;
  70. if (statfs(mnt, &stbuf) < 0)
  71. return -1;
  72. return (stbuf.f_type == CGROUP2_SUPER_MAGIC);
  73. }
  74. static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
  75. {
  76. struct 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, bool do_open)
  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. if (do_open) {
  97. cgroup->fd = open_cgroup(name);
  98. if (cgroup->fd == -1)
  99. goto out_free_name;
  100. } else {
  101. cgroup->fd = -1;
  102. }
  103. }
  104. return cgroup;
  105. out_free_name:
  106. zfree(&cgroup->name);
  107. out_err:
  108. free(cgroup);
  109. return NULL;
  110. }
  111. struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
  112. {
  113. struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
  114. return cgroup ?: cgroup__new(name, true);
  115. }
  116. static int add_cgroup(struct evlist *evlist, const char *str)
  117. {
  118. struct evsel *counter;
  119. struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
  120. int n;
  121. if (!cgrp)
  122. return -1;
  123. /*
  124. * find corresponding event
  125. * if add cgroup N, then need to find event N
  126. */
  127. n = 0;
  128. evlist__for_each_entry(evlist, counter) {
  129. if (n == nr_cgroups)
  130. goto found;
  131. n++;
  132. }
  133. cgroup__put(cgrp);
  134. return -1;
  135. found:
  136. counter->cgrp = cgrp;
  137. return 0;
  138. }
  139. static void cgroup__delete(struct cgroup *cgroup)
  140. {
  141. if (cgroup->fd >= 0)
  142. close(cgroup->fd);
  143. zfree(&cgroup->name);
  144. free(cgroup);
  145. }
  146. void cgroup__put(struct cgroup *cgrp)
  147. {
  148. if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
  149. cgroup__delete(cgrp);
  150. }
  151. }
  152. struct cgroup *cgroup__get(struct cgroup *cgroup)
  153. {
  154. if (cgroup)
  155. refcount_inc(&cgroup->refcnt);
  156. return cgroup;
  157. }
  158. static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
  159. {
  160. if (evsel->cgrp == NULL)
  161. evsel->cgrp = cgroup__get(cgroup);
  162. }
  163. void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
  164. {
  165. struct evsel *evsel;
  166. evlist__for_each_entry(evlist, evsel)
  167. evsel__set_default_cgroup(evsel, cgroup);
  168. }
  169. /* helper function for ftw() in match_cgroups and list_cgroups */
  170. static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unused,
  171. int typeflag, struct FTW *ftwbuf __maybe_unused)
  172. {
  173. struct cgroup_name *cn;
  174. if (typeflag != FTW_D)
  175. return 0;
  176. cn = malloc(sizeof(*cn) + strlen(fpath) + 1);
  177. if (cn == NULL)
  178. return -1;
  179. cn->used = false;
  180. strcpy(cn->name, fpath);
  181. list_add_tail(&cn->list, &cgroup_list);
  182. return 0;
  183. }
  184. static void release_cgroup_list(void)
  185. {
  186. struct cgroup_name *cn;
  187. while (!list_empty(&cgroup_list)) {
  188. cn = list_first_entry(&cgroup_list, struct cgroup_name, list);
  189. list_del(&cn->list);
  190. free(cn);
  191. }
  192. }
  193. /* collect given cgroups only */
  194. static int list_cgroups(const char *str)
  195. {
  196. const char *p, *e, *eos = str + strlen(str);
  197. struct cgroup_name *cn;
  198. char *s;
  199. /* use given name as is - for testing purpose */
  200. for (;;) {
  201. p = strchr(str, ',');
  202. e = p ? p : eos;
  203. if (e - str) {
  204. int ret;
  205. s = strndup(str, e - str);
  206. if (!s)
  207. return -1;
  208. /* pretend if it's added by ftw() */
  209. ret = add_cgroup_name(s, NULL, FTW_D, NULL);
  210. free(s);
  211. if (ret)
  212. return -1;
  213. } else {
  214. if (add_cgroup_name("", NULL, FTW_D, NULL) < 0)
  215. return -1;
  216. }
  217. if (!p)
  218. break;
  219. str = p+1;
  220. }
  221. /* these groups will be used */
  222. list_for_each_entry(cn, &cgroup_list, list)
  223. cn->used = true;
  224. return 0;
  225. }
  226. /* collect all cgroups first and then match with the pattern */
  227. static int match_cgroups(const char *str)
  228. {
  229. char mnt[PATH_MAX];
  230. const char *p, *e, *eos = str + strlen(str);
  231. struct cgroup_name *cn;
  232. regex_t reg;
  233. int prefix_len;
  234. char *s;
  235. if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event"))
  236. return -1;
  237. /* cgroup_name will have a full path, skip the root directory */
  238. prefix_len = strlen(mnt);
  239. /* collect all cgroups in the cgroup_list */
  240. if (nftw(mnt, add_cgroup_name, 20, 0) < 0)
  241. return -1;
  242. for (;;) {
  243. p = strchr(str, ',');
  244. e = p ? p : eos;
  245. /* allow empty cgroups, i.e., skip */
  246. if (e - str) {
  247. /* termination added */
  248. s = strndup(str, e - str);
  249. if (!s)
  250. return -1;
  251. if (regcomp(&reg, s, REG_NOSUB)) {
  252. free(s);
  253. return -1;
  254. }
  255. /* check cgroup name with the pattern */
  256. list_for_each_entry(cn, &cgroup_list, list) {
  257. char *name = cn->name + prefix_len;
  258. if (name[0] == '/' && name[1])
  259. name++;
  260. if (!regexec(&reg, name, 0, NULL, 0))
  261. cn->used = true;
  262. }
  263. regfree(&reg);
  264. free(s);
  265. } else {
  266. /* first entry to root cgroup */
  267. cn = list_first_entry(&cgroup_list, struct cgroup_name,
  268. list);
  269. cn->used = true;
  270. }
  271. if (!p)
  272. break;
  273. str = p+1;
  274. }
  275. return prefix_len;
  276. }
  277. int parse_cgroups(const struct option *opt, const char *str,
  278. int unset __maybe_unused)
  279. {
  280. struct evlist *evlist = *(struct evlist **)opt->value;
  281. struct evsel *counter;
  282. struct cgroup *cgrp = NULL;
  283. const char *p, *e, *eos = str + strlen(str);
  284. char *s;
  285. int ret, i;
  286. if (list_empty(&evlist->core.entries)) {
  287. fprintf(stderr, "must define events before cgroups\n");
  288. return -1;
  289. }
  290. for (;;) {
  291. p = strchr(str, ',');
  292. e = p ? p : eos;
  293. /* allow empty cgroups, i.e., skip */
  294. if (e - str) {
  295. /* termination added */
  296. s = strndup(str, e - str);
  297. if (!s)
  298. return -1;
  299. ret = add_cgroup(evlist, s);
  300. free(s);
  301. if (ret)
  302. return -1;
  303. }
  304. /* nr_cgroups is increased een for empty cgroups */
  305. nr_cgroups++;
  306. if (!p)
  307. break;
  308. str = p+1;
  309. }
  310. /* for the case one cgroup combine to multiple events */
  311. i = 0;
  312. if (nr_cgroups == 1) {
  313. evlist__for_each_entry(evlist, counter) {
  314. if (i == 0)
  315. cgrp = counter->cgrp;
  316. else {
  317. counter->cgrp = cgrp;
  318. refcount_inc(&cgrp->refcnt);
  319. }
  320. i++;
  321. }
  322. }
  323. return 0;
  324. }
  325. static bool has_pattern_string(const char *str)
  326. {
  327. return !!strpbrk(str, "{}[]()|*+?^$");
  328. }
  329. int evlist__expand_cgroup(struct evlist *evlist, const char *str,
  330. struct rblist *metric_events, bool open_cgroup)
  331. {
  332. struct evlist *orig_list, *tmp_list;
  333. struct evsel *pos, *evsel, *leader;
  334. struct rblist orig_metric_events;
  335. struct cgroup *cgrp = NULL;
  336. struct cgroup_name *cn;
  337. int ret = -1;
  338. int prefix_len;
  339. if (evlist->core.nr_entries == 0) {
  340. fprintf(stderr, "must define events before cgroups\n");
  341. return -EINVAL;
  342. }
  343. orig_list = evlist__new();
  344. tmp_list = evlist__new();
  345. if (orig_list == NULL || tmp_list == NULL) {
  346. fprintf(stderr, "memory allocation failed\n");
  347. return -ENOMEM;
  348. }
  349. /* save original events and init evlist */
  350. evlist__splice_list_tail(orig_list, &evlist->core.entries);
  351. evlist->core.nr_entries = 0;
  352. if (metric_events) {
  353. orig_metric_events = *metric_events;
  354. rblist__init(metric_events);
  355. } else {
  356. rblist__init(&orig_metric_events);
  357. }
  358. if (has_pattern_string(str))
  359. prefix_len = match_cgroups(str);
  360. else
  361. prefix_len = list_cgroups(str);
  362. if (prefix_len < 0)
  363. goto out_err;
  364. list_for_each_entry(cn, &cgroup_list, list) {
  365. char *name;
  366. if (!cn->used)
  367. continue;
  368. /* cgroup_name might have a full path, skip the prefix */
  369. name = cn->name + prefix_len;
  370. if (name[0] == '/' && name[1])
  371. name++;
  372. cgrp = cgroup__new(name, open_cgroup);
  373. if (cgrp == NULL)
  374. goto out_err;
  375. leader = NULL;
  376. evlist__for_each_entry(orig_list, pos) {
  377. evsel = evsel__clone(pos);
  378. if (evsel == NULL)
  379. goto out_err;
  380. cgroup__put(evsel->cgrp);
  381. evsel->cgrp = cgroup__get(cgrp);
  382. if (evsel__is_group_leader(pos))
  383. leader = evsel;
  384. evsel__set_leader(evsel, leader);
  385. evlist__add(tmp_list, evsel);
  386. }
  387. /* cgroup__new() has a refcount, release it here */
  388. cgroup__put(cgrp);
  389. nr_cgroups++;
  390. if (metric_events) {
  391. perf_stat__collect_metric_expr(tmp_list);
  392. if (metricgroup__copy_metric_events(tmp_list, cgrp,
  393. metric_events,
  394. &orig_metric_events) < 0)
  395. goto out_err;
  396. }
  397. evlist__splice_list_tail(evlist, &tmp_list->core.entries);
  398. tmp_list->core.nr_entries = 0;
  399. }
  400. if (list_empty(&evlist->core.entries)) {
  401. fprintf(stderr, "no cgroup matched: %s\n", str);
  402. goto out_err;
  403. }
  404. ret = 0;
  405. cgrp_event_expanded = true;
  406. out_err:
  407. evlist__delete(orig_list);
  408. evlist__delete(tmp_list);
  409. rblist__exit(&orig_metric_events);
  410. release_cgroup_list();
  411. return ret;
  412. }
  413. static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
  414. bool create, const char *path)
  415. {
  416. struct rb_node **p = &root->rb_node;
  417. struct rb_node *parent = NULL;
  418. struct cgroup *cgrp;
  419. while (*p != NULL) {
  420. parent = *p;
  421. cgrp = rb_entry(parent, struct cgroup, node);
  422. if (cgrp->id == id)
  423. return cgrp;
  424. if (cgrp->id < id)
  425. p = &(*p)->rb_left;
  426. else
  427. p = &(*p)->rb_right;
  428. }
  429. if (!create)
  430. return NULL;
  431. cgrp = malloc(sizeof(*cgrp));
  432. if (cgrp == NULL)
  433. return NULL;
  434. cgrp->name = strdup(path);
  435. if (cgrp->name == NULL) {
  436. free(cgrp);
  437. return NULL;
  438. }
  439. cgrp->fd = -1;
  440. cgrp->id = id;
  441. refcount_set(&cgrp->refcnt, 1);
  442. rb_link_node(&cgrp->node, parent, p);
  443. rb_insert_color(&cgrp->node, root);
  444. return cgrp;
  445. }
  446. struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
  447. const char *path)
  448. {
  449. struct cgroup *cgrp;
  450. down_write(&env->cgroups.lock);
  451. cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
  452. up_write(&env->cgroups.lock);
  453. return cgrp;
  454. }
  455. struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
  456. {
  457. struct cgroup *cgrp;
  458. down_read(&env->cgroups.lock);
  459. cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
  460. up_read(&env->cgroups.lock);
  461. return cgrp;
  462. }
  463. void perf_env__purge_cgroups(struct perf_env *env)
  464. {
  465. struct rb_node *node;
  466. struct cgroup *cgrp;
  467. down_write(&env->cgroups.lock);
  468. while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
  469. node = rb_first(&env->cgroups.tree);
  470. cgrp = rb_entry(node, struct cgroup, node);
  471. rb_erase(node, &env->cgroups.tree);
  472. cgroup__put(cgrp);
  473. }
  474. up_write(&env->cgroups.lock);
  475. }