/* * builtin-test.c * * Builtin regression testing command: ever growing number of sanity tests */ #include "builtin.h" #include "util/cache.h" #include "util/color.h" #include "util/debug.h" #include "util/debugfs.h" #include "util/evlist.h" #include "util/machine.h" #include "util/parse-options.h" #include "util/parse-events.h" #include "util/symbol.h" #include "util/thread_map.h" #include "util/pmu.h" #include "event-parse.h" #include "../../include/linux/hw_breakpoint.h" #include #include "util/cpumap.h" #include "util/evsel.h" #include #include "tests.h" #include static int test__perf_pmu(void) { return perf_pmu__test(); } static int test__syscall_open_tp_fields(void) { struct perf_record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, .no_delay = true, .freq = 1, .mmap_pages = 256, .raw_samples = true, }; const char *filename = "/etc/passwd"; int flags = O_RDONLY | O_DIRECTORY; struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); struct perf_evsel *evsel; int err = -1, i, nr_events = 0, nr_polls = 0; if (evlist == NULL) { pr_debug("%s: perf_evlist__new\n", __func__); goto out; } evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); if (evsel == NULL) { pr_debug("%s: perf_evsel__newtp\n", __func__); goto out_delete_evlist; } perf_evlist__add(evlist, evsel); err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("%s: perf_evlist__create_maps\n", __func__); goto out_delete_evlist; } perf_evsel__config(evsel, &opts, evsel); evlist->threads->map[0] = getpid(); err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror(errno)); goto out_delete_evlist; } err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); goto out_delete_evlist; } perf_evlist__enable(evlist); /* * Generate the event: */ open(filename, flags); while (1) { int before = nr_events; for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; int tp_flags; struct perf_sample sample; ++nr_events; if (type != PERF_RECORD_SAMPLE) continue; err = perf_evsel__parse_sample(evsel, event, &sample); if (err) { pr_err("Can't parse sample, err = %d\n", err); goto out_munmap; } tp_flags = perf_evsel__intval(evsel, &sample, "flags"); if (flags != tp_flags) { pr_debug("%s: Expected flags=%#x, got %#x\n", __func__, flags, tp_flags); goto out_munmap; } goto out_ok; } } if (nr_events == before) poll(evlist->pollfd, evlist->nr_fds, 10); if (++nr_polls > 5) { pr_debug("%s: no events!\n", __func__); goto out_munmap; } } out_ok: err = 0; out_munmap: perf_evlist__munmap(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: return err; } static struct test { const char *desc; int (*func)(void); } tests[] = { { .desc = "vmlinux symtab matches kallsyms", .func = test__vmlinux_matches_kallsyms, }, { .desc = "detect open syscall event", .func = test__open_syscall_event, }, { .desc = "detect open syscall event on all cpus", .func = test__open_syscall_event_on_all_cpus, }, { .desc = "read samples using the mmap interface", .func = test__basic_mmap, }, { .desc = "parse events tests", .func = parse_events__test, }, #if defined(__x86_64__) || defined(__i386__) { .desc = "x86 rdpmc test", .func = test__rdpmc, }, #endif { .desc = "Validate PERF_RECORD_* events & perf_sample fields", .func = test__PERF_RECORD, }, { .desc = "Test perf pmu format parsing", .func = test__perf_pmu, }, { .desc = "Test dso data interface", .func = dso__test_data, }, { .desc = "roundtrip evsel->name check", .func = test__perf_evsel__roundtrip_name_test, }, { .desc = "Check parsing of sched tracepoints fields", .func = test__perf_evsel__tp_sched_test, }, { .desc = "Generate and check syscalls:sys_enter_open event fields", .func = test__syscall_open_tp_fields, }, { .desc = "struct perf_event_attr setup", .func = test_attr__run, }, { .func = NULL, }, }; static bool perf_test__matches(int curr, int argc, const char *argv[]) { int i; if (argc == 0) return true; for (i = 0; i < argc; ++i) { char *end; long nr = strtoul(argv[i], &end, 10); if (*end == '\0') { if (nr == curr + 1) return true; continue; } if (strstr(tests[curr].desc, argv[i])) return true; } return false; } static int __cmd_test(int argc, const char *argv[]) { int i = 0; int width = 0; while (tests[i].func) { int len = strlen(tests[i].desc); if (width < len) width = len; ++i; } i = 0; while (tests[i].func) { int curr = i++, err; if (!perf_test__matches(curr, argc, argv)) continue; pr_info("%2d: %-*s:", i, width, tests[curr].desc); pr_debug("\n--- start ---\n"); err = tests[curr].func(); pr_debug("---- end ----\n%s:", tests[curr].desc); if (err) color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); else pr_info(" Ok\n"); } return 0; } static int perf_test__list(int argc, const char **argv) { int i = 0; while (tests[i].func) { int curr = i++; if (argc > 1 && !strstr(tests[curr].desc, argv[1])) continue; pr_info("%2d: %s\n", i, tests[curr].desc); } return 0; } int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const test_usage[] = { "perf test [] [{list |[|]}]", NULL, }; const struct option test_options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_END() }; argc = parse_options(argc, argv, test_options, test_usage, 0); if (argc >= 1 && !strcmp(argv[0], "list")) return perf_test__list(argc, argv); symbol_conf.priv_size = sizeof(int); symbol_conf.sort_by_name = true; symbol_conf.try_vmlinux_path = true; if (symbol__init() < 0) return -1; return __cmd_test(argc, argv); }