perf expr: Migrate expr ids table to a hashmap
[linux-2.6-microblaze.git] / tools / perf / util / metricgroup.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2017, Intel Corporation.
4  */
5
6 /* Manage metrics and groups of metrics from JSON files */
7
8 #include "metricgroup.h"
9 #include "debug.h"
10 #include "evlist.h"
11 #include "evsel.h"
12 #include "strbuf.h"
13 #include "pmu.h"
14 #include "expr.h"
15 #include "rblist.h"
16 #include <string.h>
17 #include <errno.h>
18 #include "pmu-events/pmu-events.h"
19 #include "strlist.h"
20 #include <assert.h>
21 #include <linux/ctype.h>
22 #include <linux/string.h>
23 #include <linux/zalloc.h>
24 #include <subcmd/parse-options.h>
25 #include <api/fs/fs.h>
26 #include "util.h"
27
28 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
29                                          struct evsel *evsel,
30                                          bool create)
31 {
32         struct rb_node *nd;
33         struct metric_event me = {
34                 .evsel = evsel
35         };
36
37         if (!metric_events)
38                 return NULL;
39
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
52 static 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
66 static 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
79 static 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
86 struct egroup {
87         struct list_head nd;
88         struct expr_parse_ctx pctx;
89         const char *metric_name;
90         const char *metric_expr;
91         const char *metric_unit;
92         int runtime;
93 };
94
95 static struct evsel *find_evsel_group(struct evlist *perf_evlist,
96                                       struct expr_parse_ctx *pctx,
97                                       struct evsel **metric_events,
98                                       bool *evlist_used)
99 {
100         struct evsel *ev;
101         bool leader_found;
102         const size_t idnum = hashmap__size(&pctx->ids);
103         size_t i = 0;
104         int j = 0;
105         double *val_ptr;
106
107         evlist__for_each_entry (perf_evlist, ev) {
108                 if (evlist_used[j++])
109                         continue;
110                 if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) {
111                         if (!metric_events[i])
112                                 metric_events[i] = ev;
113                         i++;
114                         if (i == idnum)
115                                 break;
116                 } else {
117                         /* Discard the whole match and start again */
118                         i = 0;
119                         memset(metric_events, 0,
120                                 sizeof(struct evsel *) * idnum);
121                 }
122         }
123
124         if (i != idnum) {
125                 /* Not whole match */
126                 return NULL;
127         }
128
129         metric_events[idnum] = NULL;
130
131         for (i = 0; i < idnum; i++) {
132                 leader_found = false;
133                 evlist__for_each_entry(perf_evlist, ev) {
134                         if (!leader_found && (ev == metric_events[i]))
135                                 leader_found = true;
136
137                         if (leader_found &&
138                             !strcmp(ev->name, metric_events[i]->name)) {
139                                 ev->metric_leader = metric_events[i];
140                         }
141                         j++;
142                 }
143                 ev = metric_events[i];
144                 evlist_used[ev->idx] = true;
145         }
146
147         return metric_events[0];
148 }
149
150 static int metricgroup__setup_events(struct list_head *groups,
151                                      struct evlist *perf_evlist,
152                                      struct rblist *metric_events_list)
153 {
154         struct metric_event *me;
155         struct metric_expr *expr;
156         int i = 0;
157         int ret = 0;
158         struct egroup *eg;
159         struct evsel *evsel;
160         bool *evlist_used;
161
162         evlist_used = calloc(perf_evlist->core.nr_entries, sizeof(bool));
163         if (!evlist_used) {
164                 ret = -ENOMEM;
165                 return ret;
166         }
167
168         list_for_each_entry (eg, groups, nd) {
169                 struct evsel **metric_events;
170
171                 metric_events = calloc(sizeof(void *),
172                                 hashmap__size(&eg->pctx.ids) + 1);
173                 if (!metric_events) {
174                         ret = -ENOMEM;
175                         break;
176                 }
177                 evsel = find_evsel_group(perf_evlist, &eg->pctx, metric_events,
178                                         evlist_used);
179                 if (!evsel) {
180                         pr_debug("Cannot resolve %s: %s\n",
181                                         eg->metric_name, eg->metric_expr);
182                         continue;
183                 }
184                 for (i = 0; metric_events[i]; i++)
185                         metric_events[i]->collect_stat = true;
186                 me = metricgroup__lookup(metric_events_list, evsel, true);
187                 if (!me) {
188                         ret = -ENOMEM;
189                         break;
190                 }
191                 expr = malloc(sizeof(struct metric_expr));
192                 if (!expr) {
193                         ret = -ENOMEM;
194                         break;
195                 }
196                 expr->metric_expr = eg->metric_expr;
197                 expr->metric_name = eg->metric_name;
198                 expr->metric_unit = eg->metric_unit;
199                 expr->metric_events = metric_events;
200                 expr->runtime = eg->runtime;
201                 list_add(&expr->nd, &me->head);
202         }
203
204         free(evlist_used);
205
206         return ret;
207 }
208
209 static bool match_metric(const char *n, const char *list)
210 {
211         int len;
212         char *m;
213
214         if (!list)
215                 return false;
216         if (!strcmp(list, "all"))
217                 return true;
218         if (!n)
219                 return !strcasecmp(list, "No_group");
220         len = strlen(list);
221         m = strcasestr(n, list);
222         if (!m)
223                 return false;
224         if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
225             (m[len] == 0 || m[len] == ';'))
226                 return true;
227         return false;
228 }
229
230 struct mep {
231         struct rb_node nd;
232         const char *name;
233         struct strlist *metrics;
234 };
235
236 static int mep_cmp(struct rb_node *rb_node, const void *entry)
237 {
238         struct mep *a = container_of(rb_node, struct mep, nd);
239         struct mep *b = (struct mep *)entry;
240
241         return strcmp(a->name, b->name);
242 }
243
244 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
245                                         const void *entry)
246 {
247         struct mep *me = malloc(sizeof(struct mep));
248
249         if (!me)
250                 return NULL;
251         memcpy(me, entry, sizeof(struct mep));
252         me->name = strdup(me->name);
253         if (!me->name)
254                 goto out_me;
255         me->metrics = strlist__new(NULL, NULL);
256         if (!me->metrics)
257                 goto out_name;
258         return &me->nd;
259 out_name:
260         zfree(&me->name);
261 out_me:
262         free(me);
263         return NULL;
264 }
265
266 static struct mep *mep_lookup(struct rblist *groups, const char *name)
267 {
268         struct rb_node *nd;
269         struct mep me = {
270                 .name = name
271         };
272         nd = rblist__find(groups, &me);
273         if (nd)
274                 return container_of(nd, struct mep, nd);
275         rblist__add_node(groups, &me);
276         nd = rblist__find(groups, &me);
277         if (nd)
278                 return container_of(nd, struct mep, nd);
279         return NULL;
280 }
281
282 static void mep_delete(struct rblist *rl __maybe_unused,
283                        struct rb_node *nd)
284 {
285         struct mep *me = container_of(nd, struct mep, nd);
286
287         strlist__delete(me->metrics);
288         zfree(&me->name);
289         free(me);
290 }
291
292 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
293 {
294         struct str_node *sn;
295         int n = 0;
296
297         strlist__for_each_entry (sn, metrics) {
298                 if (raw)
299                         printf("%s%s", n > 0 ? " " : "", sn->s);
300                 else
301                         printf("  %s\n", sn->s);
302                 n++;
303         }
304         if (raw)
305                 putchar('\n');
306 }
307
308 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
309                         bool raw, bool details)
310 {
311         struct pmu_events_map *map = perf_pmu__find_map(NULL);
312         struct pmu_event *pe;
313         int i;
314         struct rblist groups;
315         struct rb_node *node, *next;
316         struct strlist *metriclist = NULL;
317
318         if (!map)
319                 return;
320
321         if (!metricgroups) {
322                 metriclist = strlist__new(NULL, NULL);
323                 if (!metriclist)
324                         return;
325         }
326
327         rblist__init(&groups);
328         groups.node_new = mep_new;
329         groups.node_cmp = mep_cmp;
330         groups.node_delete = mep_delete;
331         for (i = 0; ; i++) {
332                 const char *g;
333                 pe = &map->table[i];
334
335                 if (!pe->name && !pe->metric_group && !pe->metric_name)
336                         break;
337                 if (!pe->metric_expr)
338                         continue;
339                 g = pe->metric_group;
340                 if (!g && pe->metric_name) {
341                         if (pe->name)
342                                 continue;
343                         g = "No_group";
344                 }
345                 if (g) {
346                         char *omg;
347                         char *mg = strdup(g);
348
349                         if (!mg)
350                                 return;
351                         omg = mg;
352                         while ((g = strsep(&mg, ";")) != NULL) {
353                                 struct mep *me;
354                                 char *s;
355
356                                 g = skip_spaces(g);
357                                 if (*g == 0)
358                                         g = "No_group";
359                                 if (filter && !strstr(g, filter))
360                                         continue;
361                                 if (raw)
362                                         s = (char *)pe->metric_name;
363                                 else {
364                                         if (asprintf(&s, "%s\n%*s%s]",
365                                                      pe->metric_name, 8, "[", pe->desc) < 0)
366                                                 return;
367
368                                         if (details) {
369                                                 if (asprintf(&s, "%s\n%*s%s]",
370                                                              s, 8, "[", pe->metric_expr) < 0)
371                                                         return;
372                                         }
373                                 }
374
375                                 if (!s)
376                                         continue;
377
378                                 if (!metricgroups) {
379                                         strlist__add(metriclist, s);
380                                 } else {
381                                         me = mep_lookup(&groups, g);
382                                         if (!me)
383                                                 continue;
384                                         strlist__add(me->metrics, s);
385                                 }
386                         }
387                         free(omg);
388                 }
389         }
390
391         if (metricgroups && !raw)
392                 printf("\nMetric Groups:\n\n");
393         else if (metrics && !raw)
394                 printf("\nMetrics:\n\n");
395
396         for (node = rb_first_cached(&groups.entries); node; node = next) {
397                 struct mep *me = container_of(node, struct mep, nd);
398
399                 if (metricgroups)
400                         printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
401                 if (metrics)
402                         metricgroup__print_strlist(me->metrics, raw);
403                 next = rb_next(node);
404                 rblist__remove_node(&groups, node);
405         }
406         if (!metricgroups)
407                 metricgroup__print_strlist(metriclist, raw);
408         strlist__delete(metriclist);
409 }
410
411 static void metricgroup__add_metric_weak_group(struct strbuf *events,
412                                                struct expr_parse_ctx *ctx)
413 {
414         struct hashmap_entry *cur;
415         size_t bkt, i = 0;
416         bool no_group = false;
417
418         hashmap__for_each_entry((&ctx->ids), cur, bkt) {
419                 pr_debug("found event %s\n", (const char *)cur->key);
420                 /*
421                  * Duration time maps to a software event and can make
422                  * groups not count. Always use it outside a
423                  * group.
424                  */
425                 if (!strcmp(cur->key, "duration_time")) {
426                         if (i > 0)
427                                 strbuf_addf(events, "}:W,");
428                         strbuf_addf(events, "duration_time");
429                         no_group = true;
430                         continue;
431                 }
432                 strbuf_addf(events, "%s%s",
433                         i == 0 || no_group ? "{" : ",",
434                         (const char *)cur->key);
435                 no_group = false;
436                 i++;
437         }
438         if (!no_group)
439                 strbuf_addf(events, "}:W");
440 }
441
442 static void metricgroup__add_metric_non_group(struct strbuf *events,
443                                               struct expr_parse_ctx *ctx)
444 {
445         struct hashmap_entry *cur;
446         size_t bkt;
447
448         hashmap__for_each_entry((&ctx->ids), cur, bkt)
449                 strbuf_addf(events, ",%s", (const char *)cur->key);
450 }
451
452 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
453 {
454         static bool violate_nmi_constraint;
455
456         if (!foot) {
457                 pr_warning("Splitting metric group %s into standalone metrics.\n", name);
458                 violate_nmi_constraint = true;
459                 return;
460         }
461
462         if (!violate_nmi_constraint)
463                 return;
464
465         pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
466                    "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
467                    "    perf stat ...\n"
468                    "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
469 }
470
471 static bool metricgroup__has_constraint(struct pmu_event *pe)
472 {
473         if (!pe->metric_constraint)
474                 return false;
475
476         if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
477             sysctl__nmi_watchdog_enabled()) {
478                 metricgroup___watchdog_constraint_hint(pe->metric_name, false);
479                 return true;
480         }
481
482         return false;
483 }
484
485 int __weak arch_get_runtimeparam(void)
486 {
487         return 1;
488 }
489
490 static int __metricgroup__add_metric(struct strbuf *events,
491                 struct list_head *group_list, struct pmu_event *pe, int runtime)
492 {
493         struct egroup *eg;
494
495         eg = malloc(sizeof(*eg));
496         if (!eg)
497                 return -ENOMEM;
498
499         expr__ctx_init(&eg->pctx);
500         eg->metric_name = pe->metric_name;
501         eg->metric_expr = pe->metric_expr;
502         eg->metric_unit = pe->unit;
503         eg->runtime = runtime;
504
505         if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) {
506                 expr__ctx_clear(&eg->pctx);
507                 free(eg);
508                 return -EINVAL;
509         }
510
511         if (events->len > 0)
512                 strbuf_addf(events, ",");
513
514         if (metricgroup__has_constraint(pe))
515                 metricgroup__add_metric_non_group(events, &eg->pctx);
516         else
517                 metricgroup__add_metric_weak_group(events, &eg->pctx);
518
519         list_add_tail(&eg->nd, group_list);
520
521         return 0;
522 }
523
524 static int metricgroup__add_metric(const char *metric, struct strbuf *events,
525                                    struct list_head *group_list)
526 {
527         struct pmu_events_map *map = perf_pmu__find_map(NULL);
528         struct pmu_event *pe;
529         int i, ret = -EINVAL;
530
531         if (!map)
532                 return 0;
533
534         for (i = 0; ; i++) {
535                 pe = &map->table[i];
536
537                 if (!pe->name && !pe->metric_group && !pe->metric_name)
538                         break;
539                 if (!pe->metric_expr)
540                         continue;
541                 if (match_metric(pe->metric_group, metric) ||
542                     match_metric(pe->metric_name, metric)) {
543
544                         pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
545
546                         if (!strstr(pe->metric_expr, "?")) {
547                                 ret = __metricgroup__add_metric(events, group_list, pe, 1);
548                         } else {
549                                 int j, count;
550
551                                 count = arch_get_runtimeparam();
552
553                                 /* This loop is added to create multiple
554                                  * events depend on count value and add
555                                  * those events to group_list.
556                                  */
557
558                                 for (j = 0; j < count; j++)
559                                         ret = __metricgroup__add_metric(events, group_list, pe, j);
560                         }
561                         if (ret == -ENOMEM)
562                                 break;
563                 }
564         }
565         return ret;
566 }
567
568 static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
569                                         struct list_head *group_list)
570 {
571         char *llist, *nlist, *p;
572         int ret = -EINVAL;
573
574         nlist = strdup(list);
575         if (!nlist)
576                 return -ENOMEM;
577         llist = nlist;
578
579         strbuf_init(events, 100);
580         strbuf_addf(events, "%s", "");
581
582         while ((p = strsep(&llist, ",")) != NULL) {
583                 ret = metricgroup__add_metric(p, events, group_list);
584                 if (ret == -EINVAL) {
585                         fprintf(stderr, "Cannot find metric or group `%s'\n",
586                                         p);
587                         break;
588                 }
589         }
590         free(nlist);
591
592         if (!ret)
593                 metricgroup___watchdog_constraint_hint(NULL, true);
594
595         return ret;
596 }
597
598 static void metricgroup__free_egroups(struct list_head *group_list)
599 {
600         struct egroup *eg, *egtmp;
601
602         list_for_each_entry_safe (eg, egtmp, group_list, nd) {
603                 expr__ctx_clear(&eg->pctx);
604                 list_del_init(&eg->nd);
605                 free(eg);
606         }
607 }
608
609 int metricgroup__parse_groups(const struct option *opt,
610                            const char *str,
611                            struct rblist *metric_events)
612 {
613         struct parse_events_error parse_error;
614         struct evlist *perf_evlist = *(struct evlist **)opt->value;
615         struct strbuf extra_events;
616         LIST_HEAD(group_list);
617         int ret;
618
619         if (metric_events->nr_entries == 0)
620                 metricgroup__rblist_init(metric_events);
621         ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
622         if (ret)
623                 return ret;
624         pr_debug("adding %s\n", extra_events.buf);
625         bzero(&parse_error, sizeof(parse_error));
626         ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
627         if (ret) {
628                 parse_events_print_error(&parse_error, extra_events.buf);
629                 goto out;
630         }
631         strbuf_release(&extra_events);
632         ret = metricgroup__setup_events(&group_list, perf_evlist,
633                                         metric_events);
634 out:
635         metricgroup__free_egroups(&group_list);
636         return ret;
637 }
638
639 bool metricgroup__has_metric(const char *metric)
640 {
641         struct pmu_events_map *map = perf_pmu__find_map(NULL);
642         struct pmu_event *pe;
643         int i;
644
645         if (!map)
646                 return false;
647
648         for (i = 0; ; i++) {
649                 pe = &map->table[i];
650
651                 if (!pe->name && !pe->metric_group && !pe->metric_name)
652                         break;
653                 if (!pe->metric_expr)
654                         continue;
655                 if (match_metric(pe->metric_name, metric))
656                         return true;
657         }
658         return false;
659 }