perf tools metric: Don't include duration_time in group
[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 "evlist.h"
10 #include "strbuf.h"
11 #include "pmu.h"
12 #include "expr.h"
13 #include "rblist.h"
14 #include <string.h>
15 #include <stdbool.h>
16 #include <errno.h>
17 #include "pmu-events/pmu-events.h"
18 #include "strlist.h"
19 #include <assert.h>
20 #include <linux/ctype.h>
21
22 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
23                                          struct perf_evsel *evsel,
24                                          bool create)
25 {
26         struct rb_node *nd;
27         struct metric_event me = {
28                 .evsel = evsel
29         };
30
31         if (!metric_events)
32                 return NULL;
33
34         nd = rblist__find(metric_events, &me);
35         if (nd)
36                 return container_of(nd, struct metric_event, nd);
37         if (create) {
38                 rblist__add_node(metric_events, &me);
39                 nd = rblist__find(metric_events, &me);
40                 if (nd)
41                         return container_of(nd, struct metric_event, nd);
42         }
43         return NULL;
44 }
45
46 static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
47 {
48         struct metric_event *a = container_of(rb_node,
49                                               struct metric_event,
50                                               nd);
51         const struct metric_event *b = entry;
52
53         if (a->evsel == b->evsel)
54                 return 0;
55         if ((char *)a->evsel < (char *)b->evsel)
56                 return -1;
57         return +1;
58 }
59
60 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
61                                         const void *entry)
62 {
63         struct metric_event *me = malloc(sizeof(struct metric_event));
64
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
73 static void metricgroup__rblist_init(struct rblist *metric_events)
74 {
75         rblist__init(metric_events);
76         metric_events->node_cmp = metric_event_cmp;
77         metric_events->node_new = metric_event_new;
78 }
79
80 struct egroup {
81         struct list_head nd;
82         int idnum;
83         const char **ids;
84         const char *metric_name;
85         const char *metric_expr;
86 };
87
88 static bool record_evsel(int *ind, struct perf_evsel **start,
89                          int idnum,
90                          struct perf_evsel **metric_events,
91                          struct perf_evsel *ev)
92 {
93         metric_events[*ind] = ev;
94         if (*ind == 0)
95                 *start = ev;
96         if (++*ind == idnum) {
97                 metric_events[*ind] = NULL;
98                 return true;
99         }
100         return false;
101 }
102
103 static struct perf_evsel *find_evsel_group(struct perf_evlist *perf_evlist,
104                                            const char **ids,
105                                            int idnum,
106                                            struct perf_evsel **metric_events)
107 {
108         struct perf_evsel *ev, *start = NULL;
109         int ind = 0;
110
111         evlist__for_each_entry (perf_evlist, ev) {
112                 if (ev->collect_stat)
113                         continue;
114                 if (!strcmp(ev->name, ids[ind])) {
115                         if (record_evsel(&ind, &start, idnum,
116                                          metric_events, ev))
117                                 return start;
118                 } else {
119                         /*
120                          * We saw some other event that is not
121                          * in our list of events. Discard
122                          * the whole match and start again.
123                          */
124                         ind = 0;
125                         start = NULL;
126                         if (!strcmp(ev->name, ids[ind])) {
127                                 if (record_evsel(&ind, &start, idnum,
128                                                  metric_events, ev))
129                                         return start;
130                         }
131                 }
132         }
133         /*
134          * This can happen when an alias expands to multiple
135          * events, like for uncore events.
136          * We don't support this case for now.
137          */
138         return NULL;
139 }
140
141 static int metricgroup__setup_events(struct list_head *groups,
142                                      struct perf_evlist *perf_evlist,
143                                      struct rblist *metric_events_list)
144 {
145         struct metric_event *me;
146         struct metric_expr *expr;
147         int i = 0;
148         int ret = 0;
149         struct egroup *eg;
150         struct perf_evsel *evsel;
151
152         list_for_each_entry (eg, groups, nd) {
153                 struct perf_evsel **metric_events;
154
155                 metric_events = calloc(sizeof(void *), eg->idnum + 1);
156                 if (!metric_events) {
157                         ret = -ENOMEM;
158                         break;
159                 }
160                 evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum,
161                                          metric_events);
162                 if (!evsel) {
163                         pr_debug("Cannot resolve %s: %s\n",
164                                         eg->metric_name, eg->metric_expr);
165                         continue;
166                 }
167                 for (i = 0; i < eg->idnum; i++)
168                         metric_events[i]->collect_stat = true;
169                 me = metricgroup__lookup(metric_events_list, evsel, true);
170                 if (!me) {
171                         ret = -ENOMEM;
172                         break;
173                 }
174                 expr = malloc(sizeof(struct metric_expr));
175                 if (!expr) {
176                         ret = -ENOMEM;
177                         break;
178                 }
179                 expr->metric_expr = eg->metric_expr;
180                 expr->metric_name = eg->metric_name;
181                 expr->metric_events = metric_events;
182                 list_add(&expr->nd, &me->head);
183         }
184         return ret;
185 }
186
187 static bool match_metric(const char *n, const char *list)
188 {
189         int len;
190         char *m;
191
192         if (!list)
193                 return false;
194         if (!strcmp(list, "all"))
195                 return true;
196         if (!n)
197                 return !strcasecmp(list, "No_group");
198         len = strlen(list);
199         m = strcasestr(n, list);
200         if (!m)
201                 return false;
202         if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
203             (m[len] == 0 || m[len] == ';'))
204                 return true;
205         return false;
206 }
207
208 struct mep {
209         struct rb_node nd;
210         const char *name;
211         struct strlist *metrics;
212 };
213
214 static int mep_cmp(struct rb_node *rb_node, const void *entry)
215 {
216         struct mep *a = container_of(rb_node, struct mep, nd);
217         struct mep *b = (struct mep *)entry;
218
219         return strcmp(a->name, b->name);
220 }
221
222 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
223                                         const void *entry)
224 {
225         struct mep *me = malloc(sizeof(struct mep));
226
227         if (!me)
228                 return NULL;
229         memcpy(me, entry, sizeof(struct mep));
230         me->name = strdup(me->name);
231         if (!me->name)
232                 goto out_me;
233         me->metrics = strlist__new(NULL, NULL);
234         if (!me->metrics)
235                 goto out_name;
236         return &me->nd;
237 out_name:
238         free((char *)me->name);
239 out_me:
240         free(me);
241         return NULL;
242 }
243
244 static struct mep *mep_lookup(struct rblist *groups, const char *name)
245 {
246         struct rb_node *nd;
247         struct mep me = {
248                 .name = name
249         };
250         nd = rblist__find(groups, &me);
251         if (nd)
252                 return container_of(nd, struct mep, nd);
253         rblist__add_node(groups, &me);
254         nd = rblist__find(groups, &me);
255         if (nd)
256                 return container_of(nd, struct mep, nd);
257         return NULL;
258 }
259
260 static void mep_delete(struct rblist *rl __maybe_unused,
261                        struct rb_node *nd)
262 {
263         struct mep *me = container_of(nd, struct mep, nd);
264
265         strlist__delete(me->metrics);
266         free((void *)me->name);
267         free(me);
268 }
269
270 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
271 {
272         struct str_node *sn;
273         int n = 0;
274
275         strlist__for_each_entry (sn, metrics) {
276                 if (raw)
277                         printf("%s%s", n > 0 ? " " : "", sn->s);
278                 else
279                         printf("  %s\n", sn->s);
280                 n++;
281         }
282         if (raw)
283                 putchar('\n');
284 }
285
286 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
287                         bool raw, bool details)
288 {
289         struct pmu_events_map *map = perf_pmu__find_map(NULL);
290         struct pmu_event *pe;
291         int i;
292         struct rblist groups;
293         struct rb_node *node, *next;
294         struct strlist *metriclist = NULL;
295
296         if (!map)
297                 return;
298
299         if (!metricgroups) {
300                 metriclist = strlist__new(NULL, NULL);
301                 if (!metriclist)
302                         return;
303         }
304
305         rblist__init(&groups);
306         groups.node_new = mep_new;
307         groups.node_cmp = mep_cmp;
308         groups.node_delete = mep_delete;
309         for (i = 0; ; i++) {
310                 const char *g;
311                 pe = &map->table[i];
312
313                 if (!pe->name && !pe->metric_group && !pe->metric_name)
314                         break;
315                 if (!pe->metric_expr)
316                         continue;
317                 g = pe->metric_group;
318                 if (!g && pe->metric_name) {
319                         if (pe->name)
320                                 continue;
321                         g = "No_group";
322                 }
323                 if (g) {
324                         char *omg;
325                         char *mg = strdup(g);
326
327                         if (!mg)
328                                 return;
329                         omg = mg;
330                         while ((g = strsep(&mg, ";")) != NULL) {
331                                 struct mep *me;
332                                 char *s;
333
334                                 g = skip_spaces(g);
335                                 if (*g == 0)
336                                         g = "No_group";
337                                 if (filter && !strstr(g, filter))
338                                         continue;
339                                 if (raw)
340                                         s = (char *)pe->metric_name;
341                                 else {
342                                         if (asprintf(&s, "%s\n%*s%s]",
343                                                      pe->metric_name, 8, "[", pe->desc) < 0)
344                                                 return;
345
346                                         if (details) {
347                                                 if (asprintf(&s, "%s\n%*s%s]",
348                                                              s, 8, "[", pe->metric_expr) < 0)
349                                                         return;
350                                         }
351                                 }
352
353                                 if (!s)
354                                         continue;
355
356                                 if (!metricgroups) {
357                                         strlist__add(metriclist, s);
358                                 } else {
359                                         me = mep_lookup(&groups, g);
360                                         if (!me)
361                                                 continue;
362                                         strlist__add(me->metrics, s);
363                                 }
364                         }
365                         free(omg);
366                 }
367         }
368
369         if (metricgroups && !raw)
370                 printf("\nMetric Groups:\n\n");
371         else if (metrics && !raw)
372                 printf("\nMetrics:\n\n");
373
374         for (node = rb_first_cached(&groups.entries); node; node = next) {
375                 struct mep *me = container_of(node, struct mep, nd);
376
377                 if (metricgroups)
378                         printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
379                 if (metrics)
380                         metricgroup__print_strlist(me->metrics, raw);
381                 next = rb_next(node);
382                 rblist__remove_node(&groups, node);
383         }
384         if (!metricgroups)
385                 metricgroup__print_strlist(metriclist, raw);
386         strlist__delete(metriclist);
387 }
388
389 static int metricgroup__add_metric(const char *metric, struct strbuf *events,
390                                    struct list_head *group_list)
391 {
392         struct pmu_events_map *map = perf_pmu__find_map(NULL);
393         struct pmu_event *pe;
394         int ret = -EINVAL;
395         int i, j;
396
397         if (!map)
398                 return 0;
399
400         for (i = 0; ; i++) {
401                 pe = &map->table[i];
402
403                 if (!pe->name && !pe->metric_group && !pe->metric_name)
404                         break;
405                 if (!pe->metric_expr)
406                         continue;
407                 if (match_metric(pe->metric_group, metric) ||
408                     match_metric(pe->metric_name, metric)) {
409                         const char **ids;
410                         int idnum;
411                         struct egroup *eg;
412                         bool no_group = false;
413
414                         pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
415
416                         if (expr__find_other(pe->metric_expr,
417                                              NULL, &ids, &idnum) < 0)
418                                 continue;
419                         if (events->len > 0)
420                                 strbuf_addf(events, ",");
421                         for (j = 0; j < idnum; j++) {
422                                 pr_debug("found event %s\n", ids[j]);
423                                 /*
424                                  * Duration time maps to a software event and can make
425                                  * groups not count. Always use it outside a
426                                  * group.
427                                  */
428                                 if (!strcmp(ids[j], "duration_time")) {
429                                         if (j > 0)
430                                                 strbuf_addf(events, "}:W,");
431                                         strbuf_addf(events, "duration_time");
432                                         no_group = true;
433                                         continue;
434                                 }
435                                 strbuf_addf(events, "%s%s",
436                                         j == 0 || no_group ? "{" : ",",
437                                         ids[j]);
438                                 no_group = false;
439                         }
440                         if (!no_group)
441                                 strbuf_addf(events, "}:W");
442
443                         eg = malloc(sizeof(struct egroup));
444                         if (!eg) {
445                                 ret = -ENOMEM;
446                                 break;
447                         }
448                         eg->ids = ids;
449                         eg->idnum = idnum;
450                         eg->metric_name = pe->metric_name;
451                         eg->metric_expr = pe->metric_expr;
452                         list_add_tail(&eg->nd, group_list);
453                         ret = 0;
454                 }
455         }
456         return ret;
457 }
458
459 static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
460                                         struct list_head *group_list)
461 {
462         char *llist, *nlist, *p;
463         int ret = -EINVAL;
464
465         nlist = strdup(list);
466         if (!nlist)
467                 return -ENOMEM;
468         llist = nlist;
469
470         strbuf_init(events, 100);
471         strbuf_addf(events, "%s", "");
472
473         while ((p = strsep(&llist, ",")) != NULL) {
474                 ret = metricgroup__add_metric(p, events, group_list);
475                 if (ret == -EINVAL) {
476                         fprintf(stderr, "Cannot find metric or group `%s'\n",
477                                         p);
478                         break;
479                 }
480         }
481         free(nlist);
482         return ret;
483 }
484
485 static void metricgroup__free_egroups(struct list_head *group_list)
486 {
487         struct egroup *eg, *egtmp;
488         int i;
489
490         list_for_each_entry_safe (eg, egtmp, group_list, nd) {
491                 for (i = 0; i < eg->idnum; i++)
492                         free((char *)eg->ids[i]);
493                 free(eg->ids);
494                 free(eg);
495         }
496 }
497
498 int metricgroup__parse_groups(const struct option *opt,
499                            const char *str,
500                            struct rblist *metric_events)
501 {
502         struct parse_events_error parse_error;
503         struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
504         struct strbuf extra_events;
505         LIST_HEAD(group_list);
506         int ret;
507
508         if (metric_events->nr_entries == 0)
509                 metricgroup__rblist_init(metric_events);
510         ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
511         if (ret)
512                 return ret;
513         pr_debug("adding %s\n", extra_events.buf);
514         memset(&parse_error, 0, sizeof(struct parse_events_error));
515         ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
516         if (ret) {
517                 parse_events_print_error(&parse_error, extra_events.buf);
518                 goto out;
519         }
520         strbuf_release(&extra_events);
521         ret = metricgroup__setup_events(&group_list, perf_evlist,
522                                         metric_events);
523 out:
524         metricgroup__free_egroups(&group_list);
525         return ret;
526 }
527
528 bool metricgroup__has_metric(const char *metric)
529 {
530         struct pmu_events_map *map = perf_pmu__find_map(NULL);
531         struct pmu_event *pe;
532         int i;
533
534         if (!map)
535                 return false;
536
537         for (i = 0; ; i++) {
538                 pe = &map->table[i];
539
540                 if (!pe->name && !pe->metric_group && !pe->metric_name)
541                         break;
542                 if (!pe->metric_expr)
543                         continue;
544                 if (match_metric(pe->metric_name, metric))
545                         return true;
546         }
547         return false;
548 }