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