Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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}