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 "pmu-hybrid.h"
15 #include "print-events.h"
16 #include "expr.h"
17 #include "rblist.h"
18 #include <string.h>
19 #include <errno.h>
20 #include "strlist.h"
21 #include <assert.h>
22 #include <linux/ctype.h>
23 #include <linux/list_sort.h>
24 #include <linux/string.h>
25 #include <linux/zalloc.h>
26 #include <perf/cpumap.h>
27 #include <subcmd/parse-options.h>
28 #include <api/fs/fs.h>
29 #include "util.h"
30 #include <asm/bug.h>
31 #include "cgroup.h"
32 #include "util/hashmap.h"
33
metricgroup__lookup(struct rblist * metric_events,struct evsel * evsel,bool create)34 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
35 struct evsel *evsel,
36 bool create)
37 {
38 struct rb_node *nd;
39 struct metric_event me = {
40 .evsel = evsel
41 };
42
43 if (!metric_events)
44 return NULL;
45
46 nd = rblist__find(metric_events, &me);
47 if (nd)
48 return container_of(nd, struct metric_event, nd);
49 if (create) {
50 rblist__add_node(metric_events, &me);
51 nd = rblist__find(metric_events, &me);
52 if (nd)
53 return container_of(nd, struct metric_event, nd);
54 }
55 return NULL;
56 }
57
metric_event_cmp(struct rb_node * rb_node,const void * entry)58 static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
59 {
60 struct metric_event *a = container_of(rb_node,
61 struct metric_event,
62 nd);
63 const struct metric_event *b = entry;
64
65 if (a->evsel == b->evsel)
66 return 0;
67 if ((char *)a->evsel < (char *)b->evsel)
68 return -1;
69 return +1;
70 }
71
metric_event_new(struct rblist * rblist __maybe_unused,const void * entry)72 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
73 const void *entry)
74 {
75 struct metric_event *me = malloc(sizeof(struct metric_event));
76
77 if (!me)
78 return NULL;
79 memcpy(me, entry, sizeof(struct metric_event));
80 me->evsel = ((struct metric_event *)entry)->evsel;
81 INIT_LIST_HEAD(&me->head);
82 return &me->nd;
83 }
84
metric_event_delete(struct rblist * rblist __maybe_unused,struct rb_node * rb_node)85 static void metric_event_delete(struct rblist *rblist __maybe_unused,
86 struct rb_node *rb_node)
87 {
88 struct metric_event *me = container_of(rb_node, struct metric_event, nd);
89 struct metric_expr *expr, *tmp;
90
91 list_for_each_entry_safe(expr, tmp, &me->head, nd) {
92 free((char *)expr->metric_name);
93 free(expr->metric_refs);
94 free(expr->metric_events);
95 free(expr);
96 }
97
98 free(me);
99 }
100
metricgroup__rblist_init(struct rblist * metric_events)101 static void metricgroup__rblist_init(struct rblist *metric_events)
102 {
103 rblist__init(metric_events);
104 metric_events->node_cmp = metric_event_cmp;
105 metric_events->node_new = metric_event_new;
106 metric_events->node_delete = metric_event_delete;
107 }
108
metricgroup__rblist_exit(struct rblist * metric_events)109 void metricgroup__rblist_exit(struct rblist *metric_events)
110 {
111 rblist__exit(metric_events);
112 }
113
114 /**
115 * The metric under construction. The data held here will be placed in a
116 * metric_expr.
117 */
118 struct metric {
119 struct list_head nd;
120 /**
121 * The expression parse context importantly holding the IDs contained
122 * within the expression.
123 */
124 struct expr_parse_ctx *pctx;
125 /** The name of the metric such as "IPC". */
126 const char *metric_name;
127 /** Modifier on the metric such as "u" or NULL for none. */
128 const char *modifier;
129 /** The expression to parse, for example, "instructions/cycles". */
130 const char *metric_expr;
131 /**
132 * The "ScaleUnit" that scales and adds a unit to the metric during
133 * output.
134 */
135 const char *metric_unit;
136 /** Optional null terminated array of referenced metrics. */
137 struct metric_ref *metric_refs;
138 /**
139 * Is there a constraint on the group of events? In which case the
140 * events won't be grouped.
141 */
142 bool has_constraint;
143 /**
144 * Parsed events for the metric. Optional as events may be taken from a
145 * different metric whose group contains all the IDs necessary for this
146 * one.
147 */
148 struct evlist *evlist;
149 };
150
metricgroup___watchdog_constraint_hint(const char * name,bool foot)151 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
152 {
153 static bool violate_nmi_constraint;
154
155 if (!foot) {
156 pr_warning("Splitting metric group %s into standalone metrics.\n", name);
157 violate_nmi_constraint = true;
158 return;
159 }
160
161 if (!violate_nmi_constraint)
162 return;
163
164 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
165 " echo 0 > /proc/sys/kernel/nmi_watchdog\n"
166 " perf stat ...\n"
167 " echo 1 > /proc/sys/kernel/nmi_watchdog\n");
168 }
169
metricgroup__has_constraint(const struct pmu_metric * pm)170 static bool metricgroup__has_constraint(const struct pmu_metric *pm)
171 {
172 if (!pm->metric_constraint)
173 return false;
174
175 if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") &&
176 sysctl__nmi_watchdog_enabled()) {
177 metricgroup___watchdog_constraint_hint(pm->metric_name, false);
178 return true;
179 }
180
181 return false;
182 }
183
metric__free(struct metric * m)184 static void metric__free(struct metric *m)
185 {
186 if (!m)
187 return;
188
189 free(m->metric_refs);
190 expr__ctx_free(m->pctx);
191 free((char *)m->modifier);
192 evlist__delete(m->evlist);
193 free(m);
194 }
195
metric__new(const struct pmu_metric * pm,const char * modifier,bool metric_no_group,int runtime,const char * user_requested_cpu_list,bool system_wide)196 static struct metric *metric__new(const struct pmu_metric *pm,
197 const char *modifier,
198 bool metric_no_group,
199 int runtime,
200 const char *user_requested_cpu_list,
201 bool system_wide)
202 {
203 struct metric *m;
204
205 m = zalloc(sizeof(*m));
206 if (!m)
207 return NULL;
208
209 m->pctx = expr__ctx_new();
210 if (!m->pctx)
211 goto out_err;
212
213 m->metric_name = pm->metric_name;
214 m->modifier = NULL;
215 if (modifier) {
216 m->modifier = strdup(modifier);
217 if (!m->modifier)
218 goto out_err;
219 }
220 m->metric_expr = pm->metric_expr;
221 m->metric_unit = pm->unit;
222 m->pctx->sctx.user_requested_cpu_list = NULL;
223 if (user_requested_cpu_list) {
224 m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
225 if (!m->pctx->sctx.user_requested_cpu_list)
226 goto out_err;
227 }
228 m->pctx->sctx.runtime = runtime;
229 m->pctx->sctx.system_wide = system_wide;
230 m->has_constraint = metric_no_group || metricgroup__has_constraint(pm);
231 m->metric_refs = NULL;
232 m->evlist = NULL;
233
234 return m;
235 out_err:
236 metric__free(m);
237 return NULL;
238 }
239
contains_metric_id(struct evsel ** metric_events,int num_events,const char * metric_id)240 static bool contains_metric_id(struct evsel **metric_events, int num_events,
241 const char *metric_id)
242 {
243 int i;
244
245 for (i = 0; i < num_events; i++) {
246 if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
247 return true;
248 }
249 return false;
250 }
251
252 /**
253 * setup_metric_events - Find a group of events in metric_evlist that correspond
254 * to the IDs from a parsed metric expression.
255 * @ids: the metric IDs to match.
256 * @metric_evlist: the list of perf events.
257 * @out_metric_events: holds the created metric events array.
258 */
setup_metric_events(struct hashmap * ids,struct evlist * metric_evlist,struct evsel *** out_metric_events)259 static int setup_metric_events(struct hashmap *ids,
260 struct evlist *metric_evlist,
261 struct evsel ***out_metric_events)
262 {
263 struct evsel **metric_events;
264 const char *metric_id;
265 struct evsel *ev;
266 size_t ids_size, matched_events, i;
267
268 *out_metric_events = NULL;
269 ids_size = hashmap__size(ids);
270
271 metric_events = calloc(sizeof(void *), ids_size + 1);
272 if (!metric_events)
273 return -ENOMEM;
274
275 matched_events = 0;
276 evlist__for_each_entry(metric_evlist, ev) {
277 struct expr_id_data *val_ptr;
278
279 /*
280 * Check for duplicate events with the same name. For
281 * example, uncore_imc/cas_count_read/ will turn into 6
282 * events per socket on skylakex. Only the first such
283 * event is placed in metric_events.
284 */
285 metric_id = evsel__metric_id(ev);
286 if (contains_metric_id(metric_events, matched_events, metric_id))
287 continue;
288 /*
289 * Does this event belong to the parse context? For
290 * combined or shared groups, this metric may not care
291 * about this event.
292 */
293 if (hashmap__find(ids, metric_id, &val_ptr)) {
294 metric_events[matched_events++] = ev;
295
296 if (matched_events >= ids_size)
297 break;
298 }
299 }
300 if (matched_events < ids_size) {
301 free(metric_events);
302 return -EINVAL;
303 }
304 for (i = 0; i < ids_size; i++) {
305 ev = metric_events[i];
306 ev->collect_stat = true;
307
308 /*
309 * The metric leader points to the identically named
310 * event in metric_events.
311 */
312 ev->metric_leader = ev;
313 /*
314 * Mark two events with identical names in the same
315 * group (or globally) as being in use as uncore events
316 * may be duplicated for each pmu. Set the metric leader
317 * of such events to be the event that appears in
318 * metric_events.
319 */
320 metric_id = evsel__metric_id(ev);
321 evlist__for_each_entry_continue(metric_evlist, ev) {
322 if (!strcmp(evsel__metric_id(ev), metric_id))
323 ev->metric_leader = metric_events[i];
324 }
325 }
326 *out_metric_events = metric_events;
327 return 0;
328 }
329
match_metric(const char * n,const char * list)330 static bool match_metric(const char *n, const char *list)
331 {
332 int len;
333 char *m;
334
335 if (!list)
336 return false;
337 if (!strcmp(list, "all"))
338 return true;
339 if (!n)
340 return !strcasecmp(list, "No_group");
341 len = strlen(list);
342 m = strcasestr(n, list);
343 if (!m)
344 return false;
345 if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
346 (m[len] == 0 || m[len] == ';'))
347 return true;
348 return false;
349 }
350
match_pm_metric(const struct pmu_metric * pm,const char * metric)351 static bool match_pm_metric(const struct pmu_metric *pm, const char *metric)
352 {
353 return match_metric(pm->metric_group, metric) ||
354 match_metric(pm->metric_name, metric);
355 }
356
357 /** struct mep - RB-tree node for building printing information. */
358 struct mep {
359 /** nd - RB-tree element. */
360 struct rb_node nd;
361 /** @metric_group: Owned metric group name, separated others with ';'. */
362 char *metric_group;
363 const char *metric_name;
364 const char *metric_desc;
365 const char *metric_long_desc;
366 const char *metric_expr;
367 const char *metric_unit;
368 };
369
mep_cmp(struct rb_node * rb_node,const void * entry)370 static int mep_cmp(struct rb_node *rb_node, const void *entry)
371 {
372 struct mep *a = container_of(rb_node, struct mep, nd);
373 struct mep *b = (struct mep *)entry;
374 int ret;
375
376 ret = strcmp(a->metric_group, b->metric_group);
377 if (ret)
378 return ret;
379
380 return strcmp(a->metric_name, b->metric_name);
381 }
382
mep_new(struct rblist * rl __maybe_unused,const void * entry)383 static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
384 {
385 struct mep *me = malloc(sizeof(struct mep));
386
387 if (!me)
388 return NULL;
389
390 memcpy(me, entry, sizeof(struct mep));
391 return &me->nd;
392 }
393
mep_delete(struct rblist * rl __maybe_unused,struct rb_node * nd)394 static void mep_delete(struct rblist *rl __maybe_unused,
395 struct rb_node *nd)
396 {
397 struct mep *me = container_of(nd, struct mep, nd);
398
399 zfree(&me->metric_group);
400 free(me);
401 }
402
mep_lookup(struct rblist * groups,const char * metric_group,const char * metric_name)403 static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
404 const char *metric_name)
405 {
406 struct rb_node *nd;
407 struct mep me = {
408 .metric_group = strdup(metric_group),
409 .metric_name = metric_name,
410 };
411 nd = rblist__find(groups, &me);
412 if (nd) {
413 free(me.metric_group);
414 return container_of(nd, struct mep, nd);
415 }
416 rblist__add_node(groups, &me);
417 nd = rblist__find(groups, &me);
418 if (nd)
419 return container_of(nd, struct mep, nd);
420 return NULL;
421 }
422
metricgroup__add_to_mep_groups(const struct pmu_metric * pm,struct rblist * groups)423 static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
424 struct rblist *groups)
425 {
426 const char *g;
427 char *omg, *mg;
428
429 mg = strdup(pm->metric_group ?: "No_group");
430 if (!mg)
431 return -ENOMEM;
432 omg = mg;
433 while ((g = strsep(&mg, ";")) != NULL) {
434 struct mep *me;
435
436 g = skip_spaces(g);
437 if (strlen(g))
438 me = mep_lookup(groups, g, pm->metric_name);
439 else
440 me = mep_lookup(groups, "No_group", pm->metric_name);
441
442 if (me) {
443 me->metric_desc = pm->desc;
444 me->metric_long_desc = pm->long_desc;
445 me->metric_expr = pm->metric_expr;
446 me->metric_unit = pm->unit;
447 }
448 }
449 free(omg);
450
451 return 0;
452 }
453
454 struct metricgroup_iter_data {
455 pmu_metric_iter_fn fn;
456 void *data;
457 };
458
metricgroup__sys_event_iter(const struct pmu_metric * pm,const struct pmu_metrics_table * table,void * data)459 static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
460 const struct pmu_metrics_table *table,
461 void *data)
462 {
463 struct metricgroup_iter_data *d = data;
464 struct perf_pmu *pmu = NULL;
465
466 if (!pm->metric_expr || !pm->compat)
467 return 0;
468
469 while ((pmu = perf_pmu__scan(pmu))) {
470
471 if (!pmu->id || strcmp(pmu->id, pm->compat))
472 continue;
473
474 return d->fn(pm, table, d->data);
475 }
476 return 0;
477 }
478
metricgroup__add_to_mep_groups_callback(const struct pmu_metric * pm,const struct pmu_metrics_table * table __maybe_unused,void * vdata)479 static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
480 const struct pmu_metrics_table *table __maybe_unused,
481 void *vdata)
482 {
483 struct rblist *groups = vdata;
484
485 return metricgroup__add_to_mep_groups(pm, groups);
486 }
487
metricgroup__print(const struct print_callbacks * print_cb,void * print_state)488 void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
489 {
490 struct rblist groups;
491 const struct pmu_metrics_table *table;
492 struct rb_node *node, *next;
493
494 rblist__init(&groups);
495 groups.node_new = mep_new;
496 groups.node_cmp = mep_cmp;
497 groups.node_delete = mep_delete;
498 table = pmu_metrics_table__find();
499 if (table) {
500 pmu_metrics_table_for_each_metric(table,
501 metricgroup__add_to_mep_groups_callback,
502 &groups);
503 }
504 {
505 struct metricgroup_iter_data data = {
506 .fn = metricgroup__add_to_mep_groups_callback,
507 .data = &groups,
508 };
509 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
510 }
511
512 for (node = rb_first_cached(&groups.entries); node; node = next) {
513 struct mep *me = container_of(node, struct mep, nd);
514
515 print_cb->print_metric(print_state,
516 me->metric_group,
517 me->metric_name,
518 me->metric_desc,
519 me->metric_long_desc,
520 me->metric_expr,
521 me->metric_unit);
522 next = rb_next(node);
523 rblist__remove_node(&groups, node);
524 }
525 }
526
527 static const char *code_characters = ",-=@";
528
encode_metric_id(struct strbuf * sb,const char * x)529 static int encode_metric_id(struct strbuf *sb, const char *x)
530 {
531 char *c;
532 int ret = 0;
533
534 for (; *x; x++) {
535 c = strchr(code_characters, *x);
536 if (c) {
537 ret = strbuf_addch(sb, '!');
538 if (ret)
539 break;
540
541 ret = strbuf_addch(sb, '0' + (c - code_characters));
542 if (ret)
543 break;
544 } else {
545 ret = strbuf_addch(sb, *x);
546 if (ret)
547 break;
548 }
549 }
550 return ret;
551 }
552
decode_metric_id(struct strbuf * sb,const char * x)553 static int decode_metric_id(struct strbuf *sb, const char *x)
554 {
555 const char *orig = x;
556 size_t i;
557 char c;
558 int ret;
559
560 for (; *x; x++) {
561 c = *x;
562 if (*x == '!') {
563 x++;
564 i = *x - '0';
565 if (i > strlen(code_characters)) {
566 pr_err("Bad metric-id encoding in: '%s'", orig);
567 return -1;
568 }
569 c = code_characters[i];
570 }
571 ret = strbuf_addch(sb, c);
572 if (ret)
573 return ret;
574 }
575 return 0;
576 }
577
decode_all_metric_ids(struct evlist * perf_evlist,const char * modifier)578 static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
579 {
580 struct evsel *ev;
581 struct strbuf sb = STRBUF_INIT;
582 char *cur;
583 int ret = 0;
584
585 evlist__for_each_entry(perf_evlist, ev) {
586 if (!ev->metric_id)
587 continue;
588
589 ret = strbuf_setlen(&sb, 0);
590 if (ret)
591 break;
592
593 ret = decode_metric_id(&sb, ev->metric_id);
594 if (ret)
595 break;
596
597 free((char *)ev->metric_id);
598 ev->metric_id = strdup(sb.buf);
599 if (!ev->metric_id) {
600 ret = -ENOMEM;
601 break;
602 }
603 /*
604 * If the name is just the parsed event, use the metric-id to
605 * give a more friendly display version.
606 */
607 if (strstr(ev->name, "metric-id=")) {
608 bool has_slash = false;
609
610 free(ev->name);
611 for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
612 *cur = '/';
613 has_slash = true;
614 }
615
616 if (modifier) {
617 if (!has_slash && !strchr(sb.buf, ':')) {
618 ret = strbuf_addch(&sb, ':');
619 if (ret)
620 break;
621 }
622 ret = strbuf_addstr(&sb, modifier);
623 if (ret)
624 break;
625 }
626 ev->name = strdup(sb.buf);
627 if (!ev->name) {
628 ret = -ENOMEM;
629 break;
630 }
631 }
632 }
633 strbuf_release(&sb);
634 return ret;
635 }
636
metricgroup__build_event_string(struct strbuf * events,const struct expr_parse_ctx * ctx,const char * modifier,bool has_constraint)637 static int metricgroup__build_event_string(struct strbuf *events,
638 const struct expr_parse_ctx *ctx,
639 const char *modifier,
640 bool has_constraint)
641 {
642 struct hashmap_entry *cur;
643 size_t bkt;
644 bool no_group = true, has_tool_events = false;
645 bool tool_events[PERF_TOOL_MAX] = {false};
646 int ret = 0;
647
648 #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
649
650 hashmap__for_each_entry(ctx->ids, cur, bkt) {
651 const char *sep, *rsep, *id = cur->pkey;
652 enum perf_tool_event ev;
653
654 pr_debug("found event %s\n", id);
655
656 /* Always move tool events outside of the group. */
657 ev = perf_tool_event__from_str(id);
658 if (ev != PERF_TOOL_NONE) {
659 has_tool_events = true;
660 tool_events[ev] = true;
661 continue;
662 }
663 /* Separate events with commas and open the group if necessary. */
664 if (no_group) {
665 if (!has_constraint) {
666 ret = strbuf_addch(events, '{');
667 RETURN_IF_NON_ZERO(ret);
668 }
669
670 no_group = false;
671 } else {
672 ret = strbuf_addch(events, ',');
673 RETURN_IF_NON_ZERO(ret);
674 }
675 /*
676 * Encode the ID as an event string. Add a qualifier for
677 * metric_id that is the original name except with characters
678 * that parse-events can't parse replaced. For example,
679 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
680 */
681 sep = strchr(id, '@');
682 if (sep != NULL) {
683 ret = strbuf_add(events, id, sep - id);
684 RETURN_IF_NON_ZERO(ret);
685 ret = strbuf_addch(events, '/');
686 RETURN_IF_NON_ZERO(ret);
687 rsep = strrchr(sep, '@');
688 ret = strbuf_add(events, sep + 1, rsep - sep - 1);
689 RETURN_IF_NON_ZERO(ret);
690 ret = strbuf_addstr(events, ",metric-id=");
691 RETURN_IF_NON_ZERO(ret);
692 sep = rsep;
693 } else {
694 sep = strchr(id, ':');
695 if (sep != NULL) {
696 ret = strbuf_add(events, id, sep - id);
697 RETURN_IF_NON_ZERO(ret);
698 } else {
699 ret = strbuf_addstr(events, id);
700 RETURN_IF_NON_ZERO(ret);
701 }
702 ret = strbuf_addstr(events, "/metric-id=");
703 RETURN_IF_NON_ZERO(ret);
704 }
705 ret = encode_metric_id(events, id);
706 RETURN_IF_NON_ZERO(ret);
707 ret = strbuf_addstr(events, "/");
708 RETURN_IF_NON_ZERO(ret);
709
710 if (sep != NULL) {
711 ret = strbuf_addstr(events, sep + 1);
712 RETURN_IF_NON_ZERO(ret);
713 }
714 if (modifier) {
715 ret = strbuf_addstr(events, modifier);
716 RETURN_IF_NON_ZERO(ret);
717 }
718 }
719 if (!no_group && !has_constraint) {
720 ret = strbuf_addf(events, "}:W");
721 RETURN_IF_NON_ZERO(ret);
722 }
723 if (has_tool_events) {
724 int i;
725
726 perf_tool_event__for_each_event(i) {
727 if (tool_events[i]) {
728 if (!no_group) {
729 ret = strbuf_addch(events, ',');
730 RETURN_IF_NON_ZERO(ret);
731 }
732 no_group = false;
733 ret = strbuf_addstr(events, perf_tool_event__to_str(i));
734 RETURN_IF_NON_ZERO(ret);
735 }
736 }
737 }
738
739 return ret;
740 #undef RETURN_IF_NON_ZERO
741 }
742
arch_get_runtimeparam(const struct pmu_metric * pm __maybe_unused)743 int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
744 {
745 return 1;
746 }
747
748 /*
749 * A singly linked list on the stack of the names of metrics being
750 * processed. Used to identify recursion.
751 */
752 struct visited_metric {
753 const char *name;
754 const struct visited_metric *parent;
755 };
756
757 struct metricgroup_add_iter_data {
758 struct list_head *metric_list;
759 const char *metric_name;
760 const char *modifier;
761 int *ret;
762 bool *has_match;
763 bool metric_no_group;
764 const char *user_requested_cpu_list;
765 bool system_wide;
766 struct metric *root_metric;
767 const struct visited_metric *visited;
768 const struct pmu_metrics_table *table;
769 };
770
771 static bool metricgroup__find_metric(const char *metric,
772 const struct pmu_metrics_table *table,
773 struct pmu_metric *pm);
774
775 static int add_metric(struct list_head *metric_list,
776 const struct pmu_metric *pm,
777 const char *modifier,
778 bool metric_no_group,
779 const char *user_requested_cpu_list,
780 bool system_wide,
781 struct metric *root_metric,
782 const struct visited_metric *visited,
783 const struct pmu_metrics_table *table);
784
785 /**
786 * resolve_metric - Locate metrics within the root metric and recursively add
787 * references to them.
788 * @metric_list: The list the metric is added to.
789 * @modifier: if non-null event modifiers like "u".
790 * @metric_no_group: Should events written to events be grouped "{}" or
791 * global. Grouping is the default but due to multiplexing the
792 * user may override.
793 * @user_requested_cpu_list: Command line specified CPUs to record on.
794 * @system_wide: Are events for all processes recorded.
795 * @root_metric: Metrics may reference other metrics to form a tree. In this
796 * case the root_metric holds all the IDs and a list of referenced
797 * metrics. When adding a root this argument is NULL.
798 * @visited: A singly linked list of metric names being added that is used to
799 * detect recursion.
800 * @table: The table that is searched for metrics, most commonly the table for the
801 * architecture perf is running upon.
802 */
resolve_metric(struct list_head * metric_list,const char * modifier,bool metric_no_group,const char * user_requested_cpu_list,bool system_wide,struct metric * root_metric,const struct visited_metric * visited,const struct pmu_metrics_table * table)803 static int resolve_metric(struct list_head *metric_list,
804 const char *modifier,
805 bool metric_no_group,
806 const char *user_requested_cpu_list,
807 bool system_wide,
808 struct metric *root_metric,
809 const struct visited_metric *visited,
810 const struct pmu_metrics_table *table)
811 {
812 struct hashmap_entry *cur;
813 size_t bkt;
814 struct to_resolve {
815 /* The metric to resolve. */
816 struct pmu_metric pm;
817 /*
818 * The key in the IDs map, this may differ from in case,
819 * etc. from pm->metric_name.
820 */
821 const char *key;
822 } *pending = NULL;
823 int i, ret = 0, pending_cnt = 0;
824
825 /*
826 * Iterate all the parsed IDs and if there's a matching metric and it to
827 * the pending array.
828 */
829 hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
830 struct pmu_metric pm;
831
832 if (metricgroup__find_metric(cur->pkey, table, &pm)) {
833 pending = realloc(pending,
834 (pending_cnt + 1) * sizeof(struct to_resolve));
835 if (!pending)
836 return -ENOMEM;
837
838 memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
839 pending[pending_cnt].key = cur->pkey;
840 pending_cnt++;
841 }
842 }
843
844 /* Remove the metric IDs from the context. */
845 for (i = 0; i < pending_cnt; i++)
846 expr__del_id(root_metric->pctx, pending[i].key);
847
848 /*
849 * Recursively add all the metrics, IDs are added to the root metric's
850 * context.
851 */
852 for (i = 0; i < pending_cnt; i++) {
853 ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
854 user_requested_cpu_list, system_wide, root_metric, visited,
855 table);
856 if (ret)
857 break;
858 }
859
860 free(pending);
861 return ret;
862 }
863
864 /**
865 * __add_metric - Add a metric to metric_list.
866 * @metric_list: The list the metric is added to.
867 * @pm: The pmu_metric containing the metric to be added.
868 * @modifier: if non-null event modifiers like "u".
869 * @metric_no_group: Should events written to events be grouped "{}" or
870 * global. Grouping is the default but due to multiplexing the
871 * user may override.
872 * @runtime: A special argument for the parser only known at runtime.
873 * @user_requested_cpu_list: Command line specified CPUs to record on.
874 * @system_wide: Are events for all processes recorded.
875 * @root_metric: Metrics may reference other metrics to form a tree. In this
876 * case the root_metric holds all the IDs and a list of referenced
877 * metrics. When adding a root this argument is NULL.
878 * @visited: A singly linked list of metric names being added that is used to
879 * detect recursion.
880 * @table: The table that is searched for metrics, most commonly the table for the
881 * architecture perf is running upon.
882 */
__add_metric(struct list_head * metric_list,const struct pmu_metric * pm,const char * modifier,bool metric_no_group,int runtime,const char * user_requested_cpu_list,bool system_wide,struct metric * root_metric,const struct visited_metric * visited,const struct pmu_metrics_table * table)883 static int __add_metric(struct list_head *metric_list,
884 const struct pmu_metric *pm,
885 const char *modifier,
886 bool metric_no_group,
887 int runtime,
888 const char *user_requested_cpu_list,
889 bool system_wide,
890 struct metric *root_metric,
891 const struct visited_metric *visited,
892 const struct pmu_metrics_table *table)
893 {
894 const struct visited_metric *vm;
895 int ret;
896 bool is_root = !root_metric;
897 struct visited_metric visited_node = {
898 .name = pm->metric_name,
899 .parent = visited,
900 };
901
902 for (vm = visited; vm; vm = vm->parent) {
903 if (!strcmp(pm->metric_name, vm->name)) {
904 pr_err("failed: recursion detected for %s\n", pm->metric_name);
905 return -1;
906 }
907 }
908
909 if (is_root) {
910 /*
911 * This metric is the root of a tree and may reference other
912 * metrics that are added recursively.
913 */
914 root_metric = metric__new(pm, modifier, metric_no_group, runtime,
915 user_requested_cpu_list, system_wide);
916 if (!root_metric)
917 return -ENOMEM;
918
919 } else {
920 int cnt = 0;
921
922 /*
923 * This metric was referenced in a metric higher in the
924 * tree. Check if the same metric is already resolved in the
925 * metric_refs list.
926 */
927 if (root_metric->metric_refs) {
928 for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
929 if (!strcmp(pm->metric_name,
930 root_metric->metric_refs[cnt].metric_name))
931 return 0;
932 }
933 }
934
935 /* Create reference. Need space for the entry and the terminator. */
936 root_metric->metric_refs = realloc(root_metric->metric_refs,
937 (cnt + 2) * sizeof(struct metric_ref));
938 if (!root_metric->metric_refs)
939 return -ENOMEM;
940
941 /*
942 * Intentionally passing just const char pointers,
943 * from 'pe' object, so they never go away. We don't
944 * need to change them, so there's no need to create
945 * our own copy.
946 */
947 root_metric->metric_refs[cnt].metric_name = pm->metric_name;
948 root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
949
950 /* Null terminate array. */
951 root_metric->metric_refs[cnt+1].metric_name = NULL;
952 root_metric->metric_refs[cnt+1].metric_expr = NULL;
953 }
954
955 /*
956 * For both the parent and referenced metrics, we parse
957 * all the metric's IDs and add it to the root context.
958 */
959 if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) {
960 /* Broken metric. */
961 ret = -EINVAL;
962 } else {
963 /* Resolve referenced metrics. */
964 ret = resolve_metric(metric_list, modifier, metric_no_group,
965 user_requested_cpu_list, system_wide,
966 root_metric, &visited_node, table);
967 }
968
969 if (ret) {
970 if (is_root)
971 metric__free(root_metric);
972
973 } else if (is_root)
974 list_add(&root_metric->nd, metric_list);
975
976 return ret;
977 }
978
979 struct metricgroup__find_metric_data {
980 const char *metric;
981 struct pmu_metric *pm;
982 };
983
metricgroup__find_metric_callback(const struct pmu_metric * pm,const struct pmu_metrics_table * table __maybe_unused,void * vdata)984 static int metricgroup__find_metric_callback(const struct pmu_metric *pm,
985 const struct pmu_metrics_table *table __maybe_unused,
986 void *vdata)
987 {
988 struct metricgroup__find_metric_data *data = vdata;
989
990 if (!match_metric(pm->metric_name, data->metric))
991 return 0;
992
993 memcpy(data->pm, pm, sizeof(*pm));
994 return 1;
995 }
996
metricgroup__find_metric(const char * metric,const struct pmu_metrics_table * table,struct pmu_metric * pm)997 static bool metricgroup__find_metric(const char *metric,
998 const struct pmu_metrics_table *table,
999 struct pmu_metric *pm)
1000 {
1001 struct metricgroup__find_metric_data data = {
1002 .metric = metric,
1003 .pm = pm,
1004 };
1005
1006 return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data)
1007 ? true : false;
1008 }
1009
add_metric(struct list_head * metric_list,const struct pmu_metric * pm,const char * modifier,bool metric_no_group,const char * user_requested_cpu_list,bool system_wide,struct metric * root_metric,const struct visited_metric * visited,const struct pmu_metrics_table * table)1010 static int add_metric(struct list_head *metric_list,
1011 const struct pmu_metric *pm,
1012 const char *modifier,
1013 bool metric_no_group,
1014 const char *user_requested_cpu_list,
1015 bool system_wide,
1016 struct metric *root_metric,
1017 const struct visited_metric *visited,
1018 const struct pmu_metrics_table *table)
1019 {
1020 int ret = 0;
1021
1022 pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
1023
1024 if (!strstr(pm->metric_expr, "?")) {
1025 ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0,
1026 user_requested_cpu_list, system_wide, root_metric,
1027 visited, table);
1028 } else {
1029 int j, count;
1030
1031 count = arch_get_runtimeparam(pm);
1032
1033 /* This loop is added to create multiple
1034 * events depend on count value and add
1035 * those events to metric_list.
1036 */
1037
1038 for (j = 0; j < count && !ret; j++)
1039 ret = __add_metric(metric_list, pm, modifier, metric_no_group, j,
1040 user_requested_cpu_list, system_wide,
1041 root_metric, visited, table);
1042 }
1043
1044 return ret;
1045 }
1046
metricgroup__add_metric_sys_event_iter(const struct pmu_metric * pm,const struct pmu_metrics_table * table __maybe_unused,void * data)1047 static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
1048 const struct pmu_metrics_table *table __maybe_unused,
1049 void *data)
1050 {
1051 struct metricgroup_add_iter_data *d = data;
1052 int ret;
1053
1054 if (!match_pm_metric(pm, d->metric_name))
1055 return 0;
1056
1057 ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
1058 d->user_requested_cpu_list, d->system_wide,
1059 d->root_metric, d->visited, d->table);
1060 if (ret)
1061 goto out;
1062
1063 *(d->has_match) = true;
1064
1065 out:
1066 *(d->ret) = ret;
1067 return ret;
1068 }
1069
1070 /**
1071 * metric_list_cmp - list_sort comparator that sorts metrics with more events to
1072 * the front. tool events are excluded from the count.
1073 */
metric_list_cmp(void * priv __maybe_unused,const struct list_head * l,const struct list_head * r)1074 static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
1075 const struct list_head *r)
1076 {
1077 const struct metric *left = container_of(l, struct metric, nd);
1078 const struct metric *right = container_of(r, struct metric, nd);
1079 struct expr_id_data *data;
1080 int i, left_count, right_count;
1081
1082 left_count = hashmap__size(left->pctx->ids);
1083 perf_tool_event__for_each_event(i) {
1084 if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
1085 left_count--;
1086 }
1087
1088 right_count = hashmap__size(right->pctx->ids);
1089 perf_tool_event__for_each_event(i) {
1090 if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
1091 right_count--;
1092 }
1093
1094 return right_count - left_count;
1095 }
1096
1097 struct metricgroup__add_metric_data {
1098 struct list_head *list;
1099 const char *metric_name;
1100 const char *modifier;
1101 const char *user_requested_cpu_list;
1102 bool metric_no_group;
1103 bool system_wide;
1104 bool has_match;
1105 };
1106
metricgroup__add_metric_callback(const struct pmu_metric * pm,const struct pmu_metrics_table * table,void * vdata)1107 static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
1108 const struct pmu_metrics_table *table,
1109 void *vdata)
1110 {
1111 struct metricgroup__add_metric_data *data = vdata;
1112 int ret = 0;
1113
1114 if (pm->metric_expr &&
1115 (match_metric(pm->metric_group, data->metric_name) ||
1116 match_metric(pm->metric_name, data->metric_name))) {
1117
1118 data->has_match = true;
1119 ret = add_metric(data->list, pm, data->modifier, data->metric_no_group,
1120 data->user_requested_cpu_list, data->system_wide,
1121 /*root_metric=*/NULL, /*visited_metrics=*/NULL, table);
1122 }
1123 return ret;
1124 }
1125
1126 /**
1127 * metricgroup__add_metric - Find and add a metric, or a metric group.
1128 * @metric_name: The name of the metric or metric group. For example, "IPC"
1129 * could be the name of a metric and "TopDownL1" the name of a
1130 * metric group.
1131 * @modifier: if non-null event modifiers like "u".
1132 * @metric_no_group: Should events written to events be grouped "{}" or
1133 * global. Grouping is the default but due to multiplexing the
1134 * user may override.
1135 * @user_requested_cpu_list: Command line specified CPUs to record on.
1136 * @system_wide: Are events for all processes recorded.
1137 * @metric_list: The list that the metric or metric group are added to.
1138 * @table: The table that is searched for metrics, most commonly the table for the
1139 * architecture perf is running upon.
1140 */
metricgroup__add_metric(const char * metric_name,const char * modifier,bool metric_no_group,const char * user_requested_cpu_list,bool system_wide,struct list_head * metric_list,const struct pmu_metrics_table * table)1141 static int metricgroup__add_metric(const char *metric_name, const char *modifier,
1142 bool metric_no_group,
1143 const char *user_requested_cpu_list,
1144 bool system_wide,
1145 struct list_head *metric_list,
1146 const struct pmu_metrics_table *table)
1147 {
1148 LIST_HEAD(list);
1149 int ret;
1150 bool has_match = false;
1151
1152 {
1153 struct metricgroup__add_metric_data data = {
1154 .list = &list,
1155 .metric_name = metric_name,
1156 .modifier = modifier,
1157 .metric_no_group = metric_no_group,
1158 .user_requested_cpu_list = user_requested_cpu_list,
1159 .system_wide = system_wide,
1160 .has_match = false,
1161 };
1162 /*
1163 * Iterate over all metrics seeing if metric matches either the
1164 * name or group. When it does add the metric to the list.
1165 */
1166 ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback,
1167 &data);
1168 if (ret)
1169 goto out;
1170
1171 has_match = data.has_match;
1172 }
1173 {
1174 struct metricgroup_iter_data data = {
1175 .fn = metricgroup__add_metric_sys_event_iter,
1176 .data = (void *) &(struct metricgroup_add_iter_data) {
1177 .metric_list = &list,
1178 .metric_name = metric_name,
1179 .modifier = modifier,
1180 .metric_no_group = metric_no_group,
1181 .user_requested_cpu_list = user_requested_cpu_list,
1182 .system_wide = system_wide,
1183 .has_match = &has_match,
1184 .ret = &ret,
1185 .table = table,
1186 },
1187 };
1188
1189 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
1190 }
1191 /* End of pmu events. */
1192 if (!has_match)
1193 ret = -EINVAL;
1194
1195 out:
1196 /*
1197 * add to metric_list so that they can be released
1198 * even if it's failed
1199 */
1200 list_splice(&list, metric_list);
1201 return ret;
1202 }
1203
1204 /**
1205 * metricgroup__add_metric_list - Find and add metrics, or metric groups,
1206 * specified in a list.
1207 * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
1208 * would match the IPC and CPI metrics, and TopDownL1 would match all
1209 * the metrics in the TopDownL1 group.
1210 * @metric_no_group: Should events written to events be grouped "{}" or
1211 * global. Grouping is the default but due to multiplexing the
1212 * user may override.
1213 * @user_requested_cpu_list: Command line specified CPUs to record on.
1214 * @system_wide: Are events for all processes recorded.
1215 * @metric_list: The list that metrics are added to.
1216 * @table: The table that is searched for metrics, most commonly the table for the
1217 * architecture perf is running upon.
1218 */
metricgroup__add_metric_list(const char * list,bool metric_no_group,const char * user_requested_cpu_list,bool system_wide,struct list_head * metric_list,const struct pmu_metrics_table * table)1219 static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
1220 const char *user_requested_cpu_list,
1221 bool system_wide, struct list_head *metric_list,
1222 const struct pmu_metrics_table *table)
1223 {
1224 char *list_itr, *list_copy, *metric_name, *modifier;
1225 int ret, count = 0;
1226
1227 list_copy = strdup(list);
1228 if (!list_copy)
1229 return -ENOMEM;
1230 list_itr = list_copy;
1231
1232 while ((metric_name = strsep(&list_itr, ",")) != NULL) {
1233 modifier = strchr(metric_name, ':');
1234 if (modifier)
1235 *modifier++ = '\0';
1236
1237 ret = metricgroup__add_metric(metric_name, modifier,
1238 metric_no_group, user_requested_cpu_list,
1239 system_wide, metric_list, table);
1240 if (ret == -EINVAL)
1241 pr_err("Cannot find metric or group `%s'\n", metric_name);
1242
1243 if (ret)
1244 break;
1245
1246 count++;
1247 }
1248 free(list_copy);
1249
1250 if (!ret) {
1251 /*
1252 * Warn about nmi_watchdog if any parsed metrics had the
1253 * NO_NMI_WATCHDOG constraint.
1254 */
1255 metricgroup___watchdog_constraint_hint(NULL, true);
1256 /* No metrics. */
1257 if (count == 0)
1258 return -EINVAL;
1259 }
1260 return ret;
1261 }
1262
metricgroup__free_metrics(struct list_head * metric_list)1263 static void metricgroup__free_metrics(struct list_head *metric_list)
1264 {
1265 struct metric *m, *tmp;
1266
1267 list_for_each_entry_safe (m, tmp, metric_list, nd) {
1268 list_del_init(&m->nd);
1269 metric__free(m);
1270 }
1271 }
1272
1273 /**
1274 * find_tool_events - Search for the pressence of tool events in metric_list.
1275 * @metric_list: List to take metrics from.
1276 * @tool_events: Array of false values, indices corresponding to tool events set
1277 * to true if tool event is found.
1278 */
find_tool_events(const struct list_head * metric_list,bool tool_events[PERF_TOOL_MAX])1279 static void find_tool_events(const struct list_head *metric_list,
1280 bool tool_events[PERF_TOOL_MAX])
1281 {
1282 struct metric *m;
1283
1284 list_for_each_entry(m, metric_list, nd) {
1285 int i;
1286
1287 perf_tool_event__for_each_event(i) {
1288 struct expr_id_data *data;
1289
1290 if (!tool_events[i] &&
1291 !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
1292 tool_events[i] = true;
1293 }
1294 }
1295 }
1296
1297 /**
1298 * build_combined_expr_ctx - Make an expr_parse_ctx with all has_constraint
1299 * metric IDs, as the IDs are held in a set,
1300 * duplicates will be removed.
1301 * @metric_list: List to take metrics from.
1302 * @combined: Out argument for result.
1303 */
build_combined_expr_ctx(const struct list_head * metric_list,struct expr_parse_ctx ** combined)1304 static int build_combined_expr_ctx(const struct list_head *metric_list,
1305 struct expr_parse_ctx **combined)
1306 {
1307 struct hashmap_entry *cur;
1308 size_t bkt;
1309 struct metric *m;
1310 char *dup;
1311 int ret;
1312
1313 *combined = expr__ctx_new();
1314 if (!*combined)
1315 return -ENOMEM;
1316
1317 list_for_each_entry(m, metric_list, nd) {
1318 if (m->has_constraint && !m->modifier) {
1319 hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
1320 dup = strdup(cur->pkey);
1321 if (!dup) {
1322 ret = -ENOMEM;
1323 goto err_out;
1324 }
1325 ret = expr__add_id(*combined, dup);
1326 if (ret)
1327 goto err_out;
1328 }
1329 }
1330 }
1331 return 0;
1332 err_out:
1333 expr__ctx_free(*combined);
1334 *combined = NULL;
1335 return ret;
1336 }
1337
1338 /**
1339 * parse_ids - Build the event string for the ids and parse them creating an
1340 * evlist. The encoded metric_ids are decoded.
1341 * @metric_no_merge: is metric sharing explicitly disabled.
1342 * @fake_pmu: used when testing metrics not supported by the current CPU.
1343 * @ids: the event identifiers parsed from a metric.
1344 * @modifier: any modifiers added to the events.
1345 * @has_constraint: false if events should be placed in a weak group.
1346 * @tool_events: entries set true if the tool event of index could be present in
1347 * the overall list of metrics.
1348 * @out_evlist: the created list of events.
1349 */
parse_ids(bool metric_no_merge,struct perf_pmu * fake_pmu,struct expr_parse_ctx * ids,const char * modifier,bool has_constraint,const bool tool_events[PERF_TOOL_MAX],struct evlist ** out_evlist)1350 static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
1351 struct expr_parse_ctx *ids, const char *modifier,
1352 bool has_constraint, const bool tool_events[PERF_TOOL_MAX],
1353 struct evlist **out_evlist)
1354 {
1355 struct parse_events_error parse_error;
1356 struct evlist *parsed_evlist;
1357 struct strbuf events = STRBUF_INIT;
1358 int ret;
1359
1360 *out_evlist = NULL;
1361 if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
1362 bool added_event = false;
1363 int i;
1364 /*
1365 * We may fail to share events between metrics because a tool
1366 * event isn't present in one metric. For example, a ratio of
1367 * cache misses doesn't need duration_time but the same events
1368 * may be used for a misses per second. Events without sharing
1369 * implies multiplexing, that is best avoided, so place
1370 * all tool events in every group.
1371 *
1372 * Also, there may be no ids/events in the expression parsing
1373 * context because of constant evaluation, e.g.:
1374 * event1 if #smt_on else 0
1375 * Add a tool event to avoid a parse error on an empty string.
1376 */
1377 perf_tool_event__for_each_event(i) {
1378 if (tool_events[i]) {
1379 char *tmp = strdup(perf_tool_event__to_str(i));
1380
1381 if (!tmp)
1382 return -ENOMEM;
1383 ids__insert(ids->ids, tmp);
1384 added_event = true;
1385 }
1386 }
1387 if (!added_event && hashmap__size(ids->ids) == 0) {
1388 char *tmp = strdup("duration_time");
1389
1390 if (!tmp)
1391 return -ENOMEM;
1392 ids__insert(ids->ids, tmp);
1393 }
1394 }
1395 ret = metricgroup__build_event_string(&events, ids, modifier,
1396 has_constraint);
1397 if (ret)
1398 return ret;
1399
1400 parsed_evlist = evlist__new();
1401 if (!parsed_evlist) {
1402 ret = -ENOMEM;
1403 goto err_out;
1404 }
1405 pr_debug("Parsing metric events '%s'\n", events.buf);
1406 parse_events_error__init(&parse_error);
1407 ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu);
1408 if (ret) {
1409 parse_events_error__print(&parse_error, events.buf);
1410 goto err_out;
1411 }
1412 ret = decode_all_metric_ids(parsed_evlist, modifier);
1413 if (ret)
1414 goto err_out;
1415
1416 *out_evlist = parsed_evlist;
1417 parsed_evlist = NULL;
1418 err_out:
1419 parse_events_error__exit(&parse_error);
1420 evlist__delete(parsed_evlist);
1421 strbuf_release(&events);
1422 return ret;
1423 }
1424
parse_groups(struct evlist * perf_evlist,const char * str,bool metric_no_group,bool metric_no_merge,const char * user_requested_cpu_list,bool system_wide,struct perf_pmu * fake_pmu,struct rblist * metric_events_list,const struct pmu_metrics_table * table)1425 static int parse_groups(struct evlist *perf_evlist, const char *str,
1426 bool metric_no_group,
1427 bool metric_no_merge,
1428 const char *user_requested_cpu_list,
1429 bool system_wide,
1430 struct perf_pmu *fake_pmu,
1431 struct rblist *metric_events_list,
1432 const struct pmu_metrics_table *table)
1433 {
1434 struct evlist *combined_evlist = NULL;
1435 LIST_HEAD(metric_list);
1436 struct metric *m;
1437 bool tool_events[PERF_TOOL_MAX] = {false};
1438 int ret;
1439
1440 if (metric_events_list->nr_entries == 0)
1441 metricgroup__rblist_init(metric_events_list);
1442 ret = metricgroup__add_metric_list(str, metric_no_group,
1443 user_requested_cpu_list,
1444 system_wide, &metric_list, table);
1445 if (ret)
1446 goto out;
1447
1448 /* Sort metrics from largest to smallest. */
1449 list_sort(NULL, &metric_list, metric_list_cmp);
1450
1451 if (!metric_no_merge) {
1452 struct expr_parse_ctx *combined = NULL;
1453
1454 find_tool_events(&metric_list, tool_events);
1455
1456 ret = build_combined_expr_ctx(&metric_list, &combined);
1457
1458 if (!ret && combined && hashmap__size(combined->ids)) {
1459 ret = parse_ids(metric_no_merge, fake_pmu, combined,
1460 /*modifier=*/NULL,
1461 /*has_constraint=*/true,
1462 tool_events,
1463 &combined_evlist);
1464 }
1465 if (combined)
1466 expr__ctx_free(combined);
1467
1468 if (ret)
1469 goto out;
1470 }
1471
1472 list_for_each_entry(m, &metric_list, nd) {
1473 struct metric_event *me;
1474 struct evsel **metric_events;
1475 struct evlist *metric_evlist = NULL;
1476 struct metric *n;
1477 struct metric_expr *expr;
1478
1479 if (combined_evlist && m->has_constraint) {
1480 metric_evlist = combined_evlist;
1481 } else if (!metric_no_merge) {
1482 /*
1483 * See if the IDs for this metric are a subset of an
1484 * earlier metric.
1485 */
1486 list_for_each_entry(n, &metric_list, nd) {
1487 if (m == n)
1488 break;
1489
1490 if (n->evlist == NULL)
1491 continue;
1492
1493 if ((!m->modifier && n->modifier) ||
1494 (m->modifier && !n->modifier) ||
1495 (m->modifier && n->modifier &&
1496 strcmp(m->modifier, n->modifier)))
1497 continue;
1498
1499 if (expr__subset_of_ids(n->pctx, m->pctx)) {
1500 pr_debug("Events in '%s' fully contained within '%s'\n",
1501 m->metric_name, n->metric_name);
1502 metric_evlist = n->evlist;
1503 break;
1504 }
1505
1506 }
1507 }
1508 if (!metric_evlist) {
1509 ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
1510 m->has_constraint, tool_events, &m->evlist);
1511 if (ret)
1512 goto out;
1513
1514 metric_evlist = m->evlist;
1515 }
1516 ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events);
1517 if (ret) {
1518 pr_debug("Cannot resolve IDs for %s: %s\n",
1519 m->metric_name, m->metric_expr);
1520 goto out;
1521 }
1522
1523 me = metricgroup__lookup(metric_events_list, metric_events[0], true);
1524
1525 expr = malloc(sizeof(struct metric_expr));
1526 if (!expr) {
1527 ret = -ENOMEM;
1528 free(metric_events);
1529 goto out;
1530 }
1531
1532 expr->metric_refs = m->metric_refs;
1533 m->metric_refs = NULL;
1534 expr->metric_expr = m->metric_expr;
1535 if (m->modifier) {
1536 char *tmp;
1537
1538 if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
1539 expr->metric_name = NULL;
1540 else
1541 expr->metric_name = tmp;
1542 } else
1543 expr->metric_name = strdup(m->metric_name);
1544
1545 if (!expr->metric_name) {
1546 ret = -ENOMEM;
1547 free(metric_events);
1548 goto out;
1549 }
1550 expr->metric_unit = m->metric_unit;
1551 expr->metric_events = metric_events;
1552 expr->runtime = m->pctx->sctx.runtime;
1553 list_add(&expr->nd, &me->head);
1554 }
1555
1556
1557 if (combined_evlist) {
1558 evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
1559 evlist__delete(combined_evlist);
1560 }
1561
1562 list_for_each_entry(m, &metric_list, nd) {
1563 if (m->evlist)
1564 evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
1565 }
1566
1567 out:
1568 metricgroup__free_metrics(&metric_list);
1569 return ret;
1570 }
1571
metricgroup__parse_groups(struct evlist * perf_evlist,const char * str,bool metric_no_group,bool metric_no_merge,const char * user_requested_cpu_list,bool system_wide,struct rblist * metric_events)1572 int metricgroup__parse_groups(struct evlist *perf_evlist,
1573 const char *str,
1574 bool metric_no_group,
1575 bool metric_no_merge,
1576 const char *user_requested_cpu_list,
1577 bool system_wide,
1578 struct rblist *metric_events)
1579 {
1580 const struct pmu_metrics_table *table = pmu_metrics_table__find();
1581
1582 if (!table)
1583 return -EINVAL;
1584
1585 return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge,
1586 user_requested_cpu_list, system_wide,
1587 /*fake_pmu=*/NULL, metric_events, table);
1588 }
1589
metricgroup__parse_groups_test(struct evlist * evlist,const struct pmu_metrics_table * table,const char * str,bool metric_no_group,bool metric_no_merge,struct rblist * metric_events)1590 int metricgroup__parse_groups_test(struct evlist *evlist,
1591 const struct pmu_metrics_table *table,
1592 const char *str,
1593 bool metric_no_group,
1594 bool metric_no_merge,
1595 struct rblist *metric_events)
1596 {
1597 return parse_groups(evlist, str, metric_no_group, metric_no_merge,
1598 /*user_requested_cpu_list=*/NULL,
1599 /*system_wide=*/false,
1600 &perf_pmu__fake, metric_events, table);
1601 }
1602
metricgroup__has_metric_callback(const struct pmu_metric * pm,const struct pmu_metrics_table * table __maybe_unused,void * vdata)1603 static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
1604 const struct pmu_metrics_table *table __maybe_unused,
1605 void *vdata)
1606 {
1607 const char *metric = vdata;
1608
1609 if (!pm->metric_expr)
1610 return 0;
1611
1612 if (match_metric(pm->metric_name, metric))
1613 return 1;
1614
1615 return 0;
1616 }
1617
metricgroup__has_metric(const char * metric)1618 bool metricgroup__has_metric(const char *metric)
1619 {
1620 const struct pmu_metrics_table *table = pmu_metrics_table__find();
1621
1622 if (!table)
1623 return false;
1624
1625 return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback,
1626 (void *)metric) ? true : false;
1627 }
1628
metricgroup__copy_metric_events(struct evlist * evlist,struct cgroup * cgrp,struct rblist * new_metric_events,struct rblist * old_metric_events)1629 int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
1630 struct rblist *new_metric_events,
1631 struct rblist *old_metric_events)
1632 {
1633 unsigned int i;
1634
1635 for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
1636 struct rb_node *nd;
1637 struct metric_event *old_me, *new_me;
1638 struct metric_expr *old_expr, *new_expr;
1639 struct evsel *evsel;
1640 size_t alloc_size;
1641 int idx, nr;
1642
1643 nd = rblist__entry(old_metric_events, i);
1644 old_me = container_of(nd, struct metric_event, nd);
1645
1646 evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
1647 if (!evsel)
1648 return -EINVAL;
1649 new_me = metricgroup__lookup(new_metric_events, evsel, true);
1650 if (!new_me)
1651 return -ENOMEM;
1652
1653 pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
1654 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
1655
1656 list_for_each_entry(old_expr, &old_me->head, nd) {
1657 new_expr = malloc(sizeof(*new_expr));
1658 if (!new_expr)
1659 return -ENOMEM;
1660
1661 new_expr->metric_expr = old_expr->metric_expr;
1662 new_expr->metric_name = strdup(old_expr->metric_name);
1663 if (!new_expr->metric_name)
1664 return -ENOMEM;
1665
1666 new_expr->metric_unit = old_expr->metric_unit;
1667 new_expr->runtime = old_expr->runtime;
1668
1669 if (old_expr->metric_refs) {
1670 /* calculate number of metric_events */
1671 for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
1672 continue;
1673 alloc_size = sizeof(*new_expr->metric_refs);
1674 new_expr->metric_refs = calloc(nr + 1, alloc_size);
1675 if (!new_expr->metric_refs) {
1676 free(new_expr);
1677 return -ENOMEM;
1678 }
1679
1680 memcpy(new_expr->metric_refs, old_expr->metric_refs,
1681 nr * alloc_size);
1682 } else {
1683 new_expr->metric_refs = NULL;
1684 }
1685
1686 /* calculate number of metric_events */
1687 for (nr = 0; old_expr->metric_events[nr]; nr++)
1688 continue;
1689 alloc_size = sizeof(*new_expr->metric_events);
1690 new_expr->metric_events = calloc(nr + 1, alloc_size);
1691 if (!new_expr->metric_events) {
1692 free(new_expr->metric_refs);
1693 free(new_expr);
1694 return -ENOMEM;
1695 }
1696
1697 /* copy evsel in the same position */
1698 for (idx = 0; idx < nr; idx++) {
1699 evsel = old_expr->metric_events[idx];
1700 evsel = evlist__find_evsel(evlist, evsel->core.idx);
1701 if (evsel == NULL) {
1702 free(new_expr->metric_events);
1703 free(new_expr->metric_refs);
1704 free(new_expr);
1705 return -EINVAL;
1706 }
1707 new_expr->metric_events[idx] = evsel;
1708 }
1709
1710 list_add(&new_expr->nd, &new_me->head);
1711 }
1712 }
1713 return 0;
1714 }
1715