Linux Audio

Check our new training course

Loading...
v4.17
 
  1/*
  2 * Copyright (c) 2017, Intel Corporation.
  3 *
  4 * This program is free software; you can redistribute it and/or modify it
  5 * under the terms and conditions of the GNU General Public License,
  6 * version 2, as published by the Free Software Foundation.
  7 *
  8 * This program is distributed in the hope it will be useful, but WITHOUT
  9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 11 * more details.
 12 *
 13 */
 14
 15/* Manage metrics and groups of metrics from JSON files */
 16
 17#include "metricgroup.h"
 
 18#include "evlist.h"
 
 19#include "strbuf.h"
 20#include "pmu.h"
 21#include "expr.h"
 22#include "rblist.h"
 23#include <string.h>
 24#include <stdbool.h>
 25#include <errno.h>
 26#include "pmu-events/pmu-events.h"
 27#include "strlist.h"
 28#include <assert.h>
 29#include <ctype.h>
 
 
 
 30
 31struct metric_event *metricgroup__lookup(struct rblist *metric_events,
 32					 struct perf_evsel *evsel,
 33					 bool create)
 34{
 35	struct rb_node *nd;
 36	struct metric_event me = {
 37		.evsel = evsel
 38	};
 39
 40	if (!metric_events)
 41		return NULL;
 42
 43	nd = rblist__find(metric_events, &me);
 44	if (nd)
 45		return container_of(nd, struct metric_event, nd);
 46	if (create) {
 47		rblist__add_node(metric_events, &me);
 48		nd = rblist__find(metric_events, &me);
 49		if (nd)
 50			return container_of(nd, struct metric_event, nd);
 51	}
 52	return NULL;
 53}
 54
 55static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
 56{
 57	struct metric_event *a = container_of(rb_node,
 58					      struct metric_event,
 59					      nd);
 60	const struct metric_event *b = entry;
 61
 62	if (a->evsel == b->evsel)
 63		return 0;
 64	if ((char *)a->evsel < (char *)b->evsel)
 65		return -1;
 66	return +1;
 67}
 68
 69static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
 70					const void *entry)
 71{
 72	struct metric_event *me = malloc(sizeof(struct metric_event));
 73
 74	if (!me)
 75		return NULL;
 76	memcpy(me, entry, sizeof(struct metric_event));
 77	me->evsel = ((struct metric_event *)entry)->evsel;
 78	INIT_LIST_HEAD(&me->head);
 79	return &me->nd;
 80}
 81
 82static void metricgroup__rblist_init(struct rblist *metric_events)
 83{
 84	rblist__init(metric_events);
 85	metric_events->node_cmp = metric_event_cmp;
 86	metric_events->node_new = metric_event_new;
 87}
 88
 89struct egroup {
 90	struct list_head nd;
 91	int idnum;
 92	const char **ids;
 93	const char *metric_name;
 94	const char *metric_expr;
 
 95};
 96
 97static struct perf_evsel *find_evsel(struct perf_evlist *perf_evlist,
 98				     const char **ids,
 99				     int idnum,
100				     struct perf_evsel **metric_events)
101{
102	struct perf_evsel *ev, *start = NULL;
103	int ind = 0;
 
104
105	evlist__for_each_entry (perf_evlist, ev) {
106		if (!strcmp(ev->name, ids[ind])) {
107			metric_events[ind] = ev;
108			if (ind == 0)
109				start = ev;
110			if (++ind == idnum) {
111				metric_events[ind] = NULL;
112				return start;
113			}
114		} else {
115			ind = 0;
116			start = NULL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117		}
118	}
119	/*
120	 * This can happen when an alias expands to multiple
121	 * events, like for uncore events.
122	 * We don't support this case for now.
123	 */
124	return NULL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125}
126
127static int metricgroup__setup_events(struct list_head *groups,
128				     struct perf_evlist *perf_evlist,
129				     struct rblist *metric_events_list)
130{
131	struct metric_event *me;
132	struct metric_expr *expr;
133	int i = 0;
134	int ret = 0;
135	struct egroup *eg;
136	struct perf_evsel *evsel;
137
138	list_for_each_entry (eg, groups, nd) {
139		struct perf_evsel **metric_events;
140
141		metric_events = calloc(sizeof(void *), eg->idnum + 1);
142		if (!metric_events) {
143			ret = -ENOMEM;
144			break;
145		}
146		evsel = find_evsel(perf_evlist, eg->ids, eg->idnum,
147				   metric_events);
148		if (!evsel) {
149			pr_debug("Cannot resolve %s: %s\n",
150					eg->metric_name, eg->metric_expr);
151			continue;
152		}
153		for (i = 0; i < eg->idnum; i++)
154			metric_events[i]->collect_stat = true;
155		me = metricgroup__lookup(metric_events_list, evsel, true);
156		if (!me) {
157			ret = -ENOMEM;
158			break;
159		}
160		expr = malloc(sizeof(struct metric_expr));
161		if (!expr) {
162			ret = -ENOMEM;
163			break;
164		}
165		expr->metric_expr = eg->metric_expr;
166		expr->metric_name = eg->metric_name;
 
167		expr->metric_events = metric_events;
168		list_add(&expr->nd, &me->head);
169	}
170	return ret;
171}
172
173static bool match_metric(const char *n, const char *list)
174{
175	int len;
176	char *m;
177
178	if (!list)
179		return false;
180	if (!strcmp(list, "all"))
181		return true;
182	if (!n)
183		return !strcasecmp(list, "No_group");
184	len = strlen(list);
185	m = strcasestr(n, list);
186	if (!m)
187		return false;
188	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
189	    (m[len] == 0 || m[len] == ';'))
190		return true;
191	return false;
192}
193
194struct mep {
195	struct rb_node nd;
196	const char *name;
197	struct strlist *metrics;
198};
199
200static int mep_cmp(struct rb_node *rb_node, const void *entry)
201{
202	struct mep *a = container_of(rb_node, struct mep, nd);
203	struct mep *b = (struct mep *)entry;
204
205	return strcmp(a->name, b->name);
206}
207
208static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
209					const void *entry)
210{
211	struct mep *me = malloc(sizeof(struct mep));
212
213	if (!me)
214		return NULL;
215	memcpy(me, entry, sizeof(struct mep));
216	me->name = strdup(me->name);
217	if (!me->name)
218		goto out_me;
219	me->metrics = strlist__new(NULL, NULL);
220	if (!me->metrics)
221		goto out_name;
222	return &me->nd;
223out_name:
224	free((char *)me->name);
225out_me:
226	free(me);
227	return NULL;
228}
229
230static struct mep *mep_lookup(struct rblist *groups, const char *name)
231{
232	struct rb_node *nd;
233	struct mep me = {
234		.name = name
235	};
236	nd = rblist__find(groups, &me);
237	if (nd)
238		return container_of(nd, struct mep, nd);
239	rblist__add_node(groups, &me);
240	nd = rblist__find(groups, &me);
241	if (nd)
242		return container_of(nd, struct mep, nd);
243	return NULL;
244}
245
246static void mep_delete(struct rblist *rl __maybe_unused,
247		       struct rb_node *nd)
248{
249	struct mep *me = container_of(nd, struct mep, nd);
250
251	strlist__delete(me->metrics);
252	free((void *)me->name);
253	free(me);
254}
255
256static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
257{
258	struct str_node *sn;
259	int n = 0;
260
261	strlist__for_each_entry (sn, metrics) {
262		if (raw)
263			printf("%s%s", n > 0 ? " " : "", sn->s);
264		else
265			printf("  %s\n", sn->s);
266		n++;
267	}
268	if (raw)
269		putchar('\n');
270}
271
272void metricgroup__print(bool metrics, bool metricgroups, char *filter,
273			bool raw)
274{
275	struct pmu_events_map *map = perf_pmu__find_map(NULL);
276	struct pmu_event *pe;
277	int i;
278	struct rblist groups;
279	struct rb_node *node, *next;
280	struct strlist *metriclist = NULL;
281
282	if (!map)
283		return;
284
285	if (!metricgroups) {
286		metriclist = strlist__new(NULL, NULL);
287		if (!metriclist)
288			return;
289	}
290
291	rblist__init(&groups);
292	groups.node_new = mep_new;
293	groups.node_cmp = mep_cmp;
294	groups.node_delete = mep_delete;
295	for (i = 0; ; i++) {
296		const char *g;
297		pe = &map->table[i];
298
299		if (!pe->name && !pe->metric_group && !pe->metric_name)
300			break;
301		if (!pe->metric_expr)
302			continue;
303		g = pe->metric_group;
304		if (!g && pe->metric_name) {
305			if (pe->name)
306				continue;
307			g = "No_group";
308		}
309		if (g) {
310			char *omg;
311			char *mg = strdup(g);
312
313			if (!mg)
314				return;
315			omg = mg;
316			while ((g = strsep(&mg, ";")) != NULL) {
317				struct mep *me;
318				char *s;
319
 
320				if (*g == 0)
321					g = "No_group";
322				while (isspace(*g))
323					g++;
324				if (filter && !strstr(g, filter))
325					continue;
326				if (raw)
327					s = (char *)pe->metric_name;
328				else {
329					if (asprintf(&s, "%s\n\t[%s]",
330						     pe->metric_name, pe->desc) < 0)
331						return;
 
 
 
 
 
 
332				}
333
334				if (!s)
335					continue;
336
337				if (!metricgroups) {
338					strlist__add(metriclist, s);
339				} else {
340					me = mep_lookup(&groups, g);
341					if (!me)
342						continue;
343					strlist__add(me->metrics, s);
344				}
345			}
346			free(omg);
347		}
348	}
349
350	if (metricgroups && !raw)
351		printf("\nMetric Groups:\n\n");
352	else if (metrics && !raw)
353		printf("\nMetrics:\n\n");
354
355	for (node = rb_first(&groups.entries); node; node = next) {
356		struct mep *me = container_of(node, struct mep, nd);
357
358		if (metricgroups)
359			printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n");
360		if (metrics)
361			metricgroup__print_strlist(me->metrics, raw);
362		next = rb_next(node);
363		rblist__remove_node(&groups, node);
364	}
365	if (!metricgroups)
366		metricgroup__print_strlist(metriclist, raw);
367	strlist__delete(metriclist);
368}
369
370static int metricgroup__add_metric(const char *metric, struct strbuf *events,
371				   struct list_head *group_list)
372{
373	struct pmu_events_map *map = perf_pmu__find_map(NULL);
374	struct pmu_event *pe;
375	int ret = -EINVAL;
376	int i, j;
377
378	if (!map)
379		return 0;
380
381	for (i = 0; ; i++) {
382		pe = &map->table[i];
383
384		if (!pe->name && !pe->metric_group && !pe->metric_name)
385			break;
386		if (!pe->metric_expr)
387			continue;
388		if (match_metric(pe->metric_group, metric) ||
389		    match_metric(pe->metric_name, metric)) {
390			const char **ids;
391			int idnum;
392			struct egroup *eg;
 
393
394			pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
395
396			if (expr__find_other(pe->metric_expr,
397					     NULL, &ids, &idnum) < 0)
398				continue;
399			if (events->len > 0)
400				strbuf_addf(events, ",");
401			for (j = 0; j < idnum; j++) {
402				pr_debug("found event %s\n", ids[j]);
 
 
 
 
 
 
 
 
 
 
 
 
403				strbuf_addf(events, "%s%s",
404					j == 0 ? "{" : ",",
405					ids[j]);
 
406			}
407			strbuf_addf(events, "}:W");
 
408
409			eg = malloc(sizeof(struct egroup));
410			if (!eg) {
411				ret = -ENOMEM;
412				break;
413			}
414			eg->ids = ids;
415			eg->idnum = idnum;
416			eg->metric_name = pe->metric_name;
417			eg->metric_expr = pe->metric_expr;
 
418			list_add_tail(&eg->nd, group_list);
419			ret = 0;
420		}
421	}
422	return ret;
423}
424
425static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
426				        struct list_head *group_list)
427{
428	char *llist, *nlist, *p;
429	int ret = -EINVAL;
430
431	nlist = strdup(list);
432	if (!nlist)
433		return -ENOMEM;
434	llist = nlist;
435
436	strbuf_init(events, 100);
437	strbuf_addf(events, "%s", "");
438
439	while ((p = strsep(&llist, ",")) != NULL) {
440		ret = metricgroup__add_metric(p, events, group_list);
441		if (ret == -EINVAL) {
442			fprintf(stderr, "Cannot find metric or group `%s'\n",
443					p);
444			break;
445		}
446	}
447	free(nlist);
448	return ret;
449}
450
451static void metricgroup__free_egroups(struct list_head *group_list)
452{
453	struct egroup *eg, *egtmp;
454	int i;
455
456	list_for_each_entry_safe (eg, egtmp, group_list, nd) {
457		for (i = 0; i < eg->idnum; i++)
458			free((char *)eg->ids[i]);
459		free(eg->ids);
 
460		free(eg);
461	}
462}
463
464int metricgroup__parse_groups(const struct option *opt,
465			   const char *str,
466			   struct rblist *metric_events)
467{
468	struct parse_events_error parse_error;
469	struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
470	struct strbuf extra_events;
471	LIST_HEAD(group_list);
472	int ret;
473
474	if (metric_events->nr_entries == 0)
475		metricgroup__rblist_init(metric_events);
476	ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
477	if (ret)
478		return ret;
479	pr_debug("adding %s\n", extra_events.buf);
480	memset(&parse_error, 0, sizeof(struct parse_events_error));
481	ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
482	if (ret) {
483		parse_events_print_error(&parse_error, extra_events.buf);
484		goto out;
485	}
486	strbuf_release(&extra_events);
487	ret = metricgroup__setup_events(&group_list, perf_evlist,
488					metric_events);
489out:
490	metricgroup__free_egroups(&group_list);
491	return ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492}
v5.4
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2017, Intel Corporation.
 
 
 
 
 
 
 
 
 
 
  4 */
  5
  6/* Manage metrics and groups of metrics from JSON files */
  7
  8#include "metricgroup.h"
  9#include "debug.h"
 10#include "evlist.h"
 11#include "evsel.h"
 12#include "strbuf.h"
 13#include "pmu.h"
 14#include "expr.h"
 15#include "rblist.h"
 16#include <string.h>
 
 17#include <errno.h>
 18#include "pmu-events/pmu-events.h"
 19#include "strlist.h"
 20#include <assert.h>
 21#include <linux/ctype.h>
 22#include <linux/string.h>
 23#include <linux/zalloc.h>
 24#include <subcmd/parse-options.h>
 25
 26struct metric_event *metricgroup__lookup(struct rblist *metric_events,
 27					 struct evsel *evsel,
 28					 bool create)
 29{
 30	struct rb_node *nd;
 31	struct metric_event me = {
 32		.evsel = evsel
 33	};
 34
 35	if (!metric_events)
 36		return NULL;
 37
 38	nd = rblist__find(metric_events, &me);
 39	if (nd)
 40		return container_of(nd, struct metric_event, nd);
 41	if (create) {
 42		rblist__add_node(metric_events, &me);
 43		nd = rblist__find(metric_events, &me);
 44		if (nd)
 45			return container_of(nd, struct metric_event, nd);
 46	}
 47	return NULL;
 48}
 49
 50static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
 51{
 52	struct metric_event *a = container_of(rb_node,
 53					      struct metric_event,
 54					      nd);
 55	const struct metric_event *b = entry;
 56
 57	if (a->evsel == b->evsel)
 58		return 0;
 59	if ((char *)a->evsel < (char *)b->evsel)
 60		return -1;
 61	return +1;
 62}
 63
 64static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
 65					const void *entry)
 66{
 67	struct metric_event *me = malloc(sizeof(struct metric_event));
 68
 69	if (!me)
 70		return NULL;
 71	memcpy(me, entry, sizeof(struct metric_event));
 72	me->evsel = ((struct metric_event *)entry)->evsel;
 73	INIT_LIST_HEAD(&me->head);
 74	return &me->nd;
 75}
 76
 77static void metricgroup__rblist_init(struct rblist *metric_events)
 78{
 79	rblist__init(metric_events);
 80	metric_events->node_cmp = metric_event_cmp;
 81	metric_events->node_new = metric_event_new;
 82}
 83
 84struct egroup {
 85	struct list_head nd;
 86	int idnum;
 87	const char **ids;
 88	const char *metric_name;
 89	const char *metric_expr;
 90	const char *metric_unit;
 91};
 92
 93static struct evsel *find_evsel_group(struct evlist *perf_evlist,
 94				      const char **ids,
 95				      int idnum,
 96				      struct evsel **metric_events)
 97{
 98	struct evsel *ev;
 99	int i = 0;
100	bool leader_found;
101
102	evlist__for_each_entry (perf_evlist, ev) {
103		if (!strcmp(ev->name, ids[i])) {
104			if (!metric_events[i])
105				metric_events[i] = ev;
 
 
 
 
 
106		} else {
107			if (++i == idnum) {
108				/* Discard the whole match and start again */
109				i = 0;
110				memset(metric_events, 0,
111				       sizeof(struct evsel *) * idnum);
112				continue;
113			}
114
115			if (!strcmp(ev->name, ids[i]))
116				metric_events[i] = ev;
117			else {
118				/* Discard the whole match and start again */
119				i = 0;
120				memset(metric_events, 0,
121				       sizeof(struct evsel *) * idnum);
122				continue;
123			}
124		}
125	}
126
127	if (i != idnum - 1) {
128		/* Not whole match */
129		return NULL;
130	}
131
132	metric_events[idnum] = NULL;
133
134	for (i = 0; i < idnum; i++) {
135		leader_found = false;
136		evlist__for_each_entry(perf_evlist, ev) {
137			if (!leader_found && (ev == metric_events[i]))
138				leader_found = true;
139
140			if (leader_found &&
141			    !strcmp(ev->name, metric_events[i]->name)) {
142				ev->metric_leader = metric_events[i];
143			}
144		}
145	}
146
147	return metric_events[0];
148}
149
150static int metricgroup__setup_events(struct list_head *groups,
151				     struct evlist *perf_evlist,
152				     struct rblist *metric_events_list)
153{
154	struct metric_event *me;
155	struct metric_expr *expr;
156	int i = 0;
157	int ret = 0;
158	struct egroup *eg;
159	struct evsel *evsel;
160
161	list_for_each_entry (eg, groups, nd) {
162		struct evsel **metric_events;
163
164		metric_events = calloc(sizeof(void *), eg->idnum + 1);
165		if (!metric_events) {
166			ret = -ENOMEM;
167			break;
168		}
169		evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum,
170					 metric_events);
171		if (!evsel) {
172			pr_debug("Cannot resolve %s: %s\n",
173					eg->metric_name, eg->metric_expr);
174			continue;
175		}
176		for (i = 0; i < eg->idnum; i++)
177			metric_events[i]->collect_stat = true;
178		me = metricgroup__lookup(metric_events_list, evsel, true);
179		if (!me) {
180			ret = -ENOMEM;
181			break;
182		}
183		expr = malloc(sizeof(struct metric_expr));
184		if (!expr) {
185			ret = -ENOMEM;
186			break;
187		}
188		expr->metric_expr = eg->metric_expr;
189		expr->metric_name = eg->metric_name;
190		expr->metric_unit = eg->metric_unit;
191		expr->metric_events = metric_events;
192		list_add(&expr->nd, &me->head);
193	}
194	return ret;
195}
196
197static bool match_metric(const char *n, const char *list)
198{
199	int len;
200	char *m;
201
202	if (!list)
203		return false;
204	if (!strcmp(list, "all"))
205		return true;
206	if (!n)
207		return !strcasecmp(list, "No_group");
208	len = strlen(list);
209	m = strcasestr(n, list);
210	if (!m)
211		return false;
212	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
213	    (m[len] == 0 || m[len] == ';'))
214		return true;
215	return false;
216}
217
218struct mep {
219	struct rb_node nd;
220	const char *name;
221	struct strlist *metrics;
222};
223
224static int mep_cmp(struct rb_node *rb_node, const void *entry)
225{
226	struct mep *a = container_of(rb_node, struct mep, nd);
227	struct mep *b = (struct mep *)entry;
228
229	return strcmp(a->name, b->name);
230}
231
232static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
233					const void *entry)
234{
235	struct mep *me = malloc(sizeof(struct mep));
236
237	if (!me)
238		return NULL;
239	memcpy(me, entry, sizeof(struct mep));
240	me->name = strdup(me->name);
241	if (!me->name)
242		goto out_me;
243	me->metrics = strlist__new(NULL, NULL);
244	if (!me->metrics)
245		goto out_name;
246	return &me->nd;
247out_name:
248	zfree(&me->name);
249out_me:
250	free(me);
251	return NULL;
252}
253
254static struct mep *mep_lookup(struct rblist *groups, const char *name)
255{
256	struct rb_node *nd;
257	struct mep me = {
258		.name = name
259	};
260	nd = rblist__find(groups, &me);
261	if (nd)
262		return container_of(nd, struct mep, nd);
263	rblist__add_node(groups, &me);
264	nd = rblist__find(groups, &me);
265	if (nd)
266		return container_of(nd, struct mep, nd);
267	return NULL;
268}
269
270static void mep_delete(struct rblist *rl __maybe_unused,
271		       struct rb_node *nd)
272{
273	struct mep *me = container_of(nd, struct mep, nd);
274
275	strlist__delete(me->metrics);
276	zfree(&me->name);
277	free(me);
278}
279
280static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
281{
282	struct str_node *sn;
283	int n = 0;
284
285	strlist__for_each_entry (sn, metrics) {
286		if (raw)
287			printf("%s%s", n > 0 ? " " : "", sn->s);
288		else
289			printf("  %s\n", sn->s);
290		n++;
291	}
292	if (raw)
293		putchar('\n');
294}
295
296void metricgroup__print(bool metrics, bool metricgroups, char *filter,
297			bool raw, bool details)
298{
299	struct pmu_events_map *map = perf_pmu__find_map(NULL);
300	struct pmu_event *pe;
301	int i;
302	struct rblist groups;
303	struct rb_node *node, *next;
304	struct strlist *metriclist = NULL;
305
306	if (!map)
307		return;
308
309	if (!metricgroups) {
310		metriclist = strlist__new(NULL, NULL);
311		if (!metriclist)
312			return;
313	}
314
315	rblist__init(&groups);
316	groups.node_new = mep_new;
317	groups.node_cmp = mep_cmp;
318	groups.node_delete = mep_delete;
319	for (i = 0; ; i++) {
320		const char *g;
321		pe = &map->table[i];
322
323		if (!pe->name && !pe->metric_group && !pe->metric_name)
324			break;
325		if (!pe->metric_expr)
326			continue;
327		g = pe->metric_group;
328		if (!g && pe->metric_name) {
329			if (pe->name)
330				continue;
331			g = "No_group";
332		}
333		if (g) {
334			char *omg;
335			char *mg = strdup(g);
336
337			if (!mg)
338				return;
339			omg = mg;
340			while ((g = strsep(&mg, ";")) != NULL) {
341				struct mep *me;
342				char *s;
343
344				g = skip_spaces(g);
345				if (*g == 0)
346					g = "No_group";
 
 
347				if (filter && !strstr(g, filter))
348					continue;
349				if (raw)
350					s = (char *)pe->metric_name;
351				else {
352					if (asprintf(&s, "%s\n%*s%s]",
353						     pe->metric_name, 8, "[", pe->desc) < 0)
354						return;
355
356					if (details) {
357						if (asprintf(&s, "%s\n%*s%s]",
358							     s, 8, "[", pe->metric_expr) < 0)
359							return;
360					}
361				}
362
363				if (!s)
364					continue;
365
366				if (!metricgroups) {
367					strlist__add(metriclist, s);
368				} else {
369					me = mep_lookup(&groups, g);
370					if (!me)
371						continue;
372					strlist__add(me->metrics, s);
373				}
374			}
375			free(omg);
376		}
377	}
378
379	if (metricgroups && !raw)
380		printf("\nMetric Groups:\n\n");
381	else if (metrics && !raw)
382		printf("\nMetrics:\n\n");
383
384	for (node = rb_first_cached(&groups.entries); node; node = next) {
385		struct mep *me = container_of(node, struct mep, nd);
386
387		if (metricgroups)
388			printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
389		if (metrics)
390			metricgroup__print_strlist(me->metrics, raw);
391		next = rb_next(node);
392		rblist__remove_node(&groups, node);
393	}
394	if (!metricgroups)
395		metricgroup__print_strlist(metriclist, raw);
396	strlist__delete(metriclist);
397}
398
399static int metricgroup__add_metric(const char *metric, struct strbuf *events,
400				   struct list_head *group_list)
401{
402	struct pmu_events_map *map = perf_pmu__find_map(NULL);
403	struct pmu_event *pe;
404	int ret = -EINVAL;
405	int i, j;
406
407	if (!map)
408		return 0;
409
410	for (i = 0; ; i++) {
411		pe = &map->table[i];
412
413		if (!pe->name && !pe->metric_group && !pe->metric_name)
414			break;
415		if (!pe->metric_expr)
416			continue;
417		if (match_metric(pe->metric_group, metric) ||
418		    match_metric(pe->metric_name, metric)) {
419			const char **ids;
420			int idnum;
421			struct egroup *eg;
422			bool no_group = false;
423
424			pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
425
426			if (expr__find_other(pe->metric_expr,
427					     NULL, &ids, &idnum) < 0)
428				continue;
429			if (events->len > 0)
430				strbuf_addf(events, ",");
431			for (j = 0; j < idnum; j++) {
432				pr_debug("found event %s\n", ids[j]);
433				/*
434				 * Duration time maps to a software event and can make
435				 * groups not count. Always use it outside a
436				 * group.
437				 */
438				if (!strcmp(ids[j], "duration_time")) {
439					if (j > 0)
440						strbuf_addf(events, "}:W,");
441					strbuf_addf(events, "duration_time");
442					no_group = true;
443					continue;
444				}
445				strbuf_addf(events, "%s%s",
446					j == 0 || no_group ? "{" : ",",
447					ids[j]);
448				no_group = false;
449			}
450			if (!no_group)
451				strbuf_addf(events, "}:W");
452
453			eg = malloc(sizeof(struct egroup));
454			if (!eg) {
455				ret = -ENOMEM;
456				break;
457			}
458			eg->ids = ids;
459			eg->idnum = idnum;
460			eg->metric_name = pe->metric_name;
461			eg->metric_expr = pe->metric_expr;
462			eg->metric_unit = pe->unit;
463			list_add_tail(&eg->nd, group_list);
464			ret = 0;
465		}
466	}
467	return ret;
468}
469
470static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
471				        struct list_head *group_list)
472{
473	char *llist, *nlist, *p;
474	int ret = -EINVAL;
475
476	nlist = strdup(list);
477	if (!nlist)
478		return -ENOMEM;
479	llist = nlist;
480
481	strbuf_init(events, 100);
482	strbuf_addf(events, "%s", "");
483
484	while ((p = strsep(&llist, ",")) != NULL) {
485		ret = metricgroup__add_metric(p, events, group_list);
486		if (ret == -EINVAL) {
487			fprintf(stderr, "Cannot find metric or group `%s'\n",
488					p);
489			break;
490		}
491	}
492	free(nlist);
493	return ret;
494}
495
496static void metricgroup__free_egroups(struct list_head *group_list)
497{
498	struct egroup *eg, *egtmp;
499	int i;
500
501	list_for_each_entry_safe (eg, egtmp, group_list, nd) {
502		for (i = 0; i < eg->idnum; i++)
503			zfree(&eg->ids[i]);
504		zfree(&eg->ids);
505		list_del_init(&eg->nd);
506		free(eg);
507	}
508}
509
510int metricgroup__parse_groups(const struct option *opt,
511			   const char *str,
512			   struct rblist *metric_events)
513{
514	struct parse_events_error parse_error;
515	struct evlist *perf_evlist = *(struct evlist **)opt->value;
516	struct strbuf extra_events;
517	LIST_HEAD(group_list);
518	int ret;
519
520	if (metric_events->nr_entries == 0)
521		metricgroup__rblist_init(metric_events);
522	ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
523	if (ret)
524		return ret;
525	pr_debug("adding %s\n", extra_events.buf);
526	memset(&parse_error, 0, sizeof(struct parse_events_error));
527	ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
528	if (ret) {
529		parse_events_print_error(&parse_error, extra_events.buf);
530		goto out;
531	}
532	strbuf_release(&extra_events);
533	ret = metricgroup__setup_events(&group_list, perf_evlist,
534					metric_events);
535out:
536	metricgroup__free_egroups(&group_list);
537	return ret;
538}
539
540bool metricgroup__has_metric(const char *metric)
541{
542	struct pmu_events_map *map = perf_pmu__find_map(NULL);
543	struct pmu_event *pe;
544	int i;
545
546	if (!map)
547		return false;
548
549	for (i = 0; ; i++) {
550		pe = &map->table[i];
551
552		if (!pe->name && !pe->metric_group && !pe->metric_name)
553			break;
554		if (!pe->metric_expr)
555			continue;
556		if (match_metric(pe->metric_name, metric))
557			return true;
558	}
559	return false;
560}