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