xref: /linux/tools/perf/util/metricgroup.c (revision 8a4859c50fb79fcbbf74963162389b1d3a87e484)
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;
126bd3846d0SIan Rogers 	const char *pmu;
127485fcaedSIan Rogers 	/** The name of the metric such as "IPC". */
128b18f3e36SAndi Kleen 	const char *metric_name;
129b85a4d61SIan Rogers 	/** Modifier on the metric such as "u" or NULL for none. */
130b85a4d61SIan Rogers 	const char *modifier;
131485fcaedSIan Rogers 	/** The expression to parse, for example, "instructions/cycles". */
132b18f3e36SAndi Kleen 	const char *metric_expr;
133d0a3052fSIan Rogers 	/** Optional threshold expression where zero value is green, otherwise red. */
134d0a3052fSIan Rogers 	const char *metric_threshold;
135485fcaedSIan Rogers 	/**
136485fcaedSIan Rogers 	 * The "ScaleUnit" that scales and adds a unit to the metric during
137485fcaedSIan Rogers 	 * output.
138485fcaedSIan Rogers 	 */
139287f2649SJin Yao 	const char *metric_unit;
14046bdc0bfSIan Rogers 	/** Optional null terminated array of referenced metrics. */
14146bdc0bfSIan Rogers 	struct metric_ref *metric_refs;
142485fcaedSIan Rogers 	/**
143180a5013SIan Rogers 	 * Should events of the metric be grouped?
144485fcaedSIan Rogers 	 */
145180a5013SIan Rogers 	bool group_events;
1465ecd5a0cSIan Rogers 	/**
1475ecd5a0cSIan Rogers 	 * Parsed events for the metric. Optional as events may be taken from a
1485ecd5a0cSIan Rogers 	 * different metric whose group contains all the IDs necessary for this
1495ecd5a0cSIan Rogers 	 * one.
1505ecd5a0cSIan Rogers 	 */
1515ecd5a0cSIan Rogers 	struct evlist *evlist;
152b18f3e36SAndi Kleen };
153b18f3e36SAndi Kleen 
154180a5013SIan Rogers static void metric__watchdog_constraint_hint(const char *name, bool foot)
1553d81d761SIan Rogers {
1563d81d761SIan Rogers 	static bool violate_nmi_constraint;
1573d81d761SIan Rogers 
1583d81d761SIan Rogers 	if (!foot) {
159180a5013SIan Rogers 		pr_warning("Not grouping metric %s's events.\n", name);
1603d81d761SIan Rogers 		violate_nmi_constraint = true;
1613d81d761SIan Rogers 		return;
1623d81d761SIan Rogers 	}
1633d81d761SIan Rogers 
1643d81d761SIan Rogers 	if (!violate_nmi_constraint)
1653d81d761SIan Rogers 		return;
1663d81d761SIan Rogers 
1673d81d761SIan Rogers 	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
1683d81d761SIan Rogers 		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1693d81d761SIan Rogers 		   "    perf stat ...\n"
1703d81d761SIan Rogers 		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1713d81d761SIan Rogers }
1723d81d761SIan Rogers 
173180a5013SIan Rogers static bool metric__group_events(const struct pmu_metric *pm)
1743d81d761SIan Rogers {
17590053634SIan Rogers 	switch (pm->event_grouping) {
17690053634SIan Rogers 	case MetricNoGroupEvents:
17790053634SIan Rogers 		return false;
17890053634SIan Rogers 	case MetricNoGroupEventsNmi:
17990053634SIan Rogers 		if (!sysctl__nmi_watchdog_enabled())
180180a5013SIan Rogers 			return true;
181180a5013SIan Rogers 		metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false);
182180a5013SIan Rogers 		return false;
18390053634SIan Rogers 	case MetricNoGroupEventsSmt:
18490053634SIan Rogers 		return !smt_on();
18590053634SIan Rogers 	case MetricGroupEvents:
18690053634SIan Rogers 	default:
187180a5013SIan Rogers 		return true;
1883d81d761SIan Rogers 	}
18990053634SIan Rogers }
1903d81d761SIan Rogers 
1911725e9cdSIan Rogers static void metric__free(struct metric *m)
1921725e9cdSIan Rogers {
1931725e9cdSIan Rogers 	if (!m)
1941725e9cdSIan Rogers 		return;
1951725e9cdSIan Rogers 
19611ff9bcdSArnaldo Carvalho de Melo 	zfree(&m->metric_refs);
1971725e9cdSIan Rogers 	expr__ctx_free(m->pctx);
19811ff9bcdSArnaldo Carvalho de Melo 	zfree(&m->modifier);
1991725e9cdSIan Rogers 	evlist__delete(m->evlist);
2001725e9cdSIan Rogers 	free(m);
2011725e9cdSIan Rogers }
2021725e9cdSIan Rogers 
203db95818eSIan Rogers static struct metric *metric__new(const struct pmu_metric *pm,
204b85a4d61SIan Rogers 				  const char *modifier,
2053d81d761SIan Rogers 				  bool metric_no_group,
2061725e9cdSIan Rogers 				  int runtime,
2071725e9cdSIan Rogers 				  const char *user_requested_cpu_list,
2081725e9cdSIan Rogers 				  bool system_wide)
2093d81d761SIan Rogers {
2103d81d761SIan Rogers 	struct metric *m;
2113d81d761SIan Rogers 
2123d81d761SIan Rogers 	m = zalloc(sizeof(*m));
2133d81d761SIan Rogers 	if (!m)
2143d81d761SIan Rogers 		return NULL;
2153d81d761SIan Rogers 
2163d81d761SIan Rogers 	m->pctx = expr__ctx_new();
2171725e9cdSIan Rogers 	if (!m->pctx)
2181725e9cdSIan Rogers 		goto out_err;
2193d81d761SIan Rogers 
220bd3846d0SIan Rogers 	m->pmu = pm->pmu ?: "cpu";
221db95818eSIan Rogers 	m->metric_name = pm->metric_name;
2221725e9cdSIan Rogers 	m->modifier = NULL;
2231725e9cdSIan Rogers 	if (modifier) {
2241725e9cdSIan Rogers 		m->modifier = strdup(modifier);
2251725e9cdSIan Rogers 		if (!m->modifier)
2261725e9cdSIan Rogers 			goto out_err;
227b85a4d61SIan Rogers 	}
228db95818eSIan Rogers 	m->metric_expr = pm->metric_expr;
229d0a3052fSIan Rogers 	m->metric_threshold = pm->metric_threshold;
230db95818eSIan Rogers 	m->metric_unit = pm->unit;
2311725e9cdSIan Rogers 	m->pctx->sctx.user_requested_cpu_list = NULL;
2321725e9cdSIan Rogers 	if (user_requested_cpu_list) {
2331725e9cdSIan Rogers 		m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
2341725e9cdSIan Rogers 		if (!m->pctx->sctx.user_requested_cpu_list)
2351725e9cdSIan Rogers 			goto out_err;
2361725e9cdSIan Rogers 	}
2371a6abddeSIan Rogers 	m->pctx->sctx.runtime = runtime;
2381725e9cdSIan Rogers 	m->pctx->sctx.system_wide = system_wide;
239180a5013SIan Rogers 	m->group_events = !metric_no_group && metric__group_events(pm);
24046bdc0bfSIan Rogers 	m->metric_refs = NULL;
2415ecd5a0cSIan Rogers 	m->evlist = NULL;
2423d81d761SIan Rogers 
2433d81d761SIan Rogers 	return m;
2441725e9cdSIan Rogers out_err:
2451725e9cdSIan Rogers 	metric__free(m);
2461725e9cdSIan Rogers 	return NULL;
2473d81d761SIan Rogers }
2483d81d761SIan Rogers 
249ec5c5b3dSIan Rogers static bool contains_metric_id(struct evsel **metric_events, int num_events,
250ec5c5b3dSIan Rogers 			       const char *metric_id)
251dcc81be0SIan Rogers {
252dcc81be0SIan Rogers 	int i;
253dcc81be0SIan Rogers 
254dcc81be0SIan Rogers 	for (i = 0; i < num_events; i++) {
255ec5c5b3dSIan Rogers 		if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
256dcc81be0SIan Rogers 			return true;
257dcc81be0SIan Rogers 	}
258dcc81be0SIan Rogers 	return false;
259dcc81be0SIan Rogers }
260dcc81be0SIan Rogers 
2612440689dSIan Rogers /**
2625ecd5a0cSIan Rogers  * setup_metric_events - Find a group of events in metric_evlist that correspond
2635ecd5a0cSIan Rogers  *                       to the IDs from a parsed metric expression.
264bd3846d0SIan Rogers  * @pmu: The PMU for the IDs.
2655ecd5a0cSIan Rogers  * @ids: the metric IDs to match.
2665ecd5a0cSIan Rogers  * @metric_evlist: the list of perf events.
2675ecd5a0cSIan Rogers  * @out_metric_events: holds the created metric events array.
2682440689dSIan Rogers  */
269bd3846d0SIan Rogers static int setup_metric_events(const char *pmu, struct hashmap *ids,
2705ecd5a0cSIan Rogers 			       struct evlist *metric_evlist,
27117b3867dSIan Rogers 			       struct evsel ***out_metric_events)
272b18f3e36SAndi Kleen {
2735ecd5a0cSIan Rogers 	struct evsel **metric_events;
274ec5c5b3dSIan Rogers 	const char *metric_id;
2755ecd5a0cSIan Rogers 	struct evsel *ev;
2765ecd5a0cSIan Rogers 	size_t ids_size, matched_events, i;
277*8a4859c5SIan Rogers 	bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__is_hybrid(pmu);
2782440689dSIan Rogers 
2795ecd5a0cSIan Rogers 	*out_metric_events = NULL;
2805ecd5a0cSIan Rogers 	ids_size = hashmap__size(ids);
281b18f3e36SAndi Kleen 
2825ecd5a0cSIan Rogers 	metric_events = calloc(sizeof(void *), ids_size + 1);
2835ecd5a0cSIan Rogers 	if (!metric_events)
2845ecd5a0cSIan Rogers 		return -ENOMEM;
2855ecd5a0cSIan Rogers 
2862440689dSIan Rogers 	matched_events = 0;
2875ecd5a0cSIan Rogers 	evlist__for_each_entry(metric_evlist, ev) {
2885ecd5a0cSIan Rogers 		struct expr_id_data *val_ptr;
2895ecd5a0cSIan Rogers 
290*8a4859c5SIan Rogers 		/* Don't match events for the wrong hybrid PMU. */
291*8a4859c5SIan Rogers 		if (!all_pmus && ev->pmu_name &&
292*8a4859c5SIan Rogers 		    perf_pmu__is_hybrid(ev->pmu_name) &&
293*8a4859c5SIan Rogers 		    strcmp(ev->pmu_name, pmu))
294bd3846d0SIan Rogers 			continue;
29505530a79SIan Rogers 		/*
296762a05c5SIan Rogers 		 * Check for duplicate events with the same name. For
297762a05c5SIan Rogers 		 * example, uncore_imc/cas_count_read/ will turn into 6
298762a05c5SIan Rogers 		 * events per socket on skylakex. Only the first such
2995ecd5a0cSIan Rogers 		 * event is placed in metric_events.
30005530a79SIan Rogers 		 */
301ec5c5b3dSIan Rogers 		metric_id = evsel__metric_id(ev);
302ec5c5b3dSIan Rogers 		if (contains_metric_id(metric_events, matched_events, metric_id))
30305530a79SIan Rogers 			continue;
3045ecd5a0cSIan Rogers 		/*
3055ecd5a0cSIan Rogers 		 * Does this event belong to the parse context? For
3065ecd5a0cSIan Rogers 		 * combined or shared groups, this metric may not care
3075ecd5a0cSIan Rogers 		 * about this event.
3085ecd5a0cSIan Rogers 		 */
309c302378bSEduard Zingerman 		if (hashmap__find(ids, metric_id, &val_ptr)) {
310*8a4859c5SIan Rogers 			pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev));
3112440689dSIan Rogers 			metric_events[matched_events++] = ev;
312dcc81be0SIan Rogers 
3135ecd5a0cSIan Rogers 			if (matched_events >= ids_size)
3142440689dSIan Rogers 				break;
3152440689dSIan Rogers 		}
316762a05c5SIan Rogers 	}
3175ecd5a0cSIan Rogers 	if (matched_events < ids_size) {
3185ecd5a0cSIan Rogers 		free(metric_events);
3195ecd5a0cSIan Rogers 		return -EINVAL;
320b18f3e36SAndi Kleen 	}
3215ecd5a0cSIan Rogers 	for (i = 0; i < ids_size; i++) {
32258fc90fdSKajol Jain 		ev = metric_events[i];
3235ecd5a0cSIan Rogers 		ev->collect_stat = true;
3245ecd5a0cSIan Rogers 
325dcc81be0SIan Rogers 		/*
3265ecd5a0cSIan Rogers 		 * The metric leader points to the identically named
3275ecd5a0cSIan Rogers 		 * event in metric_events.
328dcc81be0SIan Rogers 		 */
329dcc81be0SIan Rogers 		ev->metric_leader = ev;
330dcc81be0SIan Rogers 		/*
3315ecd5a0cSIan Rogers 		 * Mark two events with identical names in the same
3325ecd5a0cSIan Rogers 		 * group (or globally) as being in use as uncore events
3335ecd5a0cSIan Rogers 		 * may be duplicated for each pmu. Set the metric leader
3345ecd5a0cSIan Rogers 		 * of such events to be the event that appears in
3355ecd5a0cSIan Rogers 		 * metric_events.
336dcc81be0SIan Rogers 		 */
337ec5c5b3dSIan Rogers 		metric_id = evsel__metric_id(ev);
3385ecd5a0cSIan Rogers 		evlist__for_each_entry_continue(metric_evlist, ev) {
339d3e2bb43SIan Rogers 			if (!strcmp(evsel__metric_id(ev), metric_id))
340dcc81be0SIan Rogers 				ev->metric_leader = metric_events[i];
341dcc81be0SIan Rogers 		}
342dcc81be0SIan Rogers 	}
3435ecd5a0cSIan Rogers 	*out_metric_events = metric_events;
3445ecd5a0cSIan Rogers 	return 0;
345b18f3e36SAndi Kleen }
346b18f3e36SAndi Kleen 
347b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list)
348b18f3e36SAndi Kleen {
349b18f3e36SAndi Kleen 	int len;
350b18f3e36SAndi Kleen 	char *m;
351b18f3e36SAndi Kleen 
352b18f3e36SAndi Kleen 	if (!list)
353b18f3e36SAndi Kleen 		return false;
354b18f3e36SAndi Kleen 	if (!strcmp(list, "all"))
355b18f3e36SAndi Kleen 		return true;
356b18f3e36SAndi Kleen 	if (!n)
357b18f3e36SAndi Kleen 		return !strcasecmp(list, "No_group");
358b18f3e36SAndi Kleen 	len = strlen(list);
359b18f3e36SAndi Kleen 	m = strcasestr(n, list);
360b18f3e36SAndi Kleen 	if (!m)
361b18f3e36SAndi Kleen 		return false;
362b18f3e36SAndi Kleen 	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
363b18f3e36SAndi Kleen 	    (m[len] == 0 || m[len] == ';'))
364b18f3e36SAndi Kleen 		return true;
365b18f3e36SAndi Kleen 	return false;
366b18f3e36SAndi Kleen }
367b18f3e36SAndi Kleen 
368bd3846d0SIan Rogers static bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric)
369be335ec2SJohn Garry {
370bd3846d0SIan Rogers 	const char *pm_pmu = pm->pmu ?: "cpu";
371bd3846d0SIan Rogers 
372bd3846d0SIan Rogers 	if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu))
373bd3846d0SIan Rogers 		return false;
374bd3846d0SIan Rogers 
375db95818eSIan Rogers 	return match_metric(pm->metric_group, metric) ||
376db95818eSIan Rogers 	       match_metric(pm->metric_name, metric);
377be335ec2SJohn Garry }
378be335ec2SJohn Garry 
379e5c6109fSIan Rogers /** struct mep - RB-tree node for building printing information. */
38071b0acceSAndi Kleen struct mep {
381e5c6109fSIan Rogers 	/** nd - RB-tree element. */
38271b0acceSAndi Kleen 	struct rb_node nd;
383e5c6109fSIan Rogers 	/** @metric_group: Owned metric group name, separated others with ';'. */
384e5c6109fSIan Rogers 	char *metric_group;
385e5c6109fSIan Rogers 	const char *metric_name;
386e5c6109fSIan Rogers 	const char *metric_desc;
387e5c6109fSIan Rogers 	const char *metric_long_desc;
388e5c6109fSIan Rogers 	const char *metric_expr;
389c7551a2eSIan Rogers 	const char *metric_threshold;
390e5c6109fSIan Rogers 	const char *metric_unit;
39171b0acceSAndi Kleen };
39271b0acceSAndi Kleen 
39371b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry)
39471b0acceSAndi Kleen {
39571b0acceSAndi Kleen 	struct mep *a = container_of(rb_node, struct mep, nd);
39671b0acceSAndi Kleen 	struct mep *b = (struct mep *)entry;
397e5c6109fSIan Rogers 	int ret;
39871b0acceSAndi Kleen 
399e5c6109fSIan Rogers 	ret = strcmp(a->metric_group, b->metric_group);
400e5c6109fSIan Rogers 	if (ret)
401e5c6109fSIan Rogers 		return ret;
402e5c6109fSIan Rogers 
403e5c6109fSIan Rogers 	return strcmp(a->metric_name, b->metric_name);
40471b0acceSAndi Kleen }
40571b0acceSAndi Kleen 
406e5c6109fSIan Rogers static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
40771b0acceSAndi Kleen {
40871b0acceSAndi Kleen 	struct mep *me = malloc(sizeof(struct mep));
40971b0acceSAndi Kleen 
41071b0acceSAndi Kleen 	if (!me)
41171b0acceSAndi Kleen 		return NULL;
41271b0acceSAndi Kleen 
413e5c6109fSIan Rogers 	memcpy(me, entry, sizeof(struct mep));
414e5c6109fSIan Rogers 	return &me->nd;
41571b0acceSAndi Kleen }
41671b0acceSAndi Kleen 
41771b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused,
41871b0acceSAndi Kleen 		       struct rb_node *nd)
41971b0acceSAndi Kleen {
42071b0acceSAndi Kleen 	struct mep *me = container_of(nd, struct mep, nd);
42171b0acceSAndi Kleen 
422e5c6109fSIan Rogers 	zfree(&me->metric_group);
42371b0acceSAndi Kleen 	free(me);
42471b0acceSAndi Kleen }
42571b0acceSAndi Kleen 
426e5c6109fSIan Rogers static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
427e5c6109fSIan Rogers 			      const char *metric_name)
42871b0acceSAndi Kleen {
429e5c6109fSIan Rogers 	struct rb_node *nd;
430e5c6109fSIan Rogers 	struct mep me = {
431e5c6109fSIan Rogers 		.metric_group = strdup(metric_group),
432e5c6109fSIan Rogers 		.metric_name = metric_name,
433e5c6109fSIan Rogers 	};
434e5c6109fSIan Rogers 	nd = rblist__find(groups, &me);
435e5c6109fSIan Rogers 	if (nd) {
436e5c6109fSIan Rogers 		free(me.metric_group);
437e5c6109fSIan Rogers 		return container_of(nd, struct mep, nd);
43871b0acceSAndi Kleen 	}
439e5c6109fSIan Rogers 	rblist__add_node(groups, &me);
440e5c6109fSIan Rogers 	nd = rblist__find(groups, &me);
441e5c6109fSIan Rogers 	if (nd)
442e5c6109fSIan Rogers 		return container_of(nd, struct mep, nd);
443e5c6109fSIan Rogers 	return NULL;
44471b0acceSAndi Kleen }
44571b0acceSAndi Kleen 
446db95818eSIan Rogers static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
447e5c6109fSIan Rogers 					struct rblist *groups)
448f6fe1e48SJohn Garry {
449f6fe1e48SJohn Garry 	const char *g;
450f6fe1e48SJohn Garry 	char *omg, *mg;
451f6fe1e48SJohn Garry 
452db95818eSIan Rogers 	mg = strdup(pm->metric_group ?: "No_group");
453f6fe1e48SJohn Garry 	if (!mg)
454f6fe1e48SJohn Garry 		return -ENOMEM;
455f6fe1e48SJohn Garry 	omg = mg;
456f6fe1e48SJohn Garry 	while ((g = strsep(&mg, ";")) != NULL) {
457f6fe1e48SJohn Garry 		struct mep *me;
458f6fe1e48SJohn Garry 
459f6fe1e48SJohn Garry 		g = skip_spaces(g);
460e5c6109fSIan Rogers 		if (strlen(g))
461db95818eSIan Rogers 			me = mep_lookup(groups, g, pm->metric_name);
462e5c6109fSIan Rogers 		else
463db95818eSIan Rogers 			me = mep_lookup(groups, "No_group", pm->metric_name);
464f6fe1e48SJohn Garry 
465e5c6109fSIan Rogers 		if (me) {
466db95818eSIan Rogers 			me->metric_desc = pm->desc;
467db95818eSIan Rogers 			me->metric_long_desc = pm->long_desc;
468db95818eSIan Rogers 			me->metric_expr = pm->metric_expr;
469c7551a2eSIan Rogers 			me->metric_threshold = pm->metric_threshold;
470db95818eSIan Rogers 			me->metric_unit = pm->unit;
471f6fe1e48SJohn Garry 		}
472f6fe1e48SJohn Garry 	}
473f6fe1e48SJohn Garry 	free(omg);
474f6fe1e48SJohn Garry 
475f6fe1e48SJohn Garry 	return 0;
476f6fe1e48SJohn Garry }
477f6fe1e48SJohn Garry 
478a36fadb1SJohn Garry struct metricgroup_iter_data {
479db95818eSIan Rogers 	pmu_metric_iter_fn fn;
480a36fadb1SJohn Garry 	void *data;
481a36fadb1SJohn Garry };
482a36fadb1SJohn Garry 
483db95818eSIan Rogers static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
484f8ea2c15SIan Rogers 				       const struct pmu_metrics_table *table,
48529be2fe0SIan Rogers 				       void *data)
486a36fadb1SJohn Garry {
487a36fadb1SJohn Garry 	struct metricgroup_iter_data *d = data;
488a36fadb1SJohn Garry 	struct perf_pmu *pmu = NULL;
489a36fadb1SJohn Garry 
490db95818eSIan Rogers 	if (!pm->metric_expr || !pm->compat)
491a36fadb1SJohn Garry 		return 0;
492a36fadb1SJohn Garry 
493a36fadb1SJohn Garry 	while ((pmu = perf_pmu__scan(pmu))) {
494a36fadb1SJohn Garry 
495db95818eSIan Rogers 		if (!pmu->id || strcmp(pmu->id, pm->compat))
496a36fadb1SJohn Garry 			continue;
497a36fadb1SJohn Garry 
498db95818eSIan Rogers 		return d->fn(pm, table, d->data);
499a36fadb1SJohn Garry 	}
500a36fadb1SJohn Garry 	return 0;
501a36fadb1SJohn Garry }
502a36fadb1SJohn Garry 
503db95818eSIan Rogers static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
504f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table __maybe_unused,
505660842e4SIan Rogers 					void *vdata)
506660842e4SIan Rogers {
507e5c6109fSIan Rogers 	struct rblist *groups = vdata;
508660842e4SIan Rogers 
509db95818eSIan Rogers 	return metricgroup__add_to_mep_groups(pm, groups);
510660842e4SIan Rogers }
511660842e4SIan Rogers 
512e5c6109fSIan Rogers void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
51371b0acceSAndi Kleen {
51471b0acceSAndi Kleen 	struct rblist groups;
515f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table;
516e5c6109fSIan Rogers 	struct rb_node *node, *next;
51771b0acceSAndi Kleen 
51871b0acceSAndi Kleen 	rblist__init(&groups);
51971b0acceSAndi Kleen 	groups.node_new = mep_new;
52071b0acceSAndi Kleen 	groups.node_cmp = mep_cmp;
52171b0acceSAndi Kleen 	groups.node_delete = mep_delete;
522f8ea2c15SIan Rogers 	table = pmu_metrics_table__find();
523660842e4SIan Rogers 	if (table) {
524f8ea2c15SIan Rogers 		pmu_metrics_table_for_each_metric(table,
525e5c6109fSIan Rogers 						 metricgroup__add_to_mep_groups_callback,
526e5c6109fSIan Rogers 						 &groups);
5270e0ae874SJin Yao 	}
528a36fadb1SJohn Garry 	{
529a36fadb1SJohn Garry 		struct metricgroup_iter_data data = {
530e5c6109fSIan Rogers 			.fn = metricgroup__add_to_mep_groups_callback,
531e5c6109fSIan Rogers 			.data = &groups,
532a36fadb1SJohn Garry 		};
533db95818eSIan Rogers 		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
534a36fadb1SJohn Garry 	}
535a36fadb1SJohn Garry 
536ca227029SDavidlohr Bueso 	for (node = rb_first_cached(&groups.entries); node; node = next) {
53771b0acceSAndi Kleen 		struct mep *me = container_of(node, struct mep, nd);
53871b0acceSAndi Kleen 
539e5c6109fSIan Rogers 		print_cb->print_metric(print_state,
540e5c6109fSIan Rogers 				me->metric_group,
541e5c6109fSIan Rogers 				me->metric_name,
542e5c6109fSIan Rogers 				me->metric_desc,
543e5c6109fSIan Rogers 				me->metric_long_desc,
544e5c6109fSIan Rogers 				me->metric_expr,
545c7551a2eSIan Rogers 				me->metric_threshold,
546e5c6109fSIan Rogers 				me->metric_unit);
54771b0acceSAndi Kleen 		next = rb_next(node);
54871b0acceSAndi Kleen 		rblist__remove_node(&groups, node);
54971b0acceSAndi Kleen 	}
55071b0acceSAndi Kleen }
55171b0acceSAndi Kleen 
552ec5c5b3dSIan Rogers static const char *code_characters = ",-=@";
553ec5c5b3dSIan Rogers 
554ec5c5b3dSIan Rogers static int encode_metric_id(struct strbuf *sb, const char *x)
555ec5c5b3dSIan Rogers {
556ec5c5b3dSIan Rogers 	char *c;
557ec5c5b3dSIan Rogers 	int ret = 0;
558ec5c5b3dSIan Rogers 
559ec5c5b3dSIan Rogers 	for (; *x; x++) {
560ec5c5b3dSIan Rogers 		c = strchr(code_characters, *x);
561ec5c5b3dSIan Rogers 		if (c) {
562ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, '!');
563ec5c5b3dSIan Rogers 			if (ret)
564ec5c5b3dSIan Rogers 				break;
565ec5c5b3dSIan Rogers 
566ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, '0' + (c - code_characters));
567ec5c5b3dSIan Rogers 			if (ret)
568ec5c5b3dSIan Rogers 				break;
569ec5c5b3dSIan Rogers 		} else {
570ec5c5b3dSIan Rogers 			ret = strbuf_addch(sb, *x);
571ec5c5b3dSIan Rogers 			if (ret)
572ec5c5b3dSIan Rogers 				break;
573ec5c5b3dSIan Rogers 		}
574ec5c5b3dSIan Rogers 	}
575ec5c5b3dSIan Rogers 	return ret;
576ec5c5b3dSIan Rogers }
577ec5c5b3dSIan Rogers 
578ec5c5b3dSIan Rogers static int decode_metric_id(struct strbuf *sb, const char *x)
579ec5c5b3dSIan Rogers {
580ec5c5b3dSIan Rogers 	const char *orig = x;
581ec5c5b3dSIan Rogers 	size_t i;
582ec5c5b3dSIan Rogers 	char c;
583ec5c5b3dSIan Rogers 	int ret;
584ec5c5b3dSIan Rogers 
585ec5c5b3dSIan Rogers 	for (; *x; x++) {
586ec5c5b3dSIan Rogers 		c = *x;
587ec5c5b3dSIan Rogers 		if (*x == '!') {
588ec5c5b3dSIan Rogers 			x++;
589ec5c5b3dSIan Rogers 			i = *x - '0';
590ec5c5b3dSIan Rogers 			if (i > strlen(code_characters)) {
591ec5c5b3dSIan Rogers 				pr_err("Bad metric-id encoding in: '%s'", orig);
592ec5c5b3dSIan Rogers 				return -1;
593ec5c5b3dSIan Rogers 			}
594ec5c5b3dSIan Rogers 			c = code_characters[i];
595ec5c5b3dSIan Rogers 		}
596ec5c5b3dSIan Rogers 		ret = strbuf_addch(sb, c);
597ec5c5b3dSIan Rogers 		if (ret)
598ec5c5b3dSIan Rogers 			return ret;
599ec5c5b3dSIan Rogers 	}
600ec5c5b3dSIan Rogers 	return 0;
601ec5c5b3dSIan Rogers }
602ec5c5b3dSIan Rogers 
603b85a4d61SIan Rogers static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
604ec5c5b3dSIan Rogers {
605ec5c5b3dSIan Rogers 	struct evsel *ev;
606ec5c5b3dSIan Rogers 	struct strbuf sb = STRBUF_INIT;
607ec5c5b3dSIan Rogers 	char *cur;
608ec5c5b3dSIan Rogers 	int ret = 0;
609ec5c5b3dSIan Rogers 
610ec5c5b3dSIan Rogers 	evlist__for_each_entry(perf_evlist, ev) {
611ec5c5b3dSIan Rogers 		if (!ev->metric_id)
612ec5c5b3dSIan Rogers 			continue;
613ec5c5b3dSIan Rogers 
614ec5c5b3dSIan Rogers 		ret = strbuf_setlen(&sb, 0);
615ec5c5b3dSIan Rogers 		if (ret)
616ec5c5b3dSIan Rogers 			break;
617ec5c5b3dSIan Rogers 
618ec5c5b3dSIan Rogers 		ret = decode_metric_id(&sb, ev->metric_id);
619ec5c5b3dSIan Rogers 		if (ret)
620ec5c5b3dSIan Rogers 			break;
621ec5c5b3dSIan Rogers 
622ec5c5b3dSIan Rogers 		free((char *)ev->metric_id);
623ec5c5b3dSIan Rogers 		ev->metric_id = strdup(sb.buf);
624ec5c5b3dSIan Rogers 		if (!ev->metric_id) {
625ec5c5b3dSIan Rogers 			ret = -ENOMEM;
626ec5c5b3dSIan Rogers 			break;
627ec5c5b3dSIan Rogers 		}
628ec5c5b3dSIan Rogers 		/*
629ec5c5b3dSIan Rogers 		 * If the name is just the parsed event, use the metric-id to
630ec5c5b3dSIan Rogers 		 * give a more friendly display version.
631ec5c5b3dSIan Rogers 		 */
632ec5c5b3dSIan Rogers 		if (strstr(ev->name, "metric-id=")) {
633b85a4d61SIan Rogers 			bool has_slash = false;
634ec5c5b3dSIan Rogers 
63511ff9bcdSArnaldo Carvalho de Melo 			zfree(&ev->name);
636b85a4d61SIan Rogers 			for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
637b85a4d61SIan Rogers 				*cur = '/';
638b85a4d61SIan Rogers 				has_slash = true;
639b85a4d61SIan Rogers 			}
640b85a4d61SIan Rogers 
641b85a4d61SIan Rogers 			if (modifier) {
642b85a4d61SIan Rogers 				if (!has_slash && !strchr(sb.buf, ':')) {
643b85a4d61SIan Rogers 					ret = strbuf_addch(&sb, ':');
644b85a4d61SIan Rogers 					if (ret)
645b85a4d61SIan Rogers 						break;
646b85a4d61SIan Rogers 				}
647b85a4d61SIan Rogers 				ret = strbuf_addstr(&sb, modifier);
648b85a4d61SIan Rogers 				if (ret)
649b85a4d61SIan Rogers 					break;
650b85a4d61SIan Rogers 			}
651ec5c5b3dSIan Rogers 			ev->name = strdup(sb.buf);
652ec5c5b3dSIan Rogers 			if (!ev->name) {
653ec5c5b3dSIan Rogers 				ret = -ENOMEM;
654ec5c5b3dSIan Rogers 				break;
655ec5c5b3dSIan Rogers 			}
656ec5c5b3dSIan Rogers 		}
657ec5c5b3dSIan Rogers 	}
658ec5c5b3dSIan Rogers 	strbuf_release(&sb);
659ec5c5b3dSIan Rogers 	return ret;
660ec5c5b3dSIan Rogers }
661ec5c5b3dSIan Rogers 
662ec5c5b3dSIan Rogers static int metricgroup__build_event_string(struct strbuf *events,
663ec5c5b3dSIan Rogers 					   const struct expr_parse_ctx *ctx,
664b85a4d61SIan Rogers 					   const char *modifier,
665180a5013SIan Rogers 					   bool group_events)
666f742634aSKan Liang {
667ded80bdaSIan Rogers 	struct hashmap_entry *cur;
6684e21c13aSIan Rogers 	size_t bkt;
6699aa09230SIan Rogers 	bool no_group = true, has_tool_events = false;
6709aa09230SIan Rogers 	bool tool_events[PERF_TOOL_MAX] = {false};
671ec5c5b3dSIan Rogers 	int ret = 0;
672ec5c5b3dSIan Rogers 
673ec5c5b3dSIan Rogers #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
674f742634aSKan Liang 
675cb94a02eSIan Rogers 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
676c302378bSEduard Zingerman 		const char *sep, *rsep, *id = cur->pkey;
6779aa09230SIan Rogers 		enum perf_tool_event ev;
678ec5c5b3dSIan Rogers 
679ec5c5b3dSIan Rogers 		pr_debug("found event %s\n", id);
6809aa09230SIan Rogers 
6819aa09230SIan Rogers 		/* Always move tool events outside of the group. */
6829aa09230SIan Rogers 		ev = perf_tool_event__from_str(id);
6839aa09230SIan Rogers 		if (ev != PERF_TOOL_NONE) {
6849aa09230SIan Rogers 			has_tool_events = true;
6859aa09230SIan Rogers 			tool_events[ev] = true;
686f742634aSKan Liang 			continue;
687f742634aSKan Liang 		}
688ec5c5b3dSIan Rogers 		/* Separate events with commas and open the group if necessary. */
689ec5c5b3dSIan Rogers 		if (no_group) {
690180a5013SIan Rogers 			if (group_events) {
691ec5c5b3dSIan Rogers 				ret = strbuf_addch(events, '{');
692ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
693ec5c5b3dSIan Rogers 			}
694ec5c5b3dSIan Rogers 
695f742634aSKan Liang 			no_group = false;
696ec5c5b3dSIan Rogers 		} else {
697ec5c5b3dSIan Rogers 			ret = strbuf_addch(events, ',');
698ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
699f742634aSKan Liang 		}
700ec5c5b3dSIan Rogers 		/*
701ec5c5b3dSIan Rogers 		 * Encode the ID as an event string. Add a qualifier for
702ec5c5b3dSIan Rogers 		 * metric_id that is the original name except with characters
703ec5c5b3dSIan Rogers 		 * that parse-events can't parse replaced. For example,
704ec5c5b3dSIan Rogers 		 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
705ec5c5b3dSIan Rogers 		 */
706ec5c5b3dSIan Rogers 		sep = strchr(id, '@');
707ec5c5b3dSIan Rogers 		if (sep != NULL) {
708ec5c5b3dSIan Rogers 			ret = strbuf_add(events, id, sep - id);
709ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
710ec5c5b3dSIan Rogers 			ret = strbuf_addch(events, '/');
711ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
712ec5c5b3dSIan Rogers 			rsep = strrchr(sep, '@');
713ec5c5b3dSIan Rogers 			ret = strbuf_add(events, sep + 1, rsep - sep - 1);
714ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
715ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, ",metric-id=");
716ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
717ec5c5b3dSIan Rogers 			sep = rsep;
718ec5c5b3dSIan Rogers 		} else {
719ec5c5b3dSIan Rogers 			sep = strchr(id, ':');
720ec5c5b3dSIan Rogers 			if (sep != NULL) {
721ec5c5b3dSIan Rogers 				ret = strbuf_add(events, id, sep - id);
722ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
723ec5c5b3dSIan Rogers 			} else {
724ec5c5b3dSIan Rogers 				ret = strbuf_addstr(events, id);
725ec5c5b3dSIan Rogers 				RETURN_IF_NON_ZERO(ret);
726f742634aSKan Liang 			}
727ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, "/metric-id=");
728ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
729ec5c5b3dSIan Rogers 		}
730ec5c5b3dSIan Rogers 		ret = encode_metric_id(events, id);
731ec5c5b3dSIan Rogers 		RETURN_IF_NON_ZERO(ret);
732ec5c5b3dSIan Rogers 		ret = strbuf_addstr(events, "/");
733ec5c5b3dSIan Rogers 		RETURN_IF_NON_ZERO(ret);
734f742634aSKan Liang 
735ec5c5b3dSIan Rogers 		if (sep != NULL) {
736ec5c5b3dSIan Rogers 			ret = strbuf_addstr(events, sep + 1);
737ec5c5b3dSIan Rogers 			RETURN_IF_NON_ZERO(ret);
738e2ce1059SIan Rogers 		}
739b85a4d61SIan Rogers 		if (modifier) {
740b85a4d61SIan Rogers 			ret = strbuf_addstr(events, modifier);
741b85a4d61SIan Rogers 			RETURN_IF_NON_ZERO(ret);
742b85a4d61SIan Rogers 		}
743ab483d8bSKan Liang 	}
744180a5013SIan Rogers 	if (!no_group && group_events) {
74560344f1aSZhengjun Xing 		ret = strbuf_addf(events, "}:W");
7469aa09230SIan Rogers 		RETURN_IF_NON_ZERO(ret);
7479aa09230SIan Rogers 	}
7489aa09230SIan Rogers 	if (has_tool_events) {
7499aa09230SIan Rogers 		int i;
7509aa09230SIan Rogers 
7519aa09230SIan Rogers 		perf_tool_event__for_each_event(i) {
7529aa09230SIan Rogers 			if (tool_events[i]) {
7539aa09230SIan Rogers 				if (!no_group) {
7549aa09230SIan Rogers 					ret = strbuf_addch(events, ',');
7559aa09230SIan Rogers 					RETURN_IF_NON_ZERO(ret);
7569aa09230SIan Rogers 				}
7579aa09230SIan Rogers 				no_group = false;
7589aa09230SIan Rogers 				ret = strbuf_addstr(events, perf_tool_event__to_str(i));
7599aa09230SIan Rogers 				RETURN_IF_NON_ZERO(ret);
7609aa09230SIan Rogers 			}
7619aa09230SIan Rogers 		}
7629aa09230SIan Rogers 	}
763ec5c5b3dSIan Rogers 
764ec5c5b3dSIan Rogers 	return ret;
765ec5c5b3dSIan Rogers #undef RETURN_IF_NON_ZERO
766ec5c5b3dSIan Rogers }
767ab483d8bSKan Liang 
768db95818eSIan Rogers int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
7691e1a873dSKajol Jain {
7701e1a873dSKajol Jain 	return 1;
7711e1a873dSKajol Jain }
7721e1a873dSKajol Jain 
77380be6434SIan Rogers /*
77480be6434SIan Rogers  * A singly linked list on the stack of the names of metrics being
77580be6434SIan Rogers  * processed. Used to identify recursion.
77680be6434SIan Rogers  */
77780be6434SIan Rogers struct visited_metric {
77880be6434SIan Rogers 	const char *name;
77980be6434SIan Rogers 	const struct visited_metric *parent;
78080be6434SIan Rogers };
78180be6434SIan Rogers 
782be335ec2SJohn Garry struct metricgroup_add_iter_data {
783be335ec2SJohn Garry 	struct list_head *metric_list;
784bd3846d0SIan Rogers 	const char *pmu;
78568074811SIan Rogers 	const char *metric_name;
786b85a4d61SIan Rogers 	const char *modifier;
787be335ec2SJohn Garry 	int *ret;
788be335ec2SJohn Garry 	bool *has_match;
789be335ec2SJohn Garry 	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;
796be335ec2SJohn Garry };
797be335ec2SJohn Garry 
798bd3846d0SIan Rogers static bool metricgroup__find_metric(const char *pmu,
799bd3846d0SIan Rogers 				     const char *metric,
800f8ea2c15SIan Rogers 				     const struct pmu_metrics_table *table,
801db95818eSIan Rogers 				     struct pmu_metric *pm);
802d3abd7b8SIan Rogers 
80380be6434SIan Rogers static int add_metric(struct list_head *metric_list,
804db95818eSIan Rogers 		      const struct pmu_metric *pm,
805b85a4d61SIan Rogers 		      const char *modifier,
80680be6434SIan Rogers 		      bool metric_no_group,
8071fd09e29SIan Rogers 		      bool metric_no_threshold,
8081725e9cdSIan Rogers 		      const char *user_requested_cpu_list,
8091725e9cdSIan Rogers 		      bool system_wide,
81080be6434SIan Rogers 		      struct metric *root_metric,
81180be6434SIan Rogers 		      const struct visited_metric *visited,
812f8ea2c15SIan Rogers 		      const struct pmu_metrics_table *table);
81380be6434SIan Rogers 
81480be6434SIan Rogers /**
81580be6434SIan Rogers  * resolve_metric - Locate metrics within the root metric and recursively add
81680be6434SIan Rogers  *                    references to them.
81780be6434SIan Rogers  * @metric_list: The list the metric is added to.
818bd3846d0SIan Rogers  * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs.
819b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
82080be6434SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
82180be6434SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
82280be6434SIan Rogers  *                   user may override.
8231725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
8241725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
82580be6434SIan Rogers  * @root_metric: Metrics may reference other metrics to form a tree. In this
82680be6434SIan Rogers  *               case the root_metric holds all the IDs and a list of referenced
82780be6434SIan Rogers  *               metrics. When adding a root this argument is NULL.
82880be6434SIan Rogers  * @visited: A singly linked list of metric names being added that is used to
82980be6434SIan Rogers  *           detect recursion.
830eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
83180be6434SIan Rogers  *       architecture perf is running upon.
83280be6434SIan Rogers  */
83380be6434SIan Rogers static int resolve_metric(struct list_head *metric_list,
834bd3846d0SIan Rogers 			  const char *pmu,
835b85a4d61SIan Rogers 			  const char *modifier,
83680be6434SIan Rogers 			  bool metric_no_group,
8371fd09e29SIan Rogers 			  bool metric_no_threshold,
8381725e9cdSIan Rogers 			  const char *user_requested_cpu_list,
8391725e9cdSIan Rogers 			  bool system_wide,
84080be6434SIan Rogers 			  struct metric *root_metric,
84180be6434SIan Rogers 			  const struct visited_metric *visited,
842f8ea2c15SIan Rogers 			  const struct pmu_metrics_table *table)
84380be6434SIan Rogers {
84480be6434SIan Rogers 	struct hashmap_entry *cur;
84580be6434SIan Rogers 	size_t bkt;
84680be6434SIan Rogers 	struct to_resolve {
84780be6434SIan Rogers 		/* The metric to resolve. */
848db95818eSIan Rogers 		struct pmu_metric pm;
84980be6434SIan Rogers 		/*
85080be6434SIan Rogers 		 * The key in the IDs map, this may differ from in case,
851db95818eSIan Rogers 		 * etc. from pm->metric_name.
85280be6434SIan Rogers 		 */
85380be6434SIan Rogers 		const char *key;
85480be6434SIan Rogers 	} *pending = NULL;
85580be6434SIan Rogers 	int i, ret = 0, pending_cnt = 0;
85680be6434SIan Rogers 
85780be6434SIan Rogers 	/*
85880be6434SIan Rogers 	 * Iterate all the parsed IDs and if there's a matching metric and it to
85980be6434SIan Rogers 	 * the pending array.
86080be6434SIan Rogers 	 */
86180be6434SIan Rogers 	hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
862db95818eSIan Rogers 		struct pmu_metric pm;
86380be6434SIan Rogers 
864bd3846d0SIan Rogers 		if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) {
86580be6434SIan Rogers 			pending = realloc(pending,
86680be6434SIan Rogers 					(pending_cnt + 1) * sizeof(struct to_resolve));
86780be6434SIan Rogers 			if (!pending)
86880be6434SIan Rogers 				return -ENOMEM;
86980be6434SIan Rogers 
870db95818eSIan Rogers 			memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
871c302378bSEduard Zingerman 			pending[pending_cnt].key = cur->pkey;
87280be6434SIan Rogers 			pending_cnt++;
87380be6434SIan Rogers 		}
87480be6434SIan Rogers 	}
87580be6434SIan Rogers 
87680be6434SIan Rogers 	/* Remove the metric IDs from the context. */
87780be6434SIan Rogers 	for (i = 0; i < pending_cnt; i++)
87880be6434SIan Rogers 		expr__del_id(root_metric->pctx, pending[i].key);
87980be6434SIan Rogers 
88080be6434SIan Rogers 	/*
88180be6434SIan Rogers 	 * Recursively add all the metrics, IDs are added to the root metric's
88280be6434SIan Rogers 	 * context.
88380be6434SIan Rogers 	 */
88480be6434SIan Rogers 	for (i = 0; i < pending_cnt; i++) {
885db95818eSIan Rogers 		ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
8861fd09e29SIan Rogers 				 metric_no_threshold, user_requested_cpu_list, system_wide,
8871fd09e29SIan Rogers 				 root_metric, visited, table);
88880be6434SIan Rogers 		if (ret)
88980be6434SIan Rogers 			break;
89080be6434SIan Rogers 	}
89180be6434SIan Rogers 
89280be6434SIan Rogers 	free(pending);
89380be6434SIan Rogers 	return ret;
89480be6434SIan Rogers }
89580be6434SIan Rogers 
89668074811SIan Rogers /**
89768074811SIan Rogers  * __add_metric - Add a metric to metric_list.
89868074811SIan Rogers  * @metric_list: The list the metric is added to.
899db95818eSIan Rogers  * @pm: The pmu_metric containing the metric to be added.
900b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
90168074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
90268074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
90368074811SIan Rogers  *                   user may override.
9041fd09e29SIan Rogers  * @metric_no_threshold: Should threshold expressions be ignored?
90568074811SIan Rogers  * @runtime: A special argument for the parser only known at runtime.
9061725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
9071725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
90880be6434SIan Rogers  * @root_metric: Metrics may reference other metrics to form a tree. In this
90980be6434SIan Rogers  *               case the root_metric holds all the IDs and a list of referenced
91080be6434SIan Rogers  *               metrics. When adding a root this argument is NULL.
91180be6434SIan Rogers  * @visited: A singly linked list of metric names being added that is used to
91280be6434SIan Rogers  *           detect recursion.
913eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
91480be6434SIan Rogers  *       architecture perf is running upon.
91568074811SIan Rogers  */
916119e521aSJiri Olsa static int __add_metric(struct list_head *metric_list,
917db95818eSIan Rogers 			const struct pmu_metric *pm,
918b85a4d61SIan Rogers 			const char *modifier,
91905530a79SIan Rogers 			bool metric_no_group,
9201fd09e29SIan Rogers 			bool metric_no_threshold,
92183de0b7dSJiri Olsa 			int runtime,
9221725e9cdSIan Rogers 			const char *user_requested_cpu_list,
9231725e9cdSIan Rogers 			bool system_wide,
92480be6434SIan Rogers 			struct metric *root_metric,
92580be6434SIan Rogers 			const struct visited_metric *visited,
926f8ea2c15SIan Rogers 			const struct pmu_metrics_table *table)
92747352abaSKajol Jain {
92880be6434SIan Rogers 	const struct visited_metric *vm;
92980be6434SIan Rogers 	int ret;
93080be6434SIan Rogers 	bool is_root = !root_metric;
931d0a3052fSIan Rogers 	const char *expr;
93280be6434SIan Rogers 	struct visited_metric visited_node = {
933db95818eSIan Rogers 		.name = pm->metric_name,
93480be6434SIan Rogers 		.parent = visited,
93580be6434SIan Rogers 	};
93647352abaSKajol Jain 
93780be6434SIan Rogers 	for (vm = visited; vm; vm = vm->parent) {
938db95818eSIan Rogers 		if (!strcmp(pm->metric_name, vm->name)) {
939db95818eSIan Rogers 			pr_err("failed: recursion detected for %s\n", pm->metric_name);
94080be6434SIan Rogers 			return -1;
94180be6434SIan Rogers 		}
94280be6434SIan Rogers 	}
94380be6434SIan Rogers 
94480be6434SIan Rogers 	if (is_root) {
94583de0b7dSJiri Olsa 		/*
94680be6434SIan Rogers 		 * This metric is the root of a tree and may reference other
94780be6434SIan Rogers 		 * metrics that are added recursively.
94883de0b7dSJiri Olsa 		 */
949db95818eSIan Rogers 		root_metric = metric__new(pm, modifier, metric_no_group, runtime,
9501725e9cdSIan Rogers 					  user_requested_cpu_list, system_wide);
95180be6434SIan Rogers 		if (!root_metric)
95247352abaSKajol Jain 			return -ENOMEM;
95347352abaSKajol Jain 
95483de0b7dSJiri Olsa 	} else {
95546bdc0bfSIan Rogers 		int cnt = 0;
95646bdc0bfSIan Rogers 
95783de0b7dSJiri Olsa 		/*
958a3de7690SIan Rogers 		 * This metric was referenced in a metric higher in the
959a3de7690SIan Rogers 		 * tree. Check if the same metric is already resolved in the
960a3de7690SIan Rogers 		 * metric_refs list.
96183de0b7dSJiri Olsa 		 */
96246bdc0bfSIan Rogers 		if (root_metric->metric_refs) {
96346bdc0bfSIan Rogers 			for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
964db95818eSIan Rogers 				if (!strcmp(pm->metric_name,
96546bdc0bfSIan Rogers 					    root_metric->metric_refs[cnt].metric_name))
966a3de7690SIan Rogers 					return 0;
967a3de7690SIan Rogers 			}
96846bdc0bfSIan Rogers 		}
969a3de7690SIan Rogers 
97046bdc0bfSIan Rogers 		/* Create reference. Need space for the entry and the terminator. */
97146bdc0bfSIan Rogers 		root_metric->metric_refs = realloc(root_metric->metric_refs,
97246bdc0bfSIan Rogers 						(cnt + 2) * sizeof(struct metric_ref));
97346bdc0bfSIan Rogers 		if (!root_metric->metric_refs)
97483de0b7dSJiri Olsa 			return -ENOMEM;
97583de0b7dSJiri Olsa 
97683de0b7dSJiri Olsa 		/*
97783de0b7dSJiri Olsa 		 * Intentionally passing just const char pointers,
97883de0b7dSJiri Olsa 		 * from 'pe' object, so they never go away. We don't
97983de0b7dSJiri Olsa 		 * need to change them, so there's no need to create
98083de0b7dSJiri Olsa 		 * our own copy.
98183de0b7dSJiri Olsa 		 */
982db95818eSIan Rogers 		root_metric->metric_refs[cnt].metric_name = pm->metric_name;
983db95818eSIan Rogers 		root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
98483de0b7dSJiri Olsa 
98546bdc0bfSIan Rogers 		/* Null terminate array. */
98646bdc0bfSIan Rogers 		root_metric->metric_refs[cnt+1].metric_name = NULL;
98746bdc0bfSIan Rogers 		root_metric->metric_refs[cnt+1].metric_expr = NULL;
98883de0b7dSJiri Olsa 	}
98983de0b7dSJiri Olsa 
99083de0b7dSJiri Olsa 	/*
99183de0b7dSJiri Olsa 	 * For both the parent and referenced metrics, we parse
99280be6434SIan Rogers 	 * all the metric's IDs and add it to the root context.
99383de0b7dSJiri Olsa 	 */
994d0a3052fSIan Rogers 	ret = 0;
995d0a3052fSIan Rogers 	expr = pm->metric_expr;
996d0a3052fSIan Rogers 	if (is_root && pm->metric_threshold) {
997d0a3052fSIan Rogers 		/*
998d0a3052fSIan Rogers 		 * Threshold expressions are built off the actual metric. Switch
999d0a3052fSIan Rogers 		 * to use that in case of additional necessary events. Change
1000d0a3052fSIan Rogers 		 * the visited node name to avoid this being flagged as
10011fd09e29SIan Rogers 		 * recursion. If the threshold events are disabled, just use the
10021fd09e29SIan Rogers 		 * metric's name as a reference. This allows metric threshold
10031fd09e29SIan Rogers 		 * computation if there are sufficient events.
1004d0a3052fSIan Rogers 		 */
1005d0a3052fSIan Rogers 		assert(strstr(pm->metric_threshold, pm->metric_name));
10061fd09e29SIan Rogers 		expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
1007d0a3052fSIan Rogers 		visited_node.name = "__threshold__";
1008d0a3052fSIan Rogers 	}
1009d0a3052fSIan Rogers 	if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) {
101080be6434SIan Rogers 		/* Broken metric. */
101180be6434SIan Rogers 		ret = -EINVAL;
1012d0a3052fSIan Rogers 	}
1013d0a3052fSIan Rogers 	if (!ret) {
101480be6434SIan Rogers 		/* Resolve referenced metrics. */
1015bd3846d0SIan Rogers 		const char *pmu = pm->pmu ?: "cpu";
1016bd3846d0SIan Rogers 
1017bd3846d0SIan Rogers 		ret = resolve_metric(metric_list, pmu, modifier, metric_no_group,
10181fd09e29SIan Rogers 				     metric_no_threshold, user_requested_cpu_list,
1019bd3846d0SIan Rogers 				     system_wide, root_metric, &visited_node,
1020bd3846d0SIan Rogers 				     table);
1021ded80bdaSIan Rogers 	}
102280be6434SIan Rogers 	if (ret) {
102380be6434SIan Rogers 		if (is_root)
102480be6434SIan Rogers 			metric__free(root_metric);
102583de0b7dSJiri Olsa 
102680be6434SIan Rogers 	} else if (is_root)
102780be6434SIan Rogers 		list_add(&root_metric->nd, metric_list);
10286bf2102bSIan Rogers 
102980be6434SIan Rogers 	return ret;
103047352abaSKajol Jain }
103147352abaSKajol Jain 
1032660842e4SIan Rogers struct metricgroup__find_metric_data {
1033bd3846d0SIan Rogers 	const char *pmu;
1034660842e4SIan Rogers 	const char *metric;
1035db95818eSIan Rogers 	struct pmu_metric *pm;
1036660842e4SIan Rogers };
1037ce391940SJiri Olsa 
1038db95818eSIan Rogers static int metricgroup__find_metric_callback(const struct pmu_metric *pm,
1039f8ea2c15SIan Rogers 					     const struct pmu_metrics_table *table  __maybe_unused,
1040660842e4SIan Rogers 					     void *vdata)
1041660842e4SIan Rogers {
1042660842e4SIan Rogers 	struct metricgroup__find_metric_data *data = vdata;
1043bd3846d0SIan Rogers 	const char *pm_pmu = pm->pmu ?: "cpu";
1044bd3846d0SIan Rogers 
1045bd3846d0SIan Rogers 	if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu))
1046bd3846d0SIan Rogers 		return 0;
1047660842e4SIan Rogers 
1048db95818eSIan Rogers 	if (!match_metric(pm->metric_name, data->metric))
1049660842e4SIan Rogers 		return 0;
1050660842e4SIan Rogers 
1051db95818eSIan Rogers 	memcpy(data->pm, pm, sizeof(*pm));
1052d3abd7b8SIan Rogers 	return 1;
1053660842e4SIan Rogers }
1054ce391940SJiri Olsa 
1055bd3846d0SIan Rogers static bool metricgroup__find_metric(const char *pmu,
1056bd3846d0SIan Rogers 				     const char *metric,
1057f8ea2c15SIan Rogers 				     const struct pmu_metrics_table *table,
1058db95818eSIan Rogers 				     struct pmu_metric *pm)
105983de0b7dSJiri Olsa {
1060660842e4SIan Rogers 	struct metricgroup__find_metric_data data = {
1061bd3846d0SIan Rogers 		.pmu = pmu,
1062660842e4SIan Rogers 		.metric = metric,
1063db95818eSIan Rogers 		.pm = pm,
1064660842e4SIan Rogers 	};
106583de0b7dSJiri Olsa 
1066f8ea2c15SIan Rogers 	return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data)
1067d3abd7b8SIan Rogers 		? true : false;
106883de0b7dSJiri Olsa }
106983de0b7dSJiri Olsa 
1070119e521aSJiri Olsa static int add_metric(struct list_head *metric_list,
1071db95818eSIan Rogers 		      const struct pmu_metric *pm,
1072b85a4d61SIan Rogers 		      const char *modifier,
107383de0b7dSJiri Olsa 		      bool metric_no_group,
10741fd09e29SIan Rogers 		      bool metric_no_threshold,
10751725e9cdSIan Rogers 		      const char *user_requested_cpu_list,
10761725e9cdSIan Rogers 		      bool system_wide,
107780be6434SIan Rogers 		      struct metric *root_metric,
107880be6434SIan Rogers 		      const struct visited_metric *visited,
1079f8ea2c15SIan Rogers 		      const struct pmu_metrics_table *table)
1080a29c164aSJiri Olsa {
1081a29c164aSJiri Olsa 	int ret = 0;
1082a29c164aSJiri Olsa 
1083db95818eSIan Rogers 	pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
1084a29c164aSJiri Olsa 
1085db95818eSIan Rogers 	if (!strstr(pm->metric_expr, "?")) {
10861fd09e29SIan Rogers 		ret = __add_metric(metric_list, pm, modifier, metric_no_group,
10871fd09e29SIan Rogers 				   metric_no_threshold, 0, user_requested_cpu_list,
10881fd09e29SIan Rogers 				   system_wide, root_metric, visited, table);
1089a29c164aSJiri Olsa 	} else {
1090a29c164aSJiri Olsa 		int j, count;
1091a29c164aSJiri Olsa 
1092db95818eSIan Rogers 		count = arch_get_runtimeparam(pm);
1093a29c164aSJiri Olsa 
1094a29c164aSJiri Olsa 		/* This loop is added to create multiple
1095a29c164aSJiri Olsa 		 * events depend on count value and add
1096119e521aSJiri Olsa 		 * those events to metric_list.
1097a29c164aSJiri Olsa 		 */
1098a29c164aSJiri Olsa 
109980be6434SIan Rogers 		for (j = 0; j < count && !ret; j++)
11001fd09e29SIan Rogers 			ret = __add_metric(metric_list, pm, modifier, metric_no_group,
11011fd09e29SIan Rogers 					   metric_no_threshold, j, user_requested_cpu_list,
11021fd09e29SIan Rogers 					   system_wide, root_metric, visited, table);
1103a29c164aSJiri Olsa 	}
1104a29c164aSJiri Olsa 
1105a29c164aSJiri Olsa 	return ret;
1106a29c164aSJiri Olsa }
1107a29c164aSJiri Olsa 
1108db95818eSIan Rogers static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
1109f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table __maybe_unused,
1110be335ec2SJohn Garry 					void *data)
1111be335ec2SJohn Garry {
1112be335ec2SJohn Garry 	struct metricgroup_add_iter_data *d = data;
1113be335ec2SJohn Garry 	int ret;
1114be335ec2SJohn Garry 
1115bd3846d0SIan Rogers 	if (!match_pm_metric(pm, d->pmu, d->metric_name))
1116be335ec2SJohn Garry 		return 0;
1117be335ec2SJohn Garry 
1118db95818eSIan Rogers 	ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
11191fd09e29SIan Rogers 			 d->metric_no_threshold, d->user_requested_cpu_list,
11201fd09e29SIan Rogers 			 d->system_wide, d->root_metric, d->visited, d->table);
1121be335ec2SJohn Garry 	if (ret)
1122fe7a98b9SJohn Garry 		goto out;
1123be335ec2SJohn Garry 
1124be335ec2SJohn Garry 	*(d->has_match) = true;
1125be335ec2SJohn Garry 
1126fe7a98b9SJohn Garry out:
1127fe7a98b9SJohn Garry 	*(d->ret) = ret;
1128fe7a98b9SJohn Garry 	return ret;
1129be335ec2SJohn Garry }
1130be335ec2SJohn Garry 
11316b6b16b3SIan Rogers /**
11326b6b16b3SIan Rogers  * metric_list_cmp - list_sort comparator that sorts metrics with more events to
11339aa09230SIan Rogers  *                   the front. tool events are excluded from the count.
11346b6b16b3SIan Rogers  */
113580be6434SIan Rogers static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
113680be6434SIan Rogers 			   const struct list_head *r)
113780be6434SIan Rogers {
113880be6434SIan Rogers 	const struct metric *left = container_of(l, struct metric, nd);
113980be6434SIan Rogers 	const struct metric *right = container_of(r, struct metric, nd);
11406b6b16b3SIan Rogers 	struct expr_id_data *data;
11419aa09230SIan Rogers 	int i, left_count, right_count;
114280be6434SIan Rogers 
11436b6b16b3SIan Rogers 	left_count = hashmap__size(left->pctx->ids);
11449aa09230SIan Rogers 	perf_tool_event__for_each_event(i) {
11459aa09230SIan Rogers 		if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
11466b6b16b3SIan Rogers 			left_count--;
11479aa09230SIan Rogers 	}
11486b6b16b3SIan Rogers 
11496b6b16b3SIan Rogers 	right_count = hashmap__size(right->pctx->ids);
11509aa09230SIan Rogers 	perf_tool_event__for_each_event(i) {
11519aa09230SIan Rogers 		if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
11526b6b16b3SIan Rogers 			right_count--;
11539aa09230SIan Rogers 	}
11546b6b16b3SIan Rogers 
11556b6b16b3SIan Rogers 	return right_count - left_count;
115680be6434SIan Rogers }
115780be6434SIan Rogers 
1158660842e4SIan Rogers struct metricgroup__add_metric_data {
1159660842e4SIan Rogers 	struct list_head *list;
1160bd3846d0SIan Rogers 	const char *pmu;
1161660842e4SIan Rogers 	const char *metric_name;
1162660842e4SIan Rogers 	const char *modifier;
11631725e9cdSIan Rogers 	const char *user_requested_cpu_list;
1164660842e4SIan Rogers 	bool metric_no_group;
11651fd09e29SIan Rogers 	bool metric_no_threshold;
11661725e9cdSIan Rogers 	bool system_wide;
1167660842e4SIan Rogers 	bool has_match;
1168660842e4SIan Rogers };
1169660842e4SIan Rogers 
1170db95818eSIan Rogers static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
1171f8ea2c15SIan Rogers 					    const struct pmu_metrics_table *table,
1172660842e4SIan Rogers 					    void *vdata)
1173660842e4SIan Rogers {
1174660842e4SIan Rogers 	struct metricgroup__add_metric_data *data = vdata;
1175660842e4SIan Rogers 	int ret = 0;
1176660842e4SIan Rogers 
1177bd3846d0SIan Rogers 	if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) {
1178ccc66c60SIan Rogers 		bool metric_no_group = data->metric_no_group ||
1179ccc66c60SIan Rogers 			match_metric(data->metric_name, pm->metricgroup_no_group);
1180660842e4SIan Rogers 
1181660842e4SIan Rogers 		data->has_match = true;
1182ccc66c60SIan Rogers 		ret = add_metric(data->list, pm, data->modifier, metric_no_group,
11831fd09e29SIan Rogers 				 data->metric_no_threshold, data->user_requested_cpu_list,
11841fd09e29SIan Rogers 				 data->system_wide, /*root_metric=*/NULL,
11851fd09e29SIan Rogers 				 /*visited_metrics=*/NULL, table);
1186660842e4SIan Rogers 	}
1187660842e4SIan Rogers 	return ret;
1188660842e4SIan Rogers }
1189660842e4SIan Rogers 
119068074811SIan Rogers /**
119168074811SIan Rogers  * metricgroup__add_metric - Find and add a metric, or a metric group.
1192bd3846d0SIan Rogers  * @pmu: The PMU name to search for metrics on, or "all" for all PMUs.
119368074811SIan Rogers  * @metric_name: The name of the metric or metric group. For example, "IPC"
119468074811SIan Rogers  *               could be the name of a metric and "TopDownL1" the name of a
119568074811SIan Rogers  *               metric group.
1196b85a4d61SIan Rogers  * @modifier: if non-null event modifiers like "u".
119768074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
119868074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
119968074811SIan Rogers  *                   user may override.
12001725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
12011725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
120268074811SIan Rogers  * @metric_list: The list that the metric or metric group are added to.
1203eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
120468074811SIan Rogers  *       architecture perf is running upon.
120568074811SIan Rogers  */
1206bd3846d0SIan Rogers static int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier,
12071fd09e29SIan Rogers 				   bool metric_no_group, bool metric_no_threshold,
12081725e9cdSIan Rogers 				   const char *user_requested_cpu_list,
12091725e9cdSIan Rogers 				   bool system_wide,
1210119e521aSJiri Olsa 				   struct list_head *metric_list,
1211f8ea2c15SIan Rogers 				   const struct pmu_metrics_table *table)
1212b18f3e36SAndi Kleen {
121398461d9dSJiri Olsa 	LIST_HEAD(list);
1214660842e4SIan Rogers 	int ret;
121590810399SIan Rogers 	bool has_match = false;
1216b18f3e36SAndi Kleen 
1217660842e4SIan Rogers 	{
1218660842e4SIan Rogers 		struct metricgroup__add_metric_data data = {
1219660842e4SIan Rogers 			.list = &list,
1220bd3846d0SIan Rogers 			.pmu = pmu,
1221660842e4SIan Rogers 			.metric_name = metric_name,
1222660842e4SIan Rogers 			.modifier = modifier,
1223660842e4SIan Rogers 			.metric_no_group = metric_no_group,
12241fd09e29SIan Rogers 			.metric_no_threshold = metric_no_threshold,
12251725e9cdSIan Rogers 			.user_requested_cpu_list = user_requested_cpu_list,
12261725e9cdSIan Rogers 			.system_wide = system_wide,
1227660842e4SIan Rogers 			.has_match = false,
1228660842e4SIan Rogers 		};
122968074811SIan Rogers 		/*
1230660842e4SIan Rogers 		 * Iterate over all metrics seeing if metric matches either the
1231660842e4SIan Rogers 		 * name or group. When it does add the metric to the list.
123268074811SIan Rogers 		 */
1233f8ea2c15SIan Rogers 		ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback,
1234660842e4SIan Rogers 						       &data);
123590810399SIan Rogers 		if (ret)
123627adafcdSNamhyung Kim 			goto out;
1237ce391940SJiri Olsa 
1238660842e4SIan Rogers 		has_match = data.has_match;
1239660842e4SIan Rogers 	}
1240be335ec2SJohn Garry 	{
1241be335ec2SJohn Garry 		struct metricgroup_iter_data data = {
1242be335ec2SJohn Garry 			.fn = metricgroup__add_metric_sys_event_iter,
1243be335ec2SJohn Garry 			.data = (void *) &(struct metricgroup_add_iter_data) {
1244be335ec2SJohn Garry 				.metric_list = &list,
1245bd3846d0SIan Rogers 				.pmu = pmu,
124668074811SIan Rogers 				.metric_name = metric_name,
1247b85a4d61SIan Rogers 				.modifier = modifier,
1248be335ec2SJohn Garry 				.metric_no_group = metric_no_group,
12491725e9cdSIan Rogers 				.user_requested_cpu_list = user_requested_cpu_list,
12501725e9cdSIan Rogers 				.system_wide = system_wide,
1251be335ec2SJohn Garry 				.has_match = &has_match,
1252be335ec2SJohn Garry 				.ret = &ret,
1253eeac7730SIan Rogers 				.table = table,
1254be335ec2SJohn Garry 			},
1255be335ec2SJohn Garry 		};
1256be335ec2SJohn Garry 
1257db95818eSIan Rogers 		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
1258be335ec2SJohn Garry 	}
1259ce391940SJiri Olsa 	/* End of pmu events. */
12605ecd5a0cSIan Rogers 	if (!has_match)
126127adafcdSNamhyung Kim 		ret = -EINVAL;
126298461d9dSJiri Olsa 
126327adafcdSNamhyung Kim out:
126427adafcdSNamhyung Kim 	/*
126527adafcdSNamhyung Kim 	 * add to metric_list so that they can be released
126627adafcdSNamhyung Kim 	 * even if it's failed
126727adafcdSNamhyung Kim 	 */
1268119e521aSJiri Olsa 	list_splice(&list, metric_list);
126927adafcdSNamhyung Kim 	return ret;
127090810399SIan Rogers }
1271b18f3e36SAndi Kleen 
127268074811SIan Rogers /**
127368074811SIan Rogers  * metricgroup__add_metric_list - Find and add metrics, or metric groups,
127468074811SIan Rogers  *                                specified in a list.
1275bd3846d0SIan Rogers  * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS.
127668074811SIan Rogers  * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
127768074811SIan Rogers  *        would match the IPC and CPI metrics, and TopDownL1 would match all
127868074811SIan Rogers  *        the metrics in the TopDownL1 group.
127968074811SIan Rogers  * @metric_no_group: Should events written to events be grouped "{}" or
128068074811SIan Rogers  *                   global. Grouping is the default but due to multiplexing the
128168074811SIan Rogers  *                   user may override.
12821725e9cdSIan Rogers  * @user_requested_cpu_list: Command line specified CPUs to record on.
12831725e9cdSIan Rogers  * @system_wide: Are events for all processes recorded.
128468074811SIan Rogers  * @metric_list: The list that metrics are added to.
1285eeac7730SIan Rogers  * @table: The table that is searched for metrics, most commonly the table for the
128668074811SIan Rogers  *       architecture perf is running upon.
128768074811SIan Rogers  */
1288bd3846d0SIan Rogers static int metricgroup__add_metric_list(const char *pmu, const char *list,
1289bd3846d0SIan Rogers 					bool metric_no_group,
12901fd09e29SIan Rogers 					bool metric_no_threshold,
12911725e9cdSIan Rogers 					const char *user_requested_cpu_list,
12921725e9cdSIan Rogers 					bool system_wide, struct list_head *metric_list,
1293f8ea2c15SIan Rogers 					const struct pmu_metrics_table *table)
1294b18f3e36SAndi Kleen {
1295b85a4d61SIan Rogers 	char *list_itr, *list_copy, *metric_name, *modifier;
1296ec5c5b3dSIan Rogers 	int ret, count = 0;
1297b18f3e36SAndi Kleen 
1298b85a4d61SIan Rogers 	list_copy = strdup(list);
1299b85a4d61SIan Rogers 	if (!list_copy)
1300b18f3e36SAndi Kleen 		return -ENOMEM;
1301b85a4d61SIan Rogers 	list_itr = list_copy;
1302411bc316SAndi Kleen 
1303b85a4d61SIan Rogers 	while ((metric_name = strsep(&list_itr, ",")) != NULL) {
1304b85a4d61SIan Rogers 		modifier = strchr(metric_name, ':');
1305b85a4d61SIan Rogers 		if (modifier)
1306b85a4d61SIan Rogers 			*modifier++ = '\0';
1307b85a4d61SIan Rogers 
1308bd3846d0SIan Rogers 		ret = metricgroup__add_metric(pmu, metric_name, modifier,
13091fd09e29SIan Rogers 					      metric_no_group, metric_no_threshold,
13101fd09e29SIan Rogers 					      user_requested_cpu_list,
13111725e9cdSIan Rogers 					      system_wide, metric_list, table);
1312ec5c5b3dSIan Rogers 		if (ret == -EINVAL)
1313b85a4d61SIan Rogers 			pr_err("Cannot find metric or group `%s'\n", metric_name);
1314ec5c5b3dSIan Rogers 
1315ec5c5b3dSIan Rogers 		if (ret)
1316b18f3e36SAndi Kleen 			break;
1317ec5c5b3dSIan Rogers 
1318ec5c5b3dSIan Rogers 		count++;
1319b18f3e36SAndi Kleen 	}
1320b85a4d61SIan Rogers 	free(list_copy);
1321ab483d8bSKan Liang 
1322ec5c5b3dSIan Rogers 	if (!ret) {
1323ec5c5b3dSIan Rogers 		/*
1324ec5c5b3dSIan Rogers 		 * Warn about nmi_watchdog if any parsed metrics had the
1325ec5c5b3dSIan Rogers 		 * NO_NMI_WATCHDOG constraint.
1326ec5c5b3dSIan Rogers 		 */
1327180a5013SIan Rogers 		metric__watchdog_constraint_hint(NULL, /*foot=*/true);
1328ec5c5b3dSIan Rogers 		/* No metrics. */
1329ec5c5b3dSIan Rogers 		if (count == 0)
1330ec5c5b3dSIan Rogers 			return -EINVAL;
1331ec5c5b3dSIan Rogers 	}
1332b18f3e36SAndi Kleen 	return ret;
1333b18f3e36SAndi Kleen }
1334b18f3e36SAndi Kleen 
1335119e521aSJiri Olsa static void metricgroup__free_metrics(struct list_head *metric_list)
1336b18f3e36SAndi Kleen {
1337a0c05b36SJiri Olsa 	struct metric *m, *tmp;
1338b18f3e36SAndi Kleen 
1339119e521aSJiri Olsa 	list_for_each_entry_safe (m, tmp, metric_list, nd) {
1340a0c05b36SJiri Olsa 		list_del_init(&m->nd);
13413d81d761SIan Rogers 		metric__free(m);
1342b18f3e36SAndi Kleen 	}
1343b18f3e36SAndi Kleen }
1344b18f3e36SAndi Kleen 
13455ecd5a0cSIan Rogers /**
13468586d274SIan Rogers  * find_tool_events - Search for the pressence of tool events in metric_list.
13478586d274SIan Rogers  * @metric_list: List to take metrics from.
13488586d274SIan Rogers  * @tool_events: Array of false values, indices corresponding to tool events set
13498586d274SIan Rogers  *               to true if tool event is found.
13508586d274SIan Rogers  */
13518586d274SIan Rogers static void find_tool_events(const struct list_head *metric_list,
13528586d274SIan Rogers 			     bool tool_events[PERF_TOOL_MAX])
13538586d274SIan Rogers {
13548586d274SIan Rogers 	struct metric *m;
13558586d274SIan Rogers 
13568586d274SIan Rogers 	list_for_each_entry(m, metric_list, nd) {
13578586d274SIan Rogers 		int i;
13588586d274SIan Rogers 
13598586d274SIan Rogers 		perf_tool_event__for_each_event(i) {
13608586d274SIan Rogers 			struct expr_id_data *data;
13618586d274SIan Rogers 
13628586d274SIan Rogers 			if (!tool_events[i] &&
13638586d274SIan Rogers 			    !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
13648586d274SIan Rogers 				tool_events[i] = true;
13658586d274SIan Rogers 		}
13668586d274SIan Rogers 	}
13678586d274SIan Rogers }
13688586d274SIan Rogers 
13698586d274SIan Rogers /**
1370180a5013SIan Rogers  * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events
13715ecd5a0cSIan Rogers  *                           metric IDs, as the IDs are held in a set,
13725ecd5a0cSIan Rogers  *                           duplicates will be removed.
13735ecd5a0cSIan Rogers  * @metric_list: List to take metrics from.
13745ecd5a0cSIan Rogers  * @combined: Out argument for result.
13755ecd5a0cSIan Rogers  */
13765ecd5a0cSIan Rogers static int build_combined_expr_ctx(const struct list_head *metric_list,
13775ecd5a0cSIan Rogers 				   struct expr_parse_ctx **combined)
13785ecd5a0cSIan Rogers {
13795ecd5a0cSIan Rogers 	struct hashmap_entry *cur;
13805ecd5a0cSIan Rogers 	size_t bkt;
13815ecd5a0cSIan Rogers 	struct metric *m;
13825ecd5a0cSIan Rogers 	char *dup;
13835ecd5a0cSIan Rogers 	int ret;
13845ecd5a0cSIan Rogers 
13855ecd5a0cSIan Rogers 	*combined = expr__ctx_new();
13865ecd5a0cSIan Rogers 	if (!*combined)
13875ecd5a0cSIan Rogers 		return -ENOMEM;
13885ecd5a0cSIan Rogers 
13895ecd5a0cSIan Rogers 	list_for_each_entry(m, metric_list, nd) {
1390180a5013SIan Rogers 		if (!m->group_events && !m->modifier) {
13915ecd5a0cSIan Rogers 			hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
1392c302378bSEduard Zingerman 				dup = strdup(cur->pkey);
13935ecd5a0cSIan Rogers 				if (!dup) {
13945ecd5a0cSIan Rogers 					ret = -ENOMEM;
13955ecd5a0cSIan Rogers 					goto err_out;
13965ecd5a0cSIan Rogers 				}
13975ecd5a0cSIan Rogers 				ret = expr__add_id(*combined, dup);
13985ecd5a0cSIan Rogers 				if (ret)
13995ecd5a0cSIan Rogers 					goto err_out;
14005ecd5a0cSIan Rogers 			}
14015ecd5a0cSIan Rogers 		}
14025ecd5a0cSIan Rogers 	}
14035ecd5a0cSIan Rogers 	return 0;
14045ecd5a0cSIan Rogers err_out:
14055ecd5a0cSIan Rogers 	expr__ctx_free(*combined);
14065ecd5a0cSIan Rogers 	*combined = NULL;
14075ecd5a0cSIan Rogers 	return ret;
14085ecd5a0cSIan Rogers }
14095ecd5a0cSIan Rogers 
14105ecd5a0cSIan Rogers /**
14115ecd5a0cSIan Rogers  * parse_ids - Build the event string for the ids and parse them creating an
14125ecd5a0cSIan Rogers  *             evlist. The encoded metric_ids are decoded.
14136b6b16b3SIan Rogers  * @metric_no_merge: is metric sharing explicitly disabled.
14145ecd5a0cSIan Rogers  * @fake_pmu: used when testing metrics not supported by the current CPU.
14155ecd5a0cSIan Rogers  * @ids: the event identifiers parsed from a metric.
1416b85a4d61SIan Rogers  * @modifier: any modifiers added to the events.
1417180a5013SIan Rogers  * @group_events: should events be placed in a weak group.
14188586d274SIan Rogers  * @tool_events: entries set true if the tool event of index could be present in
14198586d274SIan Rogers  *               the overall list of metrics.
14205ecd5a0cSIan Rogers  * @out_evlist: the created list of events.
14215ecd5a0cSIan Rogers  */
14226b6b16b3SIan Rogers static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
14236b6b16b3SIan Rogers 		     struct expr_parse_ctx *ids, const char *modifier,
1424180a5013SIan Rogers 		     bool group_events, const bool tool_events[PERF_TOOL_MAX],
14258586d274SIan Rogers 		     struct evlist **out_evlist)
14265ecd5a0cSIan Rogers {
14275ecd5a0cSIan Rogers 	struct parse_events_error parse_error;
14285ecd5a0cSIan Rogers 	struct evlist *parsed_evlist;
14295ecd5a0cSIan Rogers 	struct strbuf events = STRBUF_INIT;
14305ecd5a0cSIan Rogers 	int ret;
14315ecd5a0cSIan Rogers 
14325ecd5a0cSIan Rogers 	*out_evlist = NULL;
14336b6b16b3SIan Rogers 	if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
1434c788ef61SIan Rogers 		bool added_event = false;
14359aa09230SIan Rogers 		int i;
14365ecd5a0cSIan Rogers 		/*
14379aa09230SIan Rogers 		 * We may fail to share events between metrics because a tool
14389aa09230SIan Rogers 		 * event isn't present in one metric. For example, a ratio of
14399aa09230SIan Rogers 		 * cache misses doesn't need duration_time but the same events
14409aa09230SIan Rogers 		 * may be used for a misses per second. Events without sharing
14419aa09230SIan Rogers 		 * implies multiplexing, that is best avoided, so place
14429aa09230SIan Rogers 		 * all tool events in every group.
14436b6b16b3SIan Rogers 		 *
14446b6b16b3SIan Rogers 		 * Also, there may be no ids/events in the expression parsing
14456b6b16b3SIan Rogers 		 * context because of constant evaluation, e.g.:
14465ecd5a0cSIan Rogers 		 *    event1 if #smt_on else 0
14479aa09230SIan Rogers 		 * Add a tool event to avoid a parse error on an empty string.
14485ecd5a0cSIan Rogers 		 */
14499aa09230SIan Rogers 		perf_tool_event__for_each_event(i) {
14508586d274SIan Rogers 			if (tool_events[i]) {
14519aa09230SIan Rogers 				char *tmp = strdup(perf_tool_event__to_str(i));
14529aa09230SIan Rogers 
14535ecd5a0cSIan Rogers 				if (!tmp)
14545ecd5a0cSIan Rogers 					return -ENOMEM;
14555ecd5a0cSIan Rogers 				ids__insert(ids->ids, tmp);
1456c788ef61SIan Rogers 				added_event = true;
14575ecd5a0cSIan Rogers 			}
14589aa09230SIan Rogers 		}
1459c788ef61SIan Rogers 		if (!added_event && hashmap__size(ids->ids) == 0) {
1460c788ef61SIan Rogers 			char *tmp = strdup("duration_time");
1461c788ef61SIan Rogers 
1462c788ef61SIan Rogers 			if (!tmp)
1463c788ef61SIan Rogers 				return -ENOMEM;
1464c788ef61SIan Rogers 			ids__insert(ids->ids, tmp);
1465c788ef61SIan Rogers 		}
14668586d274SIan Rogers 	}
1467b85a4d61SIan Rogers 	ret = metricgroup__build_event_string(&events, ids, modifier,
1468180a5013SIan Rogers 					      group_events);
14695ecd5a0cSIan Rogers 	if (ret)
14705ecd5a0cSIan Rogers 		return ret;
14715ecd5a0cSIan Rogers 
14725ecd5a0cSIan Rogers 	parsed_evlist = evlist__new();
14735ecd5a0cSIan Rogers 	if (!parsed_evlist) {
14745ecd5a0cSIan Rogers 		ret = -ENOMEM;
14755ecd5a0cSIan Rogers 		goto err_out;
14765ecd5a0cSIan Rogers 	}
14775ecd5a0cSIan Rogers 	pr_debug("Parsing metric events '%s'\n", events.buf);
147807eafd4eSIan Rogers 	parse_events_error__init(&parse_error);
1479411ad22eSIan Rogers 	ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL,
1480411ad22eSIan Rogers 			     &parse_error, fake_pmu, /*warn_if_reordered=*/false);
14815ecd5a0cSIan Rogers 	if (ret) {
14826c191289SIan Rogers 		parse_events_error__print(&parse_error, events.buf);
14835ecd5a0cSIan Rogers 		goto err_out;
14845ecd5a0cSIan Rogers 	}
1485b85a4d61SIan Rogers 	ret = decode_all_metric_ids(parsed_evlist, modifier);
14865ecd5a0cSIan Rogers 	if (ret)
14875ecd5a0cSIan Rogers 		goto err_out;
14885ecd5a0cSIan Rogers 
14895ecd5a0cSIan Rogers 	*out_evlist = parsed_evlist;
14905ecd5a0cSIan Rogers 	parsed_evlist = NULL;
14915ecd5a0cSIan Rogers err_out:
149207eafd4eSIan Rogers 	parse_events_error__exit(&parse_error);
14935ecd5a0cSIan Rogers 	evlist__delete(parsed_evlist);
14945ecd5a0cSIan Rogers 	strbuf_release(&events);
14955ecd5a0cSIan Rogers 	return ret;
14965ecd5a0cSIan Rogers }
14975ecd5a0cSIan Rogers 
1498bd3846d0SIan Rogers static int parse_groups(struct evlist *perf_evlist,
1499bd3846d0SIan Rogers 			const char *pmu, const char *str,
150005530a79SIan Rogers 			bool metric_no_group,
150105530a79SIan Rogers 			bool metric_no_merge,
15021fd09e29SIan Rogers 			bool metric_no_threshold,
15031725e9cdSIan Rogers 			const char *user_requested_cpu_list,
15041725e9cdSIan Rogers 			bool system_wide,
150568173bdaSJiri Olsa 			struct perf_pmu *fake_pmu,
15065ecd5a0cSIan Rogers 			struct rblist *metric_events_list,
1507f8ea2c15SIan Rogers 			const struct pmu_metrics_table *table)
1508b18f3e36SAndi Kleen {
15095ecd5a0cSIan Rogers 	struct evlist *combined_evlist = NULL;
1510119e521aSJiri Olsa 	LIST_HEAD(metric_list);
15115ecd5a0cSIan Rogers 	struct metric *m;
15128586d274SIan Rogers 	bool tool_events[PERF_TOOL_MAX] = {false};
1513b18f3e36SAndi Kleen 	int ret;
1514b18f3e36SAndi Kleen 
15155ecd5a0cSIan Rogers 	if (metric_events_list->nr_entries == 0)
15165ecd5a0cSIan Rogers 		metricgroup__rblist_init(metric_events_list);
1517bd3846d0SIan Rogers 	ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold,
15181725e9cdSIan Rogers 					   user_requested_cpu_list,
15191725e9cdSIan Rogers 					   system_wide, &metric_list, table);
1520ec5c5b3dSIan Rogers 	if (ret)
1521ec5c5b3dSIan Rogers 		goto out;
1522ec5c5b3dSIan Rogers 
15235ecd5a0cSIan Rogers 	/* Sort metrics from largest to smallest. */
15245ecd5a0cSIan Rogers 	list_sort(NULL, &metric_list, metric_list_cmp);
15255ecd5a0cSIan Rogers 
15265ecd5a0cSIan Rogers 	if (!metric_no_merge) {
15275ecd5a0cSIan Rogers 		struct expr_parse_ctx *combined = NULL;
15285ecd5a0cSIan Rogers 
15298586d274SIan Rogers 		find_tool_events(&metric_list, tool_events);
15308586d274SIan Rogers 
15315ecd5a0cSIan Rogers 		ret = build_combined_expr_ctx(&metric_list, &combined);
15325ecd5a0cSIan Rogers 
15335ecd5a0cSIan Rogers 		if (!ret && combined && hashmap__size(combined->ids)) {
15346b6b16b3SIan Rogers 			ret = parse_ids(metric_no_merge, fake_pmu, combined,
15356b6b16b3SIan Rogers 					/*modifier=*/NULL,
1536180a5013SIan Rogers 					/*group_events=*/false,
15378586d274SIan Rogers 					tool_events,
153817b3867dSIan Rogers 					&combined_evlist);
15395ecd5a0cSIan Rogers 		}
15405ecd5a0cSIan Rogers 		if (combined)
15415ecd5a0cSIan Rogers 			expr__ctx_free(combined);
15425ecd5a0cSIan Rogers 
15435ecd5a0cSIan Rogers 		if (ret)
15445ecd5a0cSIan Rogers 			goto out;
15455ecd5a0cSIan Rogers 	}
15465ecd5a0cSIan Rogers 
15475ecd5a0cSIan Rogers 	list_for_each_entry(m, &metric_list, nd) {
15485ecd5a0cSIan Rogers 		struct metric_event *me;
15495ecd5a0cSIan Rogers 		struct evsel **metric_events;
15505ecd5a0cSIan Rogers 		struct evlist *metric_evlist = NULL;
15515ecd5a0cSIan Rogers 		struct metric *n;
15525ecd5a0cSIan Rogers 		struct metric_expr *expr;
15535ecd5a0cSIan Rogers 
1554180a5013SIan Rogers 		if (combined_evlist && !m->group_events) {
15555ecd5a0cSIan Rogers 			metric_evlist = combined_evlist;
15565ecd5a0cSIan Rogers 		} else if (!metric_no_merge) {
15575ecd5a0cSIan Rogers 			/*
15585ecd5a0cSIan Rogers 			 * See if the IDs for this metric are a subset of an
15595ecd5a0cSIan Rogers 			 * earlier metric.
15605ecd5a0cSIan Rogers 			 */
15615ecd5a0cSIan Rogers 			list_for_each_entry(n, &metric_list, nd) {
15625ecd5a0cSIan Rogers 				if (m == n)
15635ecd5a0cSIan Rogers 					break;
15645ecd5a0cSIan Rogers 
15655ecd5a0cSIan Rogers 				if (n->evlist == NULL)
15665ecd5a0cSIan Rogers 					continue;
15675ecd5a0cSIan Rogers 
1568b85a4d61SIan Rogers 				if ((!m->modifier && n->modifier) ||
1569b85a4d61SIan Rogers 				    (m->modifier && !n->modifier) ||
1570b85a4d61SIan Rogers 				    (m->modifier && n->modifier &&
1571b85a4d61SIan Rogers 					    strcmp(m->modifier, n->modifier)))
1572b85a4d61SIan Rogers 					continue;
1573b85a4d61SIan Rogers 
1574bd3846d0SIan Rogers 				if ((!m->pmu && n->pmu) ||
1575bd3846d0SIan Rogers 				    (m->pmu && !n->pmu) ||
1576bd3846d0SIan Rogers 				    (m->pmu && n->pmu && strcmp(m->pmu, n->pmu)))
1577bd3846d0SIan Rogers 					continue;
1578bd3846d0SIan Rogers 
15795ecd5a0cSIan Rogers 				if (expr__subset_of_ids(n->pctx, m->pctx)) {
15805ecd5a0cSIan Rogers 					pr_debug("Events in '%s' fully contained within '%s'\n",
15815ecd5a0cSIan Rogers 						 m->metric_name, n->metric_name);
15825ecd5a0cSIan Rogers 					metric_evlist = n->evlist;
15835ecd5a0cSIan Rogers 					break;
15845ecd5a0cSIan Rogers 				}
15855ecd5a0cSIan Rogers 
15865ecd5a0cSIan Rogers 			}
15875ecd5a0cSIan Rogers 		}
15885ecd5a0cSIan Rogers 		if (!metric_evlist) {
158917b3867dSIan Rogers 			ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
1590180a5013SIan Rogers 					m->group_events, tool_events, &m->evlist);
15915ecd5a0cSIan Rogers 			if (ret)
15925ecd5a0cSIan Rogers 				goto out;
15935ecd5a0cSIan Rogers 
15945ecd5a0cSIan Rogers 			metric_evlist = m->evlist;
15955ecd5a0cSIan Rogers 		}
1596bd3846d0SIan Rogers 		ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids,
1597bd3846d0SIan Rogers 					  metric_evlist, &metric_events);
15985ecd5a0cSIan Rogers 		if (ret) {
1599*8a4859c5SIan Rogers 			pr_err("Cannot resolve IDs for %s: %s\n",
16005ecd5a0cSIan Rogers 				m->metric_name, m->metric_expr);
16015ecd5a0cSIan Rogers 			goto out;
16025ecd5a0cSIan Rogers 		}
16035ecd5a0cSIan Rogers 
16045ecd5a0cSIan Rogers 		me = metricgroup__lookup(metric_events_list, metric_events[0], true);
16055ecd5a0cSIan Rogers 
16065ecd5a0cSIan Rogers 		expr = malloc(sizeof(struct metric_expr));
16075ecd5a0cSIan Rogers 		if (!expr) {
16085ecd5a0cSIan Rogers 			ret = -ENOMEM;
16095ecd5a0cSIan Rogers 			free(metric_events);
16105ecd5a0cSIan Rogers 			goto out;
16115ecd5a0cSIan Rogers 		}
16125ecd5a0cSIan Rogers 
16135ecd5a0cSIan Rogers 		expr->metric_refs = m->metric_refs;
16145ecd5a0cSIan Rogers 		m->metric_refs = NULL;
16155ecd5a0cSIan Rogers 		expr->metric_expr = m->metric_expr;
1616b85a4d61SIan Rogers 		if (m->modifier) {
1617b85a4d61SIan Rogers 			char *tmp;
1618b85a4d61SIan Rogers 
1619b85a4d61SIan Rogers 			if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
1620b85a4d61SIan Rogers 				expr->metric_name = NULL;
1621b85a4d61SIan Rogers 			else
1622b85a4d61SIan Rogers 				expr->metric_name = tmp;
1623b85a4d61SIan Rogers 		} else
1624b85a4d61SIan Rogers 			expr->metric_name = strdup(m->metric_name);
1625b85a4d61SIan Rogers 
1626b85a4d61SIan Rogers 		if (!expr->metric_name) {
1627b85a4d61SIan Rogers 			ret = -ENOMEM;
1628b85a4d61SIan Rogers 			free(metric_events);
1629b85a4d61SIan Rogers 			goto out;
1630b85a4d61SIan Rogers 		}
1631d0a3052fSIan Rogers 		expr->metric_threshold = m->metric_threshold;
16325ecd5a0cSIan Rogers 		expr->metric_unit = m->metric_unit;
16335ecd5a0cSIan Rogers 		expr->metric_events = metric_events;
16341a6abddeSIan Rogers 		expr->runtime = m->pctx->sctx.runtime;
16355ecd5a0cSIan Rogers 		list_add(&expr->nd, &me->head);
16365ecd5a0cSIan Rogers 	}
16375ecd5a0cSIan Rogers 
16385ecd5a0cSIan Rogers 
1639aba8c5e3SIan Rogers 	if (combined_evlist) {
16405ecd5a0cSIan Rogers 		evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
1641aba8c5e3SIan Rogers 		evlist__delete(combined_evlist);
1642aba8c5e3SIan Rogers 	}
16435ecd5a0cSIan Rogers 
16445ecd5a0cSIan Rogers 	list_for_each_entry(m, &metric_list, nd) {
16455ecd5a0cSIan Rogers 		if (m->evlist)
16465ecd5a0cSIan Rogers 			evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
16475ecd5a0cSIan Rogers 	}
16485ecd5a0cSIan Rogers 
1649b18f3e36SAndi Kleen out:
1650119e521aSJiri Olsa 	metricgroup__free_metrics(&metric_list);
1651b18f3e36SAndi Kleen 	return ret;
1652b18f3e36SAndi Kleen }
1653742d92ffSThomas Richter 
1654a4b8cfcaSIan Rogers int metricgroup__parse_groups(struct evlist *perf_evlist,
1655dae47d39SIan Rogers 			      const char *pmu,
16568b4468a2SJiri Olsa 			      const char *str,
16578b4468a2SJiri Olsa 			      bool metric_no_group,
16588b4468a2SJiri Olsa 			      bool metric_no_merge,
16591fd09e29SIan Rogers 			      bool metric_no_threshold,
16601725e9cdSIan Rogers 			      const char *user_requested_cpu_list,
16611725e9cdSIan Rogers 			      bool system_wide,
16628b4468a2SJiri Olsa 			      struct rblist *metric_events)
16638b4468a2SJiri Olsa {
1664f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
16658b4468a2SJiri Olsa 
16663f5df3acSIan Rogers 	if (!table)
16673f5df3acSIan Rogers 		return -EINVAL;
16683f5df3acSIan Rogers 
1669dae47d39SIan Rogers 	return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge,
16701fd09e29SIan Rogers 			    metric_no_threshold, user_requested_cpu_list, system_wide,
16711725e9cdSIan Rogers 			    /*fake_pmu=*/NULL, metric_events, table);
16728b4468a2SJiri Olsa }
16738b4468a2SJiri Olsa 
1674f78ac00aSJiri Olsa int metricgroup__parse_groups_test(struct evlist *evlist,
1675f8ea2c15SIan Rogers 				   const struct pmu_metrics_table *table,
1676f78ac00aSJiri Olsa 				   const char *str,
1677f78ac00aSJiri Olsa 				   struct rblist *metric_events)
1678f78ac00aSJiri Olsa {
1679bd3846d0SIan Rogers 	return parse_groups(evlist, "all", str,
16801fd09e29SIan Rogers 			    /*metric_no_group=*/false,
16811fd09e29SIan Rogers 			    /*metric_no_merge=*/false,
16821fd09e29SIan Rogers 			    /*metric_no_threshold=*/false,
16831725e9cdSIan Rogers 			    /*user_requested_cpu_list=*/NULL,
16841725e9cdSIan Rogers 			    /*system_wide=*/false,
16851725e9cdSIan Rogers 			    &perf_pmu__fake, metric_events, table);
1686f78ac00aSJiri Olsa }
1687f78ac00aSJiri Olsa 
1688bd3846d0SIan Rogers struct metricgroup__has_metric_data {
1689bd3846d0SIan Rogers 	const char *pmu;
1690bd3846d0SIan Rogers 	const char *metric;
1691bd3846d0SIan Rogers };
1692db95818eSIan Rogers static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
1693f8ea2c15SIan Rogers 					    const struct pmu_metrics_table *table __maybe_unused,
1694660842e4SIan Rogers 					    void *vdata)
1695660842e4SIan Rogers {
1696bd3846d0SIan Rogers 	struct metricgroup__has_metric_data *data = vdata;
1697660842e4SIan Rogers 
1698bd3846d0SIan Rogers 	return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0;
1699660842e4SIan Rogers }
1700660842e4SIan Rogers 
1701bd3846d0SIan Rogers bool metricgroup__has_metric(const char *pmu, const char *metric)
1702742d92ffSThomas Richter {
1703f8ea2c15SIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
1704bd3846d0SIan Rogers 	struct metricgroup__has_metric_data data = {
1705bd3846d0SIan Rogers 		.pmu = pmu,
1706bd3846d0SIan Rogers 		.metric = metric,
1707bd3846d0SIan Rogers 	};
1708742d92ffSThomas Richter 
1709eeac7730SIan Rogers 	if (!table)
1710742d92ffSThomas Richter 		return false;
1711742d92ffSThomas Richter 
1712bd3846d0SIan Rogers 	return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, &data)
1713bd3846d0SIan Rogers 		? true : false;
1714742d92ffSThomas Richter }
1715b214ba8cSNamhyung Kim 
17161647cd5bSIan Rogers static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,
17171647cd5bSIan Rogers 					    const struct pmu_metrics_table *table __maybe_unused,
17181647cd5bSIan Rogers 					    void *data)
17191647cd5bSIan Rogers {
17201647cd5bSIan Rogers 	unsigned int *max_level = data;
17211647cd5bSIan Rogers 	unsigned int level;
17221647cd5bSIan Rogers 	const char *p = strstr(pm->metric_group, "TopdownL");
17231647cd5bSIan Rogers 
17241647cd5bSIan Rogers 	if (!p || p[8] == '\0')
17251647cd5bSIan Rogers 		return 0;
17261647cd5bSIan Rogers 
17271647cd5bSIan Rogers 	level = p[8] - '0';
17281647cd5bSIan Rogers 	if (level > *max_level)
17291647cd5bSIan Rogers 		*max_level = level;
17301647cd5bSIan Rogers 
17311647cd5bSIan Rogers 	return 0;
17321647cd5bSIan Rogers }
17331647cd5bSIan Rogers 
17341647cd5bSIan Rogers unsigned int metricgroups__topdown_max_level(void)
17351647cd5bSIan Rogers {
17361647cd5bSIan Rogers 	unsigned int max_level = 0;
17371647cd5bSIan Rogers 	const struct pmu_metrics_table *table = pmu_metrics_table__find();
17381647cd5bSIan Rogers 
17391647cd5bSIan Rogers 	if (!table)
17401647cd5bSIan Rogers 		return false;
17411647cd5bSIan Rogers 
17421647cd5bSIan Rogers 	pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback,
17431647cd5bSIan Rogers 					  &max_level);
17441647cd5bSIan Rogers 	return max_level;
17451647cd5bSIan Rogers }
17461647cd5bSIan Rogers 
1747b214ba8cSNamhyung Kim int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
1748b214ba8cSNamhyung Kim 				    struct rblist *new_metric_events,
1749b214ba8cSNamhyung Kim 				    struct rblist *old_metric_events)
1750b214ba8cSNamhyung Kim {
175184f879c5SXin Gao 	unsigned int i;
1752b214ba8cSNamhyung Kim 
1753b214ba8cSNamhyung Kim 	for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
1754b214ba8cSNamhyung Kim 		struct rb_node *nd;
1755b214ba8cSNamhyung Kim 		struct metric_event *old_me, *new_me;
1756b214ba8cSNamhyung Kim 		struct metric_expr *old_expr, *new_expr;
1757b214ba8cSNamhyung Kim 		struct evsel *evsel;
1758b214ba8cSNamhyung Kim 		size_t alloc_size;
1759b214ba8cSNamhyung Kim 		int idx, nr;
1760b214ba8cSNamhyung Kim 
1761b214ba8cSNamhyung Kim 		nd = rblist__entry(old_metric_events, i);
1762b214ba8cSNamhyung Kim 		old_me = container_of(nd, struct metric_event, nd);
1763b214ba8cSNamhyung Kim 
176438fe0e01SJiri Olsa 		evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
1765b214ba8cSNamhyung Kim 		if (!evsel)
1766b214ba8cSNamhyung Kim 			return -EINVAL;
1767b214ba8cSNamhyung Kim 		new_me = metricgroup__lookup(new_metric_events, evsel, true);
1768b214ba8cSNamhyung Kim 		if (!new_me)
1769b214ba8cSNamhyung Kim 			return -ENOMEM;
1770b214ba8cSNamhyung Kim 
1771b214ba8cSNamhyung Kim 		pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
177238fe0e01SJiri Olsa 			 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
1773b214ba8cSNamhyung Kim 
1774b214ba8cSNamhyung Kim 		list_for_each_entry(old_expr, &old_me->head, nd) {
1775b214ba8cSNamhyung Kim 			new_expr = malloc(sizeof(*new_expr));
1776b214ba8cSNamhyung Kim 			if (!new_expr)
1777b214ba8cSNamhyung Kim 				return -ENOMEM;
1778b214ba8cSNamhyung Kim 
1779b214ba8cSNamhyung Kim 			new_expr->metric_expr = old_expr->metric_expr;
17806c73f819SIan Rogers 			new_expr->metric_threshold = old_expr->metric_threshold;
1781b85a4d61SIan Rogers 			new_expr->metric_name = strdup(old_expr->metric_name);
1782b85a4d61SIan Rogers 			if (!new_expr->metric_name)
1783b85a4d61SIan Rogers 				return -ENOMEM;
1784b85a4d61SIan Rogers 
1785b214ba8cSNamhyung Kim 			new_expr->metric_unit = old_expr->metric_unit;
1786b214ba8cSNamhyung Kim 			new_expr->runtime = old_expr->runtime;
1787b214ba8cSNamhyung Kim 
1788b214ba8cSNamhyung Kim 			if (old_expr->metric_refs) {
1789b214ba8cSNamhyung Kim 				/* calculate number of metric_events */
1790b214ba8cSNamhyung Kim 				for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
1791b214ba8cSNamhyung Kim 					continue;
1792b214ba8cSNamhyung Kim 				alloc_size = sizeof(*new_expr->metric_refs);
1793b214ba8cSNamhyung Kim 				new_expr->metric_refs = calloc(nr + 1, alloc_size);
1794b214ba8cSNamhyung Kim 				if (!new_expr->metric_refs) {
1795b214ba8cSNamhyung Kim 					free(new_expr);
1796b214ba8cSNamhyung Kim 					return -ENOMEM;
1797b214ba8cSNamhyung Kim 				}
1798b214ba8cSNamhyung Kim 
1799b214ba8cSNamhyung Kim 				memcpy(new_expr->metric_refs, old_expr->metric_refs,
1800b214ba8cSNamhyung Kim 				       nr * alloc_size);
1801b214ba8cSNamhyung Kim 			} else {
1802b214ba8cSNamhyung Kim 				new_expr->metric_refs = NULL;
1803b214ba8cSNamhyung Kim 			}
1804b214ba8cSNamhyung Kim 
1805b214ba8cSNamhyung Kim 			/* calculate number of metric_events */
1806b214ba8cSNamhyung Kim 			for (nr = 0; old_expr->metric_events[nr]; nr++)
1807b214ba8cSNamhyung Kim 				continue;
1808b214ba8cSNamhyung Kim 			alloc_size = sizeof(*new_expr->metric_events);
1809b214ba8cSNamhyung Kim 			new_expr->metric_events = calloc(nr + 1, alloc_size);
1810b214ba8cSNamhyung Kim 			if (!new_expr->metric_events) {
181111ff9bcdSArnaldo Carvalho de Melo 				zfree(&new_expr->metric_refs);
1812b214ba8cSNamhyung Kim 				free(new_expr);
1813b214ba8cSNamhyung Kim 				return -ENOMEM;
1814b214ba8cSNamhyung Kim 			}
1815b214ba8cSNamhyung Kim 
1816b214ba8cSNamhyung Kim 			/* copy evsel in the same position */
1817b214ba8cSNamhyung Kim 			for (idx = 0; idx < nr; idx++) {
1818b214ba8cSNamhyung Kim 				evsel = old_expr->metric_events[idx];
181938fe0e01SJiri Olsa 				evsel = evlist__find_evsel(evlist, evsel->core.idx);
1820b214ba8cSNamhyung Kim 				if (evsel == NULL) {
182111ff9bcdSArnaldo Carvalho de Melo 					zfree(&new_expr->metric_events);
182211ff9bcdSArnaldo Carvalho de Melo 					zfree(&new_expr->metric_refs);
1823b214ba8cSNamhyung Kim 					free(new_expr);
1824b214ba8cSNamhyung Kim 					return -EINVAL;
1825b214ba8cSNamhyung Kim 				}
1826b214ba8cSNamhyung Kim 				new_expr->metric_events[idx] = evsel;
1827b214ba8cSNamhyung Kim 			}
1828b214ba8cSNamhyung Kim 
1829b214ba8cSNamhyung Kim 			list_add(&new_expr->nd, &new_me->head);
1830b214ba8cSNamhyung Kim 		}
1831b214ba8cSNamhyung Kim 	}
1832b214ba8cSNamhyung Kim 	return 0;
1833b214ba8cSNamhyung Kim }
1834