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