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