1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017, Intel Corporation.
6 /* Manage metrics and groups of metrics from JSON files */
8 #include "metricgroup.h"
18 #include "pmu-events/pmu-events.h"
21 #include <linux/ctype.h>
22 #include <linux/string.h>
23 #include <linux/zalloc.h>
24 #include <subcmd/parse-options.h>
26 struct metric_event
*metricgroup__lookup(struct rblist
*metric_events
,
31 struct metric_event me
= {
38 nd
= rblist__find(metric_events
, &me
);
40 return container_of(nd
, struct metric_event
, nd
);
42 rblist__add_node(metric_events
, &me
);
43 nd
= rblist__find(metric_events
, &me
);
45 return container_of(nd
, struct metric_event
, nd
);
50 static int metric_event_cmp(struct rb_node
*rb_node
, const void *entry
)
52 struct metric_event
*a
= container_of(rb_node
,
55 const struct metric_event
*b
= entry
;
57 if (a
->evsel
== b
->evsel
)
59 if ((char *)a
->evsel
< (char *)b
->evsel
)
64 static struct rb_node
*metric_event_new(struct rblist
*rblist __maybe_unused
,
67 struct metric_event
*me
= malloc(sizeof(struct metric_event
));
71 memcpy(me
, entry
, sizeof(struct metric_event
));
72 me
->evsel
= ((struct metric_event
*)entry
)->evsel
;
73 INIT_LIST_HEAD(&me
->head
);
77 static void metricgroup__rblist_init(struct rblist
*metric_events
)
79 rblist__init(metric_events
);
80 metric_events
->node_cmp
= metric_event_cmp
;
81 metric_events
->node_new
= metric_event_new
;
88 const char *metric_name
;
89 const char *metric_expr
;
90 const char *metric_unit
;
93 static struct evsel
*find_evsel_group(struct evlist
*perf_evlist
,
96 struct evsel
**metric_events
)
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
;
110 if (i
+ 1 == idnum
) {
111 /* Discard the whole match and start again */
113 memset(metric_events
, 0,
114 sizeof(struct evsel
*) * idnum
);
118 if (!strcmp(ev
->name
, ids
[i
]))
119 metric_events
[i
] = ev
;
121 /* Discard the whole match and start again */
123 memset(metric_events
, 0,
124 sizeof(struct evsel
*) * idnum
);
131 /* Not whole match */
135 metric_events
[idnum
] = NULL
;
137 for (i
= 0; i
< idnum
; i
++) {
138 leader_found
= false;
139 evlist__for_each_entry(perf_evlist
, ev
) {
140 if (!leader_found
&& (ev
== metric_events
[i
]))
144 !strcmp(ev
->name
, metric_events
[i
]->name
)) {
145 ev
->metric_leader
= metric_events
[i
];
150 return metric_events
[0];
153 static int metricgroup__setup_events(struct list_head
*groups
,
154 struct evlist
*perf_evlist
,
155 struct rblist
*metric_events_list
)
157 struct metric_event
*me
;
158 struct metric_expr
*expr
;
164 list_for_each_entry (eg
, groups
, nd
) {
165 struct evsel
**metric_events
;
167 metric_events
= calloc(sizeof(void *), eg
->idnum
+ 1);
168 if (!metric_events
) {
172 evsel
= find_evsel_group(perf_evlist
, eg
->ids
, eg
->idnum
,
175 pr_debug("Cannot resolve %s: %s\n",
176 eg
->metric_name
, eg
->metric_expr
);
179 for (i
= 0; i
< eg
->idnum
; i
++)
180 metric_events
[i
]->collect_stat
= true;
181 me
= metricgroup__lookup(metric_events_list
, evsel
, true);
186 expr
= malloc(sizeof(struct metric_expr
));
191 expr
->metric_expr
= eg
->metric_expr
;
192 expr
->metric_name
= eg
->metric_name
;
193 expr
->metric_unit
= eg
->metric_unit
;
194 expr
->metric_events
= metric_events
;
195 list_add(&expr
->nd
, &me
->head
);
200 static bool match_metric(const char *n
, const char *list
)
207 if (!strcmp(list
, "all"))
210 return !strcasecmp(list
, "No_group");
212 m
= strcasestr(n
, list
);
215 if ((m
== n
|| m
[-1] == ';' || m
[-1] == ' ') &&
216 (m
[len
] == 0 || m
[len
] == ';'))
224 struct strlist
*metrics
;
227 static int mep_cmp(struct rb_node
*rb_node
, const void *entry
)
229 struct mep
*a
= container_of(rb_node
, struct mep
, nd
);
230 struct mep
*b
= (struct mep
*)entry
;
232 return strcmp(a
->name
, b
->name
);
235 static struct rb_node
*mep_new(struct rblist
*rl __maybe_unused
,
238 struct mep
*me
= malloc(sizeof(struct mep
));
242 memcpy(me
, entry
, sizeof(struct mep
));
243 me
->name
= strdup(me
->name
);
246 me
->metrics
= strlist__new(NULL
, NULL
);
257 static struct mep
*mep_lookup(struct rblist
*groups
, const char *name
)
263 nd
= rblist__find(groups
, &me
);
265 return container_of(nd
, struct mep
, nd
);
266 rblist__add_node(groups
, &me
);
267 nd
= rblist__find(groups
, &me
);
269 return container_of(nd
, struct mep
, nd
);
273 static void mep_delete(struct rblist
*rl __maybe_unused
,
276 struct mep
*me
= container_of(nd
, struct mep
, nd
);
278 strlist__delete(me
->metrics
);
283 static void metricgroup__print_strlist(struct strlist
*metrics
, bool raw
)
288 strlist__for_each_entry (sn
, metrics
) {
290 printf("%s%s", n
> 0 ? " " : "", sn
->s
);
292 printf(" %s\n", sn
->s
);
299 void metricgroup__print(bool metrics
, bool metricgroups
, char *filter
,
300 bool raw
, bool details
)
302 struct pmu_events_map
*map
= perf_pmu__find_map(NULL
);
303 struct pmu_event
*pe
;
305 struct rblist groups
;
306 struct rb_node
*node
, *next
;
307 struct strlist
*metriclist
= NULL
;
313 metriclist
= strlist__new(NULL
, NULL
);
318 rblist__init(&groups
);
319 groups
.node_new
= mep_new
;
320 groups
.node_cmp
= mep_cmp
;
321 groups
.node_delete
= mep_delete
;
326 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
328 if (!pe
->metric_expr
)
330 g
= pe
->metric_group
;
331 if (!g
&& pe
->metric_name
) {
338 char *mg
= strdup(g
);
343 while ((g
= strsep(&mg
, ";")) != NULL
) {
350 if (filter
&& !strstr(g
, filter
))
353 s
= (char *)pe
->metric_name
;
355 if (asprintf(&s
, "%s\n%*s%s]",
356 pe
->metric_name
, 8, "[", pe
->desc
) < 0)
360 if (asprintf(&s
, "%s\n%*s%s]",
361 s
, 8, "[", pe
->metric_expr
) < 0)
370 strlist__add(metriclist
, s
);
372 me
= mep_lookup(&groups
, g
);
375 strlist__add(me
->metrics
, s
);
382 if (metricgroups
&& !raw
)
383 printf("\nMetric Groups:\n\n");
384 else if (metrics
&& !raw
)
385 printf("\nMetrics:\n\n");
387 for (node
= rb_first_cached(&groups
.entries
); node
; node
= next
) {
388 struct mep
*me
= container_of(node
, struct mep
, nd
);
391 printf("%s%s%s", me
->name
, metrics
&& !raw
? ":" : "", raw
? " " : "\n");
393 metricgroup__print_strlist(me
->metrics
, raw
);
394 next
= rb_next(node
);
395 rblist__remove_node(&groups
, node
);
398 metricgroup__print_strlist(metriclist
, raw
);
399 strlist__delete(metriclist
);
402 static int metricgroup__add_metric(const char *metric
, struct strbuf
*events
,
403 struct list_head
*group_list
)
405 struct pmu_events_map
*map
= perf_pmu__find_map(NULL
);
406 struct pmu_event
*pe
;
416 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
418 if (!pe
->metric_expr
)
420 if (match_metric(pe
->metric_group
, metric
) ||
421 match_metric(pe
->metric_name
, metric
)) {
425 bool no_group
= false;
427 pr_debug("metric expr %s for %s\n", pe
->metric_expr
, pe
->metric_name
);
429 if (expr__find_other(pe
->metric_expr
,
430 NULL
, &ids
, &idnum
) < 0)
433 strbuf_addf(events
, ",");
434 for (j
= 0; j
< idnum
; j
++) {
435 pr_debug("found event %s\n", ids
[j
]);
437 * Duration time maps to a software event and can make
438 * groups not count. Always use it outside a
441 if (!strcmp(ids
[j
], "duration_time")) {
443 strbuf_addf(events
, "}:W,");
444 strbuf_addf(events
, "duration_time");
448 strbuf_addf(events
, "%s%s",
449 j
== 0 || no_group
? "{" : ",",
454 strbuf_addf(events
, "}:W");
456 eg
= malloc(sizeof(struct egroup
));
463 eg
->metric_name
= pe
->metric_name
;
464 eg
->metric_expr
= pe
->metric_expr
;
465 eg
->metric_unit
= pe
->unit
;
466 list_add_tail(&eg
->nd
, group_list
);
473 static int metricgroup__add_metric_list(const char *list
, struct strbuf
*events
,
474 struct list_head
*group_list
)
476 char *llist
, *nlist
, *p
;
479 nlist
= strdup(list
);
484 strbuf_init(events
, 100);
485 strbuf_addf(events
, "%s", "");
487 while ((p
= strsep(&llist
, ",")) != NULL
) {
488 ret
= metricgroup__add_metric(p
, events
, group_list
);
489 if (ret
== -EINVAL
) {
490 fprintf(stderr
, "Cannot find metric or group `%s'\n",
499 static void metricgroup__free_egroups(struct list_head
*group_list
)
501 struct egroup
*eg
, *egtmp
;
504 list_for_each_entry_safe (eg
, egtmp
, group_list
, nd
) {
505 for (i
= 0; i
< eg
->idnum
; i
++)
508 list_del_init(&eg
->nd
);
513 int metricgroup__parse_groups(const struct option
*opt
,
515 struct rblist
*metric_events
)
517 struct parse_events_error parse_error
;
518 struct evlist
*perf_evlist
= *(struct evlist
**)opt
->value
;
519 struct strbuf extra_events
;
520 LIST_HEAD(group_list
);
523 if (metric_events
->nr_entries
== 0)
524 metricgroup__rblist_init(metric_events
);
525 ret
= metricgroup__add_metric_list(str
, &extra_events
, &group_list
);
528 pr_debug("adding %s\n", extra_events
.buf
);
529 bzero(&parse_error
, sizeof(parse_error
));
530 ret
= parse_events(perf_evlist
, extra_events
.buf
, &parse_error
);
532 parse_events_print_error(&parse_error
, extra_events
.buf
);
535 strbuf_release(&extra_events
);
536 ret
= metricgroup__setup_events(&group_list
, perf_evlist
,
539 metricgroup__free_egroups(&group_list
);
543 bool metricgroup__has_metric(const char *metric
)
545 struct pmu_events_map
*map
= perf_pmu__find_map(NULL
);
546 struct pmu_event
*pe
;
555 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
557 if (!pe
->metric_expr
)
559 if (match_metric(pe
->metric_name
, metric
))