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