xref: /openbmc/linux/tools/perf/tests/builtin-test.c (revision b24413180f5600bcb3bb70fbed5cf186b60864bd)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * builtin-test.c
4  *
5  * Builtin regression testing command: ever growing number of sanity tests
6  */
7 #include <errno.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <dirent.h>
12 #include <sys/wait.h>
13 #include <sys/stat.h>
14 #include "builtin.h"
15 #include "hist.h"
16 #include "intlist.h"
17 #include "tests.h"
18 #include "debug.h"
19 #include "color.h"
20 #include <subcmd/parse-options.h>
21 #include "string2.h"
22 #include "symbol.h"
23 #include <linux/kernel.h>
24 #include <subcmd/exec-cmd.h>
25 
26 static bool dont_fork;
27 
28 struct test __weak arch_tests[] = {
29 	{
30 		.func = NULL,
31 	},
32 };
33 
34 static struct test generic_tests[] = {
35 	{
36 		.desc = "vmlinux symtab matches kallsyms",
37 		.func = test__vmlinux_matches_kallsyms,
38 	},
39 	{
40 		.desc = "Detect openat syscall event",
41 		.func = test__openat_syscall_event,
42 	},
43 	{
44 		.desc = "Detect openat syscall event on all cpus",
45 		.func = test__openat_syscall_event_on_all_cpus,
46 	},
47 	{
48 		.desc = "Read samples using the mmap interface",
49 		.func = test__basic_mmap,
50 	},
51 	{
52 		.desc = "Test data source output",
53 		.func = test__mem,
54 	},
55 	{
56 		.desc = "Parse event definition strings",
57 		.func = test__parse_events,
58 	},
59 	{
60 		.desc = "Simple expression parser",
61 		.func = test__expr,
62 	},
63 	{
64 		.desc = "PERF_RECORD_* events & perf_sample fields",
65 		.func = test__PERF_RECORD,
66 	},
67 	{
68 		.desc = "Parse perf pmu format",
69 		.func = test__pmu,
70 	},
71 	{
72 		.desc = "DSO data read",
73 		.func = test__dso_data,
74 	},
75 	{
76 		.desc = "DSO data cache",
77 		.func = test__dso_data_cache,
78 	},
79 	{
80 		.desc = "DSO data reopen",
81 		.func = test__dso_data_reopen,
82 	},
83 	{
84 		.desc = "Roundtrip evsel->name",
85 		.func = test__perf_evsel__roundtrip_name_test,
86 	},
87 	{
88 		.desc = "Parse sched tracepoints fields",
89 		.func = test__perf_evsel__tp_sched_test,
90 	},
91 	{
92 		.desc = "syscalls:sys_enter_openat event fields",
93 		.func = test__syscall_openat_tp_fields,
94 	},
95 	{
96 		.desc = "Setup struct perf_event_attr",
97 		.func = test__attr,
98 	},
99 	{
100 		.desc = "Match and link multiple hists",
101 		.func = test__hists_link,
102 	},
103 	{
104 		.desc = "'import perf' in python",
105 		.func = test__python_use,
106 	},
107 	{
108 		.desc = "Breakpoint overflow signal handler",
109 		.func = test__bp_signal,
110 		.is_supported = test__bp_signal_is_supported,
111 	},
112 	{
113 		.desc = "Breakpoint overflow sampling",
114 		.func = test__bp_signal_overflow,
115 		.is_supported = test__bp_signal_is_supported,
116 	},
117 	{
118 		.desc = "Number of exit events of a simple workload",
119 		.func = test__task_exit,
120 	},
121 	{
122 		.desc = "Software clock events period values",
123 		.func = test__sw_clock_freq,
124 	},
125 	{
126 		.desc = "Object code reading",
127 		.func = test__code_reading,
128 	},
129 	{
130 		.desc = "Sample parsing",
131 		.func = test__sample_parsing,
132 	},
133 	{
134 		.desc = "Use a dummy software event to keep tracking",
135 		.func = test__keep_tracking,
136 	},
137 	{
138 		.desc = "Parse with no sample_id_all bit set",
139 		.func = test__parse_no_sample_id_all,
140 	},
141 	{
142 		.desc = "Filter hist entries",
143 		.func = test__hists_filter,
144 	},
145 	{
146 		.desc = "Lookup mmap thread",
147 		.func = test__mmap_thread_lookup,
148 	},
149 	{
150 		.desc = "Share thread mg",
151 		.func = test__thread_mg_share,
152 	},
153 	{
154 		.desc = "Sort output of hist entries",
155 		.func = test__hists_output,
156 	},
157 	{
158 		.desc = "Cumulate child hist entries",
159 		.func = test__hists_cumulate,
160 	},
161 	{
162 		.desc = "Track with sched_switch",
163 		.func = test__switch_tracking,
164 	},
165 	{
166 		.desc = "Filter fds with revents mask in a fdarray",
167 		.func = test__fdarray__filter,
168 	},
169 	{
170 		.desc = "Add fd to a fdarray, making it autogrow",
171 		.func = test__fdarray__add,
172 	},
173 	{
174 		.desc = "kmod_path__parse",
175 		.func = test__kmod_path__parse,
176 	},
177 	{
178 		.desc = "Thread map",
179 		.func = test__thread_map,
180 	},
181 	{
182 		.desc = "LLVM search and compile",
183 		.func = test__llvm,
184 		.subtest = {
185 			.skip_if_fail	= true,
186 			.get_nr		= test__llvm_subtest_get_nr,
187 			.get_desc	= test__llvm_subtest_get_desc,
188 		},
189 	},
190 	{
191 		.desc = "Session topology",
192 		.func = test__session_topology,
193 	},
194 	{
195 		.desc = "BPF filter",
196 		.func = test__bpf,
197 		.subtest = {
198 			.skip_if_fail	= true,
199 			.get_nr		= test__bpf_subtest_get_nr,
200 			.get_desc	= test__bpf_subtest_get_desc,
201 		},
202 	},
203 	{
204 		.desc = "Synthesize thread map",
205 		.func = test__thread_map_synthesize,
206 	},
207 	{
208 		.desc = "Remove thread map",
209 		.func = test__thread_map_remove,
210 	},
211 	{
212 		.desc = "Synthesize cpu map",
213 		.func = test__cpu_map_synthesize,
214 	},
215 	{
216 		.desc = "Synthesize stat config",
217 		.func = test__synthesize_stat_config,
218 	},
219 	{
220 		.desc = "Synthesize stat",
221 		.func = test__synthesize_stat,
222 	},
223 	{
224 		.desc = "Synthesize stat round",
225 		.func = test__synthesize_stat_round,
226 	},
227 	{
228 		.desc = "Synthesize attr update",
229 		.func = test__event_update,
230 	},
231 	{
232 		.desc = "Event times",
233 		.func = test__event_times,
234 	},
235 	{
236 		.desc = "Read backward ring buffer",
237 		.func = test__backward_ring_buffer,
238 	},
239 	{
240 		.desc = "Print cpu map",
241 		.func = test__cpu_map_print,
242 	},
243 	{
244 		.desc = "Probe SDT events",
245 		.func = test__sdt_event,
246 	},
247 	{
248 		.desc = "is_printable_array",
249 		.func = test__is_printable_array,
250 	},
251 	{
252 		.desc = "Print bitmap",
253 		.func = test__bitmap_print,
254 	},
255 	{
256 		.desc = "perf hooks",
257 		.func = test__perf_hooks,
258 	},
259 	{
260 		.desc = "builtin clang support",
261 		.func = test__clang,
262 		.subtest = {
263 			.skip_if_fail	= true,
264 			.get_nr		= test__clang_subtest_get_nr,
265 			.get_desc	= test__clang_subtest_get_desc,
266 		}
267 	},
268 	{
269 		.desc = "unit_number__scnprintf",
270 		.func = test__unit_number__scnprint,
271 	},
272 	{
273 		.func = NULL,
274 	},
275 };
276 
277 static struct test *tests[] = {
278 	generic_tests,
279 	arch_tests,
280 };
281 
282 static bool perf_test__matches(struct test *test, int curr, int argc, const char *argv[])
283 {
284 	int i;
285 
286 	if (argc == 0)
287 		return true;
288 
289 	for (i = 0; i < argc; ++i) {
290 		char *end;
291 		long nr = strtoul(argv[i], &end, 10);
292 
293 		if (*end == '\0') {
294 			if (nr == curr + 1)
295 				return true;
296 			continue;
297 		}
298 
299 		if (strcasestr(test->desc, argv[i]))
300 			return true;
301 	}
302 
303 	return false;
304 }
305 
306 static int run_test(struct test *test, int subtest)
307 {
308 	int status, err = -1, child = dont_fork ? 0 : fork();
309 	char sbuf[STRERR_BUFSIZE];
310 
311 	if (child < 0) {
312 		pr_err("failed to fork test: %s\n",
313 			str_error_r(errno, sbuf, sizeof(sbuf)));
314 		return -1;
315 	}
316 
317 	if (!child) {
318 		if (!dont_fork) {
319 			pr_debug("test child forked, pid %d\n", getpid());
320 
321 			if (verbose <= 0) {
322 				int nullfd = open("/dev/null", O_WRONLY);
323 
324 				if (nullfd >= 0) {
325 					close(STDERR_FILENO);
326 					close(STDOUT_FILENO);
327 
328 					dup2(nullfd, STDOUT_FILENO);
329 					dup2(STDOUT_FILENO, STDERR_FILENO);
330 					close(nullfd);
331 				}
332 			} else {
333 				signal(SIGSEGV, sighandler_dump_stack);
334 				signal(SIGFPE, sighandler_dump_stack);
335 			}
336 		}
337 
338 		err = test->func(test, subtest);
339 		if (!dont_fork)
340 			exit(err);
341 	}
342 
343 	if (!dont_fork) {
344 		wait(&status);
345 
346 		if (WIFEXITED(status)) {
347 			err = (signed char)WEXITSTATUS(status);
348 			pr_debug("test child finished with %d\n", err);
349 		} else if (WIFSIGNALED(status)) {
350 			err = -1;
351 			pr_debug("test child interrupted\n");
352 		}
353 	}
354 
355 	return err;
356 }
357 
358 #define for_each_test(j, t)	 				\
359 	for (j = 0; j < ARRAY_SIZE(tests); j++)	\
360 		for (t = &tests[j][0]; t->func; t++)
361 
362 static int test_and_print(struct test *t, bool force_skip, int subtest)
363 {
364 	int err;
365 
366 	if (!force_skip) {
367 		pr_debug("\n--- start ---\n");
368 		err = run_test(t, subtest);
369 		pr_debug("---- end ----\n");
370 	} else {
371 		pr_debug("\n--- force skipped ---\n");
372 		err = TEST_SKIP;
373 	}
374 
375 	if (!t->subtest.get_nr)
376 		pr_debug("%s:", t->desc);
377 	else
378 		pr_debug("%s subtest %d:", t->desc, subtest);
379 
380 	switch (err) {
381 	case TEST_OK:
382 		pr_info(" Ok\n");
383 		break;
384 	case TEST_SKIP:
385 		color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
386 		break;
387 	case TEST_FAIL:
388 	default:
389 		color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
390 		break;
391 	}
392 
393 	return err;
394 }
395 
396 static const char *shell_test__description(char *description, size_t size,
397 					   const char *path, const char *name)
398 {
399 	FILE *fp;
400 	char filename[PATH_MAX];
401 
402 	path__join(filename, sizeof(filename), path, name);
403 	fp = fopen(filename, "r");
404 	if (!fp)
405 		return NULL;
406 
407 	description = fgets(description, size, fp);
408 	fclose(fp);
409 
410 	return description ? trim(description + 1) : NULL;
411 }
412 
413 #define for_each_shell_test(dir, ent)		\
414 	while ((ent = readdir(dir)) != NULL)	\
415 		if (ent->d_type == DT_REG && ent->d_name[0] != '.')
416 
417 static const char *shell_tests__dir(char *path, size_t size)
418 {
419 	const char *devel_dirs[] = { "./tools/perf/tests", "./tests", };
420         char *exec_path;
421 	unsigned int i;
422 
423 	for (i = 0; i < ARRAY_SIZE(devel_dirs); ++i) {
424 		struct stat st;
425 		if (!lstat(devel_dirs[i], &st)) {
426 			scnprintf(path, size, "%s/shell", devel_dirs[i]);
427 			if (!lstat(devel_dirs[i], &st))
428 				return path;
429 		}
430 	}
431 
432         /* Then installed path. */
433         exec_path = get_argv_exec_path();
434         scnprintf(path, size, "%s/tests/shell", exec_path);
435 	free(exec_path);
436 	return path;
437 }
438 
439 static int shell_tests__max_desc_width(void)
440 {
441 	DIR *dir;
442 	struct dirent *ent;
443 	char path_dir[PATH_MAX];
444 	const char *path = shell_tests__dir(path_dir, sizeof(path_dir));
445 	int width = 0;
446 
447 	if (path == NULL)
448 		return -1;
449 
450 	dir = opendir(path);
451 	if (!dir)
452 		return -1;
453 
454 	for_each_shell_test(dir, ent) {
455 		char bf[256];
456 		const char *desc = shell_test__description(bf, sizeof(bf), path, ent->d_name);
457 
458 		if (desc) {
459 			int len = strlen(desc);
460 
461 			if (width < len)
462 				width = len;
463 		}
464 	}
465 
466 	closedir(dir);
467 	return width;
468 }
469 
470 struct shell_test {
471 	const char *dir;
472 	const char *file;
473 };
474 
475 static int shell_test__run(struct test *test, int subdir __maybe_unused)
476 {
477 	int err;
478 	char script[PATH_MAX];
479 	struct shell_test *st = test->priv;
480 
481 	path__join(script, sizeof(script), st->dir, st->file);
482 
483 	err = system(script);
484 	if (!err)
485 		return TEST_OK;
486 
487 	return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL;
488 }
489 
490 static int run_shell_tests(int argc, const char *argv[], int i, int width)
491 {
492 	DIR *dir;
493 	struct dirent *ent;
494 	char path_dir[PATH_MAX];
495 	struct shell_test st = {
496 		.dir = shell_tests__dir(path_dir, sizeof(path_dir)),
497 	};
498 
499 	if (st.dir == NULL)
500 		return -1;
501 
502 	dir = opendir(st.dir);
503 	if (!dir)
504 		return -1;
505 
506 	for_each_shell_test(dir, ent) {
507 		int curr = i++;
508 		char desc[256];
509 		struct test test = {
510 			.desc = shell_test__description(desc, sizeof(desc), st.dir, ent->d_name),
511 			.func = shell_test__run,
512 			.priv = &st,
513 		};
514 
515 		if (!perf_test__matches(&test, curr, argc, argv))
516 			continue;
517 
518 		st.file = ent->d_name;
519 		pr_info("%2d: %-*s:", i, width, test.desc);
520 		test_and_print(&test, false, -1);
521 	}
522 
523 	closedir(dir);
524 	return 0;
525 }
526 
527 static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
528 {
529 	struct test *t;
530 	unsigned int j;
531 	int i = 0;
532 	int width = shell_tests__max_desc_width();
533 
534 	for_each_test(j, t) {
535 		int len = strlen(t->desc);
536 
537 		if (width < len)
538 			width = len;
539 	}
540 
541 	for_each_test(j, t) {
542 		int curr = i++, err;
543 
544 		if (!perf_test__matches(t, curr, argc, argv))
545 			continue;
546 
547 		if (t->is_supported && !t->is_supported()) {
548 			pr_debug("%2d: %-*s: Disabled\n", i, width, t->desc);
549 			continue;
550 		}
551 
552 		pr_info("%2d: %-*s:", i, width, t->desc);
553 
554 		if (intlist__find(skiplist, i)) {
555 			color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n");
556 			continue;
557 		}
558 
559 		if (!t->subtest.get_nr) {
560 			test_and_print(t, false, -1);
561 		} else {
562 			int subn = t->subtest.get_nr();
563 			/*
564 			 * minus 2 to align with normal testcases.
565 			 * For subtest we print additional '.x' in number.
566 			 * for example:
567 			 *
568 			 * 35: Test LLVM searching and compiling                        :
569 			 * 35.1: Basic BPF llvm compiling test                          : Ok
570 			 */
571 			int subw = width > 2 ? width - 2 : width;
572 			bool skip = false;
573 			int subi;
574 
575 			if (subn <= 0) {
576 				color_fprintf(stderr, PERF_COLOR_YELLOW,
577 					      " Skip (not compiled in)\n");
578 				continue;
579 			}
580 			pr_info("\n");
581 
582 			for (subi = 0; subi < subn; subi++) {
583 				int len = strlen(t->subtest.get_desc(subi));
584 
585 				if (subw < len)
586 					subw = len;
587 			}
588 
589 			for (subi = 0; subi < subn; subi++) {
590 				pr_info("%2d.%1d: %-*s:", i, subi + 1, subw,
591 					t->subtest.get_desc(subi));
592 				err = test_and_print(t, skip, subi);
593 				if (err != TEST_OK && t->subtest.skip_if_fail)
594 					skip = true;
595 			}
596 		}
597 	}
598 
599 	return run_shell_tests(argc, argv, i, width);
600 }
601 
602 static int perf_test__list_shell(int argc, const char **argv, int i)
603 {
604 	DIR *dir;
605 	struct dirent *ent;
606 	char path_dir[PATH_MAX];
607 	const char *path = shell_tests__dir(path_dir, sizeof(path_dir));
608 
609 	if (path == NULL)
610 		return -1;
611 
612 	dir = opendir(path);
613 	if (!dir)
614 		return -1;
615 
616 	for_each_shell_test(dir, ent) {
617 		int curr = i++;
618 		char bf[256];
619 		struct test t = {
620 			.desc = shell_test__description(bf, sizeof(bf), path, ent->d_name),
621 		};
622 
623 		if (!perf_test__matches(&t, curr, argc, argv))
624 			continue;
625 
626 		pr_info("%2d: %s\n", i, t.desc);
627 	}
628 
629 	closedir(dir);
630 	return 0;
631 }
632 
633 static int perf_test__list(int argc, const char **argv)
634 {
635 	unsigned int j;
636 	struct test *t;
637 	int i = 0;
638 
639 	for_each_test(j, t) {
640 		int curr = i++;
641 
642 		if (!perf_test__matches(t, curr, argc, argv) ||
643 		    (t->is_supported && !t->is_supported()))
644 			continue;
645 
646 		pr_info("%2d: %s\n", i, t->desc);
647 	}
648 
649 	perf_test__list_shell(argc, argv, i);
650 
651 	return 0;
652 }
653 
654 int cmd_test(int argc, const char **argv)
655 {
656 	const char *test_usage[] = {
657 	"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
658 	NULL,
659 	};
660 	const char *skip = NULL;
661 	const struct option test_options[] = {
662 	OPT_STRING('s', "skip", &skip, "tests", "tests to skip"),
663 	OPT_INCR('v', "verbose", &verbose,
664 		    "be more verbose (show symbol address, etc)"),
665 	OPT_BOOLEAN('F', "dont-fork", &dont_fork,
666 		    "Do not fork for testcase"),
667 	OPT_END()
668 	};
669 	const char * const test_subcommands[] = { "list", NULL };
670 	struct intlist *skiplist = NULL;
671         int ret = hists__init();
672 
673         if (ret < 0)
674                 return ret;
675 
676 	argc = parse_options_subcommand(argc, argv, test_options, test_subcommands, test_usage, 0);
677 	if (argc >= 1 && !strcmp(argv[0], "list"))
678 		return perf_test__list(argc - 1, argv + 1);
679 
680 	symbol_conf.priv_size = sizeof(int);
681 	symbol_conf.sort_by_name = true;
682 	symbol_conf.try_vmlinux_path = true;
683 
684 	if (symbol__init(NULL) < 0)
685 		return -1;
686 
687 	if (skip != NULL)
688 		skiplist = intlist__new(skip);
689 
690 	return __cmd_test(argc, argv, skiplist);
691 }
692