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