xref: /linux/tools/perf/util/metricgroup.c (revision 8ab2e96d8ff188006f1e3346a56443cd07fe1858)
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 #include <api/fs/fs.h>
26 #include "util.h"
27 
28 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
29 					 struct evsel *evsel,
30 					 bool create)
31 {
32 	struct rb_node *nd;
33 	struct metric_event me = {
34 		.evsel = evsel
35 	};
36 
37 	if (!metric_events)
38 		return NULL;
39 
40 	nd = rblist__find(metric_events, &me);
41 	if (nd)
42 		return container_of(nd, struct metric_event, nd);
43 	if (create) {
44 		rblist__add_node(metric_events, &me);
45 		nd = rblist__find(metric_events, &me);
46 		if (nd)
47 			return container_of(nd, struct metric_event, nd);
48 	}
49 	return NULL;
50 }
51 
52 static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
53 {
54 	struct metric_event *a = container_of(rb_node,
55 					      struct metric_event,
56 					      nd);
57 	const struct metric_event *b = entry;
58 
59 	if (a->evsel == b->evsel)
60 		return 0;
61 	if ((char *)a->evsel < (char *)b->evsel)
62 		return -1;
63 	return +1;
64 }
65 
66 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
67 					const void *entry)
68 {
69 	struct metric_event *me = malloc(sizeof(struct metric_event));
70 
71 	if (!me)
72 		return NULL;
73 	memcpy(me, entry, sizeof(struct metric_event));
74 	me->evsel = ((struct metric_event *)entry)->evsel;
75 	INIT_LIST_HEAD(&me->head);
76 	return &me->nd;
77 }
78 
79 static void metricgroup__rblist_init(struct rblist *metric_events)
80 {
81 	rblist__init(metric_events);
82 	metric_events->node_cmp = metric_event_cmp;
83 	metric_events->node_new = metric_event_new;
84 }
85 
86 struct egroup {
87 	struct list_head nd;
88 	int idnum;
89 	const char **ids;
90 	const char *metric_name;
91 	const char *metric_expr;
92 	const char *metric_unit;
93 	int runtime;
94 };
95 
96 static struct evsel *find_evsel_group(struct evlist *perf_evlist,
97 				      const char **ids,
98 				      int idnum,
99 				      struct evsel **metric_events,
100 				      bool *evlist_used)
101 {
102 	struct evsel *ev;
103 	int i = 0, j = 0;
104 	bool leader_found;
105 
106 	evlist__for_each_entry (perf_evlist, ev) {
107 		if (evlist_used[j++])
108 			continue;
109 		if (!strcmp(ev->name, ids[i])) {
110 			if (!metric_events[i])
111 				metric_events[i] = ev;
112 			i++;
113 			if (i == idnum)
114 				break;
115 		} else {
116 			/* Discard the whole match and start again */
117 			i = 0;
118 			memset(metric_events, 0,
119 				sizeof(struct evsel *) * idnum);
120 
121 			if (!strcmp(ev->name, ids[i])) {
122 				if (!metric_events[i])
123 					metric_events[i] = ev;
124 				i++;
125 				if (i == idnum)
126 					break;
127 			}
128 		}
129 	}
130 
131 	if (i != idnum) {
132 		/* Not whole match */
133 		return NULL;
134 	}
135 
136 	metric_events[idnum] = NULL;
137 
138 	for (i = 0; i < idnum; i++) {
139 		leader_found = false;
140 		evlist__for_each_entry(perf_evlist, ev) {
141 			if (!leader_found && (ev == metric_events[i]))
142 				leader_found = true;
143 
144 			if (leader_found &&
145 			    !strcmp(ev->name, metric_events[i]->name)) {
146 				ev->metric_leader = metric_events[i];
147 			}
148 			j++;
149 		}
150 		ev = metric_events[i];
151 		evlist_used[ev->idx] = true;
152 	}
153 
154 	return metric_events[0];
155 }
156 
157 static int metricgroup__setup_events(struct list_head *groups,
158 				     struct evlist *perf_evlist,
159 				     struct rblist *metric_events_list)
160 {
161 	struct metric_event *me;
162 	struct metric_expr *expr;
163 	int i = 0;
164 	int ret = 0;
165 	struct egroup *eg;
166 	struct evsel *evsel;
167 	bool *evlist_used;
168 
169 	evlist_used = calloc(perf_evlist->core.nr_entries, sizeof(bool));
170 	if (!evlist_used) {
171 		ret = -ENOMEM;
172 		return ret;
173 	}
174 
175 	list_for_each_entry (eg, groups, nd) {
176 		struct evsel **metric_events;
177 
178 		metric_events = calloc(sizeof(void *), eg->idnum + 1);
179 		if (!metric_events) {
180 			ret = -ENOMEM;
181 			break;
182 		}
183 		evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum,
184 					 metric_events, evlist_used);
185 		if (!evsel) {
186 			pr_debug("Cannot resolve %s: %s\n",
187 					eg->metric_name, eg->metric_expr);
188 			continue;
189 		}
190 		for (i = 0; i < eg->idnum; i++)
191 			metric_events[i]->collect_stat = true;
192 		me = metricgroup__lookup(metric_events_list, evsel, true);
193 		if (!me) {
194 			ret = -ENOMEM;
195 			break;
196 		}
197 		expr = malloc(sizeof(struct metric_expr));
198 		if (!expr) {
199 			ret = -ENOMEM;
200 			break;
201 		}
202 		expr->metric_expr = eg->metric_expr;
203 		expr->metric_name = eg->metric_name;
204 		expr->metric_unit = eg->metric_unit;
205 		expr->metric_events = metric_events;
206 		expr->runtime = eg->runtime;
207 		list_add(&expr->nd, &me->head);
208 	}
209 
210 	free(evlist_used);
211 
212 	return ret;
213 }
214 
215 static bool match_metric(const char *n, const char *list)
216 {
217 	int len;
218 	char *m;
219 
220 	if (!list)
221 		return false;
222 	if (!strcmp(list, "all"))
223 		return true;
224 	if (!n)
225 		return !strcasecmp(list, "No_group");
226 	len = strlen(list);
227 	m = strcasestr(n, list);
228 	if (!m)
229 		return false;
230 	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
231 	    (m[len] == 0 || m[len] == ';'))
232 		return true;
233 	return false;
234 }
235 
236 struct mep {
237 	struct rb_node nd;
238 	const char *name;
239 	struct strlist *metrics;
240 };
241 
242 static int mep_cmp(struct rb_node *rb_node, const void *entry)
243 {
244 	struct mep *a = container_of(rb_node, struct mep, nd);
245 	struct mep *b = (struct mep *)entry;
246 
247 	return strcmp(a->name, b->name);
248 }
249 
250 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
251 					const void *entry)
252 {
253 	struct mep *me = malloc(sizeof(struct mep));
254 
255 	if (!me)
256 		return NULL;
257 	memcpy(me, entry, sizeof(struct mep));
258 	me->name = strdup(me->name);
259 	if (!me->name)
260 		goto out_me;
261 	me->metrics = strlist__new(NULL, NULL);
262 	if (!me->metrics)
263 		goto out_name;
264 	return &me->nd;
265 out_name:
266 	zfree(&me->name);
267 out_me:
268 	free(me);
269 	return NULL;
270 }
271 
272 static struct mep *mep_lookup(struct rblist *groups, const char *name)
273 {
274 	struct rb_node *nd;
275 	struct mep me = {
276 		.name = name
277 	};
278 	nd = rblist__find(groups, &me);
279 	if (nd)
280 		return container_of(nd, struct mep, nd);
281 	rblist__add_node(groups, &me);
282 	nd = rblist__find(groups, &me);
283 	if (nd)
284 		return container_of(nd, struct mep, nd);
285 	return NULL;
286 }
287 
288 static void mep_delete(struct rblist *rl __maybe_unused,
289 		       struct rb_node *nd)
290 {
291 	struct mep *me = container_of(nd, struct mep, nd);
292 
293 	strlist__delete(me->metrics);
294 	zfree(&me->name);
295 	free(me);
296 }
297 
298 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
299 {
300 	struct str_node *sn;
301 	int n = 0;
302 
303 	strlist__for_each_entry (sn, metrics) {
304 		if (raw)
305 			printf("%s%s", n > 0 ? " " : "", sn->s);
306 		else
307 			printf("  %s\n", sn->s);
308 		n++;
309 	}
310 	if (raw)
311 		putchar('\n');
312 }
313 
314 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
315 			bool raw, bool details)
316 {
317 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
318 	struct pmu_event *pe;
319 	int i;
320 	struct rblist groups;
321 	struct rb_node *node, *next;
322 	struct strlist *metriclist = NULL;
323 
324 	if (!map)
325 		return;
326 
327 	if (!metricgroups) {
328 		metriclist = strlist__new(NULL, NULL);
329 		if (!metriclist)
330 			return;
331 	}
332 
333 	rblist__init(&groups);
334 	groups.node_new = mep_new;
335 	groups.node_cmp = mep_cmp;
336 	groups.node_delete = mep_delete;
337 	for (i = 0; ; i++) {
338 		const char *g;
339 		pe = &map->table[i];
340 
341 		if (!pe->name && !pe->metric_group && !pe->metric_name)
342 			break;
343 		if (!pe->metric_expr)
344 			continue;
345 		g = pe->metric_group;
346 		if (!g && pe->metric_name) {
347 			if (pe->name)
348 				continue;
349 			g = "No_group";
350 		}
351 		if (g) {
352 			char *omg;
353 			char *mg = strdup(g);
354 
355 			if (!mg)
356 				return;
357 			omg = mg;
358 			while ((g = strsep(&mg, ";")) != NULL) {
359 				struct mep *me;
360 				char *s;
361 
362 				g = skip_spaces(g);
363 				if (*g == 0)
364 					g = "No_group";
365 				if (filter && !strstr(g, filter))
366 					continue;
367 				if (raw)
368 					s = (char *)pe->metric_name;
369 				else {
370 					if (asprintf(&s, "%s\n%*s%s]",
371 						     pe->metric_name, 8, "[", pe->desc) < 0)
372 						return;
373 
374 					if (details) {
375 						if (asprintf(&s, "%s\n%*s%s]",
376 							     s, 8, "[", pe->metric_expr) < 0)
377 							return;
378 					}
379 				}
380 
381 				if (!s)
382 					continue;
383 
384 				if (!metricgroups) {
385 					strlist__add(metriclist, s);
386 				} else {
387 					me = mep_lookup(&groups, g);
388 					if (!me)
389 						continue;
390 					strlist__add(me->metrics, s);
391 				}
392 			}
393 			free(omg);
394 		}
395 	}
396 
397 	if (metricgroups && !raw)
398 		printf("\nMetric Groups:\n\n");
399 	else if (metrics && !raw)
400 		printf("\nMetrics:\n\n");
401 
402 	for (node = rb_first_cached(&groups.entries); node; node = next) {
403 		struct mep *me = container_of(node, struct mep, nd);
404 
405 		if (metricgroups)
406 			printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
407 		if (metrics)
408 			metricgroup__print_strlist(me->metrics, raw);
409 		next = rb_next(node);
410 		rblist__remove_node(&groups, node);
411 	}
412 	if (!metricgroups)
413 		metricgroup__print_strlist(metriclist, raw);
414 	strlist__delete(metriclist);
415 }
416 
417 static void metricgroup__add_metric_weak_group(struct strbuf *events,
418 					       const char **ids,
419 					       int idnum)
420 {
421 	bool no_group = false;
422 	int i;
423 
424 	for (i = 0; i < idnum; i++) {
425 		pr_debug("found event %s\n", ids[i]);
426 		/*
427 		 * Duration time maps to a software event and can make
428 		 * groups not count. Always use it outside a
429 		 * group.
430 		 */
431 		if (!strcmp(ids[i], "duration_time")) {
432 			if (i > 0)
433 				strbuf_addf(events, "}:W,");
434 			strbuf_addf(events, "duration_time");
435 			no_group = true;
436 			continue;
437 		}
438 		strbuf_addf(events, "%s%s",
439 			i == 0 || no_group ? "{" : ",",
440 			ids[i]);
441 		no_group = false;
442 	}
443 	if (!no_group)
444 		strbuf_addf(events, "}:W");
445 }
446 
447 static void metricgroup__add_metric_non_group(struct strbuf *events,
448 					      const char **ids,
449 					      int idnum)
450 {
451 	int i;
452 
453 	for (i = 0; i < idnum; i++)
454 		strbuf_addf(events, ",%s", ids[i]);
455 }
456 
457 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
458 {
459 	static bool violate_nmi_constraint;
460 
461 	if (!foot) {
462 		pr_warning("Splitting metric group %s into standalone metrics.\n", name);
463 		violate_nmi_constraint = true;
464 		return;
465 	}
466 
467 	if (!violate_nmi_constraint)
468 		return;
469 
470 	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
471 		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
472 		   "    perf stat ...\n"
473 		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
474 }
475 
476 static bool metricgroup__has_constraint(struct pmu_event *pe)
477 {
478 	if (!pe->metric_constraint)
479 		return false;
480 
481 	if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
482 	    sysctl__nmi_watchdog_enabled()) {
483 		metricgroup___watchdog_constraint_hint(pe->metric_name, false);
484 		return true;
485 	}
486 
487 	return false;
488 }
489 
490 int __weak arch_get_runtimeparam(void)
491 {
492 	return 1;
493 }
494 
495 static int __metricgroup__add_metric(struct strbuf *events,
496 		struct list_head *group_list, struct pmu_event *pe, int runtime)
497 {
498 
499 	const char **ids;
500 	int idnum;
501 	struct egroup *eg;
502 
503 	if (expr__find_other(pe->metric_expr, NULL, &ids, &idnum, runtime) < 0)
504 		return -EINVAL;
505 
506 	if (events->len > 0)
507 		strbuf_addf(events, ",");
508 
509 	if (metricgroup__has_constraint(pe))
510 		metricgroup__add_metric_non_group(events, ids, idnum);
511 	else
512 		metricgroup__add_metric_weak_group(events, ids, idnum);
513 
514 	eg = malloc(sizeof(*eg));
515 	if (!eg)
516 		return -ENOMEM;
517 
518 	eg->ids = ids;
519 	eg->idnum = idnum;
520 	eg->metric_name = pe->metric_name;
521 	eg->metric_expr = pe->metric_expr;
522 	eg->metric_unit = pe->unit;
523 	eg->runtime = runtime;
524 	list_add_tail(&eg->nd, group_list);
525 
526 	return 0;
527 }
528 
529 static int metricgroup__add_metric(const char *metric, struct strbuf *events,
530 				   struct list_head *group_list)
531 {
532 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
533 	struct pmu_event *pe;
534 	int i, ret = -EINVAL;
535 
536 	if (!map)
537 		return 0;
538 
539 	for (i = 0; ; i++) {
540 		pe = &map->table[i];
541 
542 		if (!pe->name && !pe->metric_group && !pe->metric_name)
543 			break;
544 		if (!pe->metric_expr)
545 			continue;
546 		if (match_metric(pe->metric_group, metric) ||
547 		    match_metric(pe->metric_name, metric)) {
548 
549 			pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
550 
551 			if (!strstr(pe->metric_expr, "?")) {
552 				ret = __metricgroup__add_metric(events, group_list, pe, 1);
553 			} else {
554 				int j, count;
555 
556 				count = arch_get_runtimeparam();
557 
558 				/* This loop is added to create multiple
559 				 * events depend on count value and add
560 				 * those events to group_list.
561 				 */
562 
563 				for (j = 0; j < count; j++)
564 					ret = __metricgroup__add_metric(events, group_list, pe, j);
565 			}
566 			if (ret == -ENOMEM)
567 				break;
568 		}
569 	}
570 	return ret;
571 }
572 
573 static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
574 				        struct list_head *group_list)
575 {
576 	char *llist, *nlist, *p;
577 	int ret = -EINVAL;
578 
579 	nlist = strdup(list);
580 	if (!nlist)
581 		return -ENOMEM;
582 	llist = nlist;
583 
584 	strbuf_init(events, 100);
585 	strbuf_addf(events, "%s", "");
586 
587 	while ((p = strsep(&llist, ",")) != NULL) {
588 		ret = metricgroup__add_metric(p, events, group_list);
589 		if (ret == -EINVAL) {
590 			fprintf(stderr, "Cannot find metric or group `%s'\n",
591 					p);
592 			break;
593 		}
594 	}
595 	free(nlist);
596 
597 	if (!ret)
598 		metricgroup___watchdog_constraint_hint(NULL, true);
599 
600 	return ret;
601 }
602 
603 static void metricgroup__free_egroups(struct list_head *group_list)
604 {
605 	struct egroup *eg, *egtmp;
606 	int i;
607 
608 	list_for_each_entry_safe (eg, egtmp, group_list, nd) {
609 		for (i = 0; i < eg->idnum; i++)
610 			zfree(&eg->ids[i]);
611 		zfree(&eg->ids);
612 		list_del_init(&eg->nd);
613 		free(eg);
614 	}
615 }
616 
617 int metricgroup__parse_groups(const struct option *opt,
618 			   const char *str,
619 			   struct rblist *metric_events)
620 {
621 	struct parse_events_error parse_error;
622 	struct evlist *perf_evlist = *(struct evlist **)opt->value;
623 	struct strbuf extra_events;
624 	LIST_HEAD(group_list);
625 	int ret;
626 
627 	if (metric_events->nr_entries == 0)
628 		metricgroup__rblist_init(metric_events);
629 	ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
630 	if (ret)
631 		return ret;
632 	pr_debug("adding %s\n", extra_events.buf);
633 	bzero(&parse_error, sizeof(parse_error));
634 	ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
635 	if (ret) {
636 		parse_events_print_error(&parse_error, extra_events.buf);
637 		goto out;
638 	}
639 	strbuf_release(&extra_events);
640 	ret = metricgroup__setup_events(&group_list, perf_evlist,
641 					metric_events);
642 out:
643 	metricgroup__free_egroups(&group_list);
644 	return ret;
645 }
646 
647 bool metricgroup__has_metric(const char *metric)
648 {
649 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
650 	struct pmu_event *pe;
651 	int i;
652 
653 	if (!map)
654 		return false;
655 
656 	for (i = 0; ; i++) {
657 		pe = &map->table[i];
658 
659 		if (!pe->name && !pe->metric_group && !pe->metric_name)
660 			break;
661 		if (!pe->metric_expr)
662 			continue;
663 		if (match_metric(pe->metric_name, metric))
664 			return true;
665 	}
666 	return false;
667 }
668