perf metricgroup: Remove duped metric group events
[linux-block.git] / tools / perf / util / metricgroup.c
CommitLineData
2025cf9e 1// SPDX-License-Identifier: GPL-2.0-only
b18f3e36
AK
2/*
3 * Copyright (c) 2017, Intel Corporation.
b18f3e36
AK
4 */
5
6/* Manage metrics and groups of metrics from JSON files */
7
8#include "metricgroup.h"
b4209025 9#include "debug.h"
b18f3e36 10#include "evlist.h"
0b8026e8 11#include "evsel.h"
b18f3e36
AK
12#include "strbuf.h"
13#include "pmu.h"
14#include "expr.h"
15#include "rblist.h"
b18f3e36 16#include <string.h>
b18f3e36
AK
17#include <errno.h>
18#include "pmu-events/pmu-events.h"
b18f3e36
AK
19#include "strlist.h"
20#include <assert.h>
bd9860bf 21#include <linux/ctype.h>
b4209025 22#include <linux/string.h>
d8f9da24 23#include <linux/zalloc.h>
0b8026e8 24#include <subcmd/parse-options.h>
ab483d8b
KL
25#include <api/fs/fs.h>
26#include "util.h"
b18f3e36
AK
27
28struct metric_event *metricgroup__lookup(struct rblist *metric_events,
32dcd021 29 struct evsel *evsel,
b18f3e36
AK
30 bool create)
31{
32 struct rb_node *nd;
33 struct metric_event me = {
34 .evsel = evsel
35 };
4bd1bef8
AK
36
37 if (!metric_events)
38 return NULL;
39
b18f3e36
AK
40 nd = rblist__find(metric_events, &me);
41 if (nd)
42 return container_of(nd, struct metric_event, nd);
43 if (create) {
44 rblist__add_node(metric_events, &me);
45 nd = rblist__find(metric_events, &me);
46 if (nd)
47 return container_of(nd, struct metric_event, nd);
48 }
49 return NULL;
50}
51
52static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
53{
54 struct metric_event *a = container_of(rb_node,
55 struct metric_event,
56 nd);
57 const struct metric_event *b = entry;
58
59 if (a->evsel == b->evsel)
60 return 0;
61 if ((char *)a->evsel < (char *)b->evsel)
62 return -1;
63 return +1;
64}
65
66static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
67 const void *entry)
68{
69 struct metric_event *me = malloc(sizeof(struct metric_event));
70
71 if (!me)
72 return NULL;
73 memcpy(me, entry, sizeof(struct metric_event));
74 me->evsel = ((struct metric_event *)entry)->evsel;
75 INIT_LIST_HEAD(&me->head);
76 return &me->nd;
77}
78
79static void metricgroup__rblist_init(struct rblist *metric_events)
80{
81 rblist__init(metric_events);
82 metric_events->node_cmp = metric_event_cmp;
83 metric_events->node_new = metric_event_new;
84}
85
86struct egroup {
87 struct list_head nd;
ded80bda 88 struct expr_parse_ctx pctx;
b18f3e36
AK
89 const char *metric_name;
90 const char *metric_expr;
287f2649 91 const char *metric_unit;
1e1a873d 92 int runtime;
7f9eca51 93 bool has_constraint;
b18f3e36
AK
94};
95
2440689d
IR
96/**
97 * Find a group of events in perf_evlist that correpond to those from a parsed
98 * metric expression.
99 * @perf_evlist: a list of events something like: {metric1 leader, metric1
100 * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling,
101 * metric2 sibling}:W,duration_time
102 * @pctx: the parse context for the metric expression.
103 * @has_constraint: is there a contraint on the group of events? In which case
104 * the events won't be grouped.
105 * @metric_events: out argument, null terminated array of evsel's associated
106 * with the metric.
107 * @evlist_used: in/out argument, bitmap tracking which evlist events are used.
108 * @return the first metric event or NULL on failure.
109 */
63503dba 110static struct evsel *find_evsel_group(struct evlist *perf_evlist,
ded80bda 111 struct expr_parse_ctx *pctx,
2440689d 112 bool has_constraint,
58fc90fd 113 struct evsel **metric_events,
45db55f2 114 unsigned long *evlist_used)
b18f3e36 115{
2440689d 116 struct evsel *ev, *current_leader = NULL;
ded80bda 117 double *val_ptr;
2440689d
IR
118 int i = 0, matched_events = 0, events_to_match;
119 const int idnum = (int)hashmap__size(&pctx->ids);
120
121 /* duration_time is grouped separately. */
122 if (!has_constraint &&
123 hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr))
124 events_to_match = idnum - 1;
125 else
126 events_to_match = idnum;
b18f3e36
AK
127
128 evlist__for_each_entry (perf_evlist, ev) {
2440689d
IR
129 /*
130 * Events with a constraint aren't grouped and match the first
131 * events available.
132 */
133 if (has_constraint && ev->weak_group)
58fc90fd 134 continue;
2440689d
IR
135 if (!has_constraint && ev->leader != current_leader) {
136 /*
137 * Start of a new group, discard the whole match and
138 * start again.
139 */
140 matched_events = 0;
58fc90fd
KJ
141 memset(metric_events, 0,
142 sizeof(struct evsel *) * idnum);
2440689d
IR
143 current_leader = ev->leader;
144 }
145 if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr))
146 metric_events[matched_events++] = ev;
147 if (matched_events == events_to_match)
148 break;
149 }
150
151 if (events_to_match != idnum) {
152 /* Add the first duration_time. */
153 evlist__for_each_entry(perf_evlist, ev) {
154 if (!strcmp(ev->name, "duration_time")) {
155 metric_events[matched_events++] = ev;
156 break;
157 }
b18f3e36
AK
158 }
159 }
f01642e4 160
2440689d 161 if (matched_events != idnum) {
f01642e4
JY
162 /* Not whole match */
163 return NULL;
164 }
165
166 metric_events[idnum] = NULL;
167
168 for (i = 0; i < idnum; i++) {
58fc90fd 169 ev = metric_events[i];
2440689d 170 ev->metric_leader = ev;
45db55f2 171 set_bit(ev->idx, evlist_used);
f01642e4
JY
172 }
173
174 return metric_events[0];
b18f3e36
AK
175}
176
177static int metricgroup__setup_events(struct list_head *groups,
63503dba 178 struct evlist *perf_evlist,
b18f3e36
AK
179 struct rblist *metric_events_list)
180{
181 struct metric_event *me;
182 struct metric_expr *expr;
183 int i = 0;
184 int ret = 0;
185 struct egroup *eg;
2440689d 186 struct evsel *evsel, *tmp;
45db55f2 187 unsigned long *evlist_used;
58fc90fd 188
45db55f2
IR
189 evlist_used = bitmap_alloc(perf_evlist->core.nr_entries);
190 if (!evlist_used)
191 return -ENOMEM;
b18f3e36
AK
192
193 list_for_each_entry (eg, groups, nd) {
32dcd021 194 struct evsel **metric_events;
b18f3e36 195
ded80bda
IR
196 metric_events = calloc(sizeof(void *),
197 hashmap__size(&eg->pctx.ids) + 1);
b18f3e36
AK
198 if (!metric_events) {
199 ret = -ENOMEM;
200 break;
201 }
2440689d
IR
202 evsel = find_evsel_group(perf_evlist, &eg->pctx,
203 eg->has_constraint, metric_events,
ded80bda 204 evlist_used);
b18f3e36
AK
205 if (!evsel) {
206 pr_debug("Cannot resolve %s: %s\n",
207 eg->metric_name, eg->metric_expr);
a159e2fe 208 free(metric_events);
b18f3e36
AK
209 continue;
210 }
ded80bda 211 for (i = 0; metric_events[i]; i++)
b18f3e36
AK
212 metric_events[i]->collect_stat = true;
213 me = metricgroup__lookup(metric_events_list, evsel, true);
214 if (!me) {
215 ret = -ENOMEM;
a159e2fe 216 free(metric_events);
b18f3e36
AK
217 break;
218 }
219 expr = malloc(sizeof(struct metric_expr));
220 if (!expr) {
221 ret = -ENOMEM;
a159e2fe 222 free(metric_events);
b18f3e36
AK
223 break;
224 }
225 expr->metric_expr = eg->metric_expr;
226 expr->metric_name = eg->metric_name;
287f2649 227 expr->metric_unit = eg->metric_unit;
b18f3e36 228 expr->metric_events = metric_events;
1e1a873d 229 expr->runtime = eg->runtime;
b18f3e36
AK
230 list_add(&expr->nd, &me->head);
231 }
58fc90fd 232
2440689d
IR
233 evlist__for_each_entry_safe(perf_evlist, tmp, evsel) {
234 if (!test_bit(evsel->idx, evlist_used)) {
235 evlist__remove(perf_evlist, evsel);
236 evsel__delete(evsel);
237 }
238 }
45db55f2 239 bitmap_free(evlist_used);
58fc90fd 240
b18f3e36
AK
241 return ret;
242}
243
244static bool match_metric(const char *n, const char *list)
245{
246 int len;
247 char *m;
248
249 if (!list)
250 return false;
251 if (!strcmp(list, "all"))
252 return true;
253 if (!n)
254 return !strcasecmp(list, "No_group");
255 len = strlen(list);
256 m = strcasestr(n, list);
257 if (!m)
258 return false;
259 if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
260 (m[len] == 0 || m[len] == ';'))
261 return true;
262 return false;
263}
264
71b0acce
AK
265struct mep {
266 struct rb_node nd;
267 const char *name;
268 struct strlist *metrics;
269};
270
271static int mep_cmp(struct rb_node *rb_node, const void *entry)
272{
273 struct mep *a = container_of(rb_node, struct mep, nd);
274 struct mep *b = (struct mep *)entry;
275
276 return strcmp(a->name, b->name);
277}
278
279static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
280 const void *entry)
281{
282 struct mep *me = malloc(sizeof(struct mep));
283
284 if (!me)
285 return NULL;
286 memcpy(me, entry, sizeof(struct mep));
287 me->name = strdup(me->name);
288 if (!me->name)
289 goto out_me;
290 me->metrics = strlist__new(NULL, NULL);
291 if (!me->metrics)
292 goto out_name;
293 return &me->nd;
294out_name:
d8f9da24 295 zfree(&me->name);
71b0acce
AK
296out_me:
297 free(me);
298 return NULL;
299}
300
301static struct mep *mep_lookup(struct rblist *groups, const char *name)
302{
303 struct rb_node *nd;
304 struct mep me = {
305 .name = name
306 };
307 nd = rblist__find(groups, &me);
308 if (nd)
309 return container_of(nd, struct mep, nd);
310 rblist__add_node(groups, &me);
311 nd = rblist__find(groups, &me);
312 if (nd)
313 return container_of(nd, struct mep, nd);
314 return NULL;
315}
316
317static void mep_delete(struct rblist *rl __maybe_unused,
318 struct rb_node *nd)
319{
320 struct mep *me = container_of(nd, struct mep, nd);
321
322 strlist__delete(me->metrics);
d8f9da24 323 zfree(&me->name);
71b0acce
AK
324 free(me);
325}
326
327static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
328{
329 struct str_node *sn;
330 int n = 0;
331
332 strlist__for_each_entry (sn, metrics) {
333 if (raw)
334 printf("%s%s", n > 0 ? " " : "", sn->s);
335 else
336 printf(" %s\n", sn->s);
337 n++;
338 }
339 if (raw)
340 putchar('\n');
341}
342
343void metricgroup__print(bool metrics, bool metricgroups, char *filter,
33bbc571 344 bool raw, bool details)
71b0acce 345{
54e32dc0 346 struct pmu_events_map *map = perf_pmu__find_map(NULL);
71b0acce
AK
347 struct pmu_event *pe;
348 int i;
349 struct rblist groups;
350 struct rb_node *node, *next;
351 struct strlist *metriclist = NULL;
352
353 if (!map)
354 return;
355
356 if (!metricgroups) {
357 metriclist = strlist__new(NULL, NULL);
358 if (!metriclist)
359 return;
360 }
361
362 rblist__init(&groups);
363 groups.node_new = mep_new;
364 groups.node_cmp = mep_cmp;
365 groups.node_delete = mep_delete;
366 for (i = 0; ; i++) {
367 const char *g;
368 pe = &map->table[i];
369
370 if (!pe->name && !pe->metric_group && !pe->metric_name)
371 break;
372 if (!pe->metric_expr)
373 continue;
374 g = pe->metric_group;
375 if (!g && pe->metric_name) {
376 if (pe->name)
377 continue;
378 g = "No_group";
379 }
380 if (g) {
381 char *omg;
382 char *mg = strdup(g);
383
384 if (!mg)
385 return;
386 omg = mg;
387 while ((g = strsep(&mg, ";")) != NULL) {
388 struct mep *me;
389 char *s;
390
80e9073f 391 g = skip_spaces(g);
71b0acce
AK
392 if (*g == 0)
393 g = "No_group";
71b0acce
AK
394 if (filter && !strstr(g, filter))
395 continue;
396 if (raw)
397 s = (char *)pe->metric_name;
398 else {
95f04328
MP
399 if (asprintf(&s, "%s\n%*s%s]",
400 pe->metric_name, 8, "[", pe->desc) < 0)
71b0acce 401 return;
33bbc571
JO
402
403 if (details) {
404 if (asprintf(&s, "%s\n%*s%s]",
405 s, 8, "[", pe->metric_expr) < 0)
406 return;
407 }
71b0acce
AK
408 }
409
410 if (!s)
411 continue;
412
413 if (!metricgroups) {
414 strlist__add(metriclist, s);
415 } else {
416 me = mep_lookup(&groups, g);
417 if (!me)
418 continue;
419 strlist__add(me->metrics, s);
420 }
421 }
422 free(omg);
423 }
424 }
425
426 if (metricgroups && !raw)
427 printf("\nMetric Groups:\n\n");
428 else if (metrics && !raw)
429 printf("\nMetrics:\n\n");
430
ca227029 431 for (node = rb_first_cached(&groups.entries); node; node = next) {
71b0acce
AK
432 struct mep *me = container_of(node, struct mep, nd);
433
434 if (metricgroups)
9c344d15 435 printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
71b0acce
AK
436 if (metrics)
437 metricgroup__print_strlist(me->metrics, raw);
438 next = rb_next(node);
439 rblist__remove_node(&groups, node);
440 }
441 if (!metricgroups)
442 metricgroup__print_strlist(metriclist, raw);
443 strlist__delete(metriclist);
444}
445
f742634a 446static void metricgroup__add_metric_weak_group(struct strbuf *events,
ded80bda 447 struct expr_parse_ctx *ctx)
f742634a 448{
ded80bda 449 struct hashmap_entry *cur;
4e21c13a
IR
450 size_t bkt;
451 bool no_group = true, has_duration = false;
f742634a 452
ded80bda
IR
453 hashmap__for_each_entry((&ctx->ids), cur, bkt) {
454 pr_debug("found event %s\n", (const char *)cur->key);
f742634a
KL
455 /*
456 * Duration time maps to a software event and can make
457 * groups not count. Always use it outside a
458 * group.
459 */
ded80bda 460 if (!strcmp(cur->key, "duration_time")) {
4e21c13a 461 has_duration = true;
f742634a
KL
462 continue;
463 }
464 strbuf_addf(events, "%s%s",
4e21c13a 465 no_group ? "{" : ",",
ded80bda 466 (const char *)cur->key);
f742634a
KL
467 no_group = false;
468 }
4e21c13a 469 if (!no_group) {
f742634a 470 strbuf_addf(events, "}:W");
4e21c13a
IR
471 if (has_duration)
472 strbuf_addf(events, ",duration_time");
473 } else if (has_duration)
474 strbuf_addf(events, "duration_time");
f742634a
KL
475}
476
ab483d8b 477static void metricgroup__add_metric_non_group(struct strbuf *events,
ded80bda 478 struct expr_parse_ctx *ctx)
ab483d8b 479{
ded80bda
IR
480 struct hashmap_entry *cur;
481 size_t bkt;
ab483d8b 482
ded80bda
IR
483 hashmap__for_each_entry((&ctx->ids), cur, bkt)
484 strbuf_addf(events, ",%s", (const char *)cur->key);
ab483d8b
KL
485}
486
487static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
488{
489 static bool violate_nmi_constraint;
490
491 if (!foot) {
492 pr_warning("Splitting metric group %s into standalone metrics.\n", name);
493 violate_nmi_constraint = true;
494 return;
495 }
496
497 if (!violate_nmi_constraint)
498 return;
499
500 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
501 " echo 0 > /proc/sys/kernel/nmi_watchdog\n"
502 " perf stat ...\n"
503 " echo 1 > /proc/sys/kernel/nmi_watchdog\n");
504}
505
506static bool metricgroup__has_constraint(struct pmu_event *pe)
507{
508 if (!pe->metric_constraint)
509 return false;
510
511 if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
512 sysctl__nmi_watchdog_enabled()) {
513 metricgroup___watchdog_constraint_hint(pe->metric_name, false);
514 return true;
515 }
516
517 return false;
518}
519
1e1a873d
KJ
520int __weak arch_get_runtimeparam(void)
521{
522 return 1;
523}
524
7f9eca51
IR
525static int __metricgroup__add_metric(struct list_head *group_list,
526 struct pmu_event *pe, int runtime)
47352aba 527{
47352aba
KJ
528 struct egroup *eg;
529
47352aba
KJ
530 eg = malloc(sizeof(*eg));
531 if (!eg)
532 return -ENOMEM;
533
ded80bda 534 expr__ctx_init(&eg->pctx);
47352aba
KJ
535 eg->metric_name = pe->metric_name;
536 eg->metric_expr = pe->metric_expr;
537 eg->metric_unit = pe->unit;
1e1a873d 538 eg->runtime = runtime;
7f9eca51 539 eg->has_constraint = metricgroup__has_constraint(pe);
ded80bda
IR
540
541 if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) {
542 expr__ctx_clear(&eg->pctx);
543 free(eg);
544 return -EINVAL;
545 }
546
6bf2102b
IR
547 if (list_empty(group_list))
548 list_add(&eg->nd, group_list);
549 else {
550 struct list_head *pos;
551
552 /* Place the largest groups at the front. */
553 list_for_each_prev(pos, group_list) {
554 struct egroup *old = list_entry(pos, struct egroup, nd);
555
556 if (hashmap__size(&eg->pctx.ids) <=
557 hashmap__size(&old->pctx.ids))
558 break;
559 }
560 list_add(&eg->nd, pos);
561 }
47352aba
KJ
562
563 return 0;
564}
565
b18f3e36
AK
566static int metricgroup__add_metric(const char *metric, struct strbuf *events,
567 struct list_head *group_list)
568{
54e32dc0 569 struct pmu_events_map *map = perf_pmu__find_map(NULL);
b18f3e36 570 struct pmu_event *pe;
7f9eca51 571 struct egroup *eg;
90810399
IR
572 int i, ret;
573 bool has_match = false;
b18f3e36 574
b18f3e36
AK
575 if (!map)
576 return 0;
577
578 for (i = 0; ; i++) {
579 pe = &map->table[i];
580
90810399
IR
581 if (!pe->name && !pe->metric_group && !pe->metric_name) {
582 /* End of pmu events. */
583 if (!has_match)
584 return -EINVAL;
b18f3e36 585 break;
90810399 586 }
b18f3e36
AK
587 if (!pe->metric_expr)
588 continue;
589 if (match_metric(pe->metric_group, metric) ||
590 match_metric(pe->metric_name, metric)) {
90810399 591 has_match = true;
b18f3e36
AK
592 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
593
1e1a873d 594 if (!strstr(pe->metric_expr, "?")) {
7f9eca51
IR
595 ret = __metricgroup__add_metric(group_list,
596 pe, 1);
90810399
IR
597 if (ret)
598 return ret;
1e1a873d
KJ
599 } else {
600 int j, count;
601
602 count = arch_get_runtimeparam();
603
604 /* This loop is added to create multiple
605 * events depend on count value and add
606 * those events to group_list.
607 */
608
90810399 609 for (j = 0; j < count; j++) {
7f9eca51
IR
610 ret = __metricgroup__add_metric(
611 group_list, pe, j);
90810399
IR
612 if (ret)
613 return ret;
614 }
1e1a873d 615 }
b18f3e36
AK
616 }
617 }
7f9eca51
IR
618 list_for_each_entry(eg, group_list, nd) {
619 if (events->len > 0)
620 strbuf_addf(events, ",");
621
622 if (eg->has_constraint) {
623 metricgroup__add_metric_non_group(events,
624 &eg->pctx);
625 } else {
626 metricgroup__add_metric_weak_group(events,
627 &eg->pctx);
628 }
629 }
90810399 630 return 0;
b18f3e36
AK
631}
632
633static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
634 struct list_head *group_list)
635{
636 char *llist, *nlist, *p;
637 int ret = -EINVAL;
638
639 nlist = strdup(list);
640 if (!nlist)
641 return -ENOMEM;
642 llist = nlist;
411bc316
AK
643
644 strbuf_init(events, 100);
645 strbuf_addf(events, "%s", "");
646
b18f3e36
AK
647 while ((p = strsep(&llist, ",")) != NULL) {
648 ret = metricgroup__add_metric(p, events, group_list);
649 if (ret == -EINVAL) {
650 fprintf(stderr, "Cannot find metric or group `%s'\n",
651 p);
652 break;
653 }
654 }
655 free(nlist);
ab483d8b
KL
656
657 if (!ret)
658 metricgroup___watchdog_constraint_hint(NULL, true);
659
b18f3e36
AK
660 return ret;
661}
662
663static void metricgroup__free_egroups(struct list_head *group_list)
664{
665 struct egroup *eg, *egtmp;
b18f3e36
AK
666
667 list_for_each_entry_safe (eg, egtmp, group_list, nd) {
ded80bda 668 expr__ctx_clear(&eg->pctx);
acc7bfb3 669 list_del_init(&eg->nd);
b18f3e36
AK
670 free(eg);
671 }
672}
673
674int metricgroup__parse_groups(const struct option *opt,
675 const char *str,
676 struct rblist *metric_events)
677{
678 struct parse_events_error parse_error;
63503dba 679 struct evlist *perf_evlist = *(struct evlist **)opt->value;
b18f3e36
AK
680 struct strbuf extra_events;
681 LIST_HEAD(group_list);
682 int ret;
683
684 if (metric_events->nr_entries == 0)
685 metricgroup__rblist_init(metric_events);
686 ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
687 if (ret)
688 return ret;
689 pr_debug("adding %s\n", extra_events.buf);
a910e466 690 bzero(&parse_error, sizeof(parse_error));
b18f3e36
AK
691 ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
692 if (ret) {
333b5665 693 parse_events_print_error(&parse_error, extra_events.buf);
b18f3e36
AK
694 goto out;
695 }
696 strbuf_release(&extra_events);
697 ret = metricgroup__setup_events(&group_list, perf_evlist,
698 metric_events);
699out:
700 metricgroup__free_egroups(&group_list);
701 return ret;
702}
742d92ff
TR
703
704bool metricgroup__has_metric(const char *metric)
705{
706 struct pmu_events_map *map = perf_pmu__find_map(NULL);
707 struct pmu_event *pe;
708 int i;
709
710 if (!map)
711 return false;
712
713 for (i = 0; ; i++) {
714 pe = &map->table[i];
715
716 if (!pe->name && !pe->metric_group && !pe->metric_name)
717 break;
718 if (!pe->metric_expr)
719 continue;
720 if (match_metric(pe->metric_name, metric))
721 return true;
722 }
723 return false;
724}