metricgroup.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /*
  2. * Copyright (c) 2017, Intel Corporation.
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms and conditions of the GNU General Public License,
  6. * version 2, as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope it will be useful, but WITHOUT
  9. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. * more details.
  12. *
  13. */
  14. /* Manage metrics and groups of metrics from JSON files */
  15. #include "metricgroup.h"
  16. #include "evlist.h"
  17. #include "strbuf.h"
  18. #include "pmu.h"
  19. #include "expr.h"
  20. #include "rblist.h"
  21. #include <string.h>
  22. #include <stdbool.h>
  23. #include <errno.h>
  24. #include "pmu-events/pmu-events.h"
  25. #include "strlist.h"
  26. #include <assert.h>
  27. #include <ctype.h>
  28. struct metric_event *metricgroup__lookup(struct rblist *metric_events,
  29. struct perf_evsel *evsel,
  30. bool create)
  31. {
  32. struct rb_node *nd;
  33. struct metric_event me = {
  34. .evsel = evsel
  35. };
  36. if (!metric_events)
  37. return NULL;
  38. nd = rblist__find(metric_events, &me);
  39. if (nd)
  40. return container_of(nd, struct metric_event, nd);
  41. if (create) {
  42. rblist__add_node(metric_events, &me);
  43. nd = rblist__find(metric_events, &me);
  44. if (nd)
  45. return container_of(nd, struct metric_event, nd);
  46. }
  47. return NULL;
  48. }
  49. static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
  50. {
  51. struct metric_event *a = container_of(rb_node,
  52. struct metric_event,
  53. nd);
  54. const struct metric_event *b = entry;
  55. if (a->evsel == b->evsel)
  56. return 0;
  57. if ((char *)a->evsel < (char *)b->evsel)
  58. return -1;
  59. return +1;
  60. }
  61. static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
  62. const void *entry)
  63. {
  64. struct metric_event *me = malloc(sizeof(struct metric_event));
  65. if (!me)
  66. return NULL;
  67. memcpy(me, entry, sizeof(struct metric_event));
  68. me->evsel = ((struct metric_event *)entry)->evsel;
  69. INIT_LIST_HEAD(&me->head);
  70. return &me->nd;
  71. }
  72. static void metricgroup__rblist_init(struct rblist *metric_events)
  73. {
  74. rblist__init(metric_events);
  75. metric_events->node_cmp = metric_event_cmp;
  76. metric_events->node_new = metric_event_new;
  77. }
  78. struct egroup {
  79. struct list_head nd;
  80. int idnum;
  81. const char **ids;
  82. const char *metric_name;
  83. const char *metric_expr;
  84. };
  85. static struct perf_evsel *find_evsel(struct perf_evlist *perf_evlist,
  86. const char **ids,
  87. int idnum,
  88. struct perf_evsel **metric_events)
  89. {
  90. struct perf_evsel *ev, *start = NULL;
  91. int ind = 0;
  92. evlist__for_each_entry (perf_evlist, ev) {
  93. if (!strcmp(ev->name, ids[ind])) {
  94. metric_events[ind] = ev;
  95. if (ind == 0)
  96. start = ev;
  97. if (++ind == idnum) {
  98. metric_events[ind] = NULL;
  99. return start;
  100. }
  101. } else {
  102. ind = 0;
  103. start = NULL;
  104. }
  105. }
  106. /*
  107. * This can happen when an alias expands to multiple
  108. * events, like for uncore events.
  109. * We don't support this case for now.
  110. */
  111. return NULL;
  112. }
  113. static int metricgroup__setup_events(struct list_head *groups,
  114. struct perf_evlist *perf_evlist,
  115. struct rblist *metric_events_list)
  116. {
  117. struct metric_event *me;
  118. struct metric_expr *expr;
  119. int i = 0;
  120. int ret = 0;
  121. struct egroup *eg;
  122. struct perf_evsel *evsel;
  123. list_for_each_entry (eg, groups, nd) {
  124. struct perf_evsel **metric_events;
  125. metric_events = calloc(sizeof(void *), eg->idnum + 1);
  126. if (!metric_events) {
  127. ret = -ENOMEM;
  128. break;
  129. }
  130. evsel = find_evsel(perf_evlist, eg->ids, eg->idnum,
  131. metric_events);
  132. if (!evsel) {
  133. pr_debug("Cannot resolve %s: %s\n",
  134. eg->metric_name, eg->metric_expr);
  135. continue;
  136. }
  137. for (i = 0; i < eg->idnum; i++)
  138. metric_events[i]->collect_stat = true;
  139. me = metricgroup__lookup(metric_events_list, evsel, true);
  140. if (!me) {
  141. ret = -ENOMEM;
  142. break;
  143. }
  144. expr = malloc(sizeof(struct metric_expr));
  145. if (!expr) {
  146. ret = -ENOMEM;
  147. break;
  148. }
  149. expr->metric_expr = eg->metric_expr;
  150. expr->metric_name = eg->metric_name;
  151. expr->metric_events = metric_events;
  152. list_add(&expr->nd, &me->head);
  153. }
  154. return ret;
  155. }
  156. static bool match_metric(const char *n, const char *list)
  157. {
  158. int len;
  159. char *m;
  160. if (!list)
  161. return false;
  162. if (!strcmp(list, "all"))
  163. return true;
  164. if (!n)
  165. return !strcasecmp(list, "No_group");
  166. len = strlen(list);
  167. m = strcasestr(n, list);
  168. if (!m)
  169. return false;
  170. if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
  171. (m[len] == 0 || m[len] == ';'))
  172. return true;
  173. return false;
  174. }
  175. struct mep {
  176. struct rb_node nd;
  177. const char *name;
  178. struct strlist *metrics;
  179. };
  180. static int mep_cmp(struct rb_node *rb_node, const void *entry)
  181. {
  182. struct mep *a = container_of(rb_node, struct mep, nd);
  183. struct mep *b = (struct mep *)entry;
  184. return strcmp(a->name, b->name);
  185. }
  186. static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
  187. const void *entry)
  188. {
  189. struct mep *me = malloc(sizeof(struct mep));
  190. if (!me)
  191. return NULL;
  192. memcpy(me, entry, sizeof(struct mep));
  193. me->name = strdup(me->name);
  194. if (!me->name)
  195. goto out_me;
  196. me->metrics = strlist__new(NULL, NULL);
  197. if (!me->metrics)
  198. goto out_name;
  199. return &me->nd;
  200. out_name:
  201. free((char *)me->name);
  202. out_me:
  203. free(me);
  204. return NULL;
  205. }
  206. static struct mep *mep_lookup(struct rblist *groups, const char *name)
  207. {
  208. struct rb_node *nd;
  209. struct mep me = {
  210. .name = name
  211. };
  212. nd = rblist__find(groups, &me);
  213. if (nd)
  214. return container_of(nd, struct mep, nd);
  215. rblist__add_node(groups, &me);
  216. nd = rblist__find(groups, &me);
  217. if (nd)
  218. return container_of(nd, struct mep, nd);
  219. return NULL;
  220. }
  221. static void mep_delete(struct rblist *rl __maybe_unused,
  222. struct rb_node *nd)
  223. {
  224. struct mep *me = container_of(nd, struct mep, nd);
  225. strlist__delete(me->metrics);
  226. free((void *)me->name);
  227. free(me);
  228. }
  229. static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
  230. {
  231. struct str_node *sn;
  232. int n = 0;
  233. strlist__for_each_entry (sn, metrics) {
  234. if (raw)
  235. printf("%s%s", n > 0 ? " " : "", sn->s);
  236. else
  237. printf(" %s\n", sn->s);
  238. n++;
  239. }
  240. if (raw)
  241. putchar('\n');
  242. }
  243. void metricgroup__print(bool metrics, bool metricgroups, char *filter,
  244. bool raw)
  245. {
  246. struct pmu_events_map *map = perf_pmu__find_map(NULL);
  247. struct pmu_event *pe;
  248. int i;
  249. struct rblist groups;
  250. struct rb_node *node, *next;
  251. struct strlist *metriclist = NULL;
  252. if (!map)
  253. return;
  254. if (!metricgroups) {
  255. metriclist = strlist__new(NULL, NULL);
  256. if (!metriclist)
  257. return;
  258. }
  259. rblist__init(&groups);
  260. groups.node_new = mep_new;
  261. groups.node_cmp = mep_cmp;
  262. groups.node_delete = mep_delete;
  263. for (i = 0; ; i++) {
  264. const char *g;
  265. pe = &map->table[i];
  266. if (!pe->name && !pe->metric_group && !pe->metric_name)
  267. break;
  268. if (!pe->metric_expr)
  269. continue;
  270. g = pe->metric_group;
  271. if (!g && pe->metric_name) {
  272. if (pe->name)
  273. continue;
  274. g = "No_group";
  275. }
  276. if (g) {
  277. char *omg;
  278. char *mg = strdup(g);
  279. if (!mg)
  280. return;
  281. omg = mg;
  282. while ((g = strsep(&mg, ";")) != NULL) {
  283. struct mep *me;
  284. char *s;
  285. if (*g == 0)
  286. g = "No_group";
  287. while (isspace(*g))
  288. g++;
  289. if (filter && !strstr(g, filter))
  290. continue;
  291. if (raw)
  292. s = (char *)pe->metric_name;
  293. else {
  294. if (asprintf(&s, "%s\n%*s%s]",
  295. pe->metric_name, 8, "[", pe->desc) < 0)
  296. return;
  297. }
  298. if (!s)
  299. continue;
  300. if (!metricgroups) {
  301. strlist__add(metriclist, s);
  302. } else {
  303. me = mep_lookup(&groups, g);
  304. if (!me)
  305. continue;
  306. strlist__add(me->metrics, s);
  307. }
  308. }
  309. free(omg);
  310. }
  311. }
  312. if (metricgroups && !raw)
  313. printf("\nMetric Groups:\n\n");
  314. else if (metrics && !raw)
  315. printf("\nMetrics:\n\n");
  316. for (node = rb_first(&groups.entries); node; node = next) {
  317. struct mep *me = container_of(node, struct mep, nd);
  318. if (metricgroups)
  319. printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n");
  320. if (metrics)
  321. metricgroup__print_strlist(me->metrics, raw);
  322. next = rb_next(node);
  323. rblist__remove_node(&groups, node);
  324. }
  325. if (!metricgroups)
  326. metricgroup__print_strlist(metriclist, raw);
  327. strlist__delete(metriclist);
  328. }
  329. static int metricgroup__add_metric(const char *metric, struct strbuf *events,
  330. struct list_head *group_list)
  331. {
  332. struct pmu_events_map *map = perf_pmu__find_map(NULL);
  333. struct pmu_event *pe;
  334. int ret = -EINVAL;
  335. int i, j;
  336. if (!map)
  337. return 0;
  338. for (i = 0; ; i++) {
  339. pe = &map->table[i];
  340. if (!pe->name && !pe->metric_group && !pe->metric_name)
  341. break;
  342. if (!pe->metric_expr)
  343. continue;
  344. if (match_metric(pe->metric_group, metric) ||
  345. match_metric(pe->metric_name, metric)) {
  346. const char **ids;
  347. int idnum;
  348. struct egroup *eg;
  349. pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
  350. if (expr__find_other(pe->metric_expr,
  351. NULL, &ids, &idnum) < 0)
  352. continue;
  353. if (events->len > 0)
  354. strbuf_addf(events, ",");
  355. for (j = 0; j < idnum; j++) {
  356. pr_debug("found event %s\n", ids[j]);
  357. strbuf_addf(events, "%s%s",
  358. j == 0 ? "{" : ",",
  359. ids[j]);
  360. }
  361. strbuf_addf(events, "}:W");
  362. eg = malloc(sizeof(struct egroup));
  363. if (!eg) {
  364. ret = -ENOMEM;
  365. break;
  366. }
  367. eg->ids = ids;
  368. eg->idnum = idnum;
  369. eg->metric_name = pe->metric_name;
  370. eg->metric_expr = pe->metric_expr;
  371. list_add_tail(&eg->nd, group_list);
  372. ret = 0;
  373. }
  374. }
  375. return ret;
  376. }
  377. static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
  378. struct list_head *group_list)
  379. {
  380. char *llist, *nlist, *p;
  381. int ret = -EINVAL;
  382. nlist = strdup(list);
  383. if (!nlist)
  384. return -ENOMEM;
  385. llist = nlist;
  386. strbuf_init(events, 100);
  387. strbuf_addf(events, "%s", "");
  388. while ((p = strsep(&llist, ",")) != NULL) {
  389. ret = metricgroup__add_metric(p, events, group_list);
  390. if (ret == -EINVAL) {
  391. fprintf(stderr, "Cannot find metric or group `%s'\n",
  392. p);
  393. break;
  394. }
  395. }
  396. free(nlist);
  397. return ret;
  398. }
  399. static void metricgroup__free_egroups(struct list_head *group_list)
  400. {
  401. struct egroup *eg, *egtmp;
  402. int i;
  403. list_for_each_entry_safe (eg, egtmp, group_list, nd) {
  404. for (i = 0; i < eg->idnum; i++)
  405. free((char *)eg->ids[i]);
  406. free(eg->ids);
  407. free(eg);
  408. }
  409. }
  410. int metricgroup__parse_groups(const struct option *opt,
  411. const char *str,
  412. struct rblist *metric_events)
  413. {
  414. struct parse_events_error parse_error;
  415. struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
  416. struct strbuf extra_events;
  417. LIST_HEAD(group_list);
  418. int ret;
  419. if (metric_events->nr_entries == 0)
  420. metricgroup__rblist_init(metric_events);
  421. ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
  422. if (ret)
  423. return ret;
  424. pr_debug("adding %s\n", extra_events.buf);
  425. memset(&parse_error, 0, sizeof(struct parse_events_error));
  426. ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
  427. if (ret) {
  428. parse_events_print_error(&parse_error, extra_events.buf);
  429. goto out;
  430. }
  431. strbuf_release(&extra_events);
  432. ret = metricgroup__setup_events(&group_list, perf_evlist,
  433. metric_events);
  434. out:
  435. metricgroup__free_egroups(&group_list);
  436. return ret;
  437. }
  438. bool metricgroup__has_metric(const char *metric)
  439. {
  440. struct pmu_events_map *map = perf_pmu__find_map(NULL);
  441. struct pmu_event *pe;
  442. int i;
  443. if (!map)
  444. return false;
  445. for (i = 0; ; i++) {
  446. pe = &map->table[i];
  447. if (!pe->name && !pe->metric_group && !pe->metric_name)
  448. break;
  449. if (!pe->metric_expr)
  450. continue;
  451. if (match_metric(pe->metric_name, metric))
  452. return true;
  453. }
  454. return false;
  455. }