xref: /linux/tools/perf/util/metricgroup.c (revision 45db55f2ef5e98ef096096efad975dc684a9493f)
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"
14b18f3e36SAndi Kleen #include "expr.h"
15b18f3e36SAndi Kleen #include "rblist.h"
16b18f3e36SAndi Kleen #include <string.h>
17b18f3e36SAndi Kleen #include <errno.h>
18b18f3e36SAndi Kleen #include "pmu-events/pmu-events.h"
19b18f3e36SAndi Kleen #include "strlist.h"
20b18f3e36SAndi Kleen #include <assert.h>
21bd9860bfSArnaldo Carvalho de Melo #include <linux/ctype.h>
22b4209025SArnaldo Carvalho de Melo #include <linux/string.h>
23d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h>
240b8026e8SArnaldo Carvalho de Melo #include <subcmd/parse-options.h>
25ab483d8bSKan Liang #include <api/fs/fs.h>
26ab483d8bSKan Liang #include "util.h"
27b18f3e36SAndi Kleen 
28b18f3e36SAndi Kleen struct metric_event *metricgroup__lookup(struct rblist *metric_events,
2932dcd021SJiri Olsa 					 struct evsel *evsel,
30b18f3e36SAndi Kleen 					 bool create)
31b18f3e36SAndi Kleen {
32b18f3e36SAndi Kleen 	struct rb_node *nd;
33b18f3e36SAndi Kleen 	struct metric_event me = {
34b18f3e36SAndi Kleen 		.evsel = evsel
35b18f3e36SAndi Kleen 	};
364bd1bef8SAndi Kleen 
374bd1bef8SAndi Kleen 	if (!metric_events)
384bd1bef8SAndi Kleen 		return NULL;
394bd1bef8SAndi Kleen 
40b18f3e36SAndi Kleen 	nd = rblist__find(metric_events, &me);
41b18f3e36SAndi Kleen 	if (nd)
42b18f3e36SAndi Kleen 		return container_of(nd, struct metric_event, nd);
43b18f3e36SAndi Kleen 	if (create) {
44b18f3e36SAndi Kleen 		rblist__add_node(metric_events, &me);
45b18f3e36SAndi Kleen 		nd = rblist__find(metric_events, &me);
46b18f3e36SAndi Kleen 		if (nd)
47b18f3e36SAndi Kleen 			return container_of(nd, struct metric_event, nd);
48b18f3e36SAndi Kleen 	}
49b18f3e36SAndi Kleen 	return NULL;
50b18f3e36SAndi Kleen }
51b18f3e36SAndi Kleen 
52b18f3e36SAndi Kleen static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
53b18f3e36SAndi Kleen {
54b18f3e36SAndi Kleen 	struct metric_event *a = container_of(rb_node,
55b18f3e36SAndi Kleen 					      struct metric_event,
56b18f3e36SAndi Kleen 					      nd);
57b18f3e36SAndi Kleen 	const struct metric_event *b = entry;
58b18f3e36SAndi Kleen 
59b18f3e36SAndi Kleen 	if (a->evsel == b->evsel)
60b18f3e36SAndi Kleen 		return 0;
61b18f3e36SAndi Kleen 	if ((char *)a->evsel < (char *)b->evsel)
62b18f3e36SAndi Kleen 		return -1;
63b18f3e36SAndi Kleen 	return +1;
64b18f3e36SAndi Kleen }
65b18f3e36SAndi Kleen 
66b18f3e36SAndi Kleen static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
67b18f3e36SAndi Kleen 					const void *entry)
68b18f3e36SAndi Kleen {
69b18f3e36SAndi Kleen 	struct metric_event *me = malloc(sizeof(struct metric_event));
70b18f3e36SAndi Kleen 
71b18f3e36SAndi Kleen 	if (!me)
72b18f3e36SAndi Kleen 		return NULL;
73b18f3e36SAndi Kleen 	memcpy(me, entry, sizeof(struct metric_event));
74b18f3e36SAndi Kleen 	me->evsel = ((struct metric_event *)entry)->evsel;
75b18f3e36SAndi Kleen 	INIT_LIST_HEAD(&me->head);
76b18f3e36SAndi Kleen 	return &me->nd;
77b18f3e36SAndi Kleen }
78b18f3e36SAndi Kleen 
79b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events)
80b18f3e36SAndi Kleen {
81b18f3e36SAndi Kleen 	rblist__init(metric_events);
82b18f3e36SAndi Kleen 	metric_events->node_cmp = metric_event_cmp;
83b18f3e36SAndi Kleen 	metric_events->node_new = metric_event_new;
84b18f3e36SAndi Kleen }
85b18f3e36SAndi Kleen 
86b18f3e36SAndi Kleen struct egroup {
87b18f3e36SAndi Kleen 	struct list_head nd;
88ded80bdaSIan Rogers 	struct expr_parse_ctx pctx;
89b18f3e36SAndi Kleen 	const char *metric_name;
90b18f3e36SAndi Kleen 	const char *metric_expr;
91287f2649SJin Yao 	const char *metric_unit;
921e1a873dSKajol Jain 	int runtime;
93b18f3e36SAndi Kleen };
94b18f3e36SAndi Kleen 
9563503dbaSJiri Olsa static struct evsel *find_evsel_group(struct evlist *perf_evlist,
96ded80bdaSIan Rogers 				      struct expr_parse_ctx *pctx,
9758fc90fdSKajol Jain 				      struct evsel **metric_events,
98*45db55f2SIan Rogers 				      unsigned long *evlist_used)
99b18f3e36SAndi Kleen {
100f01642e4SJin Yao 	struct evsel *ev;
101f01642e4SJin Yao 	bool leader_found;
102ded80bdaSIan Rogers 	const size_t idnum = hashmap__size(&pctx->ids);
103ded80bdaSIan Rogers 	size_t i = 0;
104ded80bdaSIan Rogers 	int j = 0;
105ded80bdaSIan Rogers 	double *val_ptr;
106b18f3e36SAndi Kleen 
107b18f3e36SAndi Kleen 	evlist__for_each_entry (perf_evlist, ev) {
108*45db55f2SIan Rogers 		if (test_bit(j++, evlist_used))
10958fc90fdSKajol Jain 			continue;
110ded80bdaSIan Rogers 		if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) {
111f01642e4SJin Yao 			if (!metric_events[i])
112f01642e4SJin Yao 				metric_events[i] = ev;
113eb573e74SKajol Jain 			i++;
114eb573e74SKajol Jain 			if (i == idnum)
115eb573e74SKajol Jain 				break;
116b18f3e36SAndi Kleen 		} else {
117f01642e4SJin Yao 			/* Discard the whole match and start again */
118f01642e4SJin Yao 			i = 0;
119f01642e4SJin Yao 			memset(metric_events, 0,
120f01642e4SJin Yao 				sizeof(struct evsel *) * idnum);
121b18f3e36SAndi Kleen 		}
122b18f3e36SAndi Kleen 	}
123f01642e4SJin Yao 
124eb573e74SKajol Jain 	if (i != idnum) {
125f01642e4SJin Yao 		/* Not whole match */
126b18f3e36SAndi Kleen 		return NULL;
127b18f3e36SAndi Kleen 	}
128b18f3e36SAndi Kleen 
129f01642e4SJin Yao 	metric_events[idnum] = NULL;
130f01642e4SJin Yao 
131f01642e4SJin Yao 	for (i = 0; i < idnum; i++) {
132f01642e4SJin Yao 		leader_found = false;
133f01642e4SJin Yao 		evlist__for_each_entry(perf_evlist, ev) {
134f01642e4SJin Yao 			if (!leader_found && (ev == metric_events[i]))
135f01642e4SJin Yao 				leader_found = true;
136f01642e4SJin Yao 
137f01642e4SJin Yao 			if (leader_found &&
138f01642e4SJin Yao 			    !strcmp(ev->name, metric_events[i]->name)) {
139f01642e4SJin Yao 				ev->metric_leader = metric_events[i];
140f01642e4SJin Yao 			}
14158fc90fdSKajol Jain 			j++;
142f01642e4SJin Yao 		}
14358fc90fdSKajol Jain 		ev = metric_events[i];
144*45db55f2SIan Rogers 		set_bit(ev->idx, evlist_used);
145f01642e4SJin Yao 	}
146f01642e4SJin Yao 
147f01642e4SJin Yao 	return metric_events[0];
148f01642e4SJin Yao }
149f01642e4SJin Yao 
150b18f3e36SAndi Kleen static int metricgroup__setup_events(struct list_head *groups,
15163503dbaSJiri Olsa 				     struct evlist *perf_evlist,
152b18f3e36SAndi Kleen 				     struct rblist *metric_events_list)
153b18f3e36SAndi Kleen {
154b18f3e36SAndi Kleen 	struct metric_event *me;
155b18f3e36SAndi Kleen 	struct metric_expr *expr;
156b18f3e36SAndi Kleen 	int i = 0;
157b18f3e36SAndi Kleen 	int ret = 0;
158b18f3e36SAndi Kleen 	struct egroup *eg;
15932dcd021SJiri Olsa 	struct evsel *evsel;
160*45db55f2SIan Rogers 	unsigned long *evlist_used;
16158fc90fdSKajol Jain 
162*45db55f2SIan Rogers 	evlist_used = bitmap_alloc(perf_evlist->core.nr_entries);
163*45db55f2SIan Rogers 	if (!evlist_used)
164*45db55f2SIan Rogers 		return -ENOMEM;
165b18f3e36SAndi Kleen 
166b18f3e36SAndi Kleen 	list_for_each_entry (eg, groups, nd) {
16732dcd021SJiri Olsa 		struct evsel **metric_events;
168b18f3e36SAndi Kleen 
169ded80bdaSIan Rogers 		metric_events = calloc(sizeof(void *),
170ded80bdaSIan Rogers 				hashmap__size(&eg->pctx.ids) + 1);
171b18f3e36SAndi Kleen 		if (!metric_events) {
172b18f3e36SAndi Kleen 			ret = -ENOMEM;
173b18f3e36SAndi Kleen 			break;
174b18f3e36SAndi Kleen 		}
175ded80bdaSIan Rogers 		evsel = find_evsel_group(perf_evlist, &eg->pctx, metric_events,
176ded80bdaSIan Rogers 					evlist_used);
177b18f3e36SAndi Kleen 		if (!evsel) {
178b18f3e36SAndi Kleen 			pr_debug("Cannot resolve %s: %s\n",
179b18f3e36SAndi Kleen 					eg->metric_name, eg->metric_expr);
180b18f3e36SAndi Kleen 			continue;
181b18f3e36SAndi Kleen 		}
182ded80bdaSIan Rogers 		for (i = 0; metric_events[i]; i++)
183b18f3e36SAndi Kleen 			metric_events[i]->collect_stat = true;
184b18f3e36SAndi Kleen 		me = metricgroup__lookup(metric_events_list, evsel, true);
185b18f3e36SAndi Kleen 		if (!me) {
186b18f3e36SAndi Kleen 			ret = -ENOMEM;
187b18f3e36SAndi Kleen 			break;
188b18f3e36SAndi Kleen 		}
189b18f3e36SAndi Kleen 		expr = malloc(sizeof(struct metric_expr));
190b18f3e36SAndi Kleen 		if (!expr) {
191b18f3e36SAndi Kleen 			ret = -ENOMEM;
192b18f3e36SAndi Kleen 			break;
193b18f3e36SAndi Kleen 		}
194b18f3e36SAndi Kleen 		expr->metric_expr = eg->metric_expr;
195b18f3e36SAndi Kleen 		expr->metric_name = eg->metric_name;
196287f2649SJin Yao 		expr->metric_unit = eg->metric_unit;
197b18f3e36SAndi Kleen 		expr->metric_events = metric_events;
1981e1a873dSKajol Jain 		expr->runtime = eg->runtime;
199b18f3e36SAndi Kleen 		list_add(&expr->nd, &me->head);
200b18f3e36SAndi Kleen 	}
20158fc90fdSKajol Jain 
202*45db55f2SIan Rogers 	bitmap_free(evlist_used);
20358fc90fdSKajol Jain 
204b18f3e36SAndi Kleen 	return ret;
205b18f3e36SAndi Kleen }
206b18f3e36SAndi Kleen 
207b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list)
208b18f3e36SAndi Kleen {
209b18f3e36SAndi Kleen 	int len;
210b18f3e36SAndi Kleen 	char *m;
211b18f3e36SAndi Kleen 
212b18f3e36SAndi Kleen 	if (!list)
213b18f3e36SAndi Kleen 		return false;
214b18f3e36SAndi Kleen 	if (!strcmp(list, "all"))
215b18f3e36SAndi Kleen 		return true;
216b18f3e36SAndi Kleen 	if (!n)
217b18f3e36SAndi Kleen 		return !strcasecmp(list, "No_group");
218b18f3e36SAndi Kleen 	len = strlen(list);
219b18f3e36SAndi Kleen 	m = strcasestr(n, list);
220b18f3e36SAndi Kleen 	if (!m)
221b18f3e36SAndi Kleen 		return false;
222b18f3e36SAndi Kleen 	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
223b18f3e36SAndi Kleen 	    (m[len] == 0 || m[len] == ';'))
224b18f3e36SAndi Kleen 		return true;
225b18f3e36SAndi Kleen 	return false;
226b18f3e36SAndi Kleen }
227b18f3e36SAndi Kleen 
22871b0acceSAndi Kleen struct mep {
22971b0acceSAndi Kleen 	struct rb_node nd;
23071b0acceSAndi Kleen 	const char *name;
23171b0acceSAndi Kleen 	struct strlist *metrics;
23271b0acceSAndi Kleen };
23371b0acceSAndi Kleen 
23471b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry)
23571b0acceSAndi Kleen {
23671b0acceSAndi Kleen 	struct mep *a = container_of(rb_node, struct mep, nd);
23771b0acceSAndi Kleen 	struct mep *b = (struct mep *)entry;
23871b0acceSAndi Kleen 
23971b0acceSAndi Kleen 	return strcmp(a->name, b->name);
24071b0acceSAndi Kleen }
24171b0acceSAndi Kleen 
24271b0acceSAndi Kleen static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
24371b0acceSAndi Kleen 					const void *entry)
24471b0acceSAndi Kleen {
24571b0acceSAndi Kleen 	struct mep *me = malloc(sizeof(struct mep));
24671b0acceSAndi Kleen 
24771b0acceSAndi Kleen 	if (!me)
24871b0acceSAndi Kleen 		return NULL;
24971b0acceSAndi Kleen 	memcpy(me, entry, sizeof(struct mep));
25071b0acceSAndi Kleen 	me->name = strdup(me->name);
25171b0acceSAndi Kleen 	if (!me->name)
25271b0acceSAndi Kleen 		goto out_me;
25371b0acceSAndi Kleen 	me->metrics = strlist__new(NULL, NULL);
25471b0acceSAndi Kleen 	if (!me->metrics)
25571b0acceSAndi Kleen 		goto out_name;
25671b0acceSAndi Kleen 	return &me->nd;
25771b0acceSAndi Kleen out_name:
258d8f9da24SArnaldo Carvalho de Melo 	zfree(&me->name);
25971b0acceSAndi Kleen out_me:
26071b0acceSAndi Kleen 	free(me);
26171b0acceSAndi Kleen 	return NULL;
26271b0acceSAndi Kleen }
26371b0acceSAndi Kleen 
26471b0acceSAndi Kleen static struct mep *mep_lookup(struct rblist *groups, const char *name)
26571b0acceSAndi Kleen {
26671b0acceSAndi Kleen 	struct rb_node *nd;
26771b0acceSAndi Kleen 	struct mep me = {
26871b0acceSAndi Kleen 		.name = name
26971b0acceSAndi Kleen 	};
27071b0acceSAndi Kleen 	nd = rblist__find(groups, &me);
27171b0acceSAndi Kleen 	if (nd)
27271b0acceSAndi Kleen 		return container_of(nd, struct mep, nd);
27371b0acceSAndi Kleen 	rblist__add_node(groups, &me);
27471b0acceSAndi Kleen 	nd = rblist__find(groups, &me);
27571b0acceSAndi Kleen 	if (nd)
27671b0acceSAndi Kleen 		return container_of(nd, struct mep, nd);
27771b0acceSAndi Kleen 	return NULL;
27871b0acceSAndi Kleen }
27971b0acceSAndi Kleen 
28071b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused,
28171b0acceSAndi Kleen 		       struct rb_node *nd)
28271b0acceSAndi Kleen {
28371b0acceSAndi Kleen 	struct mep *me = container_of(nd, struct mep, nd);
28471b0acceSAndi Kleen 
28571b0acceSAndi Kleen 	strlist__delete(me->metrics);
286d8f9da24SArnaldo Carvalho de Melo 	zfree(&me->name);
28771b0acceSAndi Kleen 	free(me);
28871b0acceSAndi Kleen }
28971b0acceSAndi Kleen 
29071b0acceSAndi Kleen static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
29171b0acceSAndi Kleen {
29271b0acceSAndi Kleen 	struct str_node *sn;
29371b0acceSAndi Kleen 	int n = 0;
29471b0acceSAndi Kleen 
29571b0acceSAndi Kleen 	strlist__for_each_entry (sn, metrics) {
29671b0acceSAndi Kleen 		if (raw)
29771b0acceSAndi Kleen 			printf("%s%s", n > 0 ? " " : "", sn->s);
29871b0acceSAndi Kleen 		else
29971b0acceSAndi Kleen 			printf("  %s\n", sn->s);
30071b0acceSAndi Kleen 		n++;
30171b0acceSAndi Kleen 	}
30271b0acceSAndi Kleen 	if (raw)
30371b0acceSAndi Kleen 		putchar('\n');
30471b0acceSAndi Kleen }
30571b0acceSAndi Kleen 
30671b0acceSAndi Kleen void metricgroup__print(bool metrics, bool metricgroups, char *filter,
30733bbc571SJiri Olsa 			bool raw, bool details)
30871b0acceSAndi Kleen {
30954e32dc0SGanapatrao Kulkarni 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
31071b0acceSAndi Kleen 	struct pmu_event *pe;
31171b0acceSAndi Kleen 	int i;
31271b0acceSAndi Kleen 	struct rblist groups;
31371b0acceSAndi Kleen 	struct rb_node *node, *next;
31471b0acceSAndi Kleen 	struct strlist *metriclist = NULL;
31571b0acceSAndi Kleen 
31671b0acceSAndi Kleen 	if (!map)
31771b0acceSAndi Kleen 		return;
31871b0acceSAndi Kleen 
31971b0acceSAndi Kleen 	if (!metricgroups) {
32071b0acceSAndi Kleen 		metriclist = strlist__new(NULL, NULL);
32171b0acceSAndi Kleen 		if (!metriclist)
32271b0acceSAndi Kleen 			return;
32371b0acceSAndi Kleen 	}
32471b0acceSAndi Kleen 
32571b0acceSAndi Kleen 	rblist__init(&groups);
32671b0acceSAndi Kleen 	groups.node_new = mep_new;
32771b0acceSAndi Kleen 	groups.node_cmp = mep_cmp;
32871b0acceSAndi Kleen 	groups.node_delete = mep_delete;
32971b0acceSAndi Kleen 	for (i = 0; ; i++) {
33071b0acceSAndi Kleen 		const char *g;
33171b0acceSAndi Kleen 		pe = &map->table[i];
33271b0acceSAndi Kleen 
33371b0acceSAndi Kleen 		if (!pe->name && !pe->metric_group && !pe->metric_name)
33471b0acceSAndi Kleen 			break;
33571b0acceSAndi Kleen 		if (!pe->metric_expr)
33671b0acceSAndi Kleen 			continue;
33771b0acceSAndi Kleen 		g = pe->metric_group;
33871b0acceSAndi Kleen 		if (!g && pe->metric_name) {
33971b0acceSAndi Kleen 			if (pe->name)
34071b0acceSAndi Kleen 				continue;
34171b0acceSAndi Kleen 			g = "No_group";
34271b0acceSAndi Kleen 		}
34371b0acceSAndi Kleen 		if (g) {
34471b0acceSAndi Kleen 			char *omg;
34571b0acceSAndi Kleen 			char *mg = strdup(g);
34671b0acceSAndi Kleen 
34771b0acceSAndi Kleen 			if (!mg)
34871b0acceSAndi Kleen 				return;
34971b0acceSAndi Kleen 			omg = mg;
35071b0acceSAndi Kleen 			while ((g = strsep(&mg, ";")) != NULL) {
35171b0acceSAndi Kleen 				struct mep *me;
35271b0acceSAndi Kleen 				char *s;
35371b0acceSAndi Kleen 
35480e9073fSArnaldo Carvalho de Melo 				g = skip_spaces(g);
35571b0acceSAndi Kleen 				if (*g == 0)
35671b0acceSAndi Kleen 					g = "No_group";
35771b0acceSAndi Kleen 				if (filter && !strstr(g, filter))
35871b0acceSAndi Kleen 					continue;
35971b0acceSAndi Kleen 				if (raw)
36071b0acceSAndi Kleen 					s = (char *)pe->metric_name;
36171b0acceSAndi Kleen 				else {
36295f04328SMichael Petlan 					if (asprintf(&s, "%s\n%*s%s]",
36395f04328SMichael Petlan 						     pe->metric_name, 8, "[", pe->desc) < 0)
36471b0acceSAndi Kleen 						return;
36533bbc571SJiri Olsa 
36633bbc571SJiri Olsa 					if (details) {
36733bbc571SJiri Olsa 						if (asprintf(&s, "%s\n%*s%s]",
36833bbc571SJiri Olsa 							     s, 8, "[", pe->metric_expr) < 0)
36933bbc571SJiri Olsa 							return;
37033bbc571SJiri Olsa 					}
37171b0acceSAndi Kleen 				}
37271b0acceSAndi Kleen 
37371b0acceSAndi Kleen 				if (!s)
37471b0acceSAndi Kleen 					continue;
37571b0acceSAndi Kleen 
37671b0acceSAndi Kleen 				if (!metricgroups) {
37771b0acceSAndi Kleen 					strlist__add(metriclist, s);
37871b0acceSAndi Kleen 				} else {
37971b0acceSAndi Kleen 					me = mep_lookup(&groups, g);
38071b0acceSAndi Kleen 					if (!me)
38171b0acceSAndi Kleen 						continue;
38271b0acceSAndi Kleen 					strlist__add(me->metrics, s);
38371b0acceSAndi Kleen 				}
38471b0acceSAndi Kleen 			}
38571b0acceSAndi Kleen 			free(omg);
38671b0acceSAndi Kleen 		}
38771b0acceSAndi Kleen 	}
38871b0acceSAndi Kleen 
38971b0acceSAndi Kleen 	if (metricgroups && !raw)
39071b0acceSAndi Kleen 		printf("\nMetric Groups:\n\n");
39171b0acceSAndi Kleen 	else if (metrics && !raw)
39271b0acceSAndi Kleen 		printf("\nMetrics:\n\n");
39371b0acceSAndi Kleen 
394ca227029SDavidlohr Bueso 	for (node = rb_first_cached(&groups.entries); node; node = next) {
39571b0acceSAndi Kleen 		struct mep *me = container_of(node, struct mep, nd);
39671b0acceSAndi Kleen 
39771b0acceSAndi Kleen 		if (metricgroups)
3989c344d15SAndi Kleen 			printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
39971b0acceSAndi Kleen 		if (metrics)
40071b0acceSAndi Kleen 			metricgroup__print_strlist(me->metrics, raw);
40171b0acceSAndi Kleen 		next = rb_next(node);
40271b0acceSAndi Kleen 		rblist__remove_node(&groups, node);
40371b0acceSAndi Kleen 	}
40471b0acceSAndi Kleen 	if (!metricgroups)
40571b0acceSAndi Kleen 		metricgroup__print_strlist(metriclist, raw);
40671b0acceSAndi Kleen 	strlist__delete(metriclist);
40771b0acceSAndi Kleen }
40871b0acceSAndi Kleen 
409f742634aSKan Liang static void metricgroup__add_metric_weak_group(struct strbuf *events,
410ded80bdaSIan Rogers 					       struct expr_parse_ctx *ctx)
411f742634aSKan Liang {
412ded80bdaSIan Rogers 	struct hashmap_entry *cur;
413ded80bdaSIan Rogers 	size_t bkt, i = 0;
414f742634aSKan Liang 	bool no_group = false;
415f742634aSKan Liang 
416ded80bdaSIan Rogers 	hashmap__for_each_entry((&ctx->ids), cur, bkt) {
417ded80bdaSIan Rogers 		pr_debug("found event %s\n", (const char *)cur->key);
418f742634aSKan Liang 		/*
419f742634aSKan Liang 		 * Duration time maps to a software event and can make
420f742634aSKan Liang 		 * groups not count. Always use it outside a
421f742634aSKan Liang 		 * group.
422f742634aSKan Liang 		 */
423ded80bdaSIan Rogers 		if (!strcmp(cur->key, "duration_time")) {
424f742634aSKan Liang 			if (i > 0)
425f742634aSKan Liang 				strbuf_addf(events, "}:W,");
426f742634aSKan Liang 			strbuf_addf(events, "duration_time");
427f742634aSKan Liang 			no_group = true;
428f742634aSKan Liang 			continue;
429f742634aSKan Liang 		}
430f742634aSKan Liang 		strbuf_addf(events, "%s%s",
431f742634aSKan Liang 			i == 0 || no_group ? "{" : ",",
432ded80bdaSIan Rogers 			(const char *)cur->key);
433f742634aSKan Liang 		no_group = false;
434ded80bdaSIan Rogers 		i++;
435f742634aSKan Liang 	}
436f742634aSKan Liang 	if (!no_group)
437f742634aSKan Liang 		strbuf_addf(events, "}:W");
438f742634aSKan Liang }
439f742634aSKan Liang 
440ab483d8bSKan Liang static void metricgroup__add_metric_non_group(struct strbuf *events,
441ded80bdaSIan Rogers 					      struct expr_parse_ctx *ctx)
442ab483d8bSKan Liang {
443ded80bdaSIan Rogers 	struct hashmap_entry *cur;
444ded80bdaSIan Rogers 	size_t bkt;
445ab483d8bSKan Liang 
446ded80bdaSIan Rogers 	hashmap__for_each_entry((&ctx->ids), cur, bkt)
447ded80bdaSIan Rogers 		strbuf_addf(events, ",%s", (const char *)cur->key);
448ab483d8bSKan Liang }
449ab483d8bSKan Liang 
450ab483d8bSKan Liang static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
451ab483d8bSKan Liang {
452ab483d8bSKan Liang 	static bool violate_nmi_constraint;
453ab483d8bSKan Liang 
454ab483d8bSKan Liang 	if (!foot) {
455ab483d8bSKan Liang 		pr_warning("Splitting metric group %s into standalone metrics.\n", name);
456ab483d8bSKan Liang 		violate_nmi_constraint = true;
457ab483d8bSKan Liang 		return;
458ab483d8bSKan Liang 	}
459ab483d8bSKan Liang 
460ab483d8bSKan Liang 	if (!violate_nmi_constraint)
461ab483d8bSKan Liang 		return;
462ab483d8bSKan Liang 
463ab483d8bSKan Liang 	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
464ab483d8bSKan Liang 		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
465ab483d8bSKan Liang 		   "    perf stat ...\n"
466ab483d8bSKan Liang 		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
467ab483d8bSKan Liang }
468ab483d8bSKan Liang 
469ab483d8bSKan Liang static bool metricgroup__has_constraint(struct pmu_event *pe)
470ab483d8bSKan Liang {
471ab483d8bSKan Liang 	if (!pe->metric_constraint)
472ab483d8bSKan Liang 		return false;
473ab483d8bSKan Liang 
474ab483d8bSKan Liang 	if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
475ab483d8bSKan Liang 	    sysctl__nmi_watchdog_enabled()) {
476ab483d8bSKan Liang 		metricgroup___watchdog_constraint_hint(pe->metric_name, false);
477ab483d8bSKan Liang 		return true;
478ab483d8bSKan Liang 	}
479ab483d8bSKan Liang 
480ab483d8bSKan Liang 	return false;
481ab483d8bSKan Liang }
482ab483d8bSKan Liang 
4831e1a873dSKajol Jain int __weak arch_get_runtimeparam(void)
4841e1a873dSKajol Jain {
4851e1a873dSKajol Jain 	return 1;
4861e1a873dSKajol Jain }
4871e1a873dSKajol Jain 
48847352abaSKajol Jain static int __metricgroup__add_metric(struct strbuf *events,
4891e1a873dSKajol Jain 		struct list_head *group_list, struct pmu_event *pe, int runtime)
49047352abaSKajol Jain {
49147352abaSKajol Jain 	struct egroup *eg;
49247352abaSKajol Jain 
49347352abaSKajol Jain 	eg = malloc(sizeof(*eg));
49447352abaSKajol Jain 	if (!eg)
49547352abaSKajol Jain 		return -ENOMEM;
49647352abaSKajol Jain 
497ded80bdaSIan Rogers 	expr__ctx_init(&eg->pctx);
49847352abaSKajol Jain 	eg->metric_name = pe->metric_name;
49947352abaSKajol Jain 	eg->metric_expr = pe->metric_expr;
50047352abaSKajol Jain 	eg->metric_unit = pe->unit;
5011e1a873dSKajol Jain 	eg->runtime = runtime;
502ded80bdaSIan Rogers 
503ded80bdaSIan Rogers 	if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) {
504ded80bdaSIan Rogers 		expr__ctx_clear(&eg->pctx);
505ded80bdaSIan Rogers 		free(eg);
506ded80bdaSIan Rogers 		return -EINVAL;
507ded80bdaSIan Rogers 	}
508ded80bdaSIan Rogers 
509ded80bdaSIan Rogers 	if (events->len > 0)
510ded80bdaSIan Rogers 		strbuf_addf(events, ",");
511ded80bdaSIan Rogers 
512ded80bdaSIan Rogers 	if (metricgroup__has_constraint(pe))
513ded80bdaSIan Rogers 		metricgroup__add_metric_non_group(events, &eg->pctx);
514ded80bdaSIan Rogers 	else
515ded80bdaSIan Rogers 		metricgroup__add_metric_weak_group(events, &eg->pctx);
516ded80bdaSIan Rogers 
51747352abaSKajol Jain 	list_add_tail(&eg->nd, group_list);
51847352abaSKajol Jain 
51947352abaSKajol Jain 	return 0;
52047352abaSKajol Jain }
52147352abaSKajol Jain 
522b18f3e36SAndi Kleen static int metricgroup__add_metric(const char *metric, struct strbuf *events,
523b18f3e36SAndi Kleen 				   struct list_head *group_list)
524b18f3e36SAndi Kleen {
52554e32dc0SGanapatrao Kulkarni 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
526b18f3e36SAndi Kleen 	struct pmu_event *pe;
527f742634aSKan Liang 	int i, ret = -EINVAL;
528b18f3e36SAndi Kleen 
529b18f3e36SAndi Kleen 	if (!map)
530b18f3e36SAndi Kleen 		return 0;
531b18f3e36SAndi Kleen 
532b18f3e36SAndi Kleen 	for (i = 0; ; i++) {
533b18f3e36SAndi Kleen 		pe = &map->table[i];
534b18f3e36SAndi Kleen 
535b18f3e36SAndi Kleen 		if (!pe->name && !pe->metric_group && !pe->metric_name)
536b18f3e36SAndi Kleen 			break;
537b18f3e36SAndi Kleen 		if (!pe->metric_expr)
538b18f3e36SAndi Kleen 			continue;
539b18f3e36SAndi Kleen 		if (match_metric(pe->metric_group, metric) ||
540b18f3e36SAndi Kleen 		    match_metric(pe->metric_name, metric)) {
541b18f3e36SAndi Kleen 
542b18f3e36SAndi Kleen 			pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
543b18f3e36SAndi Kleen 
5441e1a873dSKajol Jain 			if (!strstr(pe->metric_expr, "?")) {
5451e1a873dSKajol Jain 				ret = __metricgroup__add_metric(events, group_list, pe, 1);
5461e1a873dSKajol Jain 			} else {
5471e1a873dSKajol Jain 				int j, count;
5481e1a873dSKajol Jain 
5491e1a873dSKajol Jain 				count = arch_get_runtimeparam();
5501e1a873dSKajol Jain 
5511e1a873dSKajol Jain 				/* This loop is added to create multiple
5521e1a873dSKajol Jain 				 * events depend on count value and add
5531e1a873dSKajol Jain 				 * those events to group_list.
5541e1a873dSKajol Jain 				 */
5551e1a873dSKajol Jain 
5561e1a873dSKajol Jain 				for (j = 0; j < count; j++)
5571e1a873dSKajol Jain 					ret = __metricgroup__add_metric(events, group_list, pe, j);
5581e1a873dSKajol Jain 			}
55947352abaSKajol Jain 			if (ret == -ENOMEM)
560b18f3e36SAndi Kleen 				break;
561b18f3e36SAndi Kleen 		}
562b18f3e36SAndi Kleen 	}
563b18f3e36SAndi Kleen 	return ret;
564b18f3e36SAndi Kleen }
565b18f3e36SAndi Kleen 
566b18f3e36SAndi Kleen static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
567b18f3e36SAndi Kleen 				        struct list_head *group_list)
568b18f3e36SAndi Kleen {
569b18f3e36SAndi Kleen 	char *llist, *nlist, *p;
570b18f3e36SAndi Kleen 	int ret = -EINVAL;
571b18f3e36SAndi Kleen 
572b18f3e36SAndi Kleen 	nlist = strdup(list);
573b18f3e36SAndi Kleen 	if (!nlist)
574b18f3e36SAndi Kleen 		return -ENOMEM;
575b18f3e36SAndi Kleen 	llist = nlist;
576411bc316SAndi Kleen 
577411bc316SAndi Kleen 	strbuf_init(events, 100);
578411bc316SAndi Kleen 	strbuf_addf(events, "%s", "");
579411bc316SAndi Kleen 
580b18f3e36SAndi Kleen 	while ((p = strsep(&llist, ",")) != NULL) {
581b18f3e36SAndi Kleen 		ret = metricgroup__add_metric(p, events, group_list);
582b18f3e36SAndi Kleen 		if (ret == -EINVAL) {
583b18f3e36SAndi Kleen 			fprintf(stderr, "Cannot find metric or group `%s'\n",
584b18f3e36SAndi Kleen 					p);
585b18f3e36SAndi Kleen 			break;
586b18f3e36SAndi Kleen 		}
587b18f3e36SAndi Kleen 	}
588b18f3e36SAndi Kleen 	free(nlist);
589ab483d8bSKan Liang 
590ab483d8bSKan Liang 	if (!ret)
591ab483d8bSKan Liang 		metricgroup___watchdog_constraint_hint(NULL, true);
592ab483d8bSKan Liang 
593b18f3e36SAndi Kleen 	return ret;
594b18f3e36SAndi Kleen }
595b18f3e36SAndi Kleen 
596b18f3e36SAndi Kleen static void metricgroup__free_egroups(struct list_head *group_list)
597b18f3e36SAndi Kleen {
598b18f3e36SAndi Kleen 	struct egroup *eg, *egtmp;
599b18f3e36SAndi Kleen 
600b18f3e36SAndi Kleen 	list_for_each_entry_safe (eg, egtmp, group_list, nd) {
601ded80bdaSIan Rogers 		expr__ctx_clear(&eg->pctx);
602acc7bfb3SArnaldo Carvalho de Melo 		list_del_init(&eg->nd);
603b18f3e36SAndi Kleen 		free(eg);
604b18f3e36SAndi Kleen 	}
605b18f3e36SAndi Kleen }
606b18f3e36SAndi Kleen 
607b18f3e36SAndi Kleen int metricgroup__parse_groups(const struct option *opt,
608b18f3e36SAndi Kleen 			   const char *str,
609b18f3e36SAndi Kleen 			   struct rblist *metric_events)
610b18f3e36SAndi Kleen {
611b18f3e36SAndi Kleen 	struct parse_events_error parse_error;
61263503dbaSJiri Olsa 	struct evlist *perf_evlist = *(struct evlist **)opt->value;
613b18f3e36SAndi Kleen 	struct strbuf extra_events;
614b18f3e36SAndi Kleen 	LIST_HEAD(group_list);
615b18f3e36SAndi Kleen 	int ret;
616b18f3e36SAndi Kleen 
617b18f3e36SAndi Kleen 	if (metric_events->nr_entries == 0)
618b18f3e36SAndi Kleen 		metricgroup__rblist_init(metric_events);
619b18f3e36SAndi Kleen 	ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
620b18f3e36SAndi Kleen 	if (ret)
621b18f3e36SAndi Kleen 		return ret;
622b18f3e36SAndi Kleen 	pr_debug("adding %s\n", extra_events.buf);
623a910e466SIan Rogers 	bzero(&parse_error, sizeof(parse_error));
624b18f3e36SAndi Kleen 	ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
625b18f3e36SAndi Kleen 	if (ret) {
626333b5665SAndi Kleen 		parse_events_print_error(&parse_error, extra_events.buf);
627b18f3e36SAndi Kleen 		goto out;
628b18f3e36SAndi Kleen 	}
629b18f3e36SAndi Kleen 	strbuf_release(&extra_events);
630b18f3e36SAndi Kleen 	ret = metricgroup__setup_events(&group_list, perf_evlist,
631b18f3e36SAndi Kleen 					metric_events);
632b18f3e36SAndi Kleen out:
633b18f3e36SAndi Kleen 	metricgroup__free_egroups(&group_list);
634b18f3e36SAndi Kleen 	return ret;
635b18f3e36SAndi Kleen }
636742d92ffSThomas Richter 
637742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric)
638742d92ffSThomas Richter {
639742d92ffSThomas Richter 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
640742d92ffSThomas Richter 	struct pmu_event *pe;
641742d92ffSThomas Richter 	int i;
642742d92ffSThomas Richter 
643742d92ffSThomas Richter 	if (!map)
644742d92ffSThomas Richter 		return false;
645742d92ffSThomas Richter 
646742d92ffSThomas Richter 	for (i = 0; ; i++) {
647742d92ffSThomas Richter 		pe = &map->table[i];
648742d92ffSThomas Richter 
649742d92ffSThomas Richter 		if (!pe->name && !pe->metric_group && !pe->metric_name)
650742d92ffSThomas Richter 			break;
651742d92ffSThomas Richter 		if (!pe->metric_expr)
652742d92ffSThomas Richter 			continue;
653742d92ffSThomas Richter 		if (match_metric(pe->metric_name, metric))
654742d92ffSThomas Richter 			return true;
655742d92ffSThomas Richter 	}
656742d92ffSThomas Richter 	return false;
657742d92ffSThomas Richter }
658