xref: /openbmc/linux/tools/perf/tests/pmu-events.c (revision 3a35093a)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "math.h"
3 #include "parse-events.h"
4 #include "pmu.h"
5 #include "tests.h"
6 #include <errno.h>
7 #include <stdio.h>
8 #include <linux/kernel.h>
9 #include <linux/zalloc.h>
10 #include "debug.h"
11 #include "../pmu-events/pmu-events.h"
12 #include "util/evlist.h"
13 #include "util/expr.h"
14 #include "util/parse-events.h"
15 
16 struct perf_pmu_test_event {
17 	/* used for matching against events from generated pmu-events.c */
18 	struct pmu_event event;
19 
20 	/* used for matching against event aliases */
21 	/* extra events for aliases */
22 	const char *alias_str;
23 
24 	/*
25 	 * Note: For when PublicDescription does not exist in the JSON, we
26 	 * will have no long_desc in pmu_event.long_desc, but long_desc may
27 	 * be set in the alias.
28 	 */
29 	const char *alias_long_desc;
30 };
31 
32 static struct perf_pmu_test_event test_cpu_events[] = {
33 	{
34 		.event = {
35 			.name = "bp_l1_btb_correct",
36 			.event = "event=0x8a",
37 			.desc = "L1 BTB Correction",
38 			.topic = "branch",
39 		},
40 		.alias_str = "event=0x8a",
41 		.alias_long_desc = "L1 BTB Correction",
42 	},
43 	{
44 		.event = {
45 			.name = "bp_l2_btb_correct",
46 			.event = "event=0x8b",
47 			.desc = "L2 BTB Correction",
48 			.topic = "branch",
49 		},
50 		.alias_str = "event=0x8b",
51 		.alias_long_desc = "L2 BTB Correction",
52 	},
53 	{
54 		.event = {
55 			.name = "segment_reg_loads.any",
56 			.event = "umask=0x80,period=200000,event=0x6",
57 			.desc = "Number of segment register loads",
58 			.topic = "other",
59 		},
60 		.alias_str = "umask=0x80,(null)=0x30d40,event=0x6",
61 		.alias_long_desc = "Number of segment register loads",
62 	},
63 	{
64 		.event = {
65 			.name = "dispatch_blocked.any",
66 			.event = "umask=0x20,period=200000,event=0x9",
67 			.desc = "Memory cluster signals to block micro-op dispatch for any reason",
68 			.topic = "other",
69 		},
70 		.alias_str = "umask=0x20,(null)=0x30d40,event=0x9",
71 		.alias_long_desc = "Memory cluster signals to block micro-op dispatch for any reason",
72 	},
73 	{
74 		.event = {
75 			.name = "eist_trans",
76 			.event = "umask=0x0,period=200000,event=0x3a",
77 			.desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
78 			.topic = "other",
79 		},
80 		.alias_str = "umask=0,(null)=0x30d40,event=0x3a",
81 		.alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
82 	},
83 	{
84 		.event = {
85 			.name = "l3_cache_rd",
86 			.event = "event=0x40",
87 			.desc = "L3 cache access, read",
88 			.long_desc = "Attributable Level 3 cache access, read",
89 			.topic = "cache",
90 		},
91 		.alias_str = "event=0x40",
92 		.alias_long_desc = "Attributable Level 3 cache access, read",
93 	},
94 	{ /* sentinel */
95 		.event = {
96 			.name = NULL,
97 		},
98 	},
99 };
100 
101 static struct perf_pmu_test_event test_uncore_events[] = {
102 	{
103 		.event = {
104 			.name = "uncore_hisi_ddrc.flux_wcmd",
105 			.event = "event=0x2",
106 			.desc = "DDRC write commands. Unit: hisi_sccl,ddrc ",
107 			.topic = "uncore",
108 			.long_desc = "DDRC write commands",
109 			.pmu = "hisi_sccl,ddrc",
110 		},
111 		.alias_str = "event=0x2",
112 		.alias_long_desc = "DDRC write commands",
113 	},
114 	{
115 		.event = {
116 			.name = "unc_cbo_xsnp_response.miss_eviction",
117 			.event = "umask=0x81,event=0x22",
118 			.desc = "Unit: uncore_cbox A cross-core snoop resulted from L3 Eviction which misses in some processor core",
119 			.topic = "uncore",
120 			.long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
121 			.pmu = "uncore_cbox",
122 		},
123 		.alias_str = "umask=0x81,event=0x22",
124 		.alias_long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
125 	},
126 	{ /* sentinel */
127 		.event = {
128 			.name = NULL,
129 		},
130 	}
131 };
132 
133 const int total_test_events_size = ARRAY_SIZE(test_uncore_events);
134 
135 static bool is_same(const char *reference, const char *test)
136 {
137 	if (!reference && !test)
138 		return true;
139 
140 	if (reference && !test)
141 		return false;
142 
143 	if (!reference && test)
144 		return false;
145 
146 	return !strcmp(reference, test);
147 }
148 
149 static struct pmu_events_map *__test_pmu_get_events_map(void)
150 {
151 	struct pmu_events_map *map;
152 
153 	for (map = &pmu_events_map[0]; map->cpuid; map++) {
154 		if (!strcmp(map->cpuid, "testcpu"))
155 			return map;
156 	}
157 
158 	pr_err("could not find test events map\n");
159 
160 	return NULL;
161 }
162 
163 /* Verify generated events from pmu-events.c is as expected */
164 static int test_pmu_event_table(void)
165 {
166 	struct pmu_events_map *map = __test_pmu_get_events_map();
167 	struct pmu_event *table;
168 	int map_events = 0, expected_events;
169 
170 	/* ignore 2x sentinels */
171 	expected_events = ARRAY_SIZE(test_cpu_events) +
172 			  ARRAY_SIZE(test_uncore_events) - 2;
173 
174 	if (!map)
175 		return -1;
176 
177 	for (table = map->table; table->name; table++) {
178 		struct perf_pmu_test_event *test;
179 		struct pmu_event *te;
180 		bool found = false;
181 
182 		if (table->pmu)
183 			test = &test_uncore_events[0];
184 		else
185 			test = &test_cpu_events[0];
186 
187 		te = &test->event;
188 
189 		for (; te->name; test++, te = &test->event) {
190 			if (strcmp(table->name, te->name))
191 				continue;
192 			found = true;
193 			map_events++;
194 
195 			if (!is_same(table->desc, te->desc)) {
196 				pr_debug2("testing event table %s: mismatched desc, %s vs %s\n",
197 					  table->name, table->desc, te->desc);
198 				return -1;
199 			}
200 
201 			if (!is_same(table->topic, te->topic)) {
202 				pr_debug2("testing event table %s: mismatched topic, %s vs %s\n",
203 					  table->name, table->topic,
204 					  te->topic);
205 				return -1;
206 			}
207 
208 			if (!is_same(table->long_desc, te->long_desc)) {
209 				pr_debug2("testing event table %s: mismatched long_desc, %s vs %s\n",
210 					  table->name, table->long_desc,
211 					  te->long_desc);
212 				return -1;
213 			}
214 
215 			if (!is_same(table->unit, te->unit)) {
216 				pr_debug2("testing event table %s: mismatched unit, %s vs %s\n",
217 					  table->name, table->unit,
218 					  te->unit);
219 				return -1;
220 			}
221 
222 			if (!is_same(table->perpkg, te->perpkg)) {
223 				pr_debug2("testing event table %s: mismatched perpkg, %s vs %s\n",
224 					  table->name, table->perpkg,
225 					  te->perpkg);
226 				return -1;
227 			}
228 
229 			if (!is_same(table->metric_expr, te->metric_expr)) {
230 				pr_debug2("testing event table %s: mismatched metric_expr, %s vs %s\n",
231 					  table->name, table->metric_expr,
232 					  te->metric_expr);
233 				return -1;
234 			}
235 
236 			if (!is_same(table->metric_name, te->metric_name)) {
237 				pr_debug2("testing event table %s: mismatched metric_name, %s vs %s\n",
238 					  table->name,  table->metric_name,
239 					  te->metric_name);
240 				return -1;
241 			}
242 
243 			if (!is_same(table->deprecated, te->deprecated)) {
244 				pr_debug2("testing event table %s: mismatched deprecated, %s vs %s\n",
245 					  table->name, table->deprecated,
246 					  te->deprecated);
247 				return -1;
248 			}
249 
250 			pr_debug("testing event table %s: pass\n", table->name);
251 		}
252 
253 		if (!found) {
254 			pr_err("testing event table: could not find event %s\n",
255 			       table->name);
256 			return -1;
257 		}
258 	}
259 
260 	if (map_events != expected_events) {
261 		pr_err("testing event table: found %d, but expected %d\n",
262 		       map_events, expected_events);
263 		return -1;
264 	}
265 
266 	return 0;
267 }
268 
269 static struct perf_pmu_alias *find_alias(const char *test_event, struct list_head *aliases)
270 {
271 	struct perf_pmu_alias *alias;
272 
273 	list_for_each_entry(alias, aliases, list)
274 		if (!strcmp(test_event, alias->name))
275 			return alias;
276 
277 	return NULL;
278 }
279 
280 /* Verify aliases are as expected */
281 static int __test__pmu_event_aliases(char *pmu_name, int *count)
282 {
283 	struct perf_pmu_test_event *test;
284 	struct pmu_event *te;
285 	struct perf_pmu *pmu;
286 	LIST_HEAD(aliases);
287 	int res = 0;
288 	bool use_uncore_table;
289 	struct pmu_events_map *map = __test_pmu_get_events_map();
290 	struct perf_pmu_alias *a, *tmp;
291 
292 	if (!map)
293 		return -1;
294 
295 	if (is_pmu_core(pmu_name)) {
296 		test = &test_cpu_events[0];
297 		use_uncore_table = false;
298 	} else {
299 		test = &test_uncore_events[0];
300 		use_uncore_table = true;
301 	}
302 
303 	pmu = zalloc(sizeof(*pmu));
304 	if (!pmu)
305 		return -1;
306 
307 	pmu->name = pmu_name;
308 
309 	pmu_add_cpu_aliases_map(&aliases, pmu, map);
310 
311 	for (te = &test->event; te->name; test++, te = &test->event) {
312 		struct perf_pmu_alias *alias = find_alias(te->name, &aliases);
313 
314 		if (!alias) {
315 			bool uncore_match = pmu_uncore_alias_match(pmu_name,
316 								   te->pmu);
317 
318 			if (use_uncore_table && !uncore_match) {
319 				pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
320 					  pmu_name, te->name);
321 				continue;
322 			}
323 
324 			pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
325 				  pmu_name, te->name);
326 			res = -1;
327 			break;
328 		}
329 
330 		if (!is_same(alias->desc, te->desc)) {
331 			pr_debug2("testing aliases PMU %s: mismatched desc, %s vs %s\n",
332 				  pmu_name, alias->desc, te->desc);
333 			res = -1;
334 			break;
335 		}
336 
337 		if (!is_same(alias->long_desc, test->alias_long_desc)) {
338 			pr_debug2("testing aliases PMU %s: mismatched long_desc, %s vs %s\n",
339 				  pmu_name, alias->long_desc,
340 				  test->alias_long_desc);
341 			res = -1;
342 			break;
343 		}
344 
345 		if (!is_same(alias->str, test->alias_str)) {
346 			pr_debug2("testing aliases PMU %s: mismatched str, %s vs %s\n",
347 				  pmu_name, alias->str, test->alias_str);
348 			res = -1;
349 			break;
350 		}
351 
352 		if (!is_same(alias->topic, te->topic)) {
353 			pr_debug2("testing aliases PMU %s: mismatched topic, %s vs %s\n",
354 				  pmu_name, alias->topic, te->topic);
355 			res = -1;
356 			break;
357 		}
358 
359 		(*count)++;
360 		pr_debug2("testing aliases PMU %s: matched event %s\n",
361 			  pmu_name, alias->name);
362 	}
363 
364 	list_for_each_entry_safe(a, tmp, &aliases, list) {
365 		list_del(&a->list);
366 		perf_pmu_free_alias(a);
367 	}
368 	free(pmu);
369 	return res;
370 }
371 
372 
373 /* Test that aliases generated are as expected */
374 static int test_aliases(void)
375 {
376 	struct perf_pmu *pmu = NULL;
377 
378 	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
379 		int count = 0;
380 
381 		if (list_empty(&pmu->format)) {
382 			pr_debug2("skipping testing PMU %s\n", pmu->name);
383 			continue;
384 		}
385 
386 		if (__test__pmu_event_aliases(pmu->name, &count)) {
387 			pr_debug("testing PMU %s aliases: failed\n", pmu->name);
388 			return -1;
389 		}
390 
391 		if (count == 0)
392 			pr_debug3("testing PMU %s aliases: no events to match\n",
393 				  pmu->name);
394 		else
395 			pr_debug("testing PMU %s aliases: pass\n", pmu->name);
396 	}
397 
398 	return 0;
399 }
400 
401 static bool is_number(const char *str)
402 {
403 	char *end_ptr;
404 	double v;
405 
406 	errno = 0;
407 	v = strtod(str, &end_ptr);
408 	(void)v; // We're not interested in this value, only if it is valid
409 	return errno == 0 && end_ptr != str;
410 }
411 
412 static int check_parse_id(const char *id, struct parse_events_error *error,
413 			  struct perf_pmu *fake_pmu)
414 {
415 	struct evlist *evlist;
416 	int ret;
417 
418 	/* Numbers are always valid. */
419 	if (is_number(id))
420 		return 0;
421 
422 	evlist = evlist__new();
423 	if (!evlist)
424 		return -ENOMEM;
425 	ret = __parse_events(evlist, id, error, fake_pmu);
426 	evlist__delete(evlist);
427 	return ret;
428 }
429 
430 static int check_parse_cpu(const char *id, bool same_cpu, struct pmu_event *pe)
431 {
432 	struct parse_events_error error = { .idx = 0, };
433 
434 	int ret = check_parse_id(id, &error, NULL);
435 	if (ret && same_cpu) {
436 		pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
437 			pe->metric_name, id, pe->metric_expr);
438 		pr_warning("Error string '%s' help '%s'\n", error.str,
439 			error.help);
440 	} else if (ret) {
441 		pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
442 			  id, pe->metric_name, pe->metric_expr);
443 		ret = 0;
444 	}
445 	free(error.str);
446 	free(error.help);
447 	free(error.first_str);
448 	free(error.first_help);
449 	return ret;
450 }
451 
452 static int check_parse_fake(const char *id)
453 {
454 	struct parse_events_error error = { .idx = 0, };
455 	int ret = check_parse_id(id, &error, &perf_pmu__fake);
456 
457 	free(error.str);
458 	free(error.help);
459 	free(error.first_str);
460 	free(error.first_help);
461 	return ret;
462 }
463 
464 static void expr_failure(const char *msg,
465 			 const struct pmu_events_map *map,
466 			 const struct pmu_event *pe)
467 {
468 	pr_debug("%s for map %s %s %s\n",
469 		msg, map->cpuid, map->version, map->type);
470 	pr_debug("On metric %s\n", pe->metric_name);
471 	pr_debug("On expression %s\n", pe->metric_expr);
472 }
473 
474 static int test_parsing(void)
475 {
476 	struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
477 	struct pmu_events_map *map;
478 	struct pmu_event *pe;
479 	int i, j, k;
480 	int ret = 0;
481 	struct expr_parse_ctx ctx;
482 	double result;
483 
484 	i = 0;
485 	for (;;) {
486 		map = &pmu_events_map[i++];
487 		if (!map->table)
488 			break;
489 		j = 0;
490 		for (;;) {
491 			struct hashmap_entry *cur;
492 			size_t bkt;
493 
494 			pe = &map->table[j++];
495 			if (!pe->name && !pe->metric_group && !pe->metric_name)
496 				break;
497 			if (!pe->metric_expr)
498 				continue;
499 			expr__ctx_init(&ctx);
500 			if (expr__find_other(pe->metric_expr, NULL, &ctx, 0)
501 				  < 0) {
502 				expr_failure("Parse other failed", map, pe);
503 				ret++;
504 				continue;
505 			}
506 
507 			/*
508 			 * Add all ids with a made up value. The value may
509 			 * trigger divide by zero when subtracted and so try to
510 			 * make them unique.
511 			 */
512 			k = 1;
513 			hashmap__for_each_entry((&ctx.ids), cur, bkt)
514 				expr__add_id_val(&ctx, strdup(cur->key), k++);
515 
516 			hashmap__for_each_entry((&ctx.ids), cur, bkt) {
517 				if (check_parse_cpu(cur->key, map == cpus_map,
518 						   pe))
519 					ret++;
520 			}
521 
522 			if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
523 				expr_failure("Parse failed", map, pe);
524 				ret++;
525 			}
526 			expr__ctx_clear(&ctx);
527 		}
528 	}
529 	/* TODO: fail when not ok */
530 	return ret == 0 ? TEST_OK : TEST_SKIP;
531 }
532 
533 struct test_metric {
534 	const char *str;
535 };
536 
537 static struct test_metric metrics[] = {
538 	{ "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
539 	{ "imx8_ddr0@read\\-cycles@ * 4 * 4", },
540 	{ "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
541 	{ "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
542 	{ "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
543 };
544 
545 static int metric_parse_fake(const char *str)
546 {
547 	struct expr_parse_ctx ctx;
548 	struct hashmap_entry *cur;
549 	double result;
550 	int ret = -1;
551 	size_t bkt;
552 	int i;
553 
554 	pr_debug("parsing '%s'\n", str);
555 
556 	expr__ctx_init(&ctx);
557 	if (expr__find_other(str, NULL, &ctx, 0) < 0) {
558 		pr_err("expr__find_other failed\n");
559 		return -1;
560 	}
561 
562 	/*
563 	 * Add all ids with a made up value. The value may
564 	 * trigger divide by zero when subtracted and so try to
565 	 * make them unique.
566 	 */
567 	i = 1;
568 	hashmap__for_each_entry((&ctx.ids), cur, bkt)
569 		expr__add_id_val(&ctx, strdup(cur->key), i++);
570 
571 	hashmap__for_each_entry((&ctx.ids), cur, bkt) {
572 		if (check_parse_fake(cur->key)) {
573 			pr_err("check_parse_fake failed\n");
574 			goto out;
575 		}
576 	}
577 
578 	if (expr__parse(&result, &ctx, str, 0))
579 		pr_err("expr__parse failed\n");
580 	else
581 		ret = 0;
582 
583 out:
584 	expr__ctx_clear(&ctx);
585 	return ret;
586 }
587 
588 /*
589  * Parse all the metrics for current architecture,
590  * or all defined cpus via the 'fake_pmu'
591  * in parse_events.
592  */
593 static int test_parsing_fake(void)
594 {
595 	struct pmu_events_map *map;
596 	struct pmu_event *pe;
597 	unsigned int i, j;
598 	int err = 0;
599 
600 	for (i = 0; i < ARRAY_SIZE(metrics); i++) {
601 		err = metric_parse_fake(metrics[i].str);
602 		if (err)
603 			return err;
604 	}
605 
606 	i = 0;
607 	for (;;) {
608 		map = &pmu_events_map[i++];
609 		if (!map->table)
610 			break;
611 		j = 0;
612 		for (;;) {
613 			pe = &map->table[j++];
614 			if (!pe->name && !pe->metric_group && !pe->metric_name)
615 				break;
616 			if (!pe->metric_expr)
617 				continue;
618 			err = metric_parse_fake(pe->metric_expr);
619 			if (err)
620 				return err;
621 		}
622 	}
623 
624 	return 0;
625 }
626 
627 static const struct {
628 	int (*func)(void);
629 	const char *desc;
630 } pmu_events_testcase_table[] = {
631 	{
632 		.func = test_pmu_event_table,
633 		.desc = "PMU event table sanity",
634 	},
635 	{
636 		.func = test_aliases,
637 		.desc = "PMU event map aliases",
638 	},
639 	{
640 		.func = test_parsing,
641 		.desc = "Parsing of PMU event table metrics",
642 	},
643 	{
644 		.func = test_parsing_fake,
645 		.desc = "Parsing of PMU event table metrics with fake PMUs",
646 	},
647 };
648 
649 const char *test__pmu_events_subtest_get_desc(int subtest)
650 {
651 	if (subtest < 0 ||
652 	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
653 		return NULL;
654 	return pmu_events_testcase_table[subtest].desc;
655 }
656 
657 const char *test__pmu_events_subtest_skip_reason(int subtest)
658 {
659 	if (subtest < 0 ||
660 	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
661 		return NULL;
662 	if (pmu_events_testcase_table[subtest].func != test_parsing)
663 		return NULL;
664 	return "some metrics failed";
665 }
666 
667 int test__pmu_events_subtest_get_nr(void)
668 {
669 	return (int)ARRAY_SIZE(pmu_events_testcase_table);
670 }
671 
672 int test__pmu_events(struct test *test __maybe_unused, int subtest)
673 {
674 	if (subtest < 0 ||
675 	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
676 		return TEST_FAIL;
677 	return pmu_events_testcase_table[subtest].func();
678 }
679