xref: /linux/tools/perf/util/metricgroup.c (revision 411ad22ecf0281d666a82aa7f4de90c70365da7d)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b18f3e36SAndi Kleen /*
3b18f3e36SAndi Kleen  * Copyright (c) 2017, Intel Corporation.
4b18f3e36SAndi Kleen  */
5b18f3e36SAndi Kleen 
6b18f3e36SAndi Kleen /* Manage metrics and groups of metrics from JSON files */
7b18f3e36SAndi Kleen 
8b18f3e36SAndi Kleen #include "metricgroup.h"
9b4209025SArnaldo Carvalho de Melo #include "debug.h"
10b18f3e36SAndi Kleen #include "evlist.h"
110b8026e8SArnaldo Carvalho de Melo #include "evsel.h"
12b18f3e36SAndi Kleen #include "strbuf.h"
13b18f3e36SAndi Kleen #include "pmu.h"
140e0ae874SJin Yao #include "pmu-hybrid.h"
15e5c6109fSIan Rogers #include "print-events.h"
1690053634SIan Rogers #include "smt.h"
17b18f3e36SAndi Kleen #include "expr.h"
18b18f3e36SAndi Kleen #include "rblist.h"
19b18f3e36SAndi Kleen #include <string.h>
20b18f3e36SAndi Kleen #include <errno.h>
21b18f3e36SAndi Kleen #include "strlist.h"
22b18f3e36SAndi Kleen #include <assert.h>
23bd9860bfSArnaldo Carvalho de Melo #include <linux/ctype.h>
2480be6434SIan Rogers #include <linux/list_sort.h>
25b4209025SArnaldo Carvalho de Melo #include <linux/string.h>
26d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h>
271725e9cdSIan Rogers #include <perf/cpumap.h>
280b8026e8SArnaldo Carvalho de Melo #include <subcmd/parse-options.h>
29ab483d8bSKan Liang #include <api/fs/fs.h>
30ab483d8bSKan Liang #include "util.h"
31f6fb0960SJiri Olsa #include <asm/bug.h>
32b214ba8cSNamhyung Kim #include "cgroup.h"
33bd560973SIan Rogers #include "util/hashmap.h"
34b18f3e36SAndi Kleen 
35b18f3e36SAndi Kleen struct metric_event *metricgroup__lookup(struct rblist *metric_events,
3632dcd021SJiri Olsa 					 struct evsel *evsel,
37b18f3e36SAndi Kleen 					 bool create)
38b18f3e36SAndi Kleen {
39b18f3e36SAndi Kleen 	struct rb_node *nd;
40b18f3e36SAndi Kleen 	struct metric_event me = {
41b18f3e36SAndi Kleen 		.evsel = evsel
42b18f3e36SAndi Kleen 	};
434bd1bef8SAndi Kleen 
444bd1bef8SAndi Kleen 	if (!metric_events)
454bd1bef8SAndi Kleen 		return NULL;
464bd1bef8SAndi Kleen 
47b18f3e36SAndi Kleen 	nd = rblist__find(metric_events, &me);
48b18f3e36SAndi Kleen 	if (nd)
49b18f3e36SAndi Kleen 		return container_of(nd, struct metric_event, nd);
50b18f3e36SAndi Kleen 	if (create) {
51b18f3e36SAndi Kleen 		rblist__add_node(metric_events, &me);
52b18f3e36SAndi Kleen 		nd = rblist__find(metric_events, &me);
53b18f3e36SAndi Kleen 		if (nd)
54b18f3e36SAndi Kleen 			return container_of(nd, struct metric_event, nd);
55b18f3e36SAndi Kleen 	}
56b18f3e36SAndi Kleen 	return NULL;
57b18f3e36SAndi Kleen }
58b18f3e36SAndi Kleen 
59b18f3e36SAndi Kleen static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
60b18f3e36SAndi Kleen {
61b18f3e36SAndi Kleen 	struct metric_event *a = container_of(rb_node,
62b18f3e36SAndi Kleen 					      struct metric_event,
63b18f3e36SAndi Kleen 					      nd);
64b18f3e36SAndi Kleen 	const struct metric_event *b = entry;
65b18f3e36SAndi Kleen 
66b18f3e36SAndi Kleen 	if (a->evsel == b->evsel)
67b18f3e36SAndi Kleen 		return 0;
68b18f3e36SAndi Kleen 	if ((char *)a->evsel < (char *)b->evsel)
69b18f3e36SAndi Kleen 		return -1;
70b18f3e36SAndi Kleen 	return +1;
71b18f3e36SAndi Kleen }
72b18f3e36SAndi Kleen 
73b18f3e36SAndi Kleen static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
74b18f3e36SAndi Kleen 					const void *entry)
75b18f3e36SAndi Kleen {
76b18f3e36SAndi Kleen 	struct metric_event *me = malloc(sizeof(struct metric_event));
77b18f3e36SAndi Kleen 
78b18f3e36SAndi Kleen 	if (!me)
79b18f3e36SAndi Kleen 		return NULL;
80b18f3e36SAndi Kleen 	memcpy(me, entry, sizeof(struct metric_event));
81b18f3e36SAndi Kleen 	me->evsel = ((struct metric_event *)entry)->evsel;
82b18f3e36SAndi Kleen 	INIT_LIST_HEAD(&me->head);
83b18f3e36SAndi Kleen 	return &me->nd;
84b18f3e36SAndi Kleen }
85b18f3e36SAndi Kleen 
869afe5658SJiri Olsa static void metric_event_delete(struct rblist *rblist __maybe_unused,
879afe5658SJiri Olsa 				struct rb_node *rb_node)
889afe5658SJiri Olsa {
899afe5658SJiri Olsa 	struct metric_event *me = container_of(rb_node, struct metric_event, nd);
909afe5658SJiri Olsa 	struct metric_expr *expr, *tmp;
919afe5658SJiri Olsa 
929afe5658SJiri Olsa 	list_for_each_entry_safe(expr, tmp, &me->head, nd) {
9311ff9bcdSArnaldo Carvalho de Melo 		zfree(&expr->metric_name);
9411ff9bcdSArnaldo Carvalho de Melo 		zfree(&expr->metric_refs);
9511ff9bcdSArnaldo Carvalho de Melo 		zfree(&expr->metric_events);
969afe5658SJiri Olsa 		free(expr);
979afe5658SJiri Olsa 	}
989afe5658SJiri Olsa 
999afe5658SJiri Olsa 	free(me);
1009afe5658SJiri Olsa }
1019afe5658SJiri Olsa 
102b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events)
103b18f3e36SAndi Kleen {
104b18f3e36SAndi Kleen 	rblist__init(metric_events);
105b18f3e36SAndi Kleen 	metric_events->node_cmp = metric_event_cmp;
106b18f3e36SAndi Kleen 	metric_events->node_new = metric_event_new;
1079afe5658SJiri Olsa 	metric_events->node_delete = metric_event_delete;
1089afe5658SJiri Olsa }
1099afe5658SJiri Olsa 
1109afe5658SJiri Olsa void metricgroup__rblist_exit(struct rblist *metric_events)
1119afe5658SJiri Olsa {
1129afe5658SJiri Olsa 	rblist__exit(metric_events);
113b18f3e36SAndi Kleen }
114b18f3e36SAndi Kleen 
115485fcaedSIan Rogers /**
116485fcaedSIan Rogers  * The metric under construction. The data held here will be placed in a
117485fcaedSIan Rogers  * metric_expr.
118485fcaedSIan Rogers  */
119a0c05b36SJiri Olsa struct metric {
120b18f3e36SAndi Kleen 	struct list_head nd;
121485fcaedSIan Rogers 	/**
122485fcaedSIan Rogers 	 * The expression parse context importantly holding the IDs contained
123485fcaedSIan Rogers 	 * within the expression.
124485fcaedSIan Rogers 	 */
125cb94a02eSIan Rogers 	struct expr_parse_ctx *pctx;
126485fcaedSIan Rogers 	/** The name of the metric such as "IPC". */
127b18f3e36SAndi Kleen 	const char *metric_name;
128b85a4d61SIan Rogers 	/** Modifier on the metric such as "u" or NULL for none. */
129b85a4d61SIan Rogers 	const char *modifier;
130485fcaedSIan Rogers 	/** The expression to parse, for example, "instructions/cycles". */
131b18f3e36SAndi Kleen 	const char *metric_expr;
132d0a3052fSIan Rogers 	/** Optional threshold expression where zero value is green, otherwise red. */
133d0a3052fSIan Rogers 	const char *metric_threshold;
134485fcaedSIan Rogers 	/**
135485fcaedSIan Rogers 	 * The "ScaleUnit" that scales and adds a unit to the metric during
136485fcaedSIan Rogers 	 * output.
137485fcaedSIan Rogers 	 */
138287f2649SJin Yao 	const char *metric_unit;
13946bdc0bfSIan Rogers 	/** Optional null terminated array of referenced metrics. */
14046bdc0bfSIan Rogers 	struct metric_ref *metric_refs;
141485fcaedSIan Rogers 	/**
142180a5013SIan Rogers 	 * Should events of the metric be grouped?
143485fcaedSIan Rogers 	 */
144180a5013SIan Rogers 	bool group_events;
1455ecd5a0cSIan Rogers 	/**
1465ecd5a0cSIan Rogers 	 * Parsed events for the metric. Optional as events may be taken from a
1475ecd5a0cSIan Rogers 	 * different metric whose group contains all the IDs necessary for this
1485ecd5a0cSIan Rogers 	 * one.
1495ecd5a0cSIan Rogers 	 */
1505ecd5a0cSIan Rogers 	struct evlist *evlist;
151b18f3e36SAndi Kleen };
152b18f3e36SAndi Kleen 
153180a5013SIan Rogers static void metric__watchdog_constraint_hint(const char *name, bool foot)
1543d81d761SIan Rogers {
1553d81d761SIan Rogers 	static bool violate_nmi_constraint;
1563d81d761SIan Rogers 
1573d81d761SIan Rogers 	if (!foot) {
158180a5013SIan Rogers 		pr_warning("Not grouping metric %s's events.\n", name);
1593d81d761SIan Rogers 		violate_nmi_constraint = true;
1603d81d761SIan Rogers 		return;
1613d81d761SIan Rogers 	}
1623d81d761SIan Rogers 
1633d81d761SIan Rogers 	if (!violate_nmi_constraint)
1643d81d761SIan Rogers 		return;
1653d81d761SIan Rogers 
1663d81d761SIan Rogers 	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
1673d81d761SIan Rogers 		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1683d81d761SIan Rogers 		   "    perf stat ...\n"
1693d81d761SIan Rogers 		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1703d81d761SIan Rogers }
1713d81d761SIan Rogers 
172180a5013SIan Rogers static bool metric__group_events(const struct pmu_metric *pm)
1733d81d761SIan Rogers {
17490053634SIan Rogers 	switch (pm->event_grouping) {
17590053634SIan Rogers 	case MetricNoGroupEvents:
17690053634SIan Rogers 		return false;
17790053634SIan Rogers 	case MetricNoGroupEventsNmi:
17890053634SIan Rogers 		if (!sysctl__nmi_watchdog_enabled())
179180a5013SIan Rogers 			return true;
180180a5013SIan Rogers 		metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false);
181180a5013SIan Rogers 		return false;
18290053634SIan Rogers 	case MetricNoGroupEventsSmt:
18390053634SIan Rogers 		return !smt_on();
18490053634SIan Rogers 	case MetricGroupEvents:
18590053634SIan Rogers 	default:
186180a5013SIan Rogers 		return true;
1873d81d761SIan Rogers 	}
18890053634SIan Rogers }
1893d81d761SIan Rogers 
1901725e9cdSIan Rogers static void metric__free(struct metric *m)
1911725e9cdSIan Rogers {
1921725e9cdSIan Rogers 	if (!m)
1931725e9cdSIan Rogers 		return;
1941725e9cdSIan Rogers 
19511ff9bcdSArnaldo Carvalho de Melo 	zfree(&m->metric_refs);
1961725e9cdSIan Rogers 	expr__ctx_free(m->pctx);
19711ff9bcdSArnaldo Carvalho de Melo 	zfree(&m->modifier);
1981725e9cdSIan Rogers 	evlist__delete(m->evlist);
1991725e9cdSIan Rogers 	free(m);
2001725e9cdSIan Rogers }
2011725e9cdSIan Rogers 
202db95818eSIan Rogers static struct metric *metric__new(const struct pmu_metric *pm,
203b85a4d61SIan Rogers 				  const char *modifier,
2043d81d761SIan Rogers 				  bool metric_no_group,
2051725e9cdSIan Rogers 				  int runtime,
2061725e9cdSIan Rogers 				  const char *user_requested_cpu_list,
2071725e9cdSIan Rogers 				  bool system_wide)
2083d81d761SIan Rogers {
2093d81d761SIan Rogers 	struct metric *m;
2103d81d761SIan Rogers 
2113d81d761SIan Rogers 	m = zalloc(sizeof(*m));
2123d81d761SIan Rogers 	if (!m)
2133d81d761SIan Rogers 		return NULL;
2143d81d761SIan Rogers 
2153d81d761SIan Rogers 	m->pctx = expr__ctx_new();
2161725e9cdSIan Rogers 	if (!m->pctx)
2171725e9cdSIan Rogers 		goto out_err;
2183d81d761SIan Rogers 
219db95818eSIan Rogers 	m->metric_name = pm->metric_name;
2201725e9cdSIan Rogers 	m->modifier = NULL;
2211725e9cdSIan Rogers 	if (modifier) {
2221725e9cdSIan Rogers 		m->modifier = strdup(modifier);
2231725e9cdSIan Rogers 		if (!m->modifier)
2241725e9cdSIan Rogers 			goto out_err;
225b85a4d61SIan Rogers 	}
226db95818eSIan Rogers 	m->metric_expr = pm->metric_expr;
227d0a3052fSIan Rogers 	m->metric_threshold = pm->metric_threshold;
228db95818eSIan Rogers 	m->metric_unit = pm->unit;
2291725e9cdSIan Rogers 	m->pctx->sctx.user_requested_cpu_list = NULL;
2301725e9cdSIan Rogers 	if (user_requested_cpu_list) {
2311725e9cdSIan Rogers 		m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
2321725e9cdSIan Rogers 		if (!m->pctx->sctx.user_requested_cpu_list)
2331725e9cdSIan Rogers 			goto out_err;
2341725e9cdSIan Rogers 	}
2351a6abddeSIan Rogers 	m->pctx->sctx.runtime = runtime;
2361725e9cdSIan Rogers 	m->pctx->sctx.system_wide = system_wide;
237180a5013SIan Rogers 	m->group_events = !metric_no_group && metric__group_events(pm);
23846bdc0bfSIan Rogers 	m->metric_refs = NULL;
2395ecd5a0cSIan Rogers 	m->evlist = NULL;
2403d81d761SIan Rogers 
2413d81d761SIan Rogers 	return m;
2421725e9cdSIan Rogers out_err:
2431725e9cdSIan Rogers 	metric__free(m);
2441725e9cdSIan Rogers 	return NULL;
2453d81d761SIan Rogers }
2463d81d761SIan Rogers 
247ec5c5b3dSIan Rogers static bool contains_metric_id(struct evsel **metric_events, int num_events,
248ec5c5b3dSIan Rogers 			       const char *metric_id)
249dcc81be0SIan Rogers {
250dcc81be0SIan Rogers 	int i;
251dcc81be0SIan Rogers 
252dcc81be0SIan Rogers 	for (i = 0; i < num_events; i++) {
253ec5c5b3dSIan Rogers 		if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
254dcc81be0SIan Rogers 			return true;
255dcc81be0SIan Rogers 	}
256dcc81be0SIan Rogers 	return false;
257dcc81be0SIan Rogers }
258dcc81be0SIan Rogers 
2592440689dSIan Rogers /**
2605ecd5a0cSIan Rogers  * setup_metric_events - Find a group of events in metric_evlist that correspond
2615ecd5a0cSIan Rogers  *                       to the IDs from a parsed metric expression.
2625ecd5a0cSIan Rogers  * @ids: the metric IDs to match.
2635ecd5a0cSIan Rogers  * @metric_evlist: the list of perf events.
2645ecd5a0cSIan Rogers  * @out_metric_events: holds the created metric events array.
2652440689dSIan Rogers  */
2665ecd5a0cSIan Rogers static int setup_metric_events(struct hashmap *ids,
2675ecd5a0cSIan Rogers 			       struct evlist *metric_evlist,
26817b3867dSIan Rogers 			       struct evsel ***out_metric_events)
269b18f3e36SAndi Kleen {
2705ecd5a0cSIan Rogers 	struct evsel **metric_events;
271ec5c5b3dSIan Rogers 	const char *metric_id;
2725ecd5a0cSIan Rogers 	struct evsel *ev;
2735ecd5a0cSIan Rogers 	size_t ids_size, matched_events, i;
2742440689dSIan Rogers 
2755ecd5a0cSIan Rogers 	*out_metric_events = NULL;
2765ecd5a0cSIan Rogers 	ids_size = hashmap__size(ids);
277b18f3e36SAndi Kleen 
2785ecd5a0cSIan Rogers 	metric_events = calloc(sizeof(void *), ids_size + 1);
2795ecd5a0cSIan Rogers 	if (!metric_events)
2805ecd5a0cSIan Rogers 		return -ENOMEM;
2815ecd5a0cSIan Rogers 
2822440689dSIan Rogers 	matched_events = 0;
2835ecd5a0cSIan Rogers 	evlist__for_each_entry(metric_evlist, ev) {
2845ecd5a0cSIan Rogers 		struct expr_id_data *val_ptr;
2855ecd5a0cSIan Rogers 
28605530a79SIan Rogers 		/*
287762a05c5SIan Rogers 		 * Check for duplicate events with the same name. For
288762a05c5SIan Rogers 		 * example, uncore_imc/cas_count_read/ will turn into 6
289762a05c5SIan Rogers 		 * events per socket on skylakex. Only the first such
2905ecd5a0cSIan Rogers 		 * event is placed in metric_events.
29105530a79SIan Rogers 		 */
292ec5c5b3dSIan Rogers 		metric_id = evsel__metric_id(ev);
293ec5c5b3dSIan Rogers 		if (contains_metric_id(metric_events, matched_events, metric_id))
29405530a79SIan Rogers 			continue;
2955ecd5a0cSIan Rogers 		/*
2965ecd5a0cSIan Rogers 		 * Does this event belong to the parse context? For
2975ecd5a0cSIan Rogers 		 * combined or shared groups, this metric may not care
2985ecd5a0cSIan Rogers 		 * about this event.
2995ecd5a0cSIan Rogers 		 */
300c302378bSEduard Zingerman 		if (hashmap__find(ids, metric_id, &val_ptr)) {
3012440689dSIan Rogers 			metric_events[matched_events++] = ev;
302dcc81be0SIan Rogers 
3035ecd5a0cSIan Rogers 			if (matched_events >= ids_size)
3042440689dSIan Rogers 				break;
3052440689dSIan Rogers 		}
306762a05c5SIan Rogers 	}
3075ecd5a0cSIan Rogers 	if (matched_events < ids_size) {
3085ecd5a0cSIan Rogers 		free(metric_events);
3095ecd5a0cSIan Rogers 		return -EINVAL;
310b18f3e36SAndi Kleen 	}
3115ecd5a0cSIan Rogers 	for (i = 0; i < ids_size; i++) {
31258fc90fdSKajol Jain 		ev = metric_events[i];
3135ecd5a0cSIan Rogers 		ev->collect_stat = true;
3145ecd5a0cSIan Rogers 
315dcc81be0SIan Rogers 		/*
3165ecd5a0cSIan Rogers 		 * The metric leader points to the identically named
3175ecd5a0cSIan Rogers 		 * event in metric_events.
318dcc81be0SIan Rogers 		 */
319dcc81be0SIan Rogers 		ev->metric_leader = ev;
320dcc81be0SIan Rogers 		/*
3215ecd5a0cSIan Rogers 		 * Mark two events with identical names in the same
3225ecd5a0cSIan Rogers 		 * group (or globally) as being in use as uncore events
3235ecd5a0cSIan Rogers 		 * may be duplicated for each pmu. Set the metric leader
3245ecd5a0cSIan Rogers 		 * of such events to be the event that appears in
3255ecd5a0cSIan Rogers 		 * metric_events.
326dcc81be0SIan Rogers 		 */
327ec5c5b3dSIan Rogers 		metric_id = evsel__metric_id(ev);
3285ecd5a0cSIan Rogers 		evlist__for_each_entry_continue(metric_evlist, ev) {
329d3e2bb43SIan Rogers 			if (!strcmp(evsel__metric_id(ev), metric_id))
330dcc81be0SIan Rogers 				ev->metric_leader = metric_events[i];
331dcc81be0SIan Rogers 		}
332dcc81be0SIan Rogers 	}
3335ecd5a0cSIan Rogers 	*out_metric_events = metric_events;
3345ecd5a0cSIan Rogers 	return 0;
335b18f3e36SAndi Kleen }
336b18f3e36SAndi Kleen 
337b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list)
338b18f3e36SAndi Kleen {
339b18f3e36SAndi Kleen 	int len;
340b18f3e36SAndi Kleen 	char *m;
341b18f3e36SAndi Kleen 
342b18f3e36SAndi Kleen 	if (!list)
343b18f3e36SAndi Kleen 		return false;
344b18f3e36SAndi Kleen 	if (!strcmp(list, "all"))
345b18f3e36SAndi Kleen 		return true;
346b18f3e36SAndi Kleen 	if (!n)
347b18f3e36SAndi Kleen 		return !strcasecmp(list, "No_group");
348b18f3e36SAndi Kleen 	len = strlen(list);
349b18f3e36SAndi Kleen 	m = strcasestr(n, list);
350b18f3e36SAndi Kleen 	if (!m)
351b18f3e36SAndi Kleen 		return false;
352b18f3e36SAndi Kleen 	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
353b18f3e36SAndi Kleen 	    (m[len] == 0 || m[len] == ';'))
354b18f3e36SAndi Kleen 		return true;
355b18f3e36SAndi Kleen 	return false;
356b18f3e36SAndi Kleen }
357b18f3e36SAndi Kleen 
358db95818eSIan Rogers static bool match_pm_metric(const struct pmu_metric *pm, const char *metric)
359be335ec2SJohn Garry {
360db95818eSIan Rogers 	return match_metric(pm->metric_group, metric) ||
361db95818eSIan Rogers 	       match_metric(pm->metric_name, metric);
362be335ec2SJohn Garry }
363be335ec2SJohn Garry 
364e5c6109fSIan Rogers /** struct mep - RB-tree node for building printing information. */
36571b0acceSAndi Kleen struct mep {
366e5c6109fSIan Rogers 	/** nd - RB-tree element. */
36771b0acceSAndi Kleen 	struct rb_node nd;
368e5c6109fSIan Rogers 	/** @metric_group: Owned metric group name, separated others with ';'. */
369e5c6109fSIan Rogers 	char *metric_group;
370e5c6109fSIan Rogers 	const char *metric_name;
371e5c6109fSIan Rogers 	const char *metric_desc;
372e5c6109fSIan Rogers 	const char *metric_long_desc;
373e5c6109fSIan Rogers 	const char *metric_expr;
374c7551a2eSIan Rogers 	const char *metric_threshold;
375e5c6109fSIan Rogers 	const char *metric_unit;
37671b0acceSAndi Kleen };
37771b0acceSAndi Kleen 
37871b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry)
37971b0acceSAndi Kleen {
38071b0acceSAndi Kleen 	struct mep *a = container_of(rb_node, struct mep, nd);
38171b0acceSAndi Kleen 	struct mep *b = (struct mep *)entry;
382e5c6109fSIan Rogers 	int ret;
38371b0acceSAndi Kleen 
384e5c6109fSIan Rogers 	ret = strcmp(a->metric_group, b->metric_group);
385e5c6109fSIan Rogers 	if (ret)
386e5c6109fSIan Rogers 		return ret;
387e5c6109fSIan Rogers 
388e5c6109fSIan Rogers 	return strcmp(a->metric_name, b->metric_name);
38971b0acceSAndi Kleen }
39071b0acceSAndi Kleen 
391e5c6109fSIan Rogers static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
39271b0acceSAndi Kleen {
39371b0acceSAndi Kleen 	struct mep *me = malloc(sizeof(struct mep));
39471b0acceSAndi Kleen 
39571b0acceSAndi Kleen 	if (!me)
39671b0acceSAndi Kleen 		return NULL;
39771b0acceSAndi Kleen 
398e5c6109fSIan Rogers 	memcpy(me, entry, sizeof(struct mep));
399e5c6109fSIan Rogers 	return &me->nd;
40071b0acceSAndi Kleen }
40171b0acceSAndi Kleen 
40271b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused,
40371b0acceSAndi Kleen 		       struct rb_node *nd)
40471b0acceSAndi Kleen {
40571b0acceSAndi Kleen 	struct mep *me = container_of(nd, struct mep, nd);
40671b0acceSAndi Kleen 
407e5c6109fSIan Rogers 	zfree(&me->metric_group);
40871b0acceSAndi Kleen 	free(me);
40971b0acceSAndi Kleen }
41071b0acceSAndi Kleen 
411e5c6109fSIan Rogers static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
412e5c6109fSIan Rogers 			      const char *metric_name)
41371b0acceSAndi Kleen {
414e5c6109fSIan Rogers 	struct rb_node *nd;
415e5c6109fSIan Rogers 	struct mep me = {
416e5c6109fSIan Rogers 		.metric_group = strdup(metric_group),
417e5c6109fSIan Rogers 		.metric_name = metric_name,
418e5c6109fSIan Rogers 	};
419e5c6109fSIan Rogers 	nd = rblist__find(groups, &me);
420e5c6109fSIan Rogers 	if (nd) {
421e5c6109fSIan Rogers 		free(me.metric_group);
422e5c6109fSIan Rogers 		return container_of(nd, struct mep, nd);
42371b0acceSAndi Kleen 	}
424e5c6109fSIan Rogers 	rblist__add_node(groups, &me);
425e5c6109fSIan Rogers 	nd = rblist__find(groups, &me);
426e5c6109fSIan Rogers 	if (nd)
427e5c6109fSIan Rogers 		return container_of(nd, struct mep, nd);
428e5c6109fSIan Rogers 	return NULL;
42971b0acceSAndi Kleen }
43071b0acceSAndi Kleen 
431db95818eSIan Rogers static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
432e5c6109fSIan Rogers 					struct rblist *groups)
433f6fe1e48SJohn Garry {
434f6fe1e48SJohn Garry 	const char *g;
435f6fe1e48SJohn Garry 	char *omg, *mg;
436f6fe1e48SJohn Garry 
437db95818eSIan Rogers 	mg = strdup(pm->metric_group ?: "No_group");
438f6fe1e48SJohn Garry 	if (!mg)
439f6fe1e48SJohn Garry 		return -ENOMEM;
440f6fe1e48SJohn Garry 	omg = mg;
441f6fe1e48SJohn Garry 	while ((g = strsep(&mg, ";")) != NULL) {
442f6fe1e48SJohn Garry 		struct mep *me;
443f6fe1e48SJohn Garry 
444f6fe1e48SJohn Garry 		g = skip_spaces(g);
445e5c6109fSIan Rogers 		if (strlen(g))
446db95818eSIan Rogers 			me = mep_lookup(groups, g, pm->metric_name);
447e5c6109fSIan Rogers 		else
448db95818eSIan Rogers 			me = mep_lookup(groups, "No_group", pm->metric_name);
449f6fe1e48SJohn Garry 
450e5c6109fSIan Rogers 		if (me) {
451db95818eSIan Rogers 			me->metric_desc = pm->desc;
452db95818eSIan Rogers 			me->metric_long_desc = pm->long_desc;
453db95818eSIan Rogers 			me->metric_expr = pm->metric_expr;
454c7551a2eSIan Rogers 			me->metric_threshold = pm->metric_threshold;
455db95818eSIan Rogers 			me->metric_unit = pm->unit;
456f6fe1e48SJohn Garry 		}
457f6fe1e48SJohn Garry 	}
458f6fe1e48SJohn Garry 	free(omg);
459f6fe1e48SJohn Garry 
460f6fe1e48SJohn Garry 	return 0;
461f6fe1e48SJohn Garry }
462f6fe1e48SJohn Garry 
463a36fadb1SJohn Garry struct metricgroup_iter_data {
464db95818eSIan Rogers 	pmu_metric_iter_fn fn;
465a36fadb1SJohn Garry 	void *data;
466a36fadb1SJohn Garry };
467a36fadb1SJohn Garry 
468db95818eSIan Rogers static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
469f8ea2c15SIan Rogers 				       const struct pmu_metrics_table *table,
47029be2fe0SIan Rogers 				       void *data)
471a36fadb1SJohn Garry {
472a36fadb1SJohn Garry 	struct metricgroup_iter_data *d = data;
473a36fadb1SJohn Garry 	struct perf_pmu *pmu = NULL;
474a36fadb1SJohn Garry 
475db95818eSIan Rogers 	if (!pm->metric_expr || !pm->compat)
476a36fadb1SJohn Garry 		return 0;
477a36fadb1SJohn Garry 
478a36fadb1SJohn Garry 	while ((pmu = perf_pmu__scan(pmu))) {
479a36fadb1SJohn Garry 
480db95818eSIan Rogers 		if (!pmu->id || strcmp(pmu->id, pm->compat))
481a36fadb1SJohn Garry 			continue;
482a36fadb1SJohn Garry 
483db95818eSIan Rogers 		return d->fn(pm, table, d->data);
484a36fadb1SJohn Garry 	}
485a36fadb1SJohn Garry 	return 0;
486a36fadb1SJohn Garry }
487a36fadb1SJohn Garry 
488db95818eSIan Rogers static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
489f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table __maybe_unused,
490660842e4SIan Rogers 					void *vdata)
491660842e4SIan Rogers {
492e5c6109fSIan Rogers 	struct rblist *groups = vdata;
493660842e4SIan Rogers 
494db95818eSIan Rogers 	return metricgroup__add_to_mep_groups(pm, groups);
495660842e4SIan Rogers }
496660842e4SIan Rogers 
497e5c6109fSIan Rogers void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
49871b0acceSAndi Kleen {
49971b0acceSAndi Kleen 	struct rblist groups;
500f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table;
501e5c6109fSIan Rogers 	struct rb_node *node, *next;
50271b0acceSAndi Kleen 
50371b0acceSAndi Kleen 	rblist__init(&groups);
50471b0acceSAndi Kleen 	groups.node_new = mep_new;
50571b0acceSAndi Kleen 	groups.node_cmp = mep_cmp;
50671b0acceSAndi Kleen 	groups.node_delete = mep_delete;
507f8ea2c15SIan Rogers 	table = pmu_metrics_table__find();
508660842e4SIan Rogers 	if (table) {
509f8ea2c15SIan Rogers 		pmu_metrics_table_for_each_metric(table,
510e5c6109fSIan Rogers 						 metricgroup__add_to_mep_groups_callback,
511e5c6109fSIan Rogers 						 &groups);
5120e0ae874SJin Yao 	}
513a36fadb1SJohn Garry 	{
514a36fadb1SJohn Garry 		struct metricgroup_iter_data data = {
515e5c6109fSIan Rogers 			.fn = metricgroup__add_to_mep_groups_callback,
516e5c6109fSIan Rogers 			.data = &groups,
517a36fadb1SJohn Garry 		};
518db95818eSIan Rogers 		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
519a36fadb1SJohn Garry 	}
520a36fadb1SJohn Garry 
521ca227029SDavidlohr Bueso 	for (node = rb_first_cached(&groups.entries); node; node = next) {
52271b0acceSAndi Kleen 		struct mep *me = container_of(node, struct mep, nd);
52371b0acceSAndi Kleen 
524e5c6109fSIan Rogers 		print_cb->print_metric(print_state,
525e5c6109fSIan Rogers 				me->metric_group,
526e5c6109fSIan Rogers 				me->metric_name,
527e5c6109fSIan Rogers 				me->metric_desc,
528e5c6109fSIan Rogers 				me->metric_long_desc,
529e5c6109fSIan Rogers 				me->metric_expr,
530c7551a2eSIan Rogers 				me->metric_threshold,
531e5c6109fSIan Rogers 				me->metric_unit);
53271b0acceSAndi Kleen 		next = rb_next(node);
53371b0acceSAndi Kleen 		rblist__remove_node(&groups, node);
53471b0acceSAndi Kleen 	}
53571b0acceSAndi Kleen }
53671b0acceSAndi Kleen 
537ec5c5b3dSIan Rogers static const char *code_characters = ",-=@";
538ec5c5b3dSIan Rogers 
539ec5c5b3dSIan Rogers static int encode_metric_id(struct strbuf *sb, const char *x)
540ec5c5b3dSIan Rogers {
541ec5c5b3dSIan Rogers 	char *c;
542ec5c5b3dSIan Rogers 	int ret = 0;
543ec5c5b3dSIan Rogers 
544ec5c5b3dSIan Rogers 	for (; *x; x++) {
545ec5c5b3dSIan Rogers 		c = strchr(code_characters, *x);
546ec5c5b3dSIan Rogers 		if (c) {
547ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, '!');
548ec5c5b3dSIan Rogers 			if (ret)
549ec5c5b3dSIan Rogers 				break;
550ec5c5b3dSIan Rogers 
551ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, '0' + (c - code_characters));
552ec5c5b3dSIan Rogers 			if (ret)
553ec5c5b3dSIan Rogers 				break;
554ec5c5b3dSIan Rogers 		} else {
555ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, *x);
556ec5c5b3dSIan Rogers 			if (ret)
557ec5c5b3dSIan Rogers 				break;
558ec5c5b3dSIan Rogers 		}
559ec5c5b3dSIan Rogers 	}
560ec5c5b3dSIan Rogers 	return ret;
561ec5c5b3dSIan Rogers }
562ec5c5b3dSIan Rogers 
563ec5c5b3dSIan Rogers static int decode_metric_id(struct strbuf *sb, const char *x)
564ec5c5b3dSIan Rogers {
565ec5c5b3dSIan Rogers 	const char *orig = x;
566ec5c5b3dSIan Rogers 	size_t i;
567ec5c5b3dSIan Rogers 	char c;
568ec5c5b3dSIan Rogers 	int ret;
569ec5c5b3dSIan Rogers 
570ec5c5b3dSIan Rogers 	for (; *x; x++) {
571ec5c5b3dSIan Rogers 		c = *x;
572ec5c5b3dSIan Rogers 		if (*x == '!') {
573ec5c5b3dSIan Rogers 			x++;
574ec5c5b3dSIan Rogers 			i = *x - '0';
575ec5c5b3dSIan Rogers 			if (i > strlen(code_characters)) {
576ec5c5b3dSIan Rogers 				pr_err("Bad metric-id encoding in: '%s'", orig);
577ec5c5b3dSIan Rogers 				return -1;
578ec5c5b3dSIan Rogers 			}
579ec5c5b3dSIan Rogers 			c = code_characters[i];
580ec5c5b3dSIan Rogers 		}
581ec5c5b3dSIan Rogers 		ret = strbuf_addch(sb, c);
582ec5c5b3dSIan Rogers 		if (ret)
583ec5c5b3dSIan Rogers 			return ret;
584ec5c5b3dSIan Rogers 	}
585ec5c5b3dSIan Rogers 	return 0;
586ec5c5b3dSIan Rogers }
587ec5c5b3dSIan Rogers 
588b85a4d61SIan Rogers static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
589ec5c5b3dSIan Rogers {
590ec5c5b3dSIan Rogers 	struct evsel *ev;
591ec5c5b3dSIan Rogers 	struct strbuf sb = STRBUF_INIT;
592ec5c5b3dSIan Rogers 	char *cur;
593ec5c5b3dSIan Rogers 	int ret = 0;
594ec5c5b3dSIan Rogers 
595ec5c5b3dSIan Rogers 	evlist__for_each_entry(perf_evlist, ev) {
596ec5c5b3dSIan Rogers 		if (!ev->metric_id)
597ec5c5b3dSIan Rogers 			continue;
598ec5c5b3dSIan Rogers 
599ec5c5b3dSIan Rogers 		ret = strbuf_setlen(&sb, 0);
600ec5c5b3dSIan Rogers 		if (ret)
601ec5c5b3dSIan Rogers 			break;
602ec5c5b3dSIan Rogers 
603ec5c5b3dSIan Rogers 		ret = decode_metric_id(&sb, ev->metric_id);
604ec5c5b3dSIan Rogers 		if (ret)
605ec5c5b3dSIan Rogers 			break;
606ec5c5b3dSIan Rogers 
607ec5c5b3dSIan Rogers 		free((char *)ev->metric_id);
608ec5c5b3dSIan Rogers 		ev->metric_id = strdup(sb.buf);
609ec5c5b3dSIan Rogers 		if (!ev->metric_id) {
610ec5c5b3dSIan Rogers 			ret = -ENOMEM;
611ec5c5b3dSIan Rogers 			break;
612ec5c5b3dSIan Rogers 		}
613ec5c5b3dSIan Rogers 		/*
614ec5c5b3dSIan Rogers 		 * If the name is just the parsed event, use the metric-id to
615ec5c5b3dSIan Rogers 		 * give a more friendly display version.
616ec5c5b3dSIan Rogers 		 */
617ec5c5b3dSIan Rogers 		if (strstr(ev->name, "metric-id=")) {
618b85a4d61SIan Rogers 			bool has_slash = false;
619ec5c5b3dSIan Rogers 
62011ff9bcdSArnaldo Carvalho de Melo 			zfree(&ev->name);
621b85a4d61SIan Rogers 			for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
622b85a4d61SIan Rogers 				*cur = '/';
623b85a4d61SIan Rogers 				has_slash = true;
624b85a4d61SIan Rogers 			}
625b85a4d61SIan Rogers 
626b85a4d61SIan Rogers 			if (modifier) {
627b85a4d61SIan Rogers 				if (!has_slash && !strchr(sb.buf, ':')) {
628b85a4d61SIan Rogers 					ret = strbuf_addch(&sb, ':');
629b85a4d61SIan Rogers 					if (ret)
630b85a4d61SIan Rogers 						break;
631b85a4d61SIan Rogers 				}
632b85a4d61SIan Rogers 				ret = strbuf_addstr(&sb, modifier);
633b85a4d61SIan Rogers 				if (ret)
634b85a4d61SIan Rogers 					break;
635b85a4d61SIan Rogers 			}
636ec5c5b3dSIan Rogers 			ev->name = strdup(sb.buf);
637ec5c5b3dSIan Rogers 			if (!ev->name) {
638ec5c5b3dSIan Rogers 				ret = -ENOMEM;
639ec5c5b3dSIan Rogers 				break;
640ec5c5b3dSIan Rogers 			}
641ec5c5b3dSIan Rogers 		}
642ec5c5b3dSIan Rogers 	}
643ec5c5b3dSIan Rogers 	strbuf_release(&sb);
644ec5c5b3dSIan Rogers 	return ret;
645ec5c5b3dSIan Rogers }
646ec5c5b3dSIan Rogers 
647ec5c5b3dSIan Rogers static int metricgroup__build_event_string(struct strbuf *events,
648ec5c5b3dSIan Rogers 					   const struct expr_parse_ctx *ctx,
649b85a4d61SIan Rogers 					   const char *modifier,
650180a5013SIan Rogers 					   bool group_events)
651f742634aSKan Liang {
652ded80bdaSIan Rogers 	struct hashmap_entry *cur;
6534e21c13aSIan Rogers 	size_t bkt;
6549aa09230SIan Rogers 	bool no_group = true, has_tool_events = false;
6559aa09230SIan Rogers 	bool tool_events[PERF_TOOL_MAX] = {false};
656ec5c5b3dSIan Rogers 	int ret = 0;
657ec5c5b3dSIan Rogers 
658ec5c5b3dSIan Rogers #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
659f742634aSKan Liang 
660cb94a02eSIan Rogers 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
661c302378bSEduard Zingerman 		const char *sep, *rsep, *id = cur->pkey;
6629aa09230SIan Rogers 		enum perf_tool_event ev;
663ec5c5b3dSIan Rogers 
664ec5c5b3dSIan Rogers 		pr_debug("found event %s\n", id);
6659aa09230SIan Rogers 
6669aa09230SIan Rogers 		/* Always move tool events outside of the group. */
6679aa09230SIan Rogers 		ev = perf_tool_event__from_str(id);
6689aa09230SIan Rogers 		if (ev != PERF_TOOL_NONE) {
6699aa09230SIan Rogers 			has_tool_events = true;
6709aa09230SIan Rogers 			tool_events[ev] = true;
671f742634aSKan Liang 			continue;
672f742634aSKan Liang 		}
673ec5c5b3dSIan Rogers 		/* Separate events with commas and open the group if necessary. */
674ec5c5b3dSIan Rogers 		if (no_group) {
675180a5013SIan Rogers 			if (group_events) {
676ec5c5b3dSIan Rogers 				ret = strbuf_addch(events, '{');
677ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
678ec5c5b3dSIan Rogers 			}
679ec5c5b3dSIan Rogers 
680f742634aSKan Liang 			no_group = false;
681ec5c5b3dSIan Rogers 		} else {
682ec5c5b3dSIan Rogers 			ret = strbuf_addch(events, ',');
683ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
684f742634aSKan Liang 		}
685ec5c5b3dSIan Rogers 		/*
686ec5c5b3dSIan Rogers 		 * Encode the ID as an event string. Add a qualifier for
687ec5c5b3dSIan Rogers 		 * metric_id that is the original name except with characters
688ec5c5b3dSIan Rogers 		 * that parse-events can't parse replaced. For example,
689ec5c5b3dSIan Rogers 		 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
690ec5c5b3dSIan Rogers 		 */
691ec5c5b3dSIan Rogers 		sep = strchr(id, '@');
692ec5c5b3dSIan Rogers 		if (sep != NULL) {
693ec5c5b3dSIan Rogers 			ret = strbuf_add(events, id, sep - id);
694ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
695ec5c5b3dSIan Rogers 			ret = strbuf_addch(events, '/');
696ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
697ec5c5b3dSIan Rogers 			rsep = strrchr(sep, '@');
698ec5c5b3dSIan Rogers 			ret = strbuf_add(events, sep + 1, rsep - sep - 1);
699ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
700ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, ",metric-id=");
701ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
702ec5c5b3dSIan Rogers 			sep = rsep;
703ec5c5b3dSIan Rogers 		} else {
704ec5c5b3dSIan Rogers 			sep = strchr(id, ':');
705ec5c5b3dSIan Rogers 			if (sep != NULL) {
706ec5c5b3dSIan Rogers 				ret = strbuf_add(events, id, sep - id);
707ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
708ec5c5b3dSIan Rogers 			} else {
709ec5c5b3dSIan Rogers 				ret = strbuf_addstr(events, id);
710ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
711f742634aSKan Liang 			}
712ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, "/metric-id=");
713ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
714ec5c5b3dSIan Rogers 		}
715ec5c5b3dSIan Rogers 		ret = encode_metric_id(events, id);
716ec5c5b3dSIan Rogers 		RETURN_IF_NON_ZERO(ret);
717ec5c5b3dSIan Rogers 		ret = strbuf_addstr(events, "/");
718ec5c5b3dSIan Rogers 		RETURN_IF_NON_ZERO(ret);
719f742634aSKan Liang 
720ec5c5b3dSIan Rogers 		if (sep != NULL) {
721ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, sep + 1);
722ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
723e2ce1059SIan Rogers 		}
724b85a4d61SIan Rogers 		if (modifier) {
725b85a4d61SIan Rogers 			ret = strbuf_addstr(events, modifier);
726b85a4d61SIan Rogers 			RETURN_IF_NON_ZERO(ret);
727b85a4d61SIan Rogers 		}
728ab483d8bSKan Liang 	}
729180a5013SIan Rogers 	if (!no_group && group_events) {
73060344f1aSZhengjun Xing 		ret = strbuf_addf(events, "}:W");
7319aa09230SIan Rogers 		RETURN_IF_NON_ZERO(ret);
7329aa09230SIan Rogers 	}
7339aa09230SIan Rogers 	if (has_tool_events) {
7349aa09230SIan Rogers 		int i;
7359aa09230SIan Rogers 
7369aa09230SIan Rogers 		perf_tool_event__for_each_event(i) {
7379aa09230SIan Rogers 			if (tool_events[i]) {
7389aa09230SIan Rogers 				if (!no_group) {
7399aa09230SIan Rogers 					ret = strbuf_addch(events, ',');
7409aa09230SIan Rogers 					RETURN_IF_NON_ZERO(ret);
7419aa09230SIan Rogers 				}
7429aa09230SIan Rogers 				no_group = false;
7439aa09230SIan Rogers 				ret = strbuf_addstr(events, perf_tool_event__to_str(i));
7449aa09230SIan Rogers 				RETURN_IF_NON_ZERO(ret);
7459aa09230SIan Rogers 			}
7469aa09230SIan Rogers 		}
7479aa09230SIan Rogers 	}
748ec5c5b3dSIan Rogers 
749ec5c5b3dSIan Rogers 	return ret;
750ec5c5b3dSIan Rogers #undef RETURN_IF_NON_ZERO
751ec5c5b3dSIan Rogers }
752ab483d8bSKan Liang 
753db95818eSIan Rogers int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
7541e1a873dSKajol Jain {
7551e1a873dSKajol Jain 	return 1;
7561e1a873dSKajol Jain }
7571e1a873dSKajol Jain 
75880be6434SIan Rogers /*
75980be6434SIan Rogers  * A singly linked list on the stack of the names of metrics being
76080be6434SIan Rogers  * processed. Used to identify recursion.
76180be6434SIan Rogers  */
76280be6434SIan Rogers struct visited_metric {
76380be6434SIan Rogers 	const char *name;
76480be6434SIan Rogers 	const struct visited_metric *parent;
76580be6434SIan Rogers };
76680be6434SIan Rogers 
767be335ec2SJohn Garry struct metricgroup_add_iter_data {
768be335ec2SJohn Garry 	struct list_head *metric_list;
76968074811SIan Rogers 	const char *metric_name;
770b85a4d61SIan Rogers 	const char *modifier;
771be335ec2SJohn Garry 	int *ret;
772be335ec2SJohn Garry 	bool *has_match;
773be335ec2SJohn Garry 	bool metric_no_group;
7741fd09e29SIan Rogers 	bool metric_no_threshold;
7751725e9cdSIan Rogers 	const char *user_requested_cpu_list;
7761725e9cdSIan Rogers 	bool system_wide;
77780be6434SIan Rogers 	struct metric *root_metric;
77880be6434SIan Rogers 	const struct visited_metric *visited;
779f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table;
780be335ec2SJohn Garry };
781be335ec2SJohn Garry 
782d3abd7b8SIan Rogers static bool metricgroup__find_metric(const char *metric,
783f8ea2c15SIan Rogers 				     const struct pmu_metrics_table *table,
784db95818eSIan Rogers 				     struct pmu_metric *pm);
785d3abd7b8SIan Rogers 
78680be6434SIan Rogers static int add_metric(struct list_head *metric_list,
787db95818eSIan Rogers 		      const struct pmu_metric *pm,
788b85a4d61SIan Rogers 		      const char *modifier,
78980be6434SIan Rogers 		      bool metric_no_group,
7901fd09e29SIan Rogers 		      bool metric_no_threshold,
7911725e9cdSIan Rogers 		      const char *user_requested_cpu_list,
7921725e9cdSIan Rogers 		      bool system_wide,
79380be6434SIan Rogers 		      struct metric *root_metric,
79480be6434SIan Rogers 		      const struct visited_metric *visited,
795f8ea2c15SIan Rogers 		      const struct pmu_metrics_table *table);
79680be6434SIan Rogers 
79780be6434SIan Rogers /**
79880be6434SIan Rogers  * resolve_metric - Locate metrics within the root metric and recursively add
79980be6434SIan Rogers  *                    references to them.
80080be6434SIan Rogers  * @metric_list: The list the metric is added to.
801b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
80280be6434SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
80380be6434SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
80480be6434SIan Rogers  *                   user may override.
8051725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
8061725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
80780be6434SIan Rogers  * @root_metric: Metrics may reference other metrics to form a tree. In this
80880be6434SIan Rogers  *               case the root_metric holds all the IDs and a list of referenced
80980be6434SIan Rogers  *               metrics. When adding a root this argument is NULL.
81080be6434SIan Rogers  * @visited: A singly linked list of metric names being added that is used to
81180be6434SIan Rogers  *           detect recursion.
812eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
81380be6434SIan Rogers  *       architecture perf is running upon.
81480be6434SIan Rogers  */
81580be6434SIan Rogers static int resolve_metric(struct list_head *metric_list,
816b85a4d61SIan Rogers 			  const char *modifier,
81780be6434SIan Rogers 			  bool metric_no_group,
8181fd09e29SIan Rogers 			  bool metric_no_threshold,
8191725e9cdSIan Rogers 			  const char *user_requested_cpu_list,
8201725e9cdSIan Rogers 			  bool system_wide,
82180be6434SIan Rogers 			  struct metric *root_metric,
82280be6434SIan Rogers 			  const struct visited_metric *visited,
823f8ea2c15SIan Rogers 			  const struct pmu_metrics_table *table)
82480be6434SIan Rogers {
82580be6434SIan Rogers 	struct hashmap_entry *cur;
82680be6434SIan Rogers 	size_t bkt;
82780be6434SIan Rogers 	struct to_resolve {
82880be6434SIan Rogers 		/* The metric to resolve. */
829db95818eSIan Rogers 		struct pmu_metric pm;
83080be6434SIan Rogers 		/*
83180be6434SIan Rogers 		 * The key in the IDs map, this may differ from in case,
832db95818eSIan Rogers 		 * etc. from pm->metric_name.
83380be6434SIan Rogers 		 */
83480be6434SIan Rogers 		const char *key;
83580be6434SIan Rogers 	} *pending = NULL;
83680be6434SIan Rogers 	int i, ret = 0, pending_cnt = 0;
83780be6434SIan Rogers 
83880be6434SIan Rogers 	/*
83980be6434SIan Rogers 	 * Iterate all the parsed IDs and if there's a matching metric and it to
84080be6434SIan Rogers 	 * the pending array.
84180be6434SIan Rogers 	 */
84280be6434SIan Rogers 	hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
843db95818eSIan Rogers 		struct pmu_metric pm;
84480be6434SIan Rogers 
845db95818eSIan Rogers 		if (metricgroup__find_metric(cur->pkey, table, &pm)) {
84680be6434SIan Rogers 			pending = realloc(pending,
84780be6434SIan Rogers 					(pending_cnt + 1) * sizeof(struct to_resolve));
84880be6434SIan Rogers 			if (!pending)
84980be6434SIan Rogers 				return -ENOMEM;
85080be6434SIan Rogers 
851db95818eSIan Rogers 			memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
852c302378bSEduard Zingerman 			pending[pending_cnt].key = cur->pkey;
85380be6434SIan Rogers 			pending_cnt++;
85480be6434SIan Rogers 		}
85580be6434SIan Rogers 	}
85680be6434SIan Rogers 
85780be6434SIan Rogers 	/* Remove the metric IDs from the context. */
85880be6434SIan Rogers 	for (i = 0; i < pending_cnt; i++)
85980be6434SIan Rogers 		expr__del_id(root_metric->pctx, pending[i].key);
86080be6434SIan Rogers 
86180be6434SIan Rogers 	/*
86280be6434SIan Rogers 	 * Recursively add all the metrics, IDs are added to the root metric's
86380be6434SIan Rogers 	 * context.
86480be6434SIan Rogers 	 */
86580be6434SIan Rogers 	for (i = 0; i < pending_cnt; i++) {
866db95818eSIan Rogers 		ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
8671fd09e29SIan Rogers 				 metric_no_threshold, user_requested_cpu_list, system_wide,
8681fd09e29SIan Rogers 				 root_metric, visited, table);
86980be6434SIan Rogers 		if (ret)
87080be6434SIan Rogers 			break;
87180be6434SIan Rogers 	}
87280be6434SIan Rogers 
87380be6434SIan Rogers 	free(pending);
87480be6434SIan Rogers 	return ret;
87580be6434SIan Rogers }
87680be6434SIan Rogers 
87768074811SIan Rogers /**
87868074811SIan Rogers  * __add_metric - Add a metric to metric_list.
87968074811SIan Rogers  * @metric_list: The list the metric is added to.
880db95818eSIan Rogers  * @pm: The pmu_metric containing the metric to be added.
881b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
88268074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
88368074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
88468074811SIan Rogers  *                   user may override.
8851fd09e29SIan Rogers  * @metric_no_threshold: Should threshold expressions be ignored?
88668074811SIan Rogers  * @runtime: A special argument for the parser only known at runtime.
8871725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
8881725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
88980be6434SIan Rogers  * @root_metric: Metrics may reference other metrics to form a tree. In this
89080be6434SIan Rogers  *               case the root_metric holds all the IDs and a list of referenced
89180be6434SIan Rogers  *               metrics. When adding a root this argument is NULL.
89280be6434SIan Rogers  * @visited: A singly linked list of metric names being added that is used to
89380be6434SIan Rogers  *           detect recursion.
894eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
89580be6434SIan Rogers  *       architecture perf is running upon.
89668074811SIan Rogers  */
897119e521aSJiri Olsa static int __add_metric(struct list_head *metric_list,
898db95818eSIan Rogers 			const struct pmu_metric *pm,
899b85a4d61SIan Rogers 			const char *modifier,
90005530a79SIan Rogers 			bool metric_no_group,
9011fd09e29SIan Rogers 			bool metric_no_threshold,
90283de0b7dSJiri Olsa 			int runtime,
9031725e9cdSIan Rogers 			const char *user_requested_cpu_list,
9041725e9cdSIan Rogers 			bool system_wide,
90580be6434SIan Rogers 			struct metric *root_metric,
90680be6434SIan Rogers 			const struct visited_metric *visited,
907f8ea2c15SIan Rogers 			const struct pmu_metrics_table *table)
90847352abaSKajol Jain {
90980be6434SIan Rogers 	const struct visited_metric *vm;
91080be6434SIan Rogers 	int ret;
91180be6434SIan Rogers 	bool is_root = !root_metric;
912d0a3052fSIan Rogers 	const char *expr;
91380be6434SIan Rogers 	struct visited_metric visited_node = {
914db95818eSIan Rogers 		.name = pm->metric_name,
91580be6434SIan Rogers 		.parent = visited,
91680be6434SIan Rogers 	};
91747352abaSKajol Jain 
91880be6434SIan Rogers 	for (vm = visited; vm; vm = vm->parent) {
919db95818eSIan Rogers 		if (!strcmp(pm->metric_name, vm->name)) {
920db95818eSIan Rogers 			pr_err("failed: recursion detected for %s\n", pm->metric_name);
92180be6434SIan Rogers 			return -1;
92280be6434SIan Rogers 		}
92380be6434SIan Rogers 	}
92480be6434SIan Rogers 
92580be6434SIan Rogers 	if (is_root) {
92683de0b7dSJiri Olsa 		/*
92780be6434SIan Rogers 		 * This metric is the root of a tree and may reference other
92880be6434SIan Rogers 		 * metrics that are added recursively.
92983de0b7dSJiri Olsa 		 */
930db95818eSIan Rogers 		root_metric = metric__new(pm, modifier, metric_no_group, runtime,
9311725e9cdSIan Rogers 					  user_requested_cpu_list, system_wide);
93280be6434SIan Rogers 		if (!root_metric)
93347352abaSKajol Jain 			return -ENOMEM;
93447352abaSKajol Jain 
93583de0b7dSJiri Olsa 	} else {
93646bdc0bfSIan Rogers 		int cnt = 0;
93746bdc0bfSIan Rogers 
93883de0b7dSJiri Olsa 		/*
939a3de7690SIan Rogers 		 * This metric was referenced in a metric higher in the
940a3de7690SIan Rogers 		 * tree. Check if the same metric is already resolved in the
941a3de7690SIan Rogers 		 * metric_refs list.
94283de0b7dSJiri Olsa 		 */
94346bdc0bfSIan Rogers 		if (root_metric->metric_refs) {
94446bdc0bfSIan Rogers 			for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
945db95818eSIan Rogers 				if (!strcmp(pm->metric_name,
94646bdc0bfSIan Rogers 					    root_metric->metric_refs[cnt].metric_name))
947a3de7690SIan Rogers 					return 0;
948a3de7690SIan Rogers 			}
94946bdc0bfSIan Rogers 		}
950a3de7690SIan Rogers 
95146bdc0bfSIan Rogers 		/* Create reference. Need space for the entry and the terminator. */
95246bdc0bfSIan Rogers 		root_metric->metric_refs = realloc(root_metric->metric_refs,
95346bdc0bfSIan Rogers 						(cnt + 2) * sizeof(struct metric_ref));
95446bdc0bfSIan Rogers 		if (!root_metric->metric_refs)
95583de0b7dSJiri Olsa 			return -ENOMEM;
95683de0b7dSJiri Olsa 
95783de0b7dSJiri Olsa 		/*
95883de0b7dSJiri Olsa 		 * Intentionally passing just const char pointers,
95983de0b7dSJiri Olsa 		 * from 'pe' object, so they never go away. We don't
96083de0b7dSJiri Olsa 		 * need to change them, so there's no need to create
96183de0b7dSJiri Olsa 		 * our own copy.
96283de0b7dSJiri Olsa 		 */
963db95818eSIan Rogers 		root_metric->metric_refs[cnt].metric_name = pm->metric_name;
964db95818eSIan Rogers 		root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
96583de0b7dSJiri Olsa 
96646bdc0bfSIan Rogers 		/* Null terminate array. */
96746bdc0bfSIan Rogers 		root_metric->metric_refs[cnt+1].metric_name = NULL;
96846bdc0bfSIan Rogers 		root_metric->metric_refs[cnt+1].metric_expr = NULL;
96983de0b7dSJiri Olsa 	}
97083de0b7dSJiri Olsa 
97183de0b7dSJiri Olsa 	/*
97283de0b7dSJiri Olsa 	 * For both the parent and referenced metrics, we parse
97380be6434SIan Rogers 	 * all the metric's IDs and add it to the root context.
97483de0b7dSJiri Olsa 	 */
975d0a3052fSIan Rogers 	ret = 0;
976d0a3052fSIan Rogers 	expr = pm->metric_expr;
977d0a3052fSIan Rogers 	if (is_root && pm->metric_threshold) {
978d0a3052fSIan Rogers 		/*
979d0a3052fSIan Rogers 		 * Threshold expressions are built off the actual metric. Switch
980d0a3052fSIan Rogers 		 * to use that in case of additional necessary events. Change
981d0a3052fSIan Rogers 		 * the visited node name to avoid this being flagged as
9821fd09e29SIan Rogers 		 * recursion. If the threshold events are disabled, just use the
9831fd09e29SIan Rogers 		 * metric's name as a reference. This allows metric threshold
9841fd09e29SIan Rogers 		 * computation if there are sufficient events.
985d0a3052fSIan Rogers 		 */
986d0a3052fSIan Rogers 		assert(strstr(pm->metric_threshold, pm->metric_name));
9871fd09e29SIan Rogers 		expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
988d0a3052fSIan Rogers 		visited_node.name = "__threshold__";
989d0a3052fSIan Rogers 	}
990d0a3052fSIan Rogers 	if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) {
99180be6434SIan Rogers 		/* Broken metric. */
99280be6434SIan Rogers 		ret = -EINVAL;
993d0a3052fSIan Rogers 	}
994d0a3052fSIan Rogers 	if (!ret) {
99580be6434SIan Rogers 		/* Resolve referenced metrics. */
9961725e9cdSIan Rogers 		ret = resolve_metric(metric_list, modifier, metric_no_group,
9971fd09e29SIan Rogers 				     metric_no_threshold, user_requested_cpu_list,
9981fd09e29SIan Rogers 				     system_wide, root_metric, &visited_node, table);
999ded80bdaSIan Rogers 	}
100080be6434SIan Rogers 	if (ret) {
100180be6434SIan Rogers 		if (is_root)
100280be6434SIan Rogers 			metric__free(root_metric);
100383de0b7dSJiri Olsa 
100480be6434SIan Rogers 	} else if (is_root)
100580be6434SIan Rogers 		list_add(&root_metric->nd, metric_list);
10066bf2102bSIan Rogers 
100780be6434SIan Rogers 	return ret;
100847352abaSKajol Jain }
100947352abaSKajol Jain 
1010660842e4SIan Rogers struct metricgroup__find_metric_data {
1011660842e4SIan Rogers 	const char *metric;
1012db95818eSIan Rogers 	struct pmu_metric *pm;
1013660842e4SIan Rogers };
1014ce391940SJiri Olsa 
1015db95818eSIan Rogers static int metricgroup__find_metric_callback(const struct pmu_metric *pm,
1016f8ea2c15SIan Rogers 					     const struct pmu_metrics_table *table  __maybe_unused,
1017660842e4SIan Rogers 					     void *vdata)
1018660842e4SIan Rogers {
1019660842e4SIan Rogers 	struct metricgroup__find_metric_data *data = vdata;
1020660842e4SIan Rogers 
1021db95818eSIan Rogers 	if (!match_metric(pm->metric_name, data->metric))
1022660842e4SIan Rogers 		return 0;
1023660842e4SIan Rogers 
1024db95818eSIan Rogers 	memcpy(data->pm, pm, sizeof(*pm));
1025d3abd7b8SIan Rogers 	return 1;
1026660842e4SIan Rogers }
1027ce391940SJiri Olsa 
1028d3abd7b8SIan Rogers static bool metricgroup__find_metric(const char *metric,
1029f8ea2c15SIan Rogers 				     const struct pmu_metrics_table *table,
1030db95818eSIan Rogers 				     struct pmu_metric *pm)
103183de0b7dSJiri Olsa {
1032660842e4SIan Rogers 	struct metricgroup__find_metric_data data = {
1033660842e4SIan Rogers 		.metric = metric,
1034db95818eSIan Rogers 		.pm = pm,
1035660842e4SIan Rogers 	};
103683de0b7dSJiri Olsa 
1037f8ea2c15SIan Rogers 	return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data)
1038d3abd7b8SIan Rogers 		? true : false;
103983de0b7dSJiri Olsa }
104083de0b7dSJiri Olsa 
1041119e521aSJiri Olsa static int add_metric(struct list_head *metric_list,
1042db95818eSIan Rogers 		      const struct pmu_metric *pm,
1043b85a4d61SIan Rogers 		      const char *modifier,
104483de0b7dSJiri Olsa 		      bool metric_no_group,
10451fd09e29SIan Rogers 		      bool metric_no_threshold,
10461725e9cdSIan Rogers 		      const char *user_requested_cpu_list,
10471725e9cdSIan Rogers 		      bool system_wide,
104880be6434SIan Rogers 		      struct metric *root_metric,
104980be6434SIan Rogers 		      const struct visited_metric *visited,
1050f8ea2c15SIan Rogers 		      const struct pmu_metrics_table *table)
1051a29c164aSJiri Olsa {
1052a29c164aSJiri Olsa 	int ret = 0;
1053a29c164aSJiri Olsa 
1054db95818eSIan Rogers 	pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
1055a29c164aSJiri Olsa 
1056db95818eSIan Rogers 	if (!strstr(pm->metric_expr, "?")) {
10571fd09e29SIan Rogers 		ret = __add_metric(metric_list, pm, modifier, metric_no_group,
10581fd09e29SIan Rogers 				   metric_no_threshold, 0, user_requested_cpu_list,
10591fd09e29SIan Rogers 				   system_wide, root_metric, visited, table);
1060a29c164aSJiri Olsa 	} else {
1061a29c164aSJiri Olsa 		int j, count;
1062a29c164aSJiri Olsa 
1063db95818eSIan Rogers 		count = arch_get_runtimeparam(pm);
1064a29c164aSJiri Olsa 
1065a29c164aSJiri Olsa 		/* This loop is added to create multiple
1066a29c164aSJiri Olsa 		 * events depend on count value and add
1067119e521aSJiri Olsa 		 * those events to metric_list.
1068a29c164aSJiri Olsa 		 */
1069a29c164aSJiri Olsa 
107080be6434SIan Rogers 		for (j = 0; j < count && !ret; j++)
10711fd09e29SIan Rogers 			ret = __add_metric(metric_list, pm, modifier, metric_no_group,
10721fd09e29SIan Rogers 					   metric_no_threshold, j, user_requested_cpu_list,
10731fd09e29SIan Rogers 					   system_wide, root_metric, visited, table);
1074a29c164aSJiri Olsa 	}
1075a29c164aSJiri Olsa 
1076a29c164aSJiri Olsa 	return ret;
1077a29c164aSJiri Olsa }
1078a29c164aSJiri Olsa 
1079db95818eSIan Rogers static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
1080f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table __maybe_unused,
1081be335ec2SJohn Garry 					void *data)
1082be335ec2SJohn Garry {
1083be335ec2SJohn Garry 	struct metricgroup_add_iter_data *d = data;
1084be335ec2SJohn Garry 	int ret;
1085be335ec2SJohn Garry 
1086db95818eSIan Rogers 	if (!match_pm_metric(pm, d->metric_name))
1087be335ec2SJohn Garry 		return 0;
1088be335ec2SJohn Garry 
1089db95818eSIan Rogers 	ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
10901fd09e29SIan Rogers 			 d->metric_no_threshold, d->user_requested_cpu_list,
10911fd09e29SIan Rogers 			 d->system_wide, d->root_metric, d->visited, d->table);
1092be335ec2SJohn Garry 	if (ret)
1093fe7a98b9SJohn Garry 		goto out;
1094be335ec2SJohn Garry 
1095be335ec2SJohn Garry 	*(d->has_match) = true;
1096be335ec2SJohn Garry 
1097fe7a98b9SJohn Garry out:
1098fe7a98b9SJohn Garry 	*(d->ret) = ret;
1099fe7a98b9SJohn Garry 	return ret;
1100be335ec2SJohn Garry }
1101be335ec2SJohn Garry 
11026b6b16b3SIan Rogers /**
11036b6b16b3SIan Rogers  * metric_list_cmp - list_sort comparator that sorts metrics with more events to
11049aa09230SIan Rogers  *                   the front. tool events are excluded from the count.
11056b6b16b3SIan Rogers  */
110680be6434SIan Rogers static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
110780be6434SIan Rogers 			   const struct list_head *r)
110880be6434SIan Rogers {
110980be6434SIan Rogers 	const struct metric *left = container_of(l, struct metric, nd);
111080be6434SIan Rogers 	const struct metric *right = container_of(r, struct metric, nd);
11116b6b16b3SIan Rogers 	struct expr_id_data *data;
11129aa09230SIan Rogers 	int i, left_count, right_count;
111380be6434SIan Rogers 
11146b6b16b3SIan Rogers 	left_count = hashmap__size(left->pctx->ids);
11159aa09230SIan Rogers 	perf_tool_event__for_each_event(i) {
11169aa09230SIan Rogers 		if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
11176b6b16b3SIan Rogers 			left_count--;
11189aa09230SIan Rogers 	}
11196b6b16b3SIan Rogers 
11206b6b16b3SIan Rogers 	right_count = hashmap__size(right->pctx->ids);
11219aa09230SIan Rogers 	perf_tool_event__for_each_event(i) {
11229aa09230SIan Rogers 		if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
11236b6b16b3SIan Rogers 			right_count--;
11249aa09230SIan Rogers 	}
11256b6b16b3SIan Rogers 
11266b6b16b3SIan Rogers 	return right_count - left_count;
112780be6434SIan Rogers }
112880be6434SIan Rogers 
1129660842e4SIan Rogers struct metricgroup__add_metric_data {
1130660842e4SIan Rogers 	struct list_head *list;
1131660842e4SIan Rogers 	const char *metric_name;
1132660842e4SIan Rogers 	const char *modifier;
11331725e9cdSIan Rogers 	const char *user_requested_cpu_list;
1134660842e4SIan Rogers 	bool metric_no_group;
11351fd09e29SIan Rogers 	bool metric_no_threshold;
11361725e9cdSIan Rogers 	bool system_wide;
1137660842e4SIan Rogers 	bool has_match;
1138660842e4SIan Rogers };
1139660842e4SIan Rogers 
1140db95818eSIan Rogers static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
1141f8ea2c15SIan Rogers 					    const struct pmu_metrics_table *table,
1142660842e4SIan Rogers 					    void *vdata)
1143660842e4SIan Rogers {
1144660842e4SIan Rogers 	struct metricgroup__add_metric_data *data = vdata;
1145660842e4SIan Rogers 	int ret = 0;
1146660842e4SIan Rogers 
1147ccc66c60SIan Rogers 	if (pm->metric_expr && match_pm_metric(pm, data->metric_name)) {
1148ccc66c60SIan Rogers 		bool metric_no_group = data->metric_no_group ||
1149ccc66c60SIan Rogers 			match_metric(data->metric_name, pm->metricgroup_no_group);
1150660842e4SIan Rogers 
1151660842e4SIan Rogers 		data->has_match = true;
1152ccc66c60SIan Rogers 		ret = add_metric(data->list, pm, data->modifier, metric_no_group,
11531fd09e29SIan Rogers 				 data->metric_no_threshold, data->user_requested_cpu_list,
11541fd09e29SIan Rogers 				 data->system_wide, /*root_metric=*/NULL,
11551fd09e29SIan Rogers 				 /*visited_metrics=*/NULL, table);
1156660842e4SIan Rogers 	}
1157660842e4SIan Rogers 	return ret;
1158660842e4SIan Rogers }
1159660842e4SIan Rogers 
116068074811SIan Rogers /**
116168074811SIan Rogers  * metricgroup__add_metric - Find and add a metric, or a metric group.
116268074811SIan Rogers  * @metric_name: The name of the metric or metric group. For example, "IPC"
116368074811SIan Rogers  *               could be the name of a metric and "TopDownL1" the name of a
116468074811SIan Rogers  *               metric group.
1165b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
116668074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
116768074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
116868074811SIan Rogers  *                   user may override.
11691725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
11701725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
117168074811SIan Rogers  * @metric_list: The list that the metric or metric group are added to.
1172eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
117368074811SIan Rogers  *       architecture perf is running upon.
117468074811SIan Rogers  */
117517b3867dSIan Rogers static int metricgroup__add_metric(const char *metric_name, const char *modifier,
11761fd09e29SIan Rogers 				   bool metric_no_group, bool metric_no_threshold,
11771725e9cdSIan Rogers 				   const char *user_requested_cpu_list,
11781725e9cdSIan Rogers 				   bool system_wide,
1179119e521aSJiri Olsa 				   struct list_head *metric_list,
1180f8ea2c15SIan Rogers 				   const struct pmu_metrics_table *table)
1181b18f3e36SAndi Kleen {
118298461d9dSJiri Olsa 	LIST_HEAD(list);
1183660842e4SIan Rogers 	int ret;
118490810399SIan Rogers 	bool has_match = false;
1185b18f3e36SAndi Kleen 
1186660842e4SIan Rogers 	{
1187660842e4SIan Rogers 		struct metricgroup__add_metric_data data = {
1188660842e4SIan Rogers 			.list = &list,
1189660842e4SIan Rogers 			.metric_name = metric_name,
1190660842e4SIan Rogers 			.modifier = modifier,
1191660842e4SIan Rogers 			.metric_no_group = metric_no_group,
11921fd09e29SIan Rogers 			.metric_no_threshold = metric_no_threshold,
11931725e9cdSIan Rogers 			.user_requested_cpu_list = user_requested_cpu_list,
11941725e9cdSIan Rogers 			.system_wide = system_wide,
1195660842e4SIan Rogers 			.has_match = false,
1196660842e4SIan Rogers 		};
119768074811SIan Rogers 		/*
1198660842e4SIan Rogers 		 * Iterate over all metrics seeing if metric matches either the
1199660842e4SIan Rogers 		 * name or group. When it does add the metric to the list.
120068074811SIan Rogers 		 */
1201f8ea2c15SIan Rogers 		ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback,
1202660842e4SIan Rogers 						       &data);
120390810399SIan Rogers 		if (ret)
120427adafcdSNamhyung Kim 			goto out;
1205ce391940SJiri Olsa 
1206660842e4SIan Rogers 		has_match = data.has_match;
1207660842e4SIan Rogers 	}
1208be335ec2SJohn Garry 	{
1209be335ec2SJohn Garry 		struct metricgroup_iter_data data = {
1210be335ec2SJohn Garry 			.fn = metricgroup__add_metric_sys_event_iter,
1211be335ec2SJohn Garry 			.data = (void *) &(struct metricgroup_add_iter_data) {
1212be335ec2SJohn Garry 				.metric_list = &list,
121368074811SIan Rogers 				.metric_name = metric_name,
1214b85a4d61SIan Rogers 				.modifier = modifier,
1215be335ec2SJohn Garry 				.metric_no_group = metric_no_group,
12161725e9cdSIan Rogers 				.user_requested_cpu_list = user_requested_cpu_list,
12171725e9cdSIan Rogers 				.system_wide = system_wide,
1218be335ec2SJohn Garry 				.has_match = &has_match,
1219be335ec2SJohn Garry 				.ret = &ret,
1220eeac7730SIan Rogers 				.table = table,
1221be335ec2SJohn Garry 			},
1222be335ec2SJohn Garry 		};
1223be335ec2SJohn Garry 
1224db95818eSIan Rogers 		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
1225be335ec2SJohn Garry 	}
1226ce391940SJiri Olsa 	/* End of pmu events. */
12275ecd5a0cSIan Rogers 	if (!has_match)
122827adafcdSNamhyung Kim 		ret = -EINVAL;
122998461d9dSJiri Olsa 
123027adafcdSNamhyung Kim out:
123127adafcdSNamhyung Kim 	/*
123227adafcdSNamhyung Kim 	 * add to metric_list so that they can be released
123327adafcdSNamhyung Kim 	 * even if it's failed
123427adafcdSNamhyung Kim 	 */
1235119e521aSJiri Olsa 	list_splice(&list, metric_list);
123627adafcdSNamhyung Kim 	return ret;
123790810399SIan Rogers }
1238b18f3e36SAndi Kleen 
123968074811SIan Rogers /**
124068074811SIan Rogers  * metricgroup__add_metric_list - Find and add metrics, or metric groups,
124168074811SIan Rogers  *                                specified in a list.
124268074811SIan Rogers  * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
124368074811SIan Rogers  *        would match the IPC and CPI metrics, and TopDownL1 would match all
124468074811SIan Rogers  *        the metrics in the TopDownL1 group.
124568074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
124668074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
124768074811SIan Rogers  *                   user may override.
12481725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
12491725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
125068074811SIan Rogers  * @metric_list: The list that metrics are added to.
1251eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
125268074811SIan Rogers  *       architecture perf is running upon.
125368074811SIan Rogers  */
125405530a79SIan Rogers static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
12551fd09e29SIan Rogers 					bool metric_no_threshold,
12561725e9cdSIan Rogers 					const char *user_requested_cpu_list,
12571725e9cdSIan Rogers 					bool system_wide, struct list_head *metric_list,
1258f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table)
1259b18f3e36SAndi Kleen {
1260b85a4d61SIan Rogers 	char *list_itr, *list_copy, *metric_name, *modifier;
1261ec5c5b3dSIan Rogers 	int ret, count = 0;
1262b18f3e36SAndi Kleen 
1263b85a4d61SIan Rogers 	list_copy = strdup(list);
1264b85a4d61SIan Rogers 	if (!list_copy)
1265b18f3e36SAndi Kleen 		return -ENOMEM;
1266b85a4d61SIan Rogers 	list_itr = list_copy;
1267411bc316SAndi Kleen 
1268b85a4d61SIan Rogers 	while ((metric_name = strsep(&list_itr, ",")) != NULL) {
1269b85a4d61SIan Rogers 		modifier = strchr(metric_name, ':');
1270b85a4d61SIan Rogers 		if (modifier)
1271b85a4d61SIan Rogers 			*modifier++ = '\0';
1272b85a4d61SIan Rogers 
1273b85a4d61SIan Rogers 		ret = metricgroup__add_metric(metric_name, modifier,
12741fd09e29SIan Rogers 					      metric_no_group, metric_no_threshold,
12751fd09e29SIan Rogers 					      user_requested_cpu_list,
12761725e9cdSIan Rogers 					      system_wide, metric_list, table);
1277ec5c5b3dSIan Rogers 		if (ret == -EINVAL)
1278b85a4d61SIan Rogers 			pr_err("Cannot find metric or group `%s'\n", metric_name);
1279ec5c5b3dSIan Rogers 
1280ec5c5b3dSIan Rogers 		if (ret)
1281b18f3e36SAndi Kleen 			break;
1282ec5c5b3dSIan Rogers 
1283ec5c5b3dSIan Rogers 		count++;
1284b18f3e36SAndi Kleen 	}
1285b85a4d61SIan Rogers 	free(list_copy);
1286ab483d8bSKan Liang 
1287ec5c5b3dSIan Rogers 	if (!ret) {
1288ec5c5b3dSIan Rogers 		/*
1289ec5c5b3dSIan Rogers 		 * Warn about nmi_watchdog if any parsed metrics had the
1290ec5c5b3dSIan Rogers 		 * NO_NMI_WATCHDOG constraint.
1291ec5c5b3dSIan Rogers 		 */
1292180a5013SIan Rogers 		metric__watchdog_constraint_hint(NULL, /*foot=*/true);
1293ec5c5b3dSIan Rogers 		/* No metrics. */
1294ec5c5b3dSIan Rogers 		if (count == 0)
1295ec5c5b3dSIan Rogers 			return -EINVAL;
1296ec5c5b3dSIan Rogers 	}
1297b18f3e36SAndi Kleen 	return ret;
1298b18f3e36SAndi Kleen }
1299b18f3e36SAndi Kleen 
1300119e521aSJiri Olsa static void metricgroup__free_metrics(struct list_head *metric_list)
1301b18f3e36SAndi Kleen {
1302a0c05b36SJiri Olsa 	struct metric *m, *tmp;
1303b18f3e36SAndi Kleen 
1304119e521aSJiri Olsa 	list_for_each_entry_safe (m, tmp, metric_list, nd) {
1305a0c05b36SJiri Olsa 		list_del_init(&m->nd);
13063d81d761SIan Rogers 		metric__free(m);
1307b18f3e36SAndi Kleen 	}
1308b18f3e36SAndi Kleen }
1309b18f3e36SAndi Kleen 
13105ecd5a0cSIan Rogers /**
13118586d274SIan Rogers  * find_tool_events - Search for the pressence of tool events in metric_list.
13128586d274SIan Rogers  * @metric_list: List to take metrics from.
13138586d274SIan Rogers  * @tool_events: Array of false values, indices corresponding to tool events set
13148586d274SIan Rogers  *               to true if tool event is found.
13158586d274SIan Rogers  */
13168586d274SIan Rogers static void find_tool_events(const struct list_head *metric_list,
13178586d274SIan Rogers 			     bool tool_events[PERF_TOOL_MAX])
13188586d274SIan Rogers {
13198586d274SIan Rogers 	struct metric *m;
13208586d274SIan Rogers 
13218586d274SIan Rogers 	list_for_each_entry(m, metric_list, nd) {
13228586d274SIan Rogers 		int i;
13238586d274SIan Rogers 
13248586d274SIan Rogers 		perf_tool_event__for_each_event(i) {
13258586d274SIan Rogers 			struct expr_id_data *data;
13268586d274SIan Rogers 
13278586d274SIan Rogers 			if (!tool_events[i] &&
13288586d274SIan Rogers 			    !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
13298586d274SIan Rogers 				tool_events[i] = true;
13308586d274SIan Rogers 		}
13318586d274SIan Rogers 	}
13328586d274SIan Rogers }
13338586d274SIan Rogers 
13348586d274SIan Rogers /**
1335180a5013SIan Rogers  * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events
13365ecd5a0cSIan Rogers  *                           metric IDs, as the IDs are held in a set,
13375ecd5a0cSIan Rogers  *                           duplicates will be removed.
13385ecd5a0cSIan Rogers  * @metric_list: List to take metrics from.
13395ecd5a0cSIan Rogers  * @combined: Out argument for result.
13405ecd5a0cSIan Rogers  */
13415ecd5a0cSIan Rogers static int build_combined_expr_ctx(const struct list_head *metric_list,
13425ecd5a0cSIan Rogers 				   struct expr_parse_ctx **combined)
13435ecd5a0cSIan Rogers {
13445ecd5a0cSIan Rogers 	struct hashmap_entry *cur;
13455ecd5a0cSIan Rogers 	size_t bkt;
13465ecd5a0cSIan Rogers 	struct metric *m;
13475ecd5a0cSIan Rogers 	char *dup;
13485ecd5a0cSIan Rogers 	int ret;
13495ecd5a0cSIan Rogers 
13505ecd5a0cSIan Rogers 	*combined = expr__ctx_new();
13515ecd5a0cSIan Rogers 	if (!*combined)
13525ecd5a0cSIan Rogers 		return -ENOMEM;
13535ecd5a0cSIan Rogers 
13545ecd5a0cSIan Rogers 	list_for_each_entry(m, metric_list, nd) {
1355180a5013SIan Rogers 		if (!m->group_events && !m->modifier) {
13565ecd5a0cSIan Rogers 			hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
1357c302378bSEduard Zingerman 				dup = strdup(cur->pkey);
13585ecd5a0cSIan Rogers 				if (!dup) {
13595ecd5a0cSIan Rogers 					ret = -ENOMEM;
13605ecd5a0cSIan Rogers 					goto err_out;
13615ecd5a0cSIan Rogers 				}
13625ecd5a0cSIan Rogers 				ret = expr__add_id(*combined, dup);
13635ecd5a0cSIan Rogers 				if (ret)
13645ecd5a0cSIan Rogers 					goto err_out;
13655ecd5a0cSIan Rogers 			}
13665ecd5a0cSIan Rogers 		}
13675ecd5a0cSIan Rogers 	}
13685ecd5a0cSIan Rogers 	return 0;
13695ecd5a0cSIan Rogers err_out:
13705ecd5a0cSIan Rogers 	expr__ctx_free(*combined);
13715ecd5a0cSIan Rogers 	*combined = NULL;
13725ecd5a0cSIan Rogers 	return ret;
13735ecd5a0cSIan Rogers }
13745ecd5a0cSIan Rogers 
13755ecd5a0cSIan Rogers /**
13765ecd5a0cSIan Rogers  * parse_ids - Build the event string for the ids and parse them creating an
13775ecd5a0cSIan Rogers  *             evlist. The encoded metric_ids are decoded.
13786b6b16b3SIan Rogers  * @metric_no_merge: is metric sharing explicitly disabled.
13795ecd5a0cSIan Rogers  * @fake_pmu: used when testing metrics not supported by the current CPU.
13805ecd5a0cSIan Rogers  * @ids: the event identifiers parsed from a metric.
1381b85a4d61SIan Rogers  * @modifier: any modifiers added to the events.
1382180a5013SIan Rogers  * @group_events: should events be placed in a weak group.
13838586d274SIan Rogers  * @tool_events: entries set true if the tool event of index could be present in
13848586d274SIan Rogers  *               the overall list of metrics.
13855ecd5a0cSIan Rogers  * @out_evlist: the created list of events.
13865ecd5a0cSIan Rogers  */
13876b6b16b3SIan Rogers static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
13886b6b16b3SIan Rogers 		     struct expr_parse_ctx *ids, const char *modifier,
1389180a5013SIan Rogers 		     bool group_events, const bool tool_events[PERF_TOOL_MAX],
13908586d274SIan Rogers 		     struct evlist **out_evlist)
13915ecd5a0cSIan Rogers {
13925ecd5a0cSIan Rogers 	struct parse_events_error parse_error;
13935ecd5a0cSIan Rogers 	struct evlist *parsed_evlist;
13945ecd5a0cSIan Rogers 	struct strbuf events = STRBUF_INIT;
13955ecd5a0cSIan Rogers 	int ret;
13965ecd5a0cSIan Rogers 
13975ecd5a0cSIan Rogers 	*out_evlist = NULL;
13986b6b16b3SIan Rogers 	if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
1399c788ef61SIan Rogers 		bool added_event = false;
14009aa09230SIan Rogers 		int i;
14015ecd5a0cSIan Rogers 		/*
14029aa09230SIan Rogers 		 * We may fail to share events between metrics because a tool
14039aa09230SIan Rogers 		 * event isn't present in one metric. For example, a ratio of
14049aa09230SIan Rogers 		 * cache misses doesn't need duration_time but the same events
14059aa09230SIan Rogers 		 * may be used for a misses per second. Events without sharing
14069aa09230SIan Rogers 		 * implies multiplexing, that is best avoided, so place
14079aa09230SIan Rogers 		 * all tool events in every group.
14086b6b16b3SIan Rogers 		 *
14096b6b16b3SIan Rogers 		 * Also, there may be no ids/events in the expression parsing
14106b6b16b3SIan Rogers 		 * context because of constant evaluation, e.g.:
14115ecd5a0cSIan Rogers 		 *    event1 if #smt_on else 0
14129aa09230SIan Rogers 		 * Add a tool event to avoid a parse error on an empty string.
14135ecd5a0cSIan Rogers 		 */
14149aa09230SIan Rogers 		perf_tool_event__for_each_event(i) {
14158586d274SIan Rogers 			if (tool_events[i]) {
14169aa09230SIan Rogers 				char *tmp = strdup(perf_tool_event__to_str(i));
14179aa09230SIan Rogers 
14185ecd5a0cSIan Rogers 				if (!tmp)
14195ecd5a0cSIan Rogers 					return -ENOMEM;
14205ecd5a0cSIan Rogers 				ids__insert(ids->ids, tmp);
1421c788ef61SIan Rogers 				added_event = true;
14225ecd5a0cSIan Rogers 			}
14239aa09230SIan Rogers 		}
1424c788ef61SIan Rogers 		if (!added_event && hashmap__size(ids->ids) == 0) {
1425c788ef61SIan Rogers 			char *tmp = strdup("duration_time");
1426c788ef61SIan Rogers 
1427c788ef61SIan Rogers 			if (!tmp)
1428c788ef61SIan Rogers 				return -ENOMEM;
1429c788ef61SIan Rogers 			ids__insert(ids->ids, tmp);
1430c788ef61SIan Rogers 		}
14318586d274SIan Rogers 	}
1432b85a4d61SIan Rogers 	ret = metricgroup__build_event_string(&events, ids, modifier,
1433180a5013SIan Rogers 					      group_events);
14345ecd5a0cSIan Rogers 	if (ret)
14355ecd5a0cSIan Rogers 		return ret;
14365ecd5a0cSIan Rogers 
14375ecd5a0cSIan Rogers 	parsed_evlist = evlist__new();
14385ecd5a0cSIan Rogers 	if (!parsed_evlist) {
14395ecd5a0cSIan Rogers 		ret = -ENOMEM;
14405ecd5a0cSIan Rogers 		goto err_out;
14415ecd5a0cSIan Rogers 	}
14425ecd5a0cSIan Rogers 	pr_debug("Parsing metric events '%s'\n", events.buf);
144307eafd4eSIan Rogers 	parse_events_error__init(&parse_error);
1444*411ad22eSIan Rogers 	ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL,
1445*411ad22eSIan Rogers 			     &parse_error, fake_pmu, /*warn_if_reordered=*/false);
14465ecd5a0cSIan Rogers 	if (ret) {
14476c191289SIan Rogers 		parse_events_error__print(&parse_error, events.buf);
14485ecd5a0cSIan Rogers 		goto err_out;
14495ecd5a0cSIan Rogers 	}
1450b85a4d61SIan Rogers 	ret = decode_all_metric_ids(parsed_evlist, modifier);
14515ecd5a0cSIan Rogers 	if (ret)
14525ecd5a0cSIan Rogers 		goto err_out;
14535ecd5a0cSIan Rogers 
14545ecd5a0cSIan Rogers 	*out_evlist = parsed_evlist;
14555ecd5a0cSIan Rogers 	parsed_evlist = NULL;
14565ecd5a0cSIan Rogers err_out:
145707eafd4eSIan Rogers 	parse_events_error__exit(&parse_error);
14585ecd5a0cSIan Rogers 	evlist__delete(parsed_evlist);
14595ecd5a0cSIan Rogers 	strbuf_release(&events);
14605ecd5a0cSIan Rogers 	return ret;
14615ecd5a0cSIan Rogers }
14625ecd5a0cSIan Rogers 
14638b4468a2SJiri Olsa static int parse_groups(struct evlist *perf_evlist, const char *str,
146405530a79SIan Rogers 			bool metric_no_group,
146505530a79SIan Rogers 			bool metric_no_merge,
14661fd09e29SIan Rogers 			bool metric_no_threshold,
14671725e9cdSIan Rogers 			const char *user_requested_cpu_list,
14681725e9cdSIan Rogers 			bool system_wide,
146968173bdaSJiri Olsa 			struct perf_pmu *fake_pmu,
14705ecd5a0cSIan Rogers 			struct rblist *metric_events_list,
1471f8ea2c15SIan Rogers 			const struct pmu_metrics_table *table)
1472b18f3e36SAndi Kleen {
14735ecd5a0cSIan Rogers 	struct evlist *combined_evlist = NULL;
1474119e521aSJiri Olsa 	LIST_HEAD(metric_list);
14755ecd5a0cSIan Rogers 	struct metric *m;
14768586d274SIan Rogers 	bool tool_events[PERF_TOOL_MAX] = {false};
1477b18f3e36SAndi Kleen 	int ret;
1478b18f3e36SAndi Kleen 
14795ecd5a0cSIan Rogers 	if (metric_events_list->nr_entries == 0)
14805ecd5a0cSIan Rogers 		metricgroup__rblist_init(metric_events_list);
14811fd09e29SIan Rogers 	ret = metricgroup__add_metric_list(str, metric_no_group, metric_no_threshold,
14821725e9cdSIan Rogers 					   user_requested_cpu_list,
14831725e9cdSIan Rogers 					   system_wide, &metric_list, table);
1484ec5c5b3dSIan Rogers 	if (ret)
1485ec5c5b3dSIan Rogers 		goto out;
1486ec5c5b3dSIan Rogers 
14875ecd5a0cSIan Rogers 	/* Sort metrics from largest to smallest. */
14885ecd5a0cSIan Rogers 	list_sort(NULL, &metric_list, metric_list_cmp);
14895ecd5a0cSIan Rogers 
14905ecd5a0cSIan Rogers 	if (!metric_no_merge) {
14915ecd5a0cSIan Rogers 		struct expr_parse_ctx *combined = NULL;
14925ecd5a0cSIan Rogers 
14938586d274SIan Rogers 		find_tool_events(&metric_list, tool_events);
14948586d274SIan Rogers 
14955ecd5a0cSIan Rogers 		ret = build_combined_expr_ctx(&metric_list, &combined);
14965ecd5a0cSIan Rogers 
14975ecd5a0cSIan Rogers 		if (!ret && combined && hashmap__size(combined->ids)) {
14986b6b16b3SIan Rogers 			ret = parse_ids(metric_no_merge, fake_pmu, combined,
14996b6b16b3SIan Rogers 					/*modifier=*/NULL,
1500180a5013SIan Rogers 					/*group_events=*/false,
15018586d274SIan Rogers 					tool_events,
150217b3867dSIan Rogers 					&combined_evlist);
15035ecd5a0cSIan Rogers 		}
15045ecd5a0cSIan Rogers 		if (combined)
15055ecd5a0cSIan Rogers 			expr__ctx_free(combined);
15065ecd5a0cSIan Rogers 
15075ecd5a0cSIan Rogers 		if (ret)
15085ecd5a0cSIan Rogers 			goto out;
15095ecd5a0cSIan Rogers 	}
15105ecd5a0cSIan Rogers 
15115ecd5a0cSIan Rogers 	list_for_each_entry(m, &metric_list, nd) {
15125ecd5a0cSIan Rogers 		struct metric_event *me;
15135ecd5a0cSIan Rogers 		struct evsel **metric_events;
15145ecd5a0cSIan Rogers 		struct evlist *metric_evlist = NULL;
15155ecd5a0cSIan Rogers 		struct metric *n;
15165ecd5a0cSIan Rogers 		struct metric_expr *expr;
15175ecd5a0cSIan Rogers 
1518180a5013SIan Rogers 		if (combined_evlist && !m->group_events) {
15195ecd5a0cSIan Rogers 			metric_evlist = combined_evlist;
15205ecd5a0cSIan Rogers 		} else if (!metric_no_merge) {
15215ecd5a0cSIan Rogers 			/*
15225ecd5a0cSIan Rogers 			 * See if the IDs for this metric are a subset of an
15235ecd5a0cSIan Rogers 			 * earlier metric.
15245ecd5a0cSIan Rogers 			 */
15255ecd5a0cSIan Rogers 			list_for_each_entry(n, &metric_list, nd) {
15265ecd5a0cSIan Rogers 				if (m == n)
15275ecd5a0cSIan Rogers 					break;
15285ecd5a0cSIan Rogers 
15295ecd5a0cSIan Rogers 				if (n->evlist == NULL)
15305ecd5a0cSIan Rogers 					continue;
15315ecd5a0cSIan Rogers 
1532b85a4d61SIan Rogers 				if ((!m->modifier && n->modifier) ||
1533b85a4d61SIan Rogers 				    (m->modifier && !n->modifier) ||
1534b85a4d61SIan Rogers 				    (m->modifier && n->modifier &&
1535b85a4d61SIan Rogers 					    strcmp(m->modifier, n->modifier)))
1536b85a4d61SIan Rogers 					continue;
1537b85a4d61SIan Rogers 
15385ecd5a0cSIan Rogers 				if (expr__subset_of_ids(n->pctx, m->pctx)) {
15395ecd5a0cSIan Rogers 					pr_debug("Events in '%s' fully contained within '%s'\n",
15405ecd5a0cSIan Rogers 						 m->metric_name, n->metric_name);
15415ecd5a0cSIan Rogers 					metric_evlist = n->evlist;
15425ecd5a0cSIan Rogers 					break;
15435ecd5a0cSIan Rogers 				}
15445ecd5a0cSIan Rogers 
15455ecd5a0cSIan Rogers 			}
15465ecd5a0cSIan Rogers 		}
15475ecd5a0cSIan Rogers 		if (!metric_evlist) {
154817b3867dSIan Rogers 			ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
1549180a5013SIan Rogers 					m->group_events, tool_events, &m->evlist);
15505ecd5a0cSIan Rogers 			if (ret)
15515ecd5a0cSIan Rogers 				goto out;
15525ecd5a0cSIan Rogers 
15535ecd5a0cSIan Rogers 			metric_evlist = m->evlist;
15545ecd5a0cSIan Rogers 		}
155517b3867dSIan Rogers 		ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events);
15565ecd5a0cSIan Rogers 		if (ret) {
15575ecd5a0cSIan Rogers 			pr_debug("Cannot resolve IDs for %s: %s\n",
15585ecd5a0cSIan Rogers 				m->metric_name, m->metric_expr);
15595ecd5a0cSIan Rogers 			goto out;
15605ecd5a0cSIan Rogers 		}
15615ecd5a0cSIan Rogers 
15625ecd5a0cSIan Rogers 		me = metricgroup__lookup(metric_events_list, metric_events[0], true);
15635ecd5a0cSIan Rogers 
15645ecd5a0cSIan Rogers 		expr = malloc(sizeof(struct metric_expr));
15655ecd5a0cSIan Rogers 		if (!expr) {
15665ecd5a0cSIan Rogers 			ret = -ENOMEM;
15675ecd5a0cSIan Rogers 			free(metric_events);
15685ecd5a0cSIan Rogers 			goto out;
15695ecd5a0cSIan Rogers 		}
15705ecd5a0cSIan Rogers 
15715ecd5a0cSIan Rogers 		expr->metric_refs = m->metric_refs;
15725ecd5a0cSIan Rogers 		m->metric_refs = NULL;
15735ecd5a0cSIan Rogers 		expr->metric_expr = m->metric_expr;
1574b85a4d61SIan Rogers 		if (m->modifier) {
1575b85a4d61SIan Rogers 			char *tmp;
1576b85a4d61SIan Rogers 
1577b85a4d61SIan Rogers 			if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
1578b85a4d61SIan Rogers 				expr->metric_name = NULL;
1579b85a4d61SIan Rogers 			else
1580b85a4d61SIan Rogers 				expr->metric_name = tmp;
1581b85a4d61SIan Rogers 		} else
1582b85a4d61SIan Rogers 			expr->metric_name = strdup(m->metric_name);
1583b85a4d61SIan Rogers 
1584b85a4d61SIan Rogers 		if (!expr->metric_name) {
1585b85a4d61SIan Rogers 			ret = -ENOMEM;
1586b85a4d61SIan Rogers 			free(metric_events);
1587b85a4d61SIan Rogers 			goto out;
1588b85a4d61SIan Rogers 		}
1589d0a3052fSIan Rogers 		expr->metric_threshold = m->metric_threshold;
15905ecd5a0cSIan Rogers 		expr->metric_unit = m->metric_unit;
15915ecd5a0cSIan Rogers 		expr->metric_events = metric_events;
15921a6abddeSIan Rogers 		expr->runtime = m->pctx->sctx.runtime;
15935ecd5a0cSIan Rogers 		list_add(&expr->nd, &me->head);
15945ecd5a0cSIan Rogers 	}
15955ecd5a0cSIan Rogers 
15965ecd5a0cSIan Rogers 
1597aba8c5e3SIan Rogers 	if (combined_evlist) {
15985ecd5a0cSIan Rogers 		evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
1599aba8c5e3SIan Rogers 		evlist__delete(combined_evlist);
1600aba8c5e3SIan Rogers 	}
16015ecd5a0cSIan Rogers 
16025ecd5a0cSIan Rogers 	list_for_each_entry(m, &metric_list, nd) {
16035ecd5a0cSIan Rogers 		if (m->evlist)
16045ecd5a0cSIan Rogers 			evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
16055ecd5a0cSIan Rogers 	}
16065ecd5a0cSIan Rogers 
1607b18f3e36SAndi Kleen out:
1608119e521aSJiri Olsa 	metricgroup__free_metrics(&metric_list);
1609b18f3e36SAndi Kleen 	return ret;
1610b18f3e36SAndi Kleen }
1611742d92ffSThomas Richter 
1612a4b8cfcaSIan Rogers int metricgroup__parse_groups(struct evlist *perf_evlist,
16138b4468a2SJiri Olsa 			      const char *str,
16148b4468a2SJiri Olsa 			      bool metric_no_group,
16158b4468a2SJiri Olsa 			      bool metric_no_merge,
16161fd09e29SIan Rogers 			      bool metric_no_threshold,
16171725e9cdSIan Rogers 			      const char *user_requested_cpu_list,
16181725e9cdSIan Rogers 			      bool system_wide,
16198b4468a2SJiri Olsa 			      struct rblist *metric_events)
16208b4468a2SJiri Olsa {
1621f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
16228b4468a2SJiri Olsa 
16233f5df3acSIan Rogers 	if (!table)
16243f5df3acSIan Rogers 		return -EINVAL;
16253f5df3acSIan Rogers 
16261725e9cdSIan Rogers 	return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge,
16271fd09e29SIan Rogers 			    metric_no_threshold, user_requested_cpu_list, system_wide,
16281725e9cdSIan Rogers 			    /*fake_pmu=*/NULL, metric_events, table);
16298b4468a2SJiri Olsa }
16308b4468a2SJiri Olsa 
1631f78ac00aSJiri Olsa int metricgroup__parse_groups_test(struct evlist *evlist,
1632f8ea2c15SIan Rogers 				   const struct pmu_metrics_table *table,
1633f78ac00aSJiri Olsa 				   const char *str,
1634f78ac00aSJiri Olsa 				   struct rblist *metric_events)
1635f78ac00aSJiri Olsa {
16361fd09e29SIan Rogers 	return parse_groups(evlist, str,
16371fd09e29SIan Rogers 			    /*metric_no_group=*/false,
16381fd09e29SIan Rogers 			    /*metric_no_merge=*/false,
16391fd09e29SIan Rogers 			    /*metric_no_threshold=*/false,
16401725e9cdSIan Rogers 			    /*user_requested_cpu_list=*/NULL,
16411725e9cdSIan Rogers 			    /*system_wide=*/false,
16421725e9cdSIan Rogers 			    &perf_pmu__fake, metric_events, table);
1643f78ac00aSJiri Olsa }
1644f78ac00aSJiri Olsa 
1645db95818eSIan Rogers static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
1646f8ea2c15SIan Rogers 					    const struct pmu_metrics_table *table __maybe_unused,
1647660842e4SIan Rogers 					    void *vdata)
1648660842e4SIan Rogers {
1649660842e4SIan Rogers 	const char *metric = vdata;
1650660842e4SIan Rogers 
165194b1a603SIan Rogers 	if (match_metric(pm->metric_name, metric) ||
165294b1a603SIan Rogers 	    match_metric(pm->metric_group, metric))
1653660842e4SIan Rogers 		return 1;
1654660842e4SIan Rogers 
1655660842e4SIan Rogers 	return 0;
1656660842e4SIan Rogers }
1657660842e4SIan Rogers 
1658742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric)
1659742d92ffSThomas Richter {
1660f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
1661742d92ffSThomas Richter 
1662eeac7730SIan Rogers 	if (!table)
1663742d92ffSThomas Richter 		return false;
1664742d92ffSThomas Richter 
1665f8ea2c15SIan Rogers 	return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback,
1666660842e4SIan Rogers 						(void *)metric) ? true : false;
1667742d92ffSThomas Richter }
1668b214ba8cSNamhyung Kim 
16691647cd5bSIan Rogers static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,
16701647cd5bSIan Rogers 					    const struct pmu_metrics_table *table __maybe_unused,
16711647cd5bSIan Rogers 					    void *data)
16721647cd5bSIan Rogers {
16731647cd5bSIan Rogers 	unsigned int *max_level = data;
16741647cd5bSIan Rogers 	unsigned int level;
16751647cd5bSIan Rogers 	const char *p = strstr(pm->metric_group, "TopdownL");
16761647cd5bSIan Rogers 
16771647cd5bSIan Rogers 	if (!p || p[8] == '\0')
16781647cd5bSIan Rogers 		return 0;
16791647cd5bSIan Rogers 
16801647cd5bSIan Rogers 	level = p[8] - '0';
16811647cd5bSIan Rogers 	if (level > *max_level)
16821647cd5bSIan Rogers 		*max_level = level;
16831647cd5bSIan Rogers 
16841647cd5bSIan Rogers 	return 0;
16851647cd5bSIan Rogers }
16861647cd5bSIan Rogers 
16871647cd5bSIan Rogers unsigned int metricgroups__topdown_max_level(void)
16881647cd5bSIan Rogers {
16891647cd5bSIan Rogers 	unsigned int max_level = 0;
16901647cd5bSIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
16911647cd5bSIan Rogers 
16921647cd5bSIan Rogers 	if (!table)
16931647cd5bSIan Rogers 		return false;
16941647cd5bSIan Rogers 
16951647cd5bSIan Rogers 	pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback,
16961647cd5bSIan Rogers 					  &max_level);
16971647cd5bSIan Rogers 	return max_level;
16981647cd5bSIan Rogers }
16991647cd5bSIan Rogers 
1700b214ba8cSNamhyung Kim int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
1701b214ba8cSNamhyung Kim 				    struct rblist *new_metric_events,
1702b214ba8cSNamhyung Kim 				    struct rblist *old_metric_events)
1703b214ba8cSNamhyung Kim {
170484f879c5SXin Gao 	unsigned int i;
1705b214ba8cSNamhyung Kim 
1706b214ba8cSNamhyung Kim 	for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
1707b214ba8cSNamhyung Kim 		struct rb_node *nd;
1708b214ba8cSNamhyung Kim 		struct metric_event *old_me, *new_me;
1709b214ba8cSNamhyung Kim 		struct metric_expr *old_expr, *new_expr;
1710b214ba8cSNamhyung Kim 		struct evsel *evsel;
1711b214ba8cSNamhyung Kim 		size_t alloc_size;
1712b214ba8cSNamhyung Kim 		int idx, nr;
1713b214ba8cSNamhyung Kim 
1714b214ba8cSNamhyung Kim 		nd = rblist__entry(old_metric_events, i);
1715b214ba8cSNamhyung Kim 		old_me = container_of(nd, struct metric_event, nd);
1716b214ba8cSNamhyung Kim 
171738fe0e01SJiri Olsa 		evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
1718b214ba8cSNamhyung Kim 		if (!evsel)
1719b214ba8cSNamhyung Kim 			return -EINVAL;
1720b214ba8cSNamhyung Kim 		new_me = metricgroup__lookup(new_metric_events, evsel, true);
1721b214ba8cSNamhyung Kim 		if (!new_me)
1722b214ba8cSNamhyung Kim 			return -ENOMEM;
1723b214ba8cSNamhyung Kim 
1724b214ba8cSNamhyung Kim 		pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
172538fe0e01SJiri Olsa 			 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
1726b214ba8cSNamhyung Kim 
1727b214ba8cSNamhyung Kim 		list_for_each_entry(old_expr, &old_me->head, nd) {
1728b214ba8cSNamhyung Kim 			new_expr = malloc(sizeof(*new_expr));
1729b214ba8cSNamhyung Kim 			if (!new_expr)
1730b214ba8cSNamhyung Kim 				return -ENOMEM;
1731b214ba8cSNamhyung Kim 
1732b214ba8cSNamhyung Kim 			new_expr->metric_expr = old_expr->metric_expr;
17336c73f819SIan Rogers 			new_expr->metric_threshold = old_expr->metric_threshold;
1734b85a4d61SIan Rogers 			new_expr->metric_name = strdup(old_expr->metric_name);
1735b85a4d61SIan Rogers 			if (!new_expr->metric_name)
1736b85a4d61SIan Rogers 				return -ENOMEM;
1737b85a4d61SIan Rogers 
1738b214ba8cSNamhyung Kim 			new_expr->metric_unit = old_expr->metric_unit;
1739b214ba8cSNamhyung Kim 			new_expr->runtime = old_expr->runtime;
1740b214ba8cSNamhyung Kim 
1741b214ba8cSNamhyung Kim 			if (old_expr->metric_refs) {
1742b214ba8cSNamhyung Kim 				/* calculate number of metric_events */
1743b214ba8cSNamhyung Kim 				for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
1744b214ba8cSNamhyung Kim 					continue;
1745b214ba8cSNamhyung Kim 				alloc_size = sizeof(*new_expr->metric_refs);
1746b214ba8cSNamhyung Kim 				new_expr->metric_refs = calloc(nr + 1, alloc_size);
1747b214ba8cSNamhyung Kim 				if (!new_expr->metric_refs) {
1748b214ba8cSNamhyung Kim 					free(new_expr);
1749b214ba8cSNamhyung Kim 					return -ENOMEM;
1750b214ba8cSNamhyung Kim 				}
1751b214ba8cSNamhyung Kim 
1752b214ba8cSNamhyung Kim 				memcpy(new_expr->metric_refs, old_expr->metric_refs,
1753b214ba8cSNamhyung Kim 				       nr * alloc_size);
1754b214ba8cSNamhyung Kim 			} else {
1755b214ba8cSNamhyung Kim 				new_expr->metric_refs = NULL;
1756b214ba8cSNamhyung Kim 			}
1757b214ba8cSNamhyung Kim 
1758b214ba8cSNamhyung Kim 			/* calculate number of metric_events */
1759b214ba8cSNamhyung Kim 			for (nr = 0; old_expr->metric_events[nr]; nr++)
1760b214ba8cSNamhyung Kim 				continue;
1761b214ba8cSNamhyung Kim 			alloc_size = sizeof(*new_expr->metric_events);
1762b214ba8cSNamhyung Kim 			new_expr->metric_events = calloc(nr + 1, alloc_size);
1763b214ba8cSNamhyung Kim 			if (!new_expr->metric_events) {
176411ff9bcdSArnaldo Carvalho de Melo 				zfree(&new_expr->metric_refs);
1765b214ba8cSNamhyung Kim 				free(new_expr);
1766b214ba8cSNamhyung Kim 				return -ENOMEM;
1767b214ba8cSNamhyung Kim 			}
1768b214ba8cSNamhyung Kim 
1769b214ba8cSNamhyung Kim 			/* copy evsel in the same position */
1770b214ba8cSNamhyung Kim 			for (idx = 0; idx < nr; idx++) {
1771b214ba8cSNamhyung Kim 				evsel = old_expr->metric_events[idx];
177238fe0e01SJiri Olsa 				evsel = evlist__find_evsel(evlist, evsel->core.idx);
1773b214ba8cSNamhyung Kim 				if (evsel == NULL) {
177411ff9bcdSArnaldo Carvalho de Melo 					zfree(&new_expr->metric_events);
177511ff9bcdSArnaldo Carvalho de Melo 					zfree(&new_expr->metric_refs);
1776b214ba8cSNamhyung Kim 					free(new_expr);
1777b214ba8cSNamhyung Kim 					return -EINVAL;
1778b214ba8cSNamhyung Kim 				}
1779b214ba8cSNamhyung Kim 				new_expr->metric_events[idx] = evsel;
1780b214ba8cSNamhyung Kim 			}
1781b214ba8cSNamhyung Kim 
1782b214ba8cSNamhyung Kim 			list_add(&new_expr->nd, &new_me->head);
1783b214ba8cSNamhyung Kim 		}
1784b214ba8cSNamhyung Kim 	}
1785b214ba8cSNamhyung Kim 	return 0;
1786b214ba8cSNamhyung Kim }
1787