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"
17 #include "pmu-events/pmu-events.h"
22 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
23 struct perf_evsel *evsel,
27 struct metric_event me = {
34 nd = rblist__find(metric_events, &me);
36 return container_of(nd, struct metric_event, nd);
38 rblist__add_node(metric_events, &me);
39 nd = rblist__find(metric_events, &me);
41 return container_of(nd, struct metric_event, nd);
46 static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
48 struct metric_event *a = container_of(rb_node,
51 const struct metric_event *b = entry;
53 if (a->evsel == b->evsel)
55 if ((char *)a->evsel < (char *)b->evsel)
60 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
63 struct metric_event *me = malloc(sizeof(struct metric_event));
67 memcpy(me, entry, sizeof(struct metric_event));
68 me->evsel = ((struct metric_event *)entry)->evsel;
69 INIT_LIST_HEAD(&me->head);
73 static void metricgroup__rblist_init(struct rblist *metric_events)
75 rblist__init(metric_events);
76 metric_events->node_cmp = metric_event_cmp;
77 metric_events->node_new = metric_event_new;
84 const char *metric_name;
85 const char *metric_expr;
88 static struct perf_evsel *find_evsel(struct perf_evlist *perf_evlist,
91 struct perf_evsel **metric_events)
93 struct perf_evsel *ev, *start = NULL;
96 evlist__for_each_entry (perf_evlist, ev) {
97 if (!strcmp(ev->name, ids[ind])) {
98 metric_events[ind] = ev;
101 if (++ind == idnum) {
102 metric_events[ind] = NULL;
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.
118 static int metricgroup__setup_events(struct list_head *groups,
119 struct perf_evlist *perf_evlist,
120 struct rblist *metric_events_list)
122 struct metric_event *me;
123 struct metric_expr *expr;
127 struct perf_evsel *evsel;
129 list_for_each_entry (eg, groups, nd) {
130 struct perf_evsel **metric_events;
132 metric_events = calloc(sizeof(void *), eg->idnum + 1);
133 if (!metric_events) {
137 evsel = find_evsel(perf_evlist, eg->ids, eg->idnum,
140 pr_debug("Cannot resolve %s: %s\n",
141 eg->metric_name, eg->metric_expr);
144 for (i = 0; i < eg->idnum; i++)
145 metric_events[i]->collect_stat = true;
146 me = metricgroup__lookup(metric_events_list, evsel, true);
151 expr = malloc(sizeof(struct metric_expr));
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);
164 static bool match_metric(const char *n, const char *list)
171 if (!strcmp(list, "all"))
174 return !strcasecmp(list, "No_group");
176 m = strcasestr(n, list);
179 if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
180 (m[len] == 0 || m[len] == ';'))
188 struct strlist *metrics;
191 static int mep_cmp(struct rb_node *rb_node, const void *entry)
193 struct mep *a = container_of(rb_node, struct mep, nd);
194 struct mep *b = (struct mep *)entry;
196 return strcmp(a->name, b->name);
199 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
202 struct mep *me = malloc(sizeof(struct mep));
206 memcpy(me, entry, sizeof(struct mep));
207 me->name = strdup(me->name);
210 me->metrics = strlist__new(NULL, NULL);
215 free((char *)me->name);
221 static struct mep *mep_lookup(struct rblist *groups, const char *name)
227 nd = rblist__find(groups, &me);
229 return container_of(nd, struct mep, nd);
230 rblist__add_node(groups, &me);
231 nd = rblist__find(groups, &me);
233 return container_of(nd, struct mep, nd);
237 static void mep_delete(struct rblist *rl __maybe_unused,
240 struct mep *me = container_of(nd, struct mep, nd);
242 strlist__delete(me->metrics);
243 free((void *)me->name);
247 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
252 strlist__for_each_entry (sn, metrics) {
254 printf("%s%s", n > 0 ? " " : "", sn->s);
256 printf(" %s\n", sn->s);
263 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
264 bool raw, bool details)
266 struct pmu_events_map *map = perf_pmu__find_map(NULL);
267 struct pmu_event *pe;
269 struct rblist groups;
270 struct rb_node *node, *next;
271 struct strlist *metriclist = NULL;
277 metriclist = strlist__new(NULL, NULL);
282 rblist__init(&groups);
283 groups.node_new = mep_new;
284 groups.node_cmp = mep_cmp;
285 groups.node_delete = mep_delete;
290 if (!pe->name && !pe->metric_group && !pe->metric_name)
292 if (!pe->metric_expr)
294 g = pe->metric_group;
295 if (!g && pe->metric_name) {
302 char *mg = strdup(g);
307 while ((g = strsep(&mg, ";")) != NULL) {
315 if (filter && !strstr(g, filter))
318 s = (char *)pe->metric_name;
320 if (asprintf(&s, "%s\n%*s%s]",
321 pe->metric_name, 8, "[", pe->desc) < 0)
325 if (asprintf(&s, "%s\n%*s%s]",
326 s, 8, "[", pe->metric_expr) < 0)
335 strlist__add(metriclist, s);
337 me = mep_lookup(&groups, g);
340 strlist__add(me->metrics, s);
347 if (metricgroups && !raw)
348 printf("\nMetric Groups:\n\n");
349 else if (metrics && !raw)
350 printf("\nMetrics:\n\n");
352 for (node = rb_first_cached(&groups.entries); node; node = next) {
353 struct mep *me = container_of(node, struct mep, nd);
356 printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n");
358 metricgroup__print_strlist(me->metrics, raw);
359 next = rb_next(node);
360 rblist__remove_node(&groups, node);
363 metricgroup__print_strlist(metriclist, raw);
364 strlist__delete(metriclist);
367 static int metricgroup__add_metric(const char *metric, struct strbuf *events,
368 struct list_head *group_list)
370 struct pmu_events_map *map = perf_pmu__find_map(NULL);
371 struct pmu_event *pe;
381 if (!pe->name && !pe->metric_group && !pe->metric_name)
383 if (!pe->metric_expr)
385 if (match_metric(pe->metric_group, metric) ||
386 match_metric(pe->metric_name, metric)) {
391 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
393 if (expr__find_other(pe->metric_expr,
394 NULL, &ids, &idnum) < 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",
404 strbuf_addf(events, "}:W");
406 eg = malloc(sizeof(struct egroup));
413 eg->metric_name = pe->metric_name;
414 eg->metric_expr = pe->metric_expr;
415 list_add_tail(&eg->nd, group_list);
422 static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
423 struct list_head *group_list)
425 char *llist, *nlist, *p;
428 nlist = strdup(list);
433 strbuf_init(events, 100);
434 strbuf_addf(events, "%s", "");
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",
448 static void metricgroup__free_egroups(struct list_head *group_list)
450 struct egroup *eg, *egtmp;
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]);
461 int metricgroup__parse_groups(const struct option *opt,
463 struct rblist *metric_events)
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);
471 if (metric_events->nr_entries == 0)
472 metricgroup__rblist_init(metric_events);
473 ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
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);
480 parse_events_print_error(&parse_error, extra_events.buf);
483 strbuf_release(&extra_events);
484 ret = metricgroup__setup_events(&group_list, perf_evlist,
487 metricgroup__free_egroups(&group_list);
491 bool metricgroup__has_metric(const char *metric)
493 struct pmu_events_map *map = perf_pmu__find_map(NULL);
494 struct pmu_event *pe;
503 if (!pe->name && !pe->metric_group && !pe->metric_name)
505 if (!pe->metric_expr)
507 if (match_metric(pe->metric_name, metric))