xref: /openbmc/linux/tools/perf/util/metricgroup.c (revision 74be2d3b)
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 	struct expr_parse_ctx pctx;
89 	const char *metric_name;
90 	const char *metric_expr;
91 	const char *metric_unit;
92 	int runtime;
93 	bool has_constraint;
94 };
95 
96 /**
97  * Find a group of events in perf_evlist that correpond to those from a parsed
98  * metric expression. Note, as find_evsel_group is called in the same order as
99  * perf_evlist was constructed, metric_no_merge doesn't need to test for
100  * underfilling a group.
101  * @perf_evlist: a list of events something like: {metric1 leader, metric1
102  * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling,
103  * metric2 sibling}:W,duration_time
104  * @pctx: the parse context for the metric expression.
105  * @metric_no_merge: don't attempt to share events for the metric with other
106  * metrics.
107  * @has_constraint: is there a contraint on the group of events? In which case
108  * the events won't be grouped.
109  * @metric_events: out argument, null terminated array of evsel's associated
110  * with the metric.
111  * @evlist_used: in/out argument, bitmap tracking which evlist events are used.
112  * @return the first metric event or NULL on failure.
113  */
114 static struct evsel *find_evsel_group(struct evlist *perf_evlist,
115 				      struct expr_parse_ctx *pctx,
116 				      bool metric_no_merge,
117 				      bool has_constraint,
118 				      struct evsel **metric_events,
119 				      unsigned long *evlist_used)
120 {
121 	struct evsel *ev, *current_leader = NULL;
122 	double *val_ptr;
123 	int i = 0, matched_events = 0, events_to_match;
124 	const int idnum = (int)hashmap__size(&pctx->ids);
125 
126 	/* duration_time is grouped separately. */
127 	if (!has_constraint &&
128 	    hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr))
129 		events_to_match = idnum - 1;
130 	else
131 		events_to_match = idnum;
132 
133 	evlist__for_each_entry (perf_evlist, ev) {
134 		/*
135 		 * Events with a constraint aren't grouped and match the first
136 		 * events available.
137 		 */
138 		if (has_constraint && ev->weak_group)
139 			continue;
140 		/* Ignore event if already used and merging is disabled. */
141 		if (metric_no_merge && test_bit(ev->idx, evlist_used))
142 			continue;
143 		if (!has_constraint && ev->leader != current_leader) {
144 			/*
145 			 * Start of a new group, discard the whole match and
146 			 * start again.
147 			 */
148 			matched_events = 0;
149 			memset(metric_events, 0,
150 				sizeof(struct evsel *) * idnum);
151 			current_leader = ev->leader;
152 		}
153 		if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) {
154 			if (has_constraint) {
155 				/*
156 				 * Events aren't grouped, ensure the same event
157 				 * isn't matched from two groups.
158 				 */
159 				for (i = 0; i < matched_events; i++) {
160 					if (!strcmp(ev->name,
161 						    metric_events[i]->name)) {
162 						break;
163 					}
164 				}
165 				if (i != matched_events)
166 					continue;
167 			}
168 			metric_events[matched_events++] = ev;
169 		}
170 		if (matched_events == events_to_match)
171 			break;
172 	}
173 
174 	if (events_to_match != idnum) {
175 		/* Add the first duration_time. */
176 		evlist__for_each_entry(perf_evlist, ev) {
177 			if (!strcmp(ev->name, "duration_time")) {
178 				metric_events[matched_events++] = ev;
179 				break;
180 			}
181 		}
182 	}
183 
184 	if (matched_events != idnum) {
185 		/* Not whole match */
186 		return NULL;
187 	}
188 
189 	metric_events[idnum] = NULL;
190 
191 	for (i = 0; i < idnum; i++) {
192 		ev = metric_events[i];
193 		ev->metric_leader = ev;
194 		set_bit(ev->idx, evlist_used);
195 	}
196 
197 	return metric_events[0];
198 }
199 
200 static int metricgroup__setup_events(struct list_head *groups,
201 				     bool metric_no_merge,
202 				     struct evlist *perf_evlist,
203 				     struct rblist *metric_events_list)
204 {
205 	struct metric_event *me;
206 	struct metric_expr *expr;
207 	int i = 0;
208 	int ret = 0;
209 	struct egroup *eg;
210 	struct evsel *evsel, *tmp;
211 	unsigned long *evlist_used;
212 
213 	evlist_used = bitmap_alloc(perf_evlist->core.nr_entries);
214 	if (!evlist_used)
215 		return -ENOMEM;
216 
217 	list_for_each_entry (eg, groups, nd) {
218 		struct evsel **metric_events;
219 
220 		metric_events = calloc(sizeof(void *),
221 				hashmap__size(&eg->pctx.ids) + 1);
222 		if (!metric_events) {
223 			ret = -ENOMEM;
224 			break;
225 		}
226 		evsel = find_evsel_group(perf_evlist, &eg->pctx,
227 					 metric_no_merge,
228 					 eg->has_constraint, metric_events,
229 					 evlist_used);
230 		if (!evsel) {
231 			pr_debug("Cannot resolve %s: %s\n",
232 					eg->metric_name, eg->metric_expr);
233 			free(metric_events);
234 			continue;
235 		}
236 		for (i = 0; metric_events[i]; i++)
237 			metric_events[i]->collect_stat = true;
238 		me = metricgroup__lookup(metric_events_list, evsel, true);
239 		if (!me) {
240 			ret = -ENOMEM;
241 			free(metric_events);
242 			break;
243 		}
244 		expr = malloc(sizeof(struct metric_expr));
245 		if (!expr) {
246 			ret = -ENOMEM;
247 			free(metric_events);
248 			break;
249 		}
250 		expr->metric_expr = eg->metric_expr;
251 		expr->metric_name = eg->metric_name;
252 		expr->metric_unit = eg->metric_unit;
253 		expr->metric_events = metric_events;
254 		expr->runtime = eg->runtime;
255 		list_add(&expr->nd, &me->head);
256 	}
257 
258 	evlist__for_each_entry_safe(perf_evlist, tmp, evsel) {
259 		if (!test_bit(evsel->idx, evlist_used)) {
260 			evlist__remove(perf_evlist, evsel);
261 			evsel__delete(evsel);
262 		}
263 	}
264 	bitmap_free(evlist_used);
265 
266 	return ret;
267 }
268 
269 static bool match_metric(const char *n, const char *list)
270 {
271 	int len;
272 	char *m;
273 
274 	if (!list)
275 		return false;
276 	if (!strcmp(list, "all"))
277 		return true;
278 	if (!n)
279 		return !strcasecmp(list, "No_group");
280 	len = strlen(list);
281 	m = strcasestr(n, list);
282 	if (!m)
283 		return false;
284 	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
285 	    (m[len] == 0 || m[len] == ';'))
286 		return true;
287 	return false;
288 }
289 
290 struct mep {
291 	struct rb_node nd;
292 	const char *name;
293 	struct strlist *metrics;
294 };
295 
296 static int mep_cmp(struct rb_node *rb_node, const void *entry)
297 {
298 	struct mep *a = container_of(rb_node, struct mep, nd);
299 	struct mep *b = (struct mep *)entry;
300 
301 	return strcmp(a->name, b->name);
302 }
303 
304 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
305 					const void *entry)
306 {
307 	struct mep *me = malloc(sizeof(struct mep));
308 
309 	if (!me)
310 		return NULL;
311 	memcpy(me, entry, sizeof(struct mep));
312 	me->name = strdup(me->name);
313 	if (!me->name)
314 		goto out_me;
315 	me->metrics = strlist__new(NULL, NULL);
316 	if (!me->metrics)
317 		goto out_name;
318 	return &me->nd;
319 out_name:
320 	zfree(&me->name);
321 out_me:
322 	free(me);
323 	return NULL;
324 }
325 
326 static struct mep *mep_lookup(struct rblist *groups, const char *name)
327 {
328 	struct rb_node *nd;
329 	struct mep me = {
330 		.name = name
331 	};
332 	nd = rblist__find(groups, &me);
333 	if (nd)
334 		return container_of(nd, struct mep, nd);
335 	rblist__add_node(groups, &me);
336 	nd = rblist__find(groups, &me);
337 	if (nd)
338 		return container_of(nd, struct mep, nd);
339 	return NULL;
340 }
341 
342 static void mep_delete(struct rblist *rl __maybe_unused,
343 		       struct rb_node *nd)
344 {
345 	struct mep *me = container_of(nd, struct mep, nd);
346 
347 	strlist__delete(me->metrics);
348 	zfree(&me->name);
349 	free(me);
350 }
351 
352 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
353 {
354 	struct str_node *sn;
355 	int n = 0;
356 
357 	strlist__for_each_entry (sn, metrics) {
358 		if (raw)
359 			printf("%s%s", n > 0 ? " " : "", sn->s);
360 		else
361 			printf("  %s\n", sn->s);
362 		n++;
363 	}
364 	if (raw)
365 		putchar('\n');
366 }
367 
368 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
369 			bool raw, bool details)
370 {
371 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
372 	struct pmu_event *pe;
373 	int i;
374 	struct rblist groups;
375 	struct rb_node *node, *next;
376 	struct strlist *metriclist = NULL;
377 
378 	if (!map)
379 		return;
380 
381 	if (!metricgroups) {
382 		metriclist = strlist__new(NULL, NULL);
383 		if (!metriclist)
384 			return;
385 	}
386 
387 	rblist__init(&groups);
388 	groups.node_new = mep_new;
389 	groups.node_cmp = mep_cmp;
390 	groups.node_delete = mep_delete;
391 	for (i = 0; ; i++) {
392 		const char *g;
393 		pe = &map->table[i];
394 
395 		if (!pe->name && !pe->metric_group && !pe->metric_name)
396 			break;
397 		if (!pe->metric_expr)
398 			continue;
399 		g = pe->metric_group;
400 		if (!g && pe->metric_name) {
401 			if (pe->name)
402 				continue;
403 			g = "No_group";
404 		}
405 		if (g) {
406 			char *omg;
407 			char *mg = strdup(g);
408 
409 			if (!mg)
410 				return;
411 			omg = mg;
412 			while ((g = strsep(&mg, ";")) != NULL) {
413 				struct mep *me;
414 				char *s;
415 
416 				g = skip_spaces(g);
417 				if (*g == 0)
418 					g = "No_group";
419 				if (filter && !strstr(g, filter))
420 					continue;
421 				if (raw)
422 					s = (char *)pe->metric_name;
423 				else {
424 					if (asprintf(&s, "%s\n%*s%s]",
425 						     pe->metric_name, 8, "[", pe->desc) < 0)
426 						return;
427 
428 					if (details) {
429 						if (asprintf(&s, "%s\n%*s%s]",
430 							     s, 8, "[", pe->metric_expr) < 0)
431 							return;
432 					}
433 				}
434 
435 				if (!s)
436 					continue;
437 
438 				if (!metricgroups) {
439 					strlist__add(metriclist, s);
440 				} else {
441 					me = mep_lookup(&groups, g);
442 					if (!me)
443 						continue;
444 					strlist__add(me->metrics, s);
445 				}
446 			}
447 			free(omg);
448 		}
449 	}
450 
451 	if (metricgroups && !raw)
452 		printf("\nMetric Groups:\n\n");
453 	else if (metrics && !raw)
454 		printf("\nMetrics:\n\n");
455 
456 	for (node = rb_first_cached(&groups.entries); node; node = next) {
457 		struct mep *me = container_of(node, struct mep, nd);
458 
459 		if (metricgroups)
460 			printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
461 		if (metrics)
462 			metricgroup__print_strlist(me->metrics, raw);
463 		next = rb_next(node);
464 		rblist__remove_node(&groups, node);
465 	}
466 	if (!metricgroups)
467 		metricgroup__print_strlist(metriclist, raw);
468 	strlist__delete(metriclist);
469 }
470 
471 static void metricgroup__add_metric_weak_group(struct strbuf *events,
472 					       struct expr_parse_ctx *ctx)
473 {
474 	struct hashmap_entry *cur;
475 	size_t bkt;
476 	bool no_group = true, has_duration = false;
477 
478 	hashmap__for_each_entry((&ctx->ids), cur, bkt) {
479 		pr_debug("found event %s\n", (const char *)cur->key);
480 		/*
481 		 * Duration time maps to a software event and can make
482 		 * groups not count. Always use it outside a
483 		 * group.
484 		 */
485 		if (!strcmp(cur->key, "duration_time")) {
486 			has_duration = true;
487 			continue;
488 		}
489 		strbuf_addf(events, "%s%s",
490 			no_group ? "{" : ",",
491 			(const char *)cur->key);
492 		no_group = false;
493 	}
494 	if (!no_group) {
495 		strbuf_addf(events, "}:W");
496 		if (has_duration)
497 			strbuf_addf(events, ",duration_time");
498 	} else if (has_duration)
499 		strbuf_addf(events, "duration_time");
500 }
501 
502 static void metricgroup__add_metric_non_group(struct strbuf *events,
503 					      struct expr_parse_ctx *ctx)
504 {
505 	struct hashmap_entry *cur;
506 	size_t bkt;
507 	bool first = true;
508 
509 	hashmap__for_each_entry((&ctx->ids), cur, bkt) {
510 		if (!first)
511 			strbuf_addf(events, ",");
512 		strbuf_addf(events, "%s", (const char *)cur->key);
513 		first = false;
514 	}
515 }
516 
517 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
518 {
519 	static bool violate_nmi_constraint;
520 
521 	if (!foot) {
522 		pr_warning("Splitting metric group %s into standalone metrics.\n", name);
523 		violate_nmi_constraint = true;
524 		return;
525 	}
526 
527 	if (!violate_nmi_constraint)
528 		return;
529 
530 	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
531 		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
532 		   "    perf stat ...\n"
533 		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
534 }
535 
536 static bool metricgroup__has_constraint(struct pmu_event *pe)
537 {
538 	if (!pe->metric_constraint)
539 		return false;
540 
541 	if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
542 	    sysctl__nmi_watchdog_enabled()) {
543 		metricgroup___watchdog_constraint_hint(pe->metric_name, false);
544 		return true;
545 	}
546 
547 	return false;
548 }
549 
550 int __weak arch_get_runtimeparam(void)
551 {
552 	return 1;
553 }
554 
555 static int __metricgroup__add_metric(struct list_head *group_list,
556 				     struct pmu_event *pe,
557 				     bool metric_no_group,
558 				     int runtime)
559 {
560 	struct egroup *eg;
561 
562 	eg = malloc(sizeof(*eg));
563 	if (!eg)
564 		return -ENOMEM;
565 
566 	expr__ctx_init(&eg->pctx);
567 	eg->metric_name = pe->metric_name;
568 	eg->metric_expr = pe->metric_expr;
569 	eg->metric_unit = pe->unit;
570 	eg->runtime = runtime;
571 	eg->has_constraint = metric_no_group || metricgroup__has_constraint(pe);
572 
573 	if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) {
574 		expr__ctx_clear(&eg->pctx);
575 		free(eg);
576 		return -EINVAL;
577 	}
578 
579 	if (list_empty(group_list))
580 		list_add(&eg->nd, group_list);
581 	else {
582 		struct list_head *pos;
583 
584 		/* Place the largest groups at the front. */
585 		list_for_each_prev(pos, group_list) {
586 			struct egroup *old = list_entry(pos, struct egroup, nd);
587 
588 			if (hashmap__size(&eg->pctx.ids) <=
589 			    hashmap__size(&old->pctx.ids))
590 				break;
591 		}
592 		list_add(&eg->nd, pos);
593 	}
594 
595 	return 0;
596 }
597 
598 static int metricgroup__add_metric(const char *metric, bool metric_no_group,
599 				   struct strbuf *events,
600 				   struct list_head *group_list)
601 {
602 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
603 	struct pmu_event *pe;
604 	struct egroup *eg;
605 	int i, ret;
606 	bool has_match = false;
607 
608 	if (!map)
609 		return 0;
610 
611 	for (i = 0; ; i++) {
612 		pe = &map->table[i];
613 
614 		if (!pe->name && !pe->metric_group && !pe->metric_name) {
615 			/* End of pmu events. */
616 			if (!has_match)
617 				return -EINVAL;
618 			break;
619 		}
620 		if (!pe->metric_expr)
621 			continue;
622 		if (match_metric(pe->metric_group, metric) ||
623 		    match_metric(pe->metric_name, metric)) {
624 			has_match = true;
625 			pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
626 
627 			if (!strstr(pe->metric_expr, "?")) {
628 				ret = __metricgroup__add_metric(group_list,
629 								pe,
630 								metric_no_group,
631 								1);
632 				if (ret)
633 					return ret;
634 			} else {
635 				int j, count;
636 
637 				count = arch_get_runtimeparam();
638 
639 				/* This loop is added to create multiple
640 				 * events depend on count value and add
641 				 * those events to group_list.
642 				 */
643 
644 				for (j = 0; j < count; j++) {
645 					ret = __metricgroup__add_metric(
646 						group_list, pe,
647 						metric_no_group, j);
648 					if (ret)
649 						return ret;
650 				}
651 			}
652 		}
653 	}
654 	list_for_each_entry(eg, group_list, nd) {
655 		if (events->len > 0)
656 			strbuf_addf(events, ",");
657 
658 		if (eg->has_constraint) {
659 			metricgroup__add_metric_non_group(events,
660 							  &eg->pctx);
661 		} else {
662 			metricgroup__add_metric_weak_group(events,
663 							   &eg->pctx);
664 		}
665 	}
666 	return 0;
667 }
668 
669 static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
670 					struct strbuf *events,
671 				        struct list_head *group_list)
672 {
673 	char *llist, *nlist, *p;
674 	int ret = -EINVAL;
675 
676 	nlist = strdup(list);
677 	if (!nlist)
678 		return -ENOMEM;
679 	llist = nlist;
680 
681 	strbuf_init(events, 100);
682 	strbuf_addf(events, "%s", "");
683 
684 	while ((p = strsep(&llist, ",")) != NULL) {
685 		ret = metricgroup__add_metric(p, metric_no_group, events,
686 					      group_list);
687 		if (ret == -EINVAL) {
688 			fprintf(stderr, "Cannot find metric or group `%s'\n",
689 					p);
690 			break;
691 		}
692 	}
693 	free(nlist);
694 
695 	if (!ret)
696 		metricgroup___watchdog_constraint_hint(NULL, true);
697 
698 	return ret;
699 }
700 
701 static void metricgroup__free_egroups(struct list_head *group_list)
702 {
703 	struct egroup *eg, *egtmp;
704 
705 	list_for_each_entry_safe (eg, egtmp, group_list, nd) {
706 		expr__ctx_clear(&eg->pctx);
707 		list_del_init(&eg->nd);
708 		free(eg);
709 	}
710 }
711 
712 int metricgroup__parse_groups(const struct option *opt,
713 			      const char *str,
714 			      bool metric_no_group,
715 			      bool metric_no_merge,
716 			      struct rblist *metric_events)
717 {
718 	struct parse_events_error parse_error;
719 	struct evlist *perf_evlist = *(struct evlist **)opt->value;
720 	struct strbuf extra_events;
721 	LIST_HEAD(group_list);
722 	int ret;
723 
724 	if (metric_events->nr_entries == 0)
725 		metricgroup__rblist_init(metric_events);
726 	ret = metricgroup__add_metric_list(str, metric_no_group,
727 					   &extra_events, &group_list);
728 	if (ret)
729 		return ret;
730 	pr_debug("adding %s\n", extra_events.buf);
731 	bzero(&parse_error, sizeof(parse_error));
732 	ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
733 	if (ret) {
734 		parse_events_print_error(&parse_error, extra_events.buf);
735 		goto out;
736 	}
737 	strbuf_release(&extra_events);
738 	ret = metricgroup__setup_events(&group_list, metric_no_merge,
739 					perf_evlist, metric_events);
740 out:
741 	metricgroup__free_egroups(&group_list);
742 	return ret;
743 }
744 
745 bool metricgroup__has_metric(const char *metric)
746 {
747 	struct pmu_events_map *map = perf_pmu__find_map(NULL);
748 	struct pmu_event *pe;
749 	int i;
750 
751 	if (!map)
752 		return false;
753 
754 	for (i = 0; ; i++) {
755 		pe = &map->table[i];
756 
757 		if (!pe->name && !pe->metric_group && !pe->metric_name)
758 			break;
759 		if (!pe->metric_expr)
760 			continue;
761 		if (match_metric(pe->metric_name, metric))
762 			return true;
763 	}
764 	return false;
765 }
766