diff options
Diffstat (limited to 'tools/perf/tests')
171 files changed, 6077 insertions, 1702 deletions
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 53ba9c3e20e0..4bf8d3f5eae7 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -1,82 +1,82 @@ # SPDX-License-Identifier: GPL-2.0 -perf-y += builtin-test.o -perf-y += builtin-test-list.o -perf-y += parse-events.o -perf-y += dso-data.o -perf-y += attr.o -perf-y += vmlinux-kallsyms.o -perf-$(CONFIG_LIBTRACEEVENT) += openat-syscall.o -perf-$(CONFIG_LIBTRACEEVENT) += openat-syscall-all-cpus.o -perf-$(CONFIG_LIBTRACEEVENT) += openat-syscall-tp-fields.o -perf-$(CONFIG_LIBTRACEEVENT) += mmap-basic.o -perf-y += perf-record.o -perf-y += evsel-roundtrip-name.o -perf-$(CONFIG_LIBTRACEEVENT) += evsel-tp-sched.o -perf-y += fdarray.o -perf-y += pmu.o -perf-y += pmu-events.o -perf-y += hists_common.o -perf-y += hists_link.o -perf-y += hists_filter.o -perf-y += hists_output.o -perf-y += hists_cumulate.o -perf-y += python-use.o -perf-y += bp_signal.o -perf-y += bp_signal_overflow.o -perf-y += bp_account.o -perf-y += wp.o -perf-y += task-exit.o -perf-y += sw-clock.o -perf-y += mmap-thread-lookup.o -perf-y += thread-maps-share.o -perf-$(CONFIG_LIBTRACEEVENT) += switch-tracking.o -perf-y += keep-tracking.o -perf-y += code-reading.o -perf-y += sample-parsing.o -perf-y += parse-no-sample-id-all.o -perf-y += kmod-path.o -perf-y += thread-map.o -perf-y += topology.o -perf-y += mem.o -perf-y += cpumap.o -perf-y += stat.o -perf-y += event_update.o -perf-y += event-times.o -perf-y += expr.o -perf-y += backward-ring-buffer.o -perf-y += sdt.o -perf-y += is_printable_array.o -perf-y += bitmap.o -perf-y += perf-hooks.o -perf-y += unit_number__scnprintf.o -perf-y += mem2node.o -perf-y += maps.o -perf-y += time-utils-test.o -perf-y += genelf.o -perf-y += api-io.o -perf-y += demangle-java-test.o -perf-y += demangle-ocaml-test.o -perf-y += pfm.o -perf-y += parse-metric.o -perf-y += pe-file-parsing.o -perf-y += expand-cgroup.o -perf-y += perf-time-to-tsc.o -perf-y += dlfilter-test.o -perf-y += sigtrap.o -perf-y += event_groups.o -perf-y += symbols.o -perf-y += util.o +perf-test-y += builtin-test.o +perf-test-y += tests-scripts.o +perf-test-y += parse-events.o +perf-test-y += dso-data.o +perf-test-y += vmlinux-kallsyms.o +perf-test-y += openat-syscall.o +perf-test-y += openat-syscall-all-cpus.o +perf-test-$(CONFIG_LIBTRACEEVENT) += openat-syscall-tp-fields.o +perf-test-y += mmap-basic.o +perf-test-y += perf-record.o +perf-test-y += evsel-roundtrip-name.o +perf-test-$(CONFIG_LIBTRACEEVENT) += evsel-tp-sched.o +perf-test-y += fdarray.o +perf-test-y += pmu.o +perf-test-y += pmu-events.o +perf-test-y += hists_common.o +perf-test-y += hists_link.o +perf-test-y += hists_filter.o +perf-test-y += hists_output.o +perf-test-y += hists_cumulate.o +perf-test-y += python-use.o +perf-test-y += bp_signal.o +perf-test-y += bp_signal_overflow.o +perf-test-y += bp_account.o +perf-test-y += wp.o +perf-test-y += task-exit.o +perf-test-y += sw-clock.o +perf-test-y += mmap-thread-lookup.o +perf-test-y += thread-maps-share.o +perf-test-$(CONFIG_LIBTRACEEVENT) += switch-tracking.o +perf-test-y += keep-tracking.o +perf-test-y += code-reading.o +perf-test-y += sample-parsing.o +perf-test-y += parse-no-sample-id-all.o +perf-test-y += kmod-path.o +perf-test-y += thread-map.o +perf-test-y += topology.o +perf-test-y += mem.o +perf-test-y += cpumap.o +perf-test-y += stat.o +perf-test-y += event_update.o +perf-test-y += event-times.o +perf-test-y += expr.o +perf-test-y += backward-ring-buffer.o +perf-test-y += sdt.o +perf-test-y += is_printable_array.o +perf-test-y += bitmap.o +perf-test-y += perf-hooks.o +perf-test-y += unit_number__scnprintf.o +perf-test-y += mem2node.o +perf-test-y += maps.o +perf-test-y += time-utils-test.o +perf-test-y += genelf.o +perf-test-y += api-io.o +perf-test-y += demangle-java-test.o +perf-test-y += demangle-ocaml-test.o +perf-test-y += pfm.o +perf-test-y += parse-metric.o +perf-test-y += pe-file-parsing.o +perf-test-y += expand-cgroup.o +perf-test-y += perf-time-to-tsc.o +perf-test-y += dlfilter-test.o +perf-test-y += sigtrap.o +perf-test-y += event_groups.o +perf-test-y += symbols.o +perf-test-y += util.o +perf-test-y += hwmon_pmu.o +perf-test-y += tool_pmu.o ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc)) -perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o +perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif -CFLAGS_attr.o += -DBINDIR="BUILD_STR($(bindir_SQ))" -DPYTHON="BUILD_STR($(PYTHON_WORD))" CFLAGS_python-use.o += -DPYTHONPATH="BUILD_STR($(OUTPUT)python)" -DPYTHON="BUILD_STR($(PYTHON_WORD))" CFLAGS_dwarf-unwind.o += -fno-optimize-sibling-calls -perf-y += workloads/ +perf-test-y += workloads/ ifdef SHELLCHECK SHELL_TESTS := $(shell find tests/shell -executable -type f -name '*.sh') @@ -90,4 +90,4 @@ $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) -perf-y += $(TEST_LOGS) +perf-test-y += $(TEST_LOGS) diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c deleted file mode 100644 index 97e1bdd6ec0e..000000000000 --- a/tools/perf/tests/attr.c +++ /dev/null @@ -1,218 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * The struct perf_event_attr test support. - * - * This test is embedded inside into perf directly and is governed - * by the PERF_TEST_ATTR environment variable and hook inside - * sys_perf_event_open function. - * - * The general idea is to store 'struct perf_event_attr' details for - * each event created within single perf command. Each event details - * are stored into separate text file. Once perf command is finished - * these files can be checked for values we expect for command. - * - * Besides 'struct perf_event_attr' values we also store 'fd' and - * 'group_fd' values to allow checking for groups created. - * - * This all is triggered by setting PERF_TEST_ATTR environment variable. - * It must contain name of existing directory with access and write - * permissions. All the event text files are stored there. - */ - -#include <debug.h> -#include <errno.h> -#include <inttypes.h> -#include <stdlib.h> -#include <stdio.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <subcmd/exec-cmd.h> -#include "event.h" -#include "util.h" -#include "tests.h" -#include "pmus.h" - -#define ENV "PERF_TEST_ATTR" - -static char *dir; -static bool ready; - -void test_attr__init(void) -{ - dir = getenv(ENV); - test_attr__enabled = (dir != NULL); -} - -#define BUFSIZE 1024 - -#define __WRITE_ASS(str, fmt, data) \ -do { \ - char buf[BUFSIZE]; \ - size_t size; \ - \ - size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \ - if (1 != fwrite(buf, size, 1, file)) { \ - perror("test attr - failed to write event file"); \ - fclose(file); \ - return -1; \ - } \ - \ -} while (0) - -#define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field) - -static int store_event(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, - int fd, int group_fd, unsigned long flags) -{ - FILE *file; - char path[PATH_MAX]; - - if (!ready) - return 0; - - snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, - attr->type, attr->config, fd); - - file = fopen(path, "w+"); - if (!file) { - perror("test attr - failed to open event file"); - return -1; - } - - if (fprintf(file, "[event-%d-%llu-%d]\n", - attr->type, attr->config, fd) < 0) { - perror("test attr - failed to write event file"); - fclose(file); - return -1; - } - - /* syscall arguments */ - __WRITE_ASS(fd, "d", fd); - __WRITE_ASS(group_fd, "d", group_fd); - __WRITE_ASS(cpu, "d", cpu.cpu); - __WRITE_ASS(pid, "d", pid); - __WRITE_ASS(flags, "lu", flags); - - /* struct perf_event_attr */ - WRITE_ASS(type, PRIu32); - WRITE_ASS(size, PRIu32); - WRITE_ASS(config, "llu"); - WRITE_ASS(sample_period, "llu"); - WRITE_ASS(sample_type, "llu"); - WRITE_ASS(read_format, "llu"); - WRITE_ASS(disabled, "d"); - WRITE_ASS(inherit, "d"); - WRITE_ASS(pinned, "d"); - WRITE_ASS(exclusive, "d"); - WRITE_ASS(exclude_user, "d"); - WRITE_ASS(exclude_kernel, "d"); - WRITE_ASS(exclude_hv, "d"); - WRITE_ASS(exclude_idle, "d"); - WRITE_ASS(mmap, "d"); - WRITE_ASS(comm, "d"); - WRITE_ASS(freq, "d"); - WRITE_ASS(inherit_stat, "d"); - WRITE_ASS(enable_on_exec, "d"); - WRITE_ASS(task, "d"); - WRITE_ASS(watermark, "d"); - WRITE_ASS(precise_ip, "d"); - WRITE_ASS(mmap_data, "d"); - WRITE_ASS(sample_id_all, "d"); - WRITE_ASS(exclude_host, "d"); - WRITE_ASS(exclude_guest, "d"); - WRITE_ASS(exclude_callchain_kernel, "d"); - WRITE_ASS(exclude_callchain_user, "d"); - WRITE_ASS(mmap2, "d"); - WRITE_ASS(comm_exec, "d"); - WRITE_ASS(context_switch, "d"); - WRITE_ASS(write_backward, "d"); - WRITE_ASS(namespaces, "d"); - WRITE_ASS(use_clockid, "d"); - WRITE_ASS(wakeup_events, PRIu32); - WRITE_ASS(bp_type, PRIu32); - WRITE_ASS(config1, "llu"); - WRITE_ASS(config2, "llu"); - WRITE_ASS(branch_sample_type, "llu"); - WRITE_ASS(sample_regs_user, "llu"); - WRITE_ASS(sample_stack_user, PRIu32); - - fclose(file); - return 0; -} - -void test_attr__open(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, - int fd, int group_fd, unsigned long flags) -{ - int errno_saved = errno; - - if ((fd != -1) && store_event(attr, pid, cpu, fd, group_fd, flags)) { - pr_err("test attr FAILED"); - exit(128); - } - - errno = errno_saved; -} - -void test_attr__ready(void) -{ - if (unlikely(test_attr__enabled) && !ready) - ready = true; -} - -static int run_dir(const char *d, const char *perf) -{ - char v[] = "-vvvvv"; - int vcnt = min(verbose, (int) sizeof(v) - 1); - char cmd[3*PATH_MAX]; - - if (verbose > 0) - vcnt++; - - scnprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", - d, d, perf, vcnt, v); - - return system(cmd) ? TEST_FAIL : TEST_OK; -} - -static int test__attr(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - struct stat st; - char path_perf[PATH_MAX]; - char path_dir[PATH_MAX]; - char *exec_path; - - if (perf_pmus__num_core_pmus() > 1) { - /* - * TODO: Attribute tests hard code the PMU type. If there are >1 - * core PMU then each PMU will have a different type which - * requires additional support. - */ - pr_debug("Skip test on hybrid systems"); - return TEST_SKIP; - } - - /* First try development tree tests. */ - if (!lstat("./tests", &st)) - return run_dir("./tests", "./perf"); - - exec_path = get_argv_exec_path(); - if (exec_path == NULL) - return -1; - - /* Then installed path. */ - snprintf(path_dir, PATH_MAX, "%s/tests", exec_path); - snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR); - free(exec_path); - - if (!lstat(path_dir, &st) && - !lstat(path_perf, &st)) - return run_dir(path_dir, path_perf); - - return TEST_SKIP; -} - -DEFINE_SUITE("Setup struct perf_event_attr", attr); diff --git a/tools/perf/tests/bitmap.c b/tools/perf/tests/bitmap.c index 0173f5402a35..98956e0e0765 100644 --- a/tools/perf/tests/bitmap.c +++ b/tools/perf/tests/bitmap.c @@ -11,18 +11,19 @@ static unsigned long *get_bitmap(const char *str, int nbits) { struct perf_cpu_map *map = perf_cpu_map__new(str); - unsigned long *bm = NULL; - int i; + unsigned long *bm; bm = bitmap_zalloc(nbits); if (map && bm) { - for (i = 0; i < perf_cpu_map__nr(map); i++) - __set_bit(perf_cpu_map__cpu(map, i).cpu, bm); + int i; + struct perf_cpu cpu; + + perf_cpu_map__for_each_cpu(cpu, i, map) + __set_bit(cpu.cpu, bm); } - if (map) - perf_cpu_map__put(map); + perf_cpu_map__put(map); return bm; } diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 6f921db33cf9..4cb7d486b5c1 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -16,6 +16,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "../perf-sys.h" #include "cloexec.h" @@ -50,7 +51,7 @@ static int __event(bool is_x, void *addr, struct perf_event_attr *attr) attr->config = 0; attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; attr->bp_addr = (unsigned long) addr; - attr->bp_len = sizeof(long); + attr->bp_len = is_x ? default_breakpoint_len() : sizeof(long); attr->sample_period = 1; attr->sample_type = PERF_SAMPLE_IP; @@ -92,6 +93,7 @@ static int bp_accounting(int wp_cnt, int share) attr_mod = attr; attr_mod.bp_type = HW_BREAKPOINT_X; attr_mod.bp_addr = (unsigned long) test_function; + attr_mod.bp_len = default_breakpoint_len(); ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod); TEST_ASSERT_VAL("failed to modify wp\n", ret == 0); diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index 1f2908f02389..3faeb5b6fe0b 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -26,6 +26,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "perf-sys.h" #include "cloexec.h" @@ -111,7 +112,7 @@ static int __event(bool is_x, void *addr, int sig) pe.config = 0; pe.bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; pe.bp_addr = (unsigned long) addr; - pe.bp_len = sizeof(long); + pe.bp_len = is_x ? default_breakpoint_len() : sizeof(long); pe.sample_period = 1; pe.sample_type = PERF_SAMPLE_IP; diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c index 4e897c2cf26b..ee560e156be6 100644 --- a/tools/perf/tests/bp_signal_overflow.c +++ b/tools/perf/tests/bp_signal_overflow.c @@ -25,6 +25,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "../perf-sys.h" #include "cloexec.h" @@ -88,7 +89,7 @@ static int test__bp_signal_overflow(struct test_suite *test __maybe_unused, int pe.config = 0; pe.bp_type = HW_BREAKPOINT_X; pe.bp_addr = (unsigned long) test_function; - pe.bp_len = sizeof(long); + pe.bp_len = default_breakpoint_len(); pe.sample_period = THRESHOLD; pe.sample_type = PERF_SAMPLE_IP; diff --git a/tools/perf/tests/builtin-test-list.c b/tools/perf/tests/builtin-test-list.c deleted file mode 100644 index a65b9e547d82..000000000000 --- a/tools/perf/tests/builtin-test-list.c +++ /dev/null @@ -1,207 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/zalloc.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> -#include <unistd.h> -#include <subcmd/exec-cmd.h> -#include <subcmd/parse-options.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include "builtin.h" -#include "builtin-test-list.h" -#include "color.h" -#include "debug.h" -#include "hist.h" -#include "intlist.h" -#include "string2.h" -#include "symbol.h" -#include "tests.h" -#include "util/rlimit.h" - - -/* - * As this is a singleton built once for the run of the process, there is - * no value in trying to free it and just let it stay around until process - * exits when it's cleaned up. - */ -static size_t files_num = 0; -static struct script_file *files = NULL; -static int files_max_width = 0; - -static const char *shell_tests__dir(char *path, size_t size) -{ - const char *devel_dirs[] = { "./tools/perf/tests", "./tests", }; - char *exec_path; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(devel_dirs); ++i) { - struct stat st; - - if (!lstat(devel_dirs[i], &st)) { - scnprintf(path, size, "%s/shell", devel_dirs[i]); - if (!lstat(devel_dirs[i], &st)) - return path; - } - } - - /* Then installed path. */ - exec_path = get_argv_exec_path(); - scnprintf(path, size, "%s/tests/shell", exec_path); - free(exec_path); - return path; -} - -static const char *shell_test__description(char *description, size_t size, - const char *path, const char *name) -{ - FILE *fp; - char filename[PATH_MAX]; - int ch; - - path__join(filename, sizeof(filename), path, name); - fp = fopen(filename, "r"); - if (!fp) - return NULL; - - /* Skip first line - should be #!/bin/sh Shebang */ - do { - ch = fgetc(fp); - } while (ch != EOF && ch != '\n'); - - description = fgets(description, size, fp); - fclose(fp); - - /* Assume first char on line is omment everything after that desc */ - return description ? strim(description + 1) : NULL; -} - -/* Is this full file path a shell script */ -static bool is_shell_script(const char *path) -{ - const char *ext; - - ext = strrchr(path, '.'); - if (!ext) - return false; - if (!strcmp(ext, ".sh")) { /* Has .sh extension */ - if (access(path, R_OK | X_OK) == 0) /* Is executable */ - return true; - } - return false; -} - -/* Is this file in this dir a shell script (for test purposes) */ -static bool is_test_script(const char *path, const char *name) -{ - char filename[PATH_MAX]; - - path__join(filename, sizeof(filename), path, name); - if (!is_shell_script(filename)) return false; - return true; -} - -/* Duplicate a string and fall over and die if we run out of memory */ -static char *strdup_check(const char *str) -{ - char *newstr; - - newstr = strdup(str); - if (!newstr) { - pr_err("Out of memory while duplicating test script string\n"); - abort(); - } - return newstr; -} - -static void append_script(const char *dir, const char *file, const char *desc) -{ - struct script_file *files_tmp; - size_t files_num_tmp; - int width; - - files_num_tmp = files_num + 1; - if (files_num_tmp >= SIZE_MAX) { - pr_err("Too many script files\n"); - abort(); - } - /* Realloc is good enough, though we could realloc by chunks, not that - * anyone will ever measure performance here */ - files_tmp = realloc(files, - (files_num_tmp + 1) * sizeof(struct script_file)); - if (files_tmp == NULL) { - pr_err("Out of memory while building test list\n"); - abort(); - } - /* Add file to end and NULL terminate the struct array */ - files = files_tmp; - files_num = files_num_tmp; - files[files_num - 1].dir = strdup_check(dir); - files[files_num - 1].file = strdup_check(file); - files[files_num - 1].desc = strdup_check(desc); - files[files_num].dir = NULL; - files[files_num].file = NULL; - files[files_num].desc = NULL; - - width = strlen(desc); /* Track max width of desc */ - if (width > files_max_width) - files_max_width = width; -} - -static void append_scripts_in_dir(const char *path) -{ - struct dirent **entlist; - struct dirent *ent; - int n_dirs, i; - char filename[PATH_MAX]; - - /* List files, sorted by alpha */ - n_dirs = scandir(path, &entlist, NULL, alphasort); - if (n_dirs == -1) - return; - for (i = 0; i < n_dirs && (ent = entlist[i]); i++) { - if (ent->d_name[0] == '.') - continue; /* Skip hidden files */ - if (is_test_script(path, ent->d_name)) { /* It's a test */ - char bf[256]; - const char *desc = shell_test__description - (bf, sizeof(bf), path, ent->d_name); - - if (desc) /* It has a desc line - valid script */ - append_script(path, ent->d_name, desc); - } else if (is_directory(path, ent)) { /* Scan the subdir */ - path__join(filename, sizeof(filename), - path, ent->d_name); - append_scripts_in_dir(filename); - } - } - for (i = 0; i < n_dirs; i++) /* Clean up */ - zfree(&entlist[i]); - free(entlist); -} - -const struct script_file *list_script_files(void) -{ - char path_dir[PATH_MAX]; - const char *path; - - if (files) - return files; /* Singleton - we already know our list */ - - path = shell_tests__dir(path_dir, sizeof(path_dir)); /* Walk dir */ - append_scripts_in_dir(path); - - return files; -} - -int list_script_max_width(void) -{ - list_script_files(); /* Ensure we have scanned all scripts */ - return files_max_width; -} diff --git a/tools/perf/tests/builtin-test-list.h b/tools/perf/tests/builtin-test-list.h deleted file mode 100644 index eb81f3aa6683..000000000000 --- a/tools/perf/tests/builtin-test-list.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -struct script_file { - char *dir; - char *file; - char *desc; -}; - -/* List available script tests to run - singleton - never freed */ -const struct script_file *list_script_files(void); -/* Get maximum width of description string */ -int list_script_max_width(void); diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 4a5973f9bb9b..14d30a5053be 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -6,7 +6,9 @@ */ #include <fcntl.h> #include <errno.h> +#include <poll.h> #include <unistd.h> +#include <setjmp.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> @@ -21,17 +23,27 @@ #include "debug.h" #include "color.h" #include <subcmd/parse-options.h> +#include <subcmd/run-command.h> #include "string2.h" #include "symbol.h" #include "util/rlimit.h" +#include "util/strbuf.h" #include <linux/kernel.h> #include <linux/string.h> #include <subcmd/exec-cmd.h> #include <linux/zalloc.h> -#include "builtin-test-list.h" +#include "tests-scripts.h" +/* + * Command line option to not fork the test running in the same process and + * making them easier to debug. + */ static bool dont_fork; +/* Fork the tests in parallel and wait for their completion. */ +static bool sequential; +/* Number of times each test is run. */ +static unsigned int runs_per_test = 1; const char *dso_to_test; const char *test_objdump_path = "objdump"; @@ -50,24 +62,23 @@ static struct test_suite *arch_tests[] = { static struct test_suite *generic_tests[] = { &suite__vmlinux_matches_kallsyms, -#ifdef HAVE_LIBTRACEEVENT &suite__openat_syscall_event, &suite__openat_syscall_event_on_all_cpus, &suite__basic_mmap, -#endif &suite__mem, &suite__parse_events, &suite__expr, &suite__PERF_RECORD, &suite__pmu, &suite__pmu_events, + &suite__hwmon_pmu, + &suite__tool_pmu, &suite__dso_data, &suite__perf_evsel__roundtrip_name_test, #ifdef HAVE_LIBTRACEEVENT &suite__perf_evsel__tp_sched_test, &suite__syscall_openat_tp_fields, #endif - &suite__attr, &suite__hists_link, &suite__python_use, &suite__bp_signal, @@ -127,11 +138,6 @@ static struct test_suite *generic_tests[] = { NULL, }; -static struct test_suite **tests[] = { - generic_tests, - arch_tests, -}; - static struct test_workload *workloads[] = { &workload__noploop, &workload__thloop, @@ -139,52 +145,57 @@ static struct test_workload *workloads[] = { &workload__sqrtloop, &workload__brstack, &workload__datasym, + &workload__landlock, }; -static int num_subtests(const struct test_suite *t) +#define workloads__for_each(workload) \ + for (unsigned i = 0; i < ARRAY_SIZE(workloads) && ({ workload = workloads[i]; 1; }); i++) + +#define test_suite__for_each_test_case(suite, idx) \ + for (idx = 0; (suite)->test_cases && (suite)->test_cases[idx].name != NULL; idx++) + +static int test_suite__num_test_cases(const struct test_suite *t) { int num; - if (!t->test_cases) - return 0; - - num = 0; - while (t->test_cases[num].name) - num++; + test_suite__for_each_test_case(t, num); return num; } -static bool has_subtests(const struct test_suite *t) -{ - return num_subtests(t) > 1; -} - -static const char *skip_reason(const struct test_suite *t, int subtest) +static const char *skip_reason(const struct test_suite *t, int test_case) { if (!t->test_cases) return NULL; - return t->test_cases[subtest >= 0 ? subtest : 0].skip_reason; + return t->test_cases[test_case >= 0 ? test_case : 0].skip_reason; } -static const char *test_description(const struct test_suite *t, int subtest) +static const char *test_description(const struct test_suite *t, int test_case) { - if (t->test_cases && subtest >= 0) - return t->test_cases[subtest].desc; + if (t->test_cases && test_case >= 0) + return t->test_cases[test_case].desc; return t->desc; } -static test_fnptr test_function(const struct test_suite *t, int subtest) +static test_fnptr test_function(const struct test_suite *t, int test_case) { - if (subtest <= 0) + if (test_case <= 0) return t->test_cases[0].run_case; - return t->test_cases[subtest].run_case; + return t->test_cases[test_case].run_case; +} + +static bool test_exclusive(const struct test_suite *t, int test_case) +{ + if (test_case <= 0) + return t->test_cases[0].exclusive; + + return t->test_cases[test_case].exclusive; } -static bool perf_test__matches(const char *desc, int curr, int argc, const char *argv[]) +static bool perf_test__matches(const char *desc, int suite_num, int argc, const char *argv[]) { int i; @@ -196,7 +207,7 @@ static bool perf_test__matches(const char *desc, int curr, int argc, const char long nr = strtoul(argv[i], &end, 10); if (*end == '\0') { - if (nr == curr + 1) + if (nr == suite_num + 1) return true; continue; } @@ -208,81 +219,72 @@ static bool perf_test__matches(const char *desc, int curr, int argc, const char return false; } -static int run_test(struct test_suite *test, int subtest) -{ - int status, err = -1, child = dont_fork ? 0 : fork(); - char sbuf[STRERR_BUFSIZE]; - - if (child < 0) { - pr_err("failed to fork test: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); - return -1; - } - - if (!child) { - if (!dont_fork) { - pr_debug("test child forked, pid %d\n", getpid()); +struct child_test { + struct child_process process; + struct test_suite *test; + int suite_num; + int test_case_num; +}; - if (verbose <= 0) { - int nullfd = open("/dev/null", O_WRONLY); +static jmp_buf run_test_jmp_buf; - if (nullfd >= 0) { - close(STDERR_FILENO); - close(STDOUT_FILENO); +static void child_test_sig_handler(int sig) +{ + siglongjmp(run_test_jmp_buf, sig); +} - dup2(nullfd, STDOUT_FILENO); - dup2(STDOUT_FILENO, STDERR_FILENO); - close(nullfd); - } - } else { - signal(SIGSEGV, sighandler_dump_stack); - signal(SIGFPE, sighandler_dump_stack); - } - } +static int run_test_child(struct child_process *process) +{ + const int signals[] = { + SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM, + }; + struct child_test *child = container_of(process, struct child_test, process); + int err; - err = test_function(test, subtest)(test, subtest); - if (!dont_fork) - exit(err); + err = sigsetjmp(run_test_jmp_buf, 1); + if (err) { + fprintf(stderr, "\n---- unexpected signal (%d) ----\n", err); + err = err > 0 ? -err : -1; + goto err_out; } - if (!dont_fork) { - wait(&status); + for (size_t i = 0; i < ARRAY_SIZE(signals); i++) + signal(signals[i], child_test_sig_handler); - if (WIFEXITED(status)) { - err = (signed char)WEXITSTATUS(status); - pr_debug("test child finished with %d\n", err); - } else if (WIFSIGNALED(status)) { - err = -1; - pr_debug("test child interrupted\n"); - } - } + pr_debug("--- start ---\n"); + pr_debug("test child forked, pid %d\n", getpid()); + err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num); + pr_debug("---- end(%d) ----\n", err); - return err; +err_out: + fflush(NULL); + for (size_t i = 0; i < ARRAY_SIZE(signals); i++) + signal(signals[i], SIG_DFL); + return -err; } -#define for_each_test(j, k, t) \ - for (j = 0, k = 0; j < ARRAY_SIZE(tests); j++, k = 0) \ - while ((t = tests[j][k++]) != NULL) +#define TEST_RUNNING -3 -static int test_and_print(struct test_suite *t, int subtest) +static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case, + int result, int width, int running) { - int err; - - pr_debug("\n--- start ---\n"); - err = run_test(t, subtest); - pr_debug("---- end ----\n"); + if (test_suite__num_test_cases(t) > 1) { + int subw = width > 2 ? width - 2 : width; - if (!has_subtests(t)) - pr_debug("%s:", t->desc); - else - pr_debug("%s subtest %d:", t->desc, subtest + 1); + pr_info("%3d.%1d: %-*s:", curr_suite + 1, curr_test_case + 1, subw, + test_description(t, curr_test_case)); + } else + pr_info("%3d: %-*s:", curr_suite + 1, width, test_description(t, curr_test_case)); - switch (err) { + switch (result) { + case TEST_RUNNING: + color_fprintf(stderr, PERF_COLOR_YELLOW, " Running (%d active)\n", running); + break; case TEST_OK: pr_info(" Ok\n"); break; case TEST_SKIP: { - const char *reason = skip_reason(t, subtest); + const char *reason = skip_reason(t, curr_test_case); if (reason) color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (%s)\n", reason); @@ -296,215 +298,330 @@ static int test_and_print(struct test_suite *t, int subtest) break; } - return err; + return 0; } -struct shell_test { - const char *dir; - const char *file; -}; - -static int shell_test__run(struct test_suite *test, int subdir __maybe_unused) +static void finish_test(struct child_test **child_tests, int running_test, int child_test_num, + int width) { - int err; - char script[PATH_MAX]; - struct shell_test *st = test->priv; - - path__join(script, sizeof(script) - 3, st->dir, st->file); + struct child_test *child_test = child_tests[running_test]; + struct test_suite *t; + int curr_suite, curr_test_case, err; + bool err_done = false; + struct strbuf err_output = STRBUF_INIT; + int last_running = -1; + int ret; + + if (child_test == NULL) { + /* Test wasn't started. */ + return; + } + t = child_test->test; + curr_suite = child_test->suite_num; + curr_test_case = child_test->test_case_num; + err = child_test->process.err; + /* + * For test suites with subtests, display the suite name ahead of the + * sub test names. + */ + if (test_suite__num_test_cases(t) > 1 && curr_test_case == 0) + pr_info("%3d: %-*s:\n", curr_suite + 1, width, test_description(t, -1)); - if (verbose > 0) - strncat(script, " -v", sizeof(script) - strlen(script) - 1); + /* + * Busy loop reading from the child's stdout/stderr that are set to be + * non-blocking until EOF. + */ + if (err > 0) + fcntl(err, F_SETFL, O_NONBLOCK); + if (verbose > 1) { + if (test_suite__num_test_cases(t) > 1) + pr_info("%3d.%1d: %s:\n", curr_suite + 1, curr_test_case + 1, + test_description(t, curr_test_case)); + else + pr_info("%3d: %s:\n", curr_suite + 1, test_description(t, -1)); + } + while (!err_done) { + struct pollfd pfds[1] = { + { .fd = err, + .events = POLLIN | POLLERR | POLLHUP | POLLNVAL, + }, + }; + if (perf_use_color_default) { + int running = 0; - err = system(script); - if (!err) - return TEST_OK; + for (int y = running_test; y < child_test_num; y++) { + if (child_tests[y] == NULL) + continue; + if (check_if_command_finished(&child_tests[y]->process) == 0) + running++; + } + if (running != last_running) { + if (last_running != -1) { + /* + * Erase "Running (.. active)" line + * printed before poll/sleep. + */ + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); + } + print_test_result(t, curr_suite, curr_test_case, TEST_RUNNING, + width, running); + last_running = running; + } + } - return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL; + err_done = true; + if (err <= 0) { + /* No child stderr to poll, sleep for 10ms for child to complete. */ + usleep(10 * 1000); + } else { + /* Poll to avoid excessive spinning, timeout set for 100ms. */ + poll(pfds, ARRAY_SIZE(pfds), /*timeout=*/100); + if (pfds[0].revents) { + char buf[512]; + ssize_t len; + + len = read(err, buf, sizeof(buf) - 1); + + if (len > 0) { + err_done = false; + buf[len] = '\0'; + strbuf_addstr(&err_output, buf); + } + } + } + if (err_done) + err_done = check_if_command_finished(&child_test->process); + } + if (perf_use_color_default && last_running != -1) { + /* Erase "Running (.. active)" line printed before poll/sleep. */ + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); + } + /* Clean up child process. */ + ret = finish_command(&child_test->process); + if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL)) + fprintf(stderr, "%s", err_output.buf); + + strbuf_release(&err_output); + print_test_result(t, curr_suite, curr_test_case, ret, width, /*running=*/0); + if (err > 0) + close(err); + zfree(&child_tests[running_test]); } -static int run_shell_tests(int argc, const char *argv[], int i, int width, - struct intlist *skiplist) +static int start_test(struct test_suite *test, int curr_suite, int curr_test_case, + struct child_test **child, int width, int pass) { - struct shell_test st; - const struct script_file *files, *file; + int err; - files = list_script_files(); - if (!files) + *child = NULL; + if (dont_fork) { + if (pass == 1) { + pr_debug("--- start ---\n"); + err = test_function(test, curr_test_case)(test, curr_test_case); + pr_debug("---- end ----\n"); + print_test_result(test, curr_suite, curr_test_case, err, width, + /*running=*/0); + } return 0; - for (file = files; file->dir; file++) { - int curr = i++; - struct test_case test_cases[] = { - { - .desc = file->desc, - .run_case = shell_test__run, - }, - { .name = NULL, } - }; - struct test_suite test_suite = { - .desc = test_cases[0].desc, - .test_cases = test_cases, - .priv = &st, - }; - st.dir = file->dir; - - if (test_suite.desc == NULL || - !perf_test__matches(test_suite.desc, curr, argc, argv)) - continue; + } + if (pass == 1 && !sequential && test_exclusive(test, curr_test_case)) { + /* When parallel, skip exclusive tests on the first pass. */ + return 0; + } + if (pass != 1 && (sequential || !test_exclusive(test, curr_test_case))) { + /* Sequential and non-exclusive tests were run on the first pass. */ + return 0; + } + *child = zalloc(sizeof(**child)); + if (!*child) + return -ENOMEM; + + (*child)->test = test; + (*child)->suite_num = curr_suite; + (*child)->test_case_num = curr_test_case; + (*child)->process.pid = -1; + (*child)->process.no_stdin = 1; + if (verbose <= 0) { + (*child)->process.no_stdout = 1; + (*child)->process.no_stderr = 1; + } else { + (*child)->process.stdout_to_stderr = 1; + (*child)->process.out = -1; + (*child)->process.err = -1; + } + (*child)->process.no_exec_cmd = run_test_child; + if (sequential || pass == 2) { + err = start_command(&(*child)->process); + if (err) + return err; + finish_test(child, /*running_test=*/0, /*child_test_num=*/1, width); + return 0; + } + return start_command(&(*child)->process); +} - st.file = file->file; - pr_info("%3d: %-*s:", i, width, test_suite.desc); +/* State outside of __cmd_test for the sake of the signal handler. */ - if (intlist__find(skiplist, i)) { - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); - continue; - } +static size_t num_tests; +static struct child_test **child_tests; +static jmp_buf cmd_test_jmp_buf; - test_and_print(&test_suite, 0); - } - return 0; +static void cmd_test_sig_handler(int sig) +{ + siglongjmp(cmd_test_jmp_buf, sig); } -static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) +static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], + struct intlist *skiplist) { - struct test_suite *t; - unsigned int j, k; - int i = 0; - int width = list_script_max_width(); + static int width = 0; + int err = 0; - for_each_test(j, k, t) { - int len = strlen(test_description(t, -1)); + for (struct test_suite **t = suites; *t; t++) { + int i, len = strlen(test_description(*t, -1)); if (width < len) width = len; - } - - for_each_test(j, k, t) { - int curr = i++; - int subi; - - if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) { - bool skip = true; - int subn; - - subn = num_subtests(t); - - for (subi = 0; subi < subn; subi++) { - if (perf_test__matches(test_description(t, subi), - curr, argc, argv)) - skip = false; - } - if (skip) - continue; + test_suite__for_each_test_case(*t, i) { + len = strlen(test_description(*t, i)); + if (width < len) + width = len; + num_tests += runs_per_test; } + } + child_tests = calloc(num_tests, sizeof(*child_tests)); + if (!child_tests) + return -ENOMEM; + + err = sigsetjmp(cmd_test_jmp_buf, 1); + if (err) { + pr_err("\nSignal (%d) while running tests.\nTerminating tests with the same signal\n", + err); + for (size_t x = 0; x < num_tests; x++) { + struct child_test *child_test = child_tests[x]; + + if (!child_test || child_test->process.pid <= 0) + continue; - pr_info("%3d: %-*s:", i, width, test_description(t, -1)); - - if (intlist__find(skiplist, i)) { - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); - continue; + pr_debug3("Killing %d pid %d\n", + child_test->suite_num + 1, + child_test->process.pid); + kill(child_test->process.pid, err); } + goto err_out; + } + signal(SIGINT, cmd_test_sig_handler); + signal(SIGTERM, cmd_test_sig_handler); - if (!has_subtests(t)) { - test_and_print(t, -1); - } else { - int subn = num_subtests(t); - /* - * minus 2 to align with normal testcases. - * For subtest we print additional '.x' in number. - * for example: - * - * 35: Test LLVM searching and compiling : - * 35.1: Basic BPF llvm compiling test : Ok - */ - int subw = width > 2 ? width - 2 : width; - - if (subn <= 0) { - color_fprintf(stderr, PERF_COLOR_YELLOW, - " Skip (not compiled in)\n"); - continue; + /* + * In parallel mode pass 1 runs non-exclusive tests in parallel, pass 2 + * runs the exclusive tests sequentially. In other modes all tests are + * run in pass 1. + */ + for (int pass = 1; pass <= 2; pass++) { + int child_test_num = 0; + int curr_suite = 0; + + for (struct test_suite **t = suites; *t; t++, curr_suite++) { + int curr_test_case; + + if (!perf_test__matches(test_description(*t, -1), curr_suite, argc, argv)) { + /* + * Test suite shouldn't be run based on + * description. See if any test case should. + */ + bool skip = true; + + test_suite__for_each_test_case(*t, curr_test_case) { + if (perf_test__matches(test_description(*t, curr_test_case), + curr_suite, argc, argv)) { + skip = false; + break; + } + } + if (skip) + continue; } - pr_info("\n"); - for (subi = 0; subi < subn; subi++) { - int len = strlen(test_description(t, subi)); - - if (subw < len) - subw = len; + if (intlist__find(skiplist, curr_suite + 1)) { + pr_info("%3d: %-*s:", curr_suite + 1, width, + test_description(*t, -1)); + color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); + continue; } - for (subi = 0; subi < subn; subi++) { - if (!perf_test__matches(test_description(t, subi), - curr, argc, argv)) - continue; - - pr_info("%3d.%1d: %-*s:", i, subi + 1, subw, - test_description(t, subi)); - test_and_print(t, subi); + for (unsigned int run = 0; run < runs_per_test; run++) { + test_suite__for_each_test_case(*t, curr_test_case) { + if (!perf_test__matches(test_description(*t, curr_test_case), + curr_suite, argc, argv)) + continue; + + err = start_test(*t, curr_suite, curr_test_case, + &child_tests[child_test_num++], + width, pass); + if (err) + goto err_out; + } } } + if (!sequential) { + /* Parallel mode starts tests but doesn't finish them. Do that now. */ + for (size_t x = 0; x < num_tests; x++) + finish_test(child_tests, x, num_tests, width); + } } - - return run_shell_tests(argc, argv, i, width, skiplist); -} - -static int perf_test__list_shell(int argc, const char **argv, int i) -{ - const struct script_file *files, *file; - - files = list_script_files(); - if (!files) - return 0; - for (file = files; file->dir; file++) { - int curr = i++; - struct test_suite t = { - .desc = file->desc - }; - - if (!perf_test__matches(t.desc, curr, argc, argv)) - continue; - - pr_info("%3d: %s\n", i, t.desc); +err_out: + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + if (err) { + pr_err("Internal test harness failure. Completing any started tests:\n:"); + for (size_t x = 0; x < num_tests; x++) + finish_test(child_tests, x, num_tests, width); } - return 0; + free(child_tests); + return err; } -static int perf_test__list(int argc, const char **argv) +static int perf_test__list(FILE *fp, struct test_suite **suites, int argc, const char **argv) { - unsigned int j, k; - struct test_suite *t; - int i = 0; + int curr_suite = 0; - for_each_test(j, k, t) { - int curr = i++; + for (struct test_suite **t = suites; *t; t++, curr_suite++) { + int curr_test_case; - if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) + if (!perf_test__matches(test_description(*t, -1), curr_suite, argc, argv)) continue; - pr_info("%3d: %s\n", i, test_description(t, -1)); + fprintf(fp, "%3d: %s\n", curr_suite + 1, test_description(*t, -1)); - if (has_subtests(t)) { - int subn = num_subtests(t); - int subi; + if (test_suite__num_test_cases(*t) <= 1) + continue; - for (subi = 0; subi < subn; subi++) - pr_info("%3d:%1d: %s\n", i, subi + 1, - test_description(t, subi)); + test_suite__for_each_test_case(*t, curr_test_case) { + fprintf(fp, "%3d.%1d: %s\n", curr_suite + 1, curr_test_case + 1, + test_description(*t, curr_test_case)); } } + return 0; +} - perf_test__list_shell(argc, argv, i); +static int workloads__fprintf_list(FILE *fp) +{ + struct test_workload *twl; + int printed = 0; - return 0; + workloads__for_each(twl) + printed += fprintf(fp, "%s\n", twl->name); + + return printed; } static int run_workload(const char *work, int argc, const char **argv) { - unsigned int i = 0; struct test_workload *twl; - for (i = 0; i < ARRAY_SIZE(workloads); i++) { - twl = workloads[i]; + workloads__for_each(twl) { if (!strcmp(twl->name, work)) return twl->func(argc, argv); } @@ -522,6 +639,52 @@ static int perf_test__config(const char *var, const char *value, return 0; } +static struct test_suite **build_suites(void) +{ + /* + * TODO: suites is static to avoid needing to clean up the scripts tests + * for leak sanitizer. + */ + static struct test_suite **suites[] = { + generic_tests, + arch_tests, + NULL, + }; + struct test_suite **result; + struct test_suite *t; + size_t n = 0, num_suites = 0; + + if (suites[2] == NULL) + suites[2] = create_script_test_suites(); + +#define for_each_suite(suite) \ + for (size_t i = 0, j = 0; i < ARRAY_SIZE(suites); i++, j = 0) \ + while ((suite = suites[i][j++]) != NULL) + + for_each_suite(t) + num_suites++; + + result = calloc(num_suites + 1, sizeof(struct test_suite *)); + + for (int pass = 1; pass <= 2; pass++) { + for_each_suite(t) { + bool exclusive = false; + int curr_test_case; + + test_suite__for_each_test_case(t, curr_test_case) { + if (test_exclusive(t, curr_test_case)) { + exclusive = true; + break; + } + } + if ((!exclusive && pass == 1) || (exclusive && pass == 2)) + result[n++] = t; + } + } + return result; +#undef for_each_suite +} + int cmd_test(int argc, const char **argv) { const char *test_usage[] = { @@ -530,13 +693,19 @@ int cmd_test(int argc, const char **argv) }; const char *skip = NULL; const char *workload = NULL; + bool list_workloads = false; const struct option test_options[] = { OPT_STRING('s', "skip", &skip, "tests", "tests to skip"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('F', "dont-fork", &dont_fork, "Do not fork for testcase"), - OPT_STRING('w', "workload", &workload, "work", "workload to run for testing"), + OPT_BOOLEAN('S', "sequential", &sequential, + "Run the tests one after another rather than in parallel"), + OPT_UINTEGER('r', "runs-per-test", &runs_per_test, + "Run each test the given number of times, default 1"), + OPT_STRING('w', "workload", &workload, "work", "workload to run for testing, use '--list-workloads' to list the available ones."), + OPT_BOOLEAN(0, "list-workloads", &list_workloads, "List the available builtin workloads to use with -w/--workload"), OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"), OPT_STRING(0, "objdump", &test_objdump_path, "path", "objdump binary to use for disassembly and annotations"), @@ -545,6 +714,7 @@ int cmd_test(int argc, const char **argv) const char * const test_subcommands[] = { "list", NULL }; struct intlist *skiplist = NULL; int ret = hists__init(); + struct test_suite **suites; if (ret < 0) return ret; @@ -555,15 +725,28 @@ int cmd_test(int argc, const char **argv) setvbuf(stdout, NULL, _IONBF, 0); argc = parse_options_subcommand(argc, argv, test_options, test_subcommands, test_usage, 0); - if (argc >= 1 && !strcmp(argv[0], "list")) - return perf_test__list(argc - 1, argv + 1); + if (argc >= 1 && !strcmp(argv[0], "list")) { + suites = build_suites(); + ret = perf_test__list(stdout, suites, argc - 1, argv + 1); + free(suites); + return ret; + } if (workload) return run_workload(workload, argc, argv); + if (list_workloads) { + workloads__fprintf_list(stdout); + return 0; + } + + if (dont_fork) + sequential = true; + symbol_conf.priv_size = sizeof(int); symbol_conf.try_vmlinux_path = true; + if (symbol__init(NULL) < 0) return -1; @@ -575,5 +758,8 @@ int cmd_test(int argc, const char **argv) */ rlimit__bump_memlock(); - return __cmd_test(argc, argv, skiplist); + suites = build_suites(); + ret = __cmd_test(suites, argc, argv, skiplist); + free(suites); + return ret; } diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 7a3a7bbbec71..b1abb34d7818 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <errno.h> +#include <linux/kconfig.h> #include <linux/kernel.h> #include <linux/types.h> #include <inttypes.h> @@ -8,6 +9,7 @@ #include <stdio.h> #include <string.h> #include <sys/param.h> +#include <sys/utsname.h> #include <perf/cpumap.h> #include <perf/evlist.h> #include <perf/mmap.h> @@ -176,16 +178,104 @@ static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) return err; } +/* + * Only gets GNU objdump version. Returns 0 for llvm-objdump. + */ +static int objdump_version(void) +{ + size_t line_len; + char cmd[PATH_MAX * 2]; + char *line = NULL; + const char *fmt; + FILE *f; + int ret; + + int version_tmp, version_num = 0; + char *version = 0, *token; + + fmt = "%s --version"; + ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path); + if (ret <= 0 || (size_t)ret >= sizeof(cmd)) + return -1; + /* Ignore objdump errors */ + strcat(cmd, " 2>/dev/null"); + f = popen(cmd, "r"); + if (!f) { + pr_debug("popen failed\n"); + return -1; + } + /* Get first line of objdump --version output */ + ret = getline(&line, &line_len, f); + pclose(f); + if (ret < 0) { + pr_debug("getline failed\n"); + return -1; + } + + token = strsep(&line, " "); + if (token != NULL && !strcmp(token, "GNU")) { + // version is last part of first line of objdump --version output. + while ((token = strsep(&line, " "))) + version = token; + + // Convert version into a format we can compare with + token = strsep(&version, "."); + version_num = atoi(token); + if (version_num) + version_num *= 10000; + + token = strsep(&version, "."); + version_tmp = atoi(token); + if (token) + version_num += version_tmp * 100; + + token = strsep(&version, "."); + version_tmp = atoi(token); + if (token) + version_num += version_tmp; + } + + return version_num; +} + static int read_via_objdump(const char *filename, u64 addr, void *buf, size_t len) { + u64 stop_address = addr + len; + struct utsname uname_buf; char cmd[PATH_MAX * 2]; const char *fmt; FILE *f; int ret; + ret = uname(&uname_buf); + if (ret) { + pr_debug("uname failed\n"); + return -1; + } + + if (!strncmp(uname_buf.machine, "riscv", 5)) { + int version = objdump_version(); + + /* Default to this workaround if version parsing fails */ + if (version < 0 || version > 24100) { + /* + * Starting at riscv objdump version 2.41, dumping in + * the middle of an instruction is not supported. riscv + * instructions are aligned along 2-byte intervals and + * can be either 2-bytes or 4-bytes. This makes it + * possible that the stop-address lands in the middle of + * a 4-byte instruction. Increase the stop_address by + * two to ensure an instruction is not cut in half, but + * leave the len as-is so only the expected number of + * bytes are collected. + */ + stop_address += 2; + } + } + fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s"; - ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, addr + len, + ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, stop_address, filename); if (ret <= 0 || (size_t)ret >= sizeof(cmd)) return -1; @@ -253,9 +343,9 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, goto out; } dso = map__dso(al.map); - pr_debug("File is: %s\n", dso->long_name); + pr_debug("File is: %s\n", dso__long_name(dso)); - if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) { + if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) { pr_debug("Unexpected kernel address - skipping\n"); goto out; } @@ -274,7 +364,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, * modules to manage long jumps. Check if the ip offset falls in stubs * sections for kernel modules. And skip module address after text end */ - if (dso->is_kmod && al.addr > dso->text_end) { + if (dso__is_kmod(dso) && al.addr > dso__text_end(dso)) { pr_debug("skipping the module address %#"PRIx64" after text end\n", al.addr); goto out; } @@ -315,7 +405,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, state->done[state->done_cnt++] = map__start(al.map); } - objdump_name = dso->long_name; + objdump_name = dso__long_name(dso); if (dso__needs_decompress(dso)) { if (dso__decompress_kmodule_path(dso, objdump_name, decomp_name, @@ -637,11 +727,11 @@ static int do_test_code_reading(bool try_kcore) evlist__config(evlist, &opts, NULL); - evsel = evlist__first(evlist); - - evsel->core.attr.comm = 1; - evsel->core.attr.disabled = 1; - evsel->core.attr.enable_on_exec = 0; + evlist__for_each_entry(evlist, evsel) { + evsel->core.attr.comm = 1; + evsel->core.attr.disabled = 1; + evsel->core.attr.enable_on_exec = 0; + } ret = evlist__open(evlist); if (ret < 0) { diff --git a/tools/perf/tests/config-fragments/config b/tools/perf/tests/config-fragments/config index c340b3195fca..4fca12851016 100644 --- a/tools/perf/tests/config-fragments/config +++ b/tools/perf/tests/config-fragments/config @@ -9,3 +9,6 @@ CONFIG_GENERIC_TRACER=y CONFIG_FTRACE=y CONFIG_FTRACE_SYSCALLS=y CONFIG_BRANCH_PROFILE_NONE=y +CONFIG_KPROBES=y +CONFIG_KPROBE_EVENTS=y +CONFIG_UPROBE_EVENTS=y diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index bd8e396f3e57..2354246afc5a 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -11,7 +11,7 @@ struct machine; -static int process_event_mask(struct perf_tool *tool __maybe_unused, +static int process_event_mask(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -47,7 +47,7 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -73,7 +73,7 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_range_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_range_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -156,21 +156,54 @@ static int test__cpu_map_print(struct test_suite *test __maybe_unused, int subte return 0; } -static int test__cpu_map_merge(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +static int __test__cpu_map_merge(const char *lhs, const char *rhs, int nr, const char *expected) { - struct perf_cpu_map *a = perf_cpu_map__new("4,2,1"); - struct perf_cpu_map *b = perf_cpu_map__new("4,5,7"); - struct perf_cpu_map *c = perf_cpu_map__merge(a, b); + struct perf_cpu_map *a = perf_cpu_map__new(lhs); + struct perf_cpu_map *b = perf_cpu_map__new(rhs); char buf[100]; - TEST_ASSERT_VAL("failed to merge map: bad nr", perf_cpu_map__nr(c) == 5); - cpu_map__snprint(c, buf, sizeof(buf)); - TEST_ASSERT_VAL("failed to merge map: bad result", !strcmp(buf, "1-2,4-5,7")); + perf_cpu_map__merge(&a, b); + TEST_ASSERT_VAL("failed to merge map: bad nr", perf_cpu_map__nr(a) == nr); + cpu_map__snprint(a, buf, sizeof(buf)); + TEST_ASSERT_VAL("failed to merge map: bad result", !strcmp(buf, expected)); perf_cpu_map__put(b); - perf_cpu_map__put(c); + + /* + * If 'b' is a superset of 'a', 'a' points to the same map with the + * map 'b'. In this case, the owner 'b' has released the resource above + * but 'a' still keeps the ownership, the reference counter should be 1. + */ + TEST_ASSERT_VAL("unexpected refcnt: bad result", + refcount_read(perf_cpu_map__refcnt(a)) == 1); + + perf_cpu_map__put(a); return 0; } +static int test__cpu_map_merge(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int ret; + + ret = __test__cpu_map_merge("4,2,1", "4,5,7", 5, "1-2,4-5,7"); + if (ret) + return ret; + ret = __test__cpu_map_merge("1-8", "6-9", 9, "1-9"); + if (ret) + return ret; + ret = __test__cpu_map_merge("1-8,12-20", "6-9,15", 18, "1-9,12-20"); + if (ret) + return ret; + ret = __test__cpu_map_merge("4,2,1", "1", 3, "1-2,4"); + if (ret) + return ret; + ret = __test__cpu_map_merge("1", "4,2,1", 3, "1-2,4"); + if (ret) + return ret; + ret = __test__cpu_map_merge("1", "1", 1, "1"); + return ret; +} + static int __test__cpu_map_intersect(const char *lhs, const char *rhs, int nr, const char *expected) { struct perf_cpu_map *a = perf_cpu_map__new(lhs); @@ -219,30 +252,29 @@ static int test__cpu_map_equal(struct test_suite *test __maybe_unused, int subte struct perf_cpu_map *empty = perf_cpu_map__intersect(one, two); struct perf_cpu_map *pair = perf_cpu_map__new("1-2"); struct perf_cpu_map *tmp; - struct perf_cpu_map *maps[] = {empty, any, one, two, pair}; + struct perf_cpu_map **maps[] = {&empty, &any, &one, &two, &pair}; for (size_t i = 0; i < ARRAY_SIZE(maps); i++) { /* Maps equal themself. */ - TEST_ASSERT_VAL("equal", perf_cpu_map__equal(maps[i], maps[i])); + TEST_ASSERT_VAL("equal", perf_cpu_map__equal(*maps[i], *maps[i])); for (size_t j = 0; j < ARRAY_SIZE(maps); j++) { /* Maps dont't equal each other. */ if (i == j) continue; - TEST_ASSERT_VAL("not equal", !perf_cpu_map__equal(maps[i], maps[j])); + TEST_ASSERT_VAL("not equal", !perf_cpu_map__equal(*maps[i], *maps[j])); } } /* Maps equal made maps. */ - tmp = perf_cpu_map__merge(perf_cpu_map__get(one), two); - TEST_ASSERT_VAL("pair", perf_cpu_map__equal(pair, tmp)); - perf_cpu_map__put(tmp); + perf_cpu_map__merge(&two, one); + TEST_ASSERT_VAL("pair", perf_cpu_map__equal(pair, two)); tmp = perf_cpu_map__intersect(pair, one); TEST_ASSERT_VAL("one", perf_cpu_map__equal(one, tmp)); perf_cpu_map__put(tmp); for (size_t i = 0; i < ARRAY_SIZE(maps); i++) - perf_cpu_map__put(maps[i]); + perf_cpu_map__put(*maps[i]); return TEST_OK; } diff --git a/tools/perf/tests/demangle-java-test.c b/tools/perf/tests/demangle-java-test.c index 44d1be303b67..93c94408bdc8 100644 --- a/tools/perf/tests/demangle-java-test.c +++ b/tools/perf/tests/demangle-java-test.c @@ -2,6 +2,7 @@ #include <string.h> #include <stdlib.h> #include <stdio.h> +#include <linux/kernel.h> #include "tests.h" #include "session.h" #include "debug.h" @@ -28,7 +29,7 @@ static int test__demangle_java(struct test_suite *test __maybe_unused, int subte "void java.lang.Object<init>()" }, }; - for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { buf = java_demangle_sym(test_cases[i].mangled, 0); if (strcmp(buf, test_cases[i].demangled)) { pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled, diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index da3a9b50b1b1..54f59d1246bc 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -62,7 +62,7 @@ static int test_result(const char *msg, int ret) return ret; } -static int process(struct perf_tool *tool, union perf_event *event, +static int process(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 2d67422c1222..5286ae8bd2d7 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -10,6 +10,7 @@ #include <sys/resource.h> #include <api/fs/fs.h> #include "dso.h" +#include "dsos.h" #include "machine.h" #include "symbol.h" #include "tests.h" @@ -123,9 +124,10 @@ static int test__dso_data(struct test_suite *test __maybe_unused, int subtest __ TEST_ASSERT_VAL("No test file", file); memset(&machine, 0, sizeof(machine)); + dsos__init(&machine.dsos); - dso = dso__new((const char *)file); - + dso = dso__new(file); + TEST_ASSERT_VAL("Failed to add dso", !dsos__add(&machine.dsos, dso)); TEST_ASSERT_VAL("Failed to access to dso", dso__data_fd(dso, &machine) >= 0); @@ -170,6 +172,7 @@ static int test__dso_data(struct test_suite *test __maybe_unused, int subtest __ } dso__put(dso); + dsos__exit(&machine.dsos); unlink(file); return 0; } @@ -199,40 +202,35 @@ static long open_files_cnt(void) return nr - 1; } -static struct dso **dsos; - -static int dsos__create(int cnt, int size) +static int dsos__create(int cnt, int size, struct dsos *dsos) { int i; - dsos = malloc(sizeof(*dsos) * cnt); - TEST_ASSERT_VAL("failed to alloc dsos array", dsos); + dsos__init(dsos); for (i = 0; i < cnt; i++) { - char *file; + struct dso *dso; + char *file = test_file(size); - file = test_file(size); TEST_ASSERT_VAL("failed to get dso file", file); - - dsos[i] = dso__new(file); - TEST_ASSERT_VAL("failed to get dso", dsos[i]); + dso = dso__new(file); + TEST_ASSERT_VAL("failed to get dso", dso); + TEST_ASSERT_VAL("failed to add dso", !dsos__add(dsos, dso)); + dso__put(dso); } return 0; } -static void dsos__delete(int cnt) +static void dsos__delete(struct dsos *dsos) { - int i; + for (unsigned int i = 0; i < dsos->cnt; i++) { + struct dso *dso = dsos->dsos[i]; - for (i = 0; i < cnt; i++) { - struct dso *dso = dsos[i]; - - unlink(dso->name); - dso__put(dso); + dso__data_close(dso); + unlink(dso__name(dso)); } - - free(dsos); + dsos__exit(dsos); } static int set_fd_limit(int n) @@ -266,10 +264,10 @@ static int test__dso_data_cache(struct test_suite *test __maybe_unused, int subt /* and this is now our dso open FDs limit */ dso_cnt = limit / 2; TEST_ASSERT_VAL("failed to create dsos\n", - !dsos__create(dso_cnt, TEST_FILE_SIZE)); + !dsos__create(dso_cnt, TEST_FILE_SIZE, &machine.dsos)); for (i = 0; i < (dso_cnt - 1); i++) { - struct dso *dso = dsos[i]; + struct dso *dso = machine.dsos.dsos[i]; /* * Open dsos via dso__data_fd(), it opens the data @@ -289,17 +287,17 @@ static int test__dso_data_cache(struct test_suite *test __maybe_unused, int subt } /* verify the first one is already open */ - TEST_ASSERT_VAL("dsos[0] is not open", dsos[0]->data.fd != -1); + TEST_ASSERT_VAL("dsos[0] is not open", dso__data(machine.dsos.dsos[0])->fd != -1); /* open +1 dso to reach the allowed limit */ - fd = dso__data_fd(dsos[i], &machine); + fd = dso__data_fd(machine.dsos.dsos[i], &machine); TEST_ASSERT_VAL("failed to get fd", fd > 0); /* should force the first one to be closed */ - TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1); + TEST_ASSERT_VAL("failed to close dsos[0]", dso__data(machine.dsos.dsos[0])->fd == -1); /* cleanup everything */ - dsos__delete(dso_cnt); + dsos__delete(&machine.dsos); /* Make sure we did not leak any file descriptor. */ nr_end = open_files_cnt(); @@ -324,9 +322,9 @@ static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int sub long nr_end, nr = open_files_cnt(), lim = new_limit(3); int fd, fd_extra; -#define dso_0 (dsos[0]) -#define dso_1 (dsos[1]) -#define dso_2 (dsos[2]) +#define dso_0 (machine.dsos.dsos[0]) +#define dso_1 (machine.dsos.dsos[1]) +#define dso_2 (machine.dsos.dsos[2]) /* Rest the internal dso open counter limit. */ reset_fd_limit(); @@ -346,7 +344,8 @@ static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int sub TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit((lim))); - TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); + TEST_ASSERT_VAL("failed to create dsos\n", + !dsos__create(3, TEST_FILE_SIZE, &machine.dsos)); /* open dso_0 */ fd = dso__data_fd(dso_0, &machine); @@ -371,7 +370,7 @@ static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int sub * dso_0 should get closed, because we reached * the file descriptor limit */ - TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1); + TEST_ASSERT_VAL("failed to close dso_0", dso__data(dso_0)->fd == -1); /* open dso_0 */ fd = dso__data_fd(dso_0, &machine); @@ -381,11 +380,11 @@ static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int sub * dso_1 should get closed, because we reached * the file descriptor limit */ - TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1); + TEST_ASSERT_VAL("failed to close dso_1", dso__data(dso_1)->fd == -1); /* cleanup everything */ close(fd_extra); - dsos__delete(3); + dsos__delete(&machine.dsos); /* Make sure we did not leak any file descriptor. */ nr_end = open_files_cnt(); diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index d01aa931fe81..f85d391ced98 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -37,7 +37,7 @@ #define NO_TAIL_CALL_BARRIER __asm__ __volatile__("" : : : "memory"); #endif -static int mmap_handler(struct perf_tool *tool __maybe_unused, +static int mmap_handler(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index e155f0e0e04d..deefe5003bfc 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -126,6 +126,7 @@ static int attach__cpu_disabled(struct evlist *evlist) evsel->core.attr.disabled = 1; err = evsel__open_per_cpu(evsel, cpus, -1); + perf_cpu_map__put(cpus); if (err) { if (err == -EACCES) return TEST_SKIP; @@ -134,7 +135,6 @@ static int attach__cpu_disabled(struct evlist *evlist) return err; } - perf_cpu_map__put(cpus); return evsel__enable(evsel); } @@ -153,10 +153,10 @@ static int attach__cpu_enabled(struct evlist *evlist) } err = evsel__open_per_cpu(evsel, cpus, -1); + perf_cpu_map__put(cpus); if (err == -EACCES) return TEST_SKIP; - perf_cpu_map__put(cpus); return err ? TEST_FAIL : TEST_OK; } @@ -188,6 +188,7 @@ static int test_times(int (attach)(struct evlist *), err = attach(evlist); if (err == TEST_SKIP) { pr_debug(" SKIP : not enough rights\n"); + evlist__delete(evlist); return err; } diff --git a/tools/perf/tests/event_groups.c b/tools/perf/tests/event_groups.c index ccd9d8b2903f..c119ff114948 100644 --- a/tools/perf/tests/event_groups.c +++ b/tools/perf/tests/event_groups.c @@ -10,9 +10,10 @@ #include "header.h" #include "../perf-sys.h" -/* hw: cycles, sw: context-switch, uncore: [arch dependent] */ +/* hw: cycles,instructions sw: context-switch, uncore: [arch dependent] */ static int types[] = {0, 1, -1}; static unsigned long configs[] = {0, 3, 0}; +static unsigned long configs_hw[] = {1}; #define NR_UNCORE_PMUS 5 @@ -93,7 +94,18 @@ static int run_test(int i, int j, int k) return erroneous ? 0 : -1; } - sibling_fd2 = event_open(types[k], configs[k], group_fd); + /* + * if all three events (leader and two sibling events) + * are hardware events, use instructions as one of the + * sibling event. There is event constraint in powerpc that + * events using same counter cannot be programmed in a group. + * Since PERF_COUNT_HW_INSTRUCTIONS is a generic hardware + * event and present in all platforms, lets use that. + */ + if (!i && !j && !k) + sibling_fd2 = event_open(types[k], configs_hw[k], group_fd); + else + sibling_fd2 = event_open(types[k], configs[k], group_fd); if (sibling_fd2 == -1) { close(sibling_fd1); close(group_fd); @@ -124,9 +136,18 @@ static int test__event_groups(struct test_suite *text __maybe_unused, int subtes if (r) ret = TEST_FAIL; - pr_debug("0x%x 0x%lx, 0x%x 0x%lx, 0x%x 0x%lx: %s\n", - types[i], configs[i], types[j], configs[j], - types[k], configs[k], r ? "Fail" : "Pass"); + /* + * For all three events as HW events, second sibling + * event is picked from configs_hw. So print accordingly + */ + if (!i && !j && !k) + pr_debug("0x%x 0x%lx, 0x%x 0x%lx, 0x%x 0x%lx: %s\n", + types[i], configs[i], types[j], configs[j], + types[k], configs_hw[k], r ? "Fail" : "Pass"); + else + pr_debug("0x%x 0x%lx, 0x%x 0x%lx, 0x%x 0x%lx: %s\n", + types[i], configs[i], types[j], configs[j], + types[k], configs[k], r ? "Fail" : "Pass"); } } } diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index d093a9b878d1..d6b4ce3ef4ee 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -12,7 +12,7 @@ #include "tests.h" #include "debug.h" -static int process_event_unit(struct perf_tool *tool __maybe_unused, +static int process_event_unit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -25,7 +25,7 @@ static int process_event_unit(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_scale(struct perf_tool *tool __maybe_unused, +static int process_event_scale(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -43,7 +43,7 @@ struct event_name { const char *name; }; -static int process_event_name(struct perf_tool *tool, +static int process_event_name(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -57,7 +57,7 @@ static int process_event_name(struct perf_tool *tool, return 0; } -static int process_event_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -103,6 +103,7 @@ static int test__event_update(struct test_suite *test __maybe_unused, int subtes TEST_ASSERT_VAL("failed to synthesize attr update scale", !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + perf_tool__init(&tmp.tool, /*ordered_events=*/false); tmp.name = evsel__name(evsel); TEST_ASSERT_VAL("failed to synthesize attr update name", diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 15ff86f9da0b..1922cac13a24 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -37,7 +37,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) continue; } evlist__for_each_entry(evlist, evsel) { - if (strcmp(evsel__name(evsel), name)) { + if (!evsel__name_is(evsel, name)) { pr_debug("%s != %s\n", evsel__name(evsel), name); ret = TEST_FAIL; } @@ -71,7 +71,7 @@ static int perf_evsel__name_array_test(const char *const names[], int nr_names) continue; } evlist__for_each_entry(evlist, evsel) { - if (strcmp(evsel__name(evsel), names[i])) { + if (!evsel__name_is(evsel, names[i])) { pr_debug("%s != %s\n", evsel__name(evsel), names[i]); ret = TEST_FAIL; } diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index cf4da3d748c2..226196fb9677 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/err.h> -#include <traceevent/event-parse.h> +#include <event-parse.h> #include "evsel.h" #include "tests.h" #include "debug.h" @@ -36,33 +36,33 @@ static int test__perf_evsel__tp_sched_test(struct test_suite *test __maybe_unuse int subtest __maybe_unused) { struct evsel *evsel = evsel__newtp("sched", "sched_switch"); - int ret = 0; + int ret = TEST_OK; if (IS_ERR(evsel)) { pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel)); - return -1; + return PTR_ERR(evsel) == -EACCES ? TEST_SKIP : TEST_FAIL; } if (evsel__test_field(evsel, "prev_comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_prio", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_state", sizeof(long), true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_prio", 4, true)) - ret = -1; + ret = TEST_FAIL; evsel__delete(evsel); @@ -70,23 +70,33 @@ static int test__perf_evsel__tp_sched_test(struct test_suite *test __maybe_unuse if (IS_ERR(evsel)) { pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel)); - return -1; + return TEST_FAIL; } if (evsel__test_field(evsel, "comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prio", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "target_cpu", 4, true)) - ret = -1; + ret = TEST_FAIL; evsel__delete(evsel); return ret; } -DEFINE_SUITE("Parse sched tracepoints fields", perf_evsel__tp_sched_test); +static struct test_case tests__perf_evsel__tp_sched_test[] = { + TEST_CASE_REASON("Parse sched tracepoints fields", + perf_evsel__tp_sched_test, + "permissions"), + { .name = NULL, } +}; + +struct test_suite suite__perf_evsel__tp_sched_test = { + .desc = "Parse sched tracepoints fields", + .test_cases = tests__perf_evsel__tp_sched_test, +}; diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c index 9c1a1f18db75..31966ff856f8 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -127,8 +127,7 @@ static int expand_group_events(void) parse_events_error__init(&err); ret = parse_events(evlist, event_str, &err); if (ret < 0) { - pr_debug("failed to parse event '%s', err %d, str '%s'\n", - event_str, ret, err.str); + pr_debug("failed to parse event '%s', err %d\n", event_str, ret); parse_events_error__print(&err, event_str); goto out; } diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index e3aa9d4fcf3a..726cf8d4da28 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -6,6 +6,7 @@ #include "util/header.h" #include "util/smt.h" #include "tests.h" +#include <perf/cpumap.h> #include <math.h> #include <stdlib.h> #include <string.h> @@ -74,14 +75,12 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u double val, num_cpus_online, num_cpus, num_cores, num_dies, num_packages; int ret; struct expr_parse_ctx *ctx; - bool is_intel = false; char strcmp_cpuid_buf[256]; - struct perf_pmu *pmu = perf_pmus__find_core_pmu(); - char *cpuid = perf_pmu__getcpuid(pmu); + struct perf_cpu cpu = {-1}; + char *cpuid = get_cpuid_allow_env_override(cpu); char *escaped_cpuid1, *escaped_cpuid2; TEST_ASSERT_VAL("get_cpuid", cpuid); - is_intel = strstr(cpuid, "Intel") != NULL; TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0); @@ -244,12 +243,19 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u if (num_dies) // Some platforms do not have CPU die support, for example s390 TEST_ASSERT_VAL("#num_dies >= #num_packages", num_dies >= num_packages); - TEST_ASSERT_VAL("#system_tsc_freq", expr__parse(&val, ctx, "#system_tsc_freq") == 0); - if (is_intel) - TEST_ASSERT_VAL("#system_tsc_freq > 0", val > 0); - else - TEST_ASSERT_VAL("#system_tsc_freq == 0", fpclassify(val) == FP_ZERO); + if (expr__parse(&val, ctx, "#system_tsc_freq") == 0) { + bool is_intel = strstr(cpuid, "Intel") != NULL; + + if (is_intel) + TEST_ASSERT_VAL("#system_tsc_freq > 0", val > 0); + else + TEST_ASSERT_VAL("#system_tsc_freq == 0", fpclassify(val) == FP_ZERO); + } else { +#if defined(__i386__) || defined(__x86_64__) + TEST_ASSERT_VAL("#system_tsc_freq unsupported", 0); +#endif + } /* * Source count returns the number of events aggregating in a leader * event including the leader. Check parsing yields an id. diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index d08add0f4da6..187f12f5bc21 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c @@ -146,7 +146,7 @@ struct machine *setup_fake_machine(struct machines *machines) goto out; } - symbols__insert(&dso->symbols, sym); + symbols__insert(dso__symbols(dso), sym); } dso__put(dso); @@ -183,7 +183,7 @@ void print_hists_in(struct hists *hists) pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", i, thread__comm_str(he->thread), - dso->short_name, + dso__short_name(dso), he->ms.sym->name, he->stat.period); } @@ -212,7 +212,7 @@ void print_hists_out(struct hists *hists) pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n", i, thread__comm_str(he->thread), thread__tid(he->thread), - dso->short_name, + dso__short_name(dso), he->ms.sym->name, he->stat.period, he->stat_acc ? he->stat_acc->period : 0); } diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 71dacb0fec4d..1e0f5a310fd5 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -164,11 +164,11 @@ static void put_fake_samples(void) typedef int (*test_fn_t)(struct evsel *, struct machine *); #define COMM(he) (thread__comm_str(he->thread)) -#define DSO(he) (map__dso(he->ms.map)->short_name) +#define DSO(he) (dso__short_name(map__dso(he->ms.map))) #define SYM(he) (he->ms.sym->name) #define CPU(he) (he->cpu) #define DEPTH(he) (he->callchain->max_depth) -#define CDSO(cl) (map__dso(cl->ms.map)->short_name) +#define CDSO(cl) (dso__short_name(map__dso(cl->ms.map))) #define CSYM(cl) (cl->ms.sym->name) struct result { diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index ba1cccf57049..33b5cc8352a7 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -129,7 +129,7 @@ static void put_fake_samples(void) typedef int (*test_fn_t)(struct evsel *, struct machine *); #define COMM(he) (thread__comm_str(he->thread)) -#define DSO(he) (map__dso(he->ms.map)->short_name) +#define DSO(he) (dso__short_name(map__dso(he->ms.map))) #define SYM(he) (he->ms.sym->name) #define CPU(he) (he->cpu) #define PID(he) (thread__tid(he->thread)) diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c new file mode 100644 index 000000000000..d2b066a2b557 --- /dev/null +++ b/tools/perf/tests/hwmon_pmu.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "debug.h" +#include "evlist.h" +#include "hwmon_pmu.h" +#include "parse-events.h" +#include "tests.h" +#include <fcntl.h> +#include <sys/stat.h> +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/string.h> + +static const struct test_event { + const char *name; + const char *alias; + long config; +} test_events[] = { + { + "temp_test_hwmon_event1", + "temp1", + 0xA0001, + }, + { + "temp_test_hwmon_event2", + "temp2", + 0xA0002, + }, +}; + +/* Cleanup test PMU directory. */ +static int test_pmu_put(const char *dir, struct perf_pmu *hwm) +{ + char buf[PATH_MAX + 20]; + int ret; + + if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) { + pr_err("Failure to set up buffer for \"%s\"\n", dir); + return -EINVAL; + } + ret = system(buf); + if (ret) + pr_err("Failure to \"%s\"\n", buf); + + list_del(&hwm->list); + perf_pmu__delete(hwm); + return ret; +} + +/* + * Prepare test PMU directory data, normally exported by kernel at + * /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file + * path, the result is PMU loaded using that directory. + */ +static struct perf_pmu *test_pmu_get(char *dir, size_t sz) +{ + const char *test_hwmon_name_nl = "A test hwmon PMU\n"; + const char *test_hwmon_name = "A test hwmon PMU"; + /* Simulated hwmon items. */ + const struct test_item { + const char *name; + const char *value; + } test_items[] = { + { "temp1_label", "test hwmon event1\n", }, + { "temp1_input", "40000\n", }, + { "temp2_label", "test hwmon event2\n", }, + { "temp2_input", "50000\n", }, + }; + int hwmon_dirfd = -1, test_dirfd = -1, file; + struct perf_pmu *hwm = NULL; + ssize_t len; + + /* Create equivalent of sysfs mount point. */ + scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX"); + if (!mkdtemp(dir)) { + pr_err("mkdtemp failed\n"); + dir[0] = '\0'; + return NULL; + } + test_dirfd = open(dir, O_PATH|O_DIRECTORY); + if (test_dirfd < 0) { + pr_err("Failed to open test directory \"%s\"\n", dir); + goto err_out; + } + + /* Create the test hwmon directory and give it a name. */ + if (mkdirat(test_dirfd, "hwmon1234", 0755) < 0) { + pr_err("Failed to mkdir hwmon directory\n"); + goto err_out; + } + hwmon_dirfd = openat(test_dirfd, "hwmon1234", O_DIRECTORY); + if (hwmon_dirfd < 0) { + pr_err("Failed to open test hwmon directory \"%s/hwmon1234\"\n", dir); + goto err_out; + } + file = openat(hwmon_dirfd, "name", O_WRONLY | O_CREAT, 0600); + if (file < 0) { + pr_err("Failed to open for writing file \"name\"\n"); + goto err_out; + } + len = strlen(test_hwmon_name_nl); + if (write(file, test_hwmon_name_nl, len) < len) { + close(file); + pr_err("Failed to write to 'name' file\n"); + goto err_out; + } + close(file); + + /* Create test hwmon files. */ + for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) { + const struct test_item *item = &test_items[i]; + + file = openat(hwmon_dirfd, item->name, O_WRONLY | O_CREAT, 0600); + if (file < 0) { + pr_err("Failed to open for writing file \"%s\"\n", item->name); + goto err_out; + } + + if (write(file, item->value, strlen(item->value)) < 0) { + pr_err("Failed to write to file \"%s\"\n", item->name); + close(file); + goto err_out; + } + close(file); + } + + /* Make the PMU reading the files created above. */ + hwm = perf_pmus__add_test_hwmon_pmu(hwmon_dirfd, "hwmon1234", test_hwmon_name); + if (!hwm) + pr_err("Test hwmon creation failed\n"); + +err_out: + if (!hwm) { + test_pmu_put(dir, hwm); + if (hwmon_dirfd >= 0) + close(hwmon_dirfd); + } + if (test_dirfd >= 0) + close(test_dirfd); + return hwm; +} + +static int do_test(size_t i, bool with_pmu, bool with_alias) +{ + const char *test_event = with_alias ? test_events[i].alias : test_events[i].name; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + struct parse_events_error err; + int ret; + char str[128]; + bool found = false; + + if (!evlist) { + pr_err("evlist allocation failed\n"); + return TEST_FAIL; + } + + if (with_pmu) + snprintf(str, sizeof(str), "hwmon_a_test_hwmon_pmu/%s/", test_event); + else + strlcpy(str, test_event, sizeof(str)); + + pr_debug("Testing '%s'\n", str); + parse_events_error__init(&err); + ret = parse_events(evlist, str, &err); + if (ret) { + pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n", + __FILE__, __LINE__, str, ret); + parse_events_error__print(&err, str); + ret = TEST_FAIL; + goto out; + } + + ret = TEST_OK; + if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) { + pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", + __FILE__, __LINE__, str, evlist->core.nr_entries); + ret = TEST_FAIL; + goto out; + } + + evlist__for_each_entry(evlist, evsel) { + if (!evsel->pmu || !evsel->pmu->name || + strcmp(evsel->pmu->name, "hwmon_a_test_hwmon_pmu")) + continue; + + if (evsel->core.attr.config != (u64)test_events[i].config) { + pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", + __FILE__, __LINE__, str, + evsel->core.attr.config, + test_events[i].config); + ret = TEST_FAIL; + goto out; + } + found = true; + } + + if (!found) { + pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n", + __FILE__, __LINE__, str); + ret = TEST_FAIL; + } + +out: + parse_events_error__exit(&err); + evlist__delete(evlist); + return ret; +} + +static int test__hwmon_pmu(bool with_pmu) +{ + char dir[PATH_MAX]; + struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir)); + int ret = TEST_OK; + + if (!pmu) + return TEST_FAIL; + + for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) { + ret = do_test(i, with_pmu, /*with_alias=*/false); + + if (ret != TEST_OK) + break; + + ret = do_test(i, with_pmu, /*with_alias=*/true); + + if (ret != TEST_OK) + break; + } + test_pmu_put(dir, pmu); + return ret; +} + +static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test__hwmon_pmu(/*with_pmu=*/false); +} + +static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test__hwmon_pmu(/*with_pmu=*/true); +} + +static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + const struct hwmon_parse_test { + const char *filename; + enum hwmon_type type; + int number; + enum hwmon_item item; + bool alarm; + bool parse_ok; + } tests[] = { + { + .filename = "cpu0_accuracy", + .type = HWMON_TYPE_CPU, + .number = 0, + .item = HWMON_ITEM_ACCURACY, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "temp1_input", + .type = HWMON_TYPE_TEMP, + .number = 1, + .item = HWMON_ITEM_INPUT, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "fan2_vid", + .type = HWMON_TYPE_FAN, + .number = 2, + .item = HWMON_ITEM_VID, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "power3_crit_alarm", + .type = HWMON_TYPE_POWER, + .number = 3, + .item = HWMON_ITEM_CRIT, + .alarm = true, + .parse_ok = true, + }, + { + .filename = "intrusion4_average_interval_min_alarm", + .type = HWMON_TYPE_INTRUSION, + .number = 4, + .item = HWMON_ITEM_AVERAGE_INTERVAL_MIN, + .alarm = true, + .parse_ok = true, + }, + { + .filename = "badtype5_baditem", + .type = HWMON_TYPE_NONE, + .number = 5, + .item = HWMON_ITEM_NONE, + .alarm = false, + .parse_ok = false, + }, + { + .filename = "humidity6_baditem", + .type = HWMON_TYPE_NONE, + .number = 6, + .item = HWMON_ITEM_NONE, + .alarm = false, + .parse_ok = false, + }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(tests); i++) { + enum hwmon_type type; + int number; + enum hwmon_item item; + bool alarm; + + TEST_ASSERT_EQUAL("parse_hwmon_filename", + parse_hwmon_filename( + tests[i].filename, + &type, + &number, + &item, + &alarm), + tests[i].parse_ok + ); + if (tests[i].parse_ok) { + TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type); + TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number); + TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item); + TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm); + } + } + return TEST_OK; +} + +static struct test_case tests__hwmon_pmu[] = { + TEST_CASE("Basic parsing test", parse_hwmon_filename), + TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu), + TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu), + { .name = NULL, } +}; + +struct test_suite suite__hwmon_pmu = { + .desc = "Hwmon PMU", + .test_cases = tests__hwmon_pmu, +}; diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 8a4da7eb637a..0ee94caf9ec1 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -70,6 +70,7 @@ make_python_perf_so := $(python_perf_so) make_debug := DEBUG=1 make_nondistro := BUILD_NONDISTRO=1 make_extra_tests := EXTRA_TESTS=1 +make_jevents_all := JEVENTS_ARCH=all make_no_bpf_skel := BUILD_BPF_SKEL=0 make_gen_vmlinux_h := GEN_VMLINUX_H=1 make_no_libperl := NO_LIBPERL=1 @@ -80,21 +81,21 @@ make_no_gtk2 := NO_GTK2=1 make_no_ui := NO_SLANG=1 NO_GTK2=1 make_no_demangle := NO_DEMANGLE=1 make_no_libelf := NO_LIBELF=1 -make_no_libunwind := NO_LIBUNWIND=1 +make_libunwind := LIBUNWIND=1 make_no_libdw_dwarf_unwind := NO_LIBDW_DWARF_UNWIND=1 make_no_backtrace := NO_BACKTRACE=1 +make_no_libcapstone := NO_CAPSTONE=1 make_no_libnuma := NO_LIBNUMA=1 -make_no_libaudit := NO_LIBAUDIT=1 make_no_libbionic := NO_LIBBIONIC=1 make_no_auxtrace := NO_AUXTRACE=1 make_no_libbpf := NO_LIBBPF=1 make_libbpf_dynamic := LIBBPF_DYNAMIC=1 make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1 make_no_libcrypto := NO_LIBCRYPTO=1 +make_no_libllvm := NO_LIBLLVM=1 make_with_babeltrace:= LIBBABELTRACE=1 make_with_coresight := CORESIGHT=1 make_no_sdt := NO_SDT=1 -make_no_syscall_tbl := NO_SYSCALL_TABLE=1 make_no_libpfm4 := NO_LIBPFM4=1 make_with_gtk2 := GTK2=1 make_refcnt_check := EXTRA_CFLAGS="-DREFCNT_CHECKING=1" @@ -118,11 +119,11 @@ make_static := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX3 # all the NO_* variable combined make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_GTK2=1 -make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 -make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 +make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1 +make_minimal += NO_LIBNUMA=1 NO_LIBBIONIC=1 make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1 -make_minimal += NO_LIBCAP=1 NO_SYSCALL_TABLE=1 +make_minimal += NO_LIBCAP=1 NO_CAPSTONE=1 # $(run) contains all available tests run := make_pure @@ -139,6 +140,7 @@ run += make_python_perf_so run += make_debug run += make_nondistro run += make_extra_tests +run += make_jevents_all run += make_no_bpf_skel run += make_gen_vmlinux_h run += make_no_libperl @@ -149,16 +151,17 @@ run += make_no_gtk2 run += make_no_ui run += make_no_demangle run += make_no_libelf -run += make_no_libunwind +run += make_libunwind run += make_no_libdw_dwarf_unwind run += make_no_backtrace +run += make_no_libcapstone run += make_no_libnuma -run += make_no_libaudit run += make_no_libbionic run += make_no_auxtrace run += make_no_libbpf run += make_no_libbpf_DEBUG run += make_no_libcrypto +run += make_no_libllvm run += make_no_sdt run += make_no_syscall_tbl run += make_with_babeltrace diff --git a/tools/perf/tests/maps.c b/tools/perf/tests/maps.c index bb3fbfe5a73e..4f1f9385ea9c 100644 --- a/tools/perf/tests/maps.c +++ b/tools/perf/tests/maps.c @@ -26,7 +26,7 @@ static int check_maps_cb(struct map *map, void *data) if (map__start(map) != merged->start || map__end(map) != merged->end || - strcmp(map__dso(map)->name, merged->name) || + strcmp(dso__name(map__dso(map)), merged->name) || refcount_read(map__refcnt(map)) != 1) { return 1; } @@ -39,7 +39,7 @@ static int failed_cb(struct map *map, void *data __maybe_unused) pr_debug("\tstart: %" PRIu64 " end: %" PRIu64 " name: '%s' refcnt: %d\n", map__start(map), map__end(map), - map__dso(map)->name, + dso__name(map__dso(map)), refcount_read(map__refcnt(map))); return 0; @@ -156,6 +156,9 @@ static int test__maps__merge_in(struct test_suite *t __maybe_unused, int subtest TEST_ASSERT_VAL("merge check failed", !ret); maps__zput(maps); + map__zput(map_kcore1); + map__zput(map_kcore2); + map__zput(map_kcore3); return TEST_OK; } diff --git a/tools/perf/tests/mem.c b/tools/perf/tests/mem.c index 56014ec7d49d..cb3d749e157b 100644 --- a/tools/perf/tests/mem.c +++ b/tools/perf/tests/mem.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "util/map_symbol.h" #include "util/mem-events.h" +#include "util/mem-info.h" #include "util/symbol.h" #include "linux/perf_event.h" #include "util/debug.h" @@ -12,12 +13,14 @@ static int check(union perf_mem_data_src data_src, { char out[100]; char failure[100]; - struct mem_info mi = { .data_src = data_src }; - + struct mem_info *mi = mem_info__new(); int n; - n = perf_mem__snp_scnprintf(out, sizeof out, &mi); - n += perf_mem__lvl_scnprintf(out + n, sizeof out - n, &mi); + TEST_ASSERT_VAL("Memory allocation failed", mi); + *mem_info__data_src(mi) = data_src; + n = perf_mem__snp_scnprintf(out, sizeof out, mi); + n += perf_mem__lvl_scnprintf(out + n, sizeof out - n, mi); + mem_info__put(mi); scnprintf(failure, sizeof failure, "unexpected %s", out); TEST_ASSERT_VAL(failure, !strcmp(string, out)); return 0; diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 888df8eca981..3943da441979 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -40,7 +40,7 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused int flags = O_RDONLY | O_DIRECTORY; struct evlist *evlist = evlist__new(); struct evsel *evsel; - int err = -1, i, nr_events = 0, nr_polls = 0; + int ret = TEST_FAIL, err, i, nr_events = 0, nr_polls = 0; char sbuf[STRERR_BUFSIZE]; if (evlist == NULL) { @@ -51,6 +51,7 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused evsel = evsel__newtp("syscalls", "sys_enter_openat"); if (IS_ERR(evsel)) { pr_debug("%s: evsel__newtp\n", __func__); + ret = PTR_ERR(evsel) == -EACCES ? TEST_SKIP : TEST_FAIL; goto out_delete_evlist; } @@ -138,11 +139,21 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused } } out_ok: - err = 0; + ret = TEST_OK; out_delete_evlist: evlist__delete(evlist); out: - return err; + return ret; } -DEFINE_SUITE("syscalls:sys_enter_openat event fields", syscall_openat_tp_fields); +static struct test_case tests__syscall_openat_tp_fields[] = { + TEST_CASE_REASON("syscalls:sys_enter_openat event fields", + syscall_openat_tp_fields, + "permissions"), + { .name = NULL, } +}; + +struct test_suite suite__syscall_openat_tp_fields = { + .desc = "syscalls:sys_enter_openat event fields", + .test_cases = tests__syscall_openat_tp_fields, +}; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index fbdf710d5eea..5ec2e5607987 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -54,8 +54,6 @@ static bool test_perf_config(const struct perf_evsel *evsel, __u64 expected_conf return (evsel->attr.config & PERF_HW_EVENT_MASK) == expected_config; } -#ifdef HAVE_LIBTRACEEVENT - #if defined(__s390x__) /* Return true if kvm module is available and loaded. Test this * and return success when trace point kvm_s390_create_vm @@ -112,7 +110,6 @@ static int test__checkevent_tracepoint_multi(struct evlist *evlist) } return TEST_OK; } -#endif /* HAVE_LIBTRACEEVENT */ static int test__checkevent_raw(struct evlist *evlist) { @@ -262,7 +259,7 @@ static int test__checkevent_breakpoint_x(struct evlist *evlist) TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_X == evsel->core.attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->core.attr.bp_len); + TEST_ASSERT_VAL("wrong bp_len", default_breakpoint_len() == evsel->core.attr.bp_len); return TEST_OK; } @@ -311,7 +308,6 @@ static int test__checkevent_breakpoint_rw(struct evlist *evlist) return TEST_OK; } -#ifdef HAVE_LIBTRACEEVENT static int test__checkevent_tracepoint_modifier(struct evlist *evlist) { struct evsel *evsel = evlist__first(evlist); @@ -340,7 +336,6 @@ test__checkevent_tracepoint_multi_modifier(struct evlist *evlist) return test__checkevent_tracepoint_multi(evlist); } -#endif /* HAVE_LIBTRACEEVENT */ static int test__checkevent_raw_modifier(struct evlist *evlist) { @@ -470,8 +465,7 @@ static int test__checkevent_breakpoint_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "mem:0:u")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "mem:0:u")); return test__checkevent_breakpoint(evlist); } @@ -484,8 +478,7 @@ static int test__checkevent_breakpoint_x_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "mem:0:x:k")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "mem:0:x:k")); return test__checkevent_breakpoint_x(evlist); } @@ -498,8 +491,7 @@ static int test__checkevent_breakpoint_r_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "mem:0:r:hp")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "mem:0:r:hp")); return test__checkevent_breakpoint_r(evlist); } @@ -512,8 +504,7 @@ static int test__checkevent_breakpoint_w_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "mem:0:w:up")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "mem:0:w:up")); return test__checkevent_breakpoint_w(evlist); } @@ -526,8 +517,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "mem:0:rw:kp")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "mem:0:rw:kp")); return test__checkevent_breakpoint_rw(evlist); } @@ -540,8 +530,7 @@ static int test__checkevent_breakpoint_modifier_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "breakpoint")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint")); return test__checkevent_breakpoint(evlist); } @@ -554,8 +543,7 @@ static int test__checkevent_breakpoint_x_modifier_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "breakpoint")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint")); return test__checkevent_breakpoint_x(evlist); } @@ -568,8 +556,7 @@ static int test__checkevent_breakpoint_r_modifier_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "breakpoint")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint")); return test__checkevent_breakpoint_r(evlist); } @@ -582,8 +569,7 @@ static int test__checkevent_breakpoint_w_modifier_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "breakpoint")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint")); return test__checkevent_breakpoint_w(evlist); } @@ -596,8 +582,7 @@ static int test__checkevent_breakpoint_rw_modifier_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "breakpoint")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint")); return test__checkevent_breakpoint_rw(evlist); } @@ -609,12 +594,12 @@ static int test__checkevent_breakpoint_2_events(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "breakpoint1")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint1")); evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type); - TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "breakpoint2")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "breakpoint2")); return TEST_OK; } @@ -639,7 +624,6 @@ static int test__checkevent_pmu(struct evlist *evlist) return TEST_OK; } -#ifdef HAVE_LIBTRACEEVENT static int test__checkevent_list(struct evlist *evlist) { struct evsel *evsel = evlist__first(evlist); @@ -681,7 +665,6 @@ static int test__checkevent_list(struct evlist *evlist) return TEST_OK; } -#endif static int test__checkevent_pmu_name(struct evlist *evlist) { @@ -691,15 +674,14 @@ static int test__checkevent_pmu_name(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", test_config(evsel, 1)); - TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "krava")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "krava")); /* cpu/config=2/u" */ evsel = evsel__next(evsel); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type); TEST_ASSERT_VAL("wrong config", test_config(evsel, 2)); - TEST_ASSERT_VAL("wrong name", - !strcmp(evsel__name(evsel), "cpu/config=2/u")); + TEST_ASSERT_VAL("wrong name", evsel__name_is(evsel, "cpu/config=2/u")); return TEST_OK; } @@ -741,7 +723,7 @@ static int test__checkevent_pmu_events(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || - strcmp(evsel->pmu_name, "cpu")); + strcmp(evsel->pmu->name, "cpu")); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -909,8 +891,7 @@ static int test__group1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -943,7 +924,7 @@ static int test__group2(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -953,12 +934,12 @@ static int test__group2(struct evlist *evlist) continue; } if (evsel->core.attr.type == PERF_TYPE_HARDWARE && - test_config(evsel, PERF_COUNT_HW_CACHE_REFERENCES)) { - /* cache-references + :u modifier */ + test_config(evsel, PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) { + /* branches + :u modifier */ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); if (evsel__has_leader(evsel, leader)) @@ -983,7 +964,6 @@ static int test__group2(struct evlist *evlist) return TEST_OK; } -#ifdef HAVE_LIBTRACEEVENT static int test__group3(struct evlist *evlist __maybe_unused) { struct evsel *evsel, *group1_leader = NULL, *group2_leader = NULL; @@ -1027,9 +1007,8 @@ static int test__group3(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ TEST_ASSERT_VAL("wrong exclude guest", - evsel->core.attr.exclude_guest); + !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", @@ -1083,7 +1062,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -1091,7 +1070,6 @@ static int test__group3(struct evlist *evlist __maybe_unused) } return TEST_OK; } -#endif static int test__group4(struct evlist *evlist __maybe_unused) { @@ -1114,8 +1092,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1133,8 +1110,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -1233,7 +1209,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -1448,7 +1424,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1464,7 +1440,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -1479,7 +1455,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1508,7 +1484,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1524,7 +1500,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1828,7 +1804,6 @@ static int test__term_equal_legacy(struct evlist *evlist) return TEST_OK; } -#ifdef HAVE_LIBTRACEEVENT static int count_tracepoints(void) { struct dirent *events_ent; @@ -1882,7 +1857,6 @@ static int test__all_tracepoints(struct evlist *evlist) return test__checkevent_tracepoint_multi(evlist); } -#endif /* HAVE_LIBTRACEVENT */ struct evlist_test { const char *name; @@ -1891,7 +1865,6 @@ struct evlist_test { }; static const struct evlist_test test__events[] = { -#ifdef HAVE_LIBTRACEEVENT { .name = "syscalls:sys_enter_openat", .check = test__checkevent_tracepoint, @@ -1902,7 +1875,6 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_tracepoint_multi, /* 1 */ }, -#endif { .name = "r1a", .check = test__checkevent_raw, @@ -1953,7 +1925,6 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_breakpoint_w, /* 1 */ }, -#ifdef HAVE_LIBTRACEEVENT { .name = "syscalls:sys_enter_openat:k", .check = test__checkevent_tracepoint_modifier, @@ -1964,7 +1935,6 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_tracepoint_multi_modifier, /* 3 */ }, -#endif { .name = "r1a:kp", .check = test__checkevent_raw_modifier, @@ -2010,13 +1980,11 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_breakpoint_w_modifier, /* 2 */ }, -#ifdef HAVE_LIBTRACEEVENT { .name = "r1,syscalls:sys_enter_openat:k,1:1:hp", .check = test__checkevent_list, /* 3 */ }, -#endif { .name = "instructions:G", .check = test__checkevent_exclude_host_modifier, @@ -2043,17 +2011,15 @@ static const struct evlist_test test__events[] = { /* 8 */ }, { - .name = "{faults:k,cache-references}:u,cycles:k", + .name = "{faults:k,branches}:u,cycles:k", .check = test__group2, /* 9 */ }, -#ifdef HAVE_LIBTRACEEVENT { .name = "group1{syscalls:sys_enter_openat:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", .check = test__group3, /* 0 */ }, -#endif { .name = "{cycles:u,instructions:kp}:p", .check = test__group4, @@ -2064,13 +2030,11 @@ static const struct evlist_test test__events[] = { .check = test__group5, /* 2 */ }, -#ifdef HAVE_LIBTRACEEVENT { .name = "*:*", .check = test__all_tracepoints, /* 3 */ }, -#endif { .name = "{cycles,cache-misses:G}:H", .check = test__group_gh1, @@ -2126,7 +2090,7 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_breakpoint_len_rw_modifier, /* 4 */ }, -#if defined(__s390x__) && defined(HAVE_LIBTRACEEVENT) +#if defined(__s390x__) { .name = "kvm-s390:kvm_s390_create_vm", .check = test__checkevent_tracepoint, @@ -2280,6 +2244,11 @@ static const struct evlist_test test__events[] = { .check = test__checkevent_breakpoint_2_events, /* 3 */ }, + { + .name = "9p:9p_client_req", + .check = test__checkevent_tracepoint, + /* 4 */ + }, }; static const struct evlist_test test__events_pmu[] = { @@ -2504,13 +2473,13 @@ static int test_event(const struct evlist_test *e) return TEST_FAIL; } parse_events_error__init(&err); - ret = parse_events(evlist, e->name, &err); + ret = __parse_events(evlist, e->name, /*pmu_filter=*/NULL, &err, /*fake_pmu=*/false, + /*warn_if_reordered=*/true, /*fake_tp=*/true); if (ret) { - pr_debug("failed to parse event '%s', err %d, str '%s'\n", - e->name, ret, err.str); + pr_debug("failed to parse event '%s', err %d\n", e->name, ret); parse_events_error__print(&err, e->name); ret = TEST_FAIL; - if (err.str && strstr(err.str, "can't access trace events")) + if (parse_events_error__contains(&err, "can't access trace events")) ret = TEST_SKIP; } else { ret = e->check(evlist); @@ -2533,10 +2502,11 @@ static int test_event_fake_pmu(const char *str) parse_events_error__init(&err); ret = __parse_events(evlist, str, /*pmu_filter=*/NULL, &err, - &perf_pmu__fake, /*warn_if_reordered=*/true); + /*fake_pmu=*/true, /*warn_if_reordered=*/true, + /*fake_tp=*/true); if (ret) { - pr_debug("failed to parse event '%s', err %d, str '%s'\n", - str, ret, err.str); + pr_debug("failed to parse event '%s', err %d\n", + str, ret); parse_events_error__print(&err, str); } diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index a56d32905743..db004d26fcb0 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -70,7 +70,7 @@ static const struct perf_pmu_test_event segment_reg_loads_any = { .event = { .pmu = "default_core", .name = "segment_reg_loads.any", - .event = "event=0x6,period=200000,umask=0x80", + .event = "event=6,period=200000,umask=0x80", .desc = "Number of segment register loads", .topic = "other", }, @@ -82,7 +82,7 @@ static const struct perf_pmu_test_event dispatch_blocked_any = { .event = { .pmu = "default_core", .name = "dispatch_blocked.any", - .event = "event=0x9,period=200000,umask=0x20", + .event = "event=9,period=200000,umask=0x20", .desc = "Memory cluster signals to block micro-op dispatch for any reason", .topic = "other", }, @@ -94,11 +94,11 @@ static const struct perf_pmu_test_event eist_trans = { .event = { .pmu = "default_core", .name = "eist_trans", - .event = "event=0x3a,period=200000,umask=0x0", + .event = "event=0x3a,period=200000", .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", .topic = "other", }, - .alias_str = "event=0x3a,period=0x30d40,umask=0", + .alias_str = "event=0x3a,period=0x30d40", .alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", }; @@ -128,7 +128,7 @@ static const struct perf_pmu_test_event *core_events[] = { static const struct perf_pmu_test_event uncore_hisi_ddrc_flux_wcmd = { .event = { .name = "uncore_hisi_ddrc.flux_wcmd", - .event = "event=0x2", + .event = "event=2", .desc = "DDRC write commands", .topic = "uncore", .long_desc = "DDRC write commands", @@ -156,13 +156,13 @@ static const struct perf_pmu_test_event unc_cbo_xsnp_response_miss_eviction = { static const struct perf_pmu_test_event uncore_hyphen = { .event = { .name = "event-hyphen", - .event = "event=0xe0,umask=0x00", + .event = "event=0xe0", .desc = "UNC_CBO_HYPHEN", .topic = "uncore", .long_desc = "UNC_CBO_HYPHEN", .pmu = "uncore_cbox", }, - .alias_str = "event=0xe0,umask=0", + .alias_str = "event=0xe0", .alias_long_desc = "UNC_CBO_HYPHEN", .matching_pmu = "uncore_cbox_0", }; @@ -170,13 +170,13 @@ static const struct perf_pmu_test_event uncore_hyphen = { static const struct perf_pmu_test_event uncore_two_hyph = { .event = { .name = "event-two-hyph", - .event = "event=0xc0,umask=0x00", + .event = "event=0xc0", .desc = "UNC_CBO_TWO_HYPH", .topic = "uncore", .long_desc = "UNC_CBO_TWO_HYPH", .pmu = "uncore_cbox", }, - .alias_str = "event=0xc0,umask=0", + .alias_str = "event=0xc0", .alias_long_desc = "UNC_CBO_TWO_HYPH", .matching_pmu = "uncore_cbox_0", }; @@ -184,7 +184,7 @@ static const struct perf_pmu_test_event uncore_two_hyph = { static const struct perf_pmu_test_event uncore_hisi_l3c_rd_hit_cpipe = { .event = { .name = "uncore_hisi_l3c.rd_hit_cpipe", - .event = "event=0x7", + .event = "event=7", .desc = "Total read hits", .topic = "uncore", .long_desc = "Total read hits", @@ -265,7 +265,7 @@ static const struct perf_pmu_test_event sys_ccn_pmu_read_cycles = { static const struct perf_pmu_test_event sys_cmn_pmu_hnf_cache_miss = { .event = { .name = "sys_cmn_pmu.hnf_cache_miss", - .event = "eventid=0x1,type=0x5", + .event = "eventid=1,type=5", .desc = "Counts total cache misses in first lookup result (high priority)", .topic = "uncore", .pmu = "uncore_sys_cmn_pmu", @@ -819,8 +819,7 @@ static bool is_number(const char *str) return errno == 0 && end_ptr != str; } -static int check_parse_id(const char *id, struct parse_events_error *error, - struct perf_pmu *fake_pmu) +static int check_parse_id(const char *id, struct parse_events_error *error) { struct evlist *evlist; int ret; @@ -841,8 +840,8 @@ static int check_parse_id(const char *id, struct parse_events_error *error, for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@')) *cur = '/'; - ret = __parse_events(evlist, dup, /*pmu_filter=*/NULL, error, fake_pmu, - /*warn_if_reordered=*/true); + ret = __parse_events(evlist, dup, /*pmu_filter=*/NULL, error, /*fake_pmu=*/true, + /*warn_if_reordered=*/true, /*fake_tp=*/false); free(dup); evlist__delete(evlist); @@ -855,7 +854,7 @@ static int check_parse_fake(const char *id) int ret; parse_events_error__init(&error); - ret = check_parse_id(id, &error, &perf_pmu__fake); + ret = check_parse_id(id, &error); parse_events_error__exit(&error); return ret; } @@ -1051,9 +1050,8 @@ static int test__parsing_fake_callback(const struct pmu_metric *pm, } /* - * Parse all the metrics for current architecture, - * or all defined cpus via the 'fake_pmu' - * in parse_events. + * Parse all the metrics for current architecture, or all defined cpus via the + * 'fake_pmu' in parse_events. */ static int test__parsing_fake(struct test_suite *test __maybe_unused, int subtest __maybe_unused) @@ -1105,6 +1103,6 @@ static struct test_case pmu_events_tests[] = { }; struct test_suite suite__pmu_events = { - .desc = "PMU events", + .desc = "PMU JSON event tests", .test_cases = pmu_events_tests, }; diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 8f18127d876a..6a681e3fb552 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -1,204 +1,546 @@ // SPDX-License-Identifier: GPL-2.0 +#include "evlist.h" +#include "evsel.h" #include "parse-events.h" #include "pmu.h" +#include "pmus.h" #include "tests.h" +#include "debug.h" +#include "fncache.h" +#include <api/fs/fs.h> +#include <ctype.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> -#include <linux/kernel.h> -#include <linux/limits.h> -#include <linux/zalloc.h> - -/* Simulated format definitions. */ -static struct test_format { - const char *name; - const char *value; -} test_formats[] = { - { "krava01", "config:0-1,62-63\n", }, - { "krava02", "config:10-17\n", }, - { "krava03", "config:5\n", }, - { "krava11", "config1:0,2,4,6,8,20-28\n", }, - { "krava12", "config1:63\n", }, - { "krava13", "config1:45-47\n", }, - { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, - { "krava22", "config2:8,18,48,58\n", }, - { "krava23", "config2:28-29,38\n", }, -}; +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> -/* Simulated users input. */ -static struct parse_events_term test_terms[] = { - { - .config = "krava01", - .val.num = 15, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava02", - .val.num = 170, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava03", - .val.num = 1, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava11", - .val.num = 27, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava12", - .val.num = 1, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava13", - .val.num = 2, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava21", - .val.num = 119, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava22", - .val.num = 11, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, - { - .config = "krava23", - .val.num = 2, - .type_val = PARSE_EVENTS__TERM_TYPE_NUM, - .type_term = PARSE_EVENTS__TERM_TYPE_USER, - }, -}; +/* Cleanup test PMU directory. */ +static int test_pmu_put(const char *dir, struct perf_pmu *pmu) +{ + char buf[PATH_MAX + 20]; + int ret; + + if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) { + pr_err("Failure to set up buffer for \"%s\"\n", dir); + return -EINVAL; + } + ret = system(buf); + if (ret) + pr_err("Failure to \"%s\"\n", buf); + + list_del(&pmu->list); + perf_pmu__delete(pmu); + return ret; +} /* - * Prepare format directory data, exported by kernel - * at /sys/bus/event_source/devices/<dev>/format. + * Prepare test PMU directory data, normally exported by kernel at + * /sys/bus/event_source/devices/<pmu>/. Give as input a buffer to hold the file + * path, the result is PMU loaded using that directory. */ -static char *test_format_dir_get(char *dir, size_t sz) +static struct perf_pmu *test_pmu_get(char *dir, size_t sz) { - unsigned int i; + /* Simulated format definitions. */ + const struct test_format { + const char *name; + const char *value; + } test_formats[] = { + { "krava01", "config:0-1,62-63\n", }, + { "krava02", "config:10-17\n", }, + { "krava03", "config:5\n", }, + { "krava11", "config1:0,2,4,6,8,20-28\n", }, + { "krava12", "config1:63\n", }, + { "krava13", "config1:45-47\n", }, + { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, + { "krava22", "config2:8,18,48,58\n", }, + { "krava23", "config2:28-29,38\n", }, + }; + const char *test_event = "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1," + "krava13=2,krava21=119,krava22=11,krava23=2\n"; - snprintf(dir, sz, "/tmp/perf-pmu-test-format-XXXXXX"); - if (!mkdtemp(dir)) + char name[PATH_MAX]; + int dirfd, file; + struct perf_pmu *pmu = NULL; + ssize_t len; + + /* Create equivalent of sysfs mount point. */ + scnprintf(dir, sz, "/tmp/perf-pmu-test-XXXXXX"); + if (!mkdtemp(dir)) { + pr_err("mkdtemp failed\n"); + dir[0] = '\0'; return NULL; + } + dirfd = open(dir, O_DIRECTORY); + if (dirfd < 0) { + pr_err("Failed to open test directory \"%s\"\n", dir); + goto err_out; + } - for (i = 0; i < ARRAY_SIZE(test_formats); i++) { - char name[PATH_MAX]; - struct test_format *format = &test_formats[i]; - FILE *file; + /* Create the test PMU directory and give it a perf_event_attr type number. */ + if (mkdirat(dirfd, "perf-pmu-test", 0755) < 0) { + pr_err("Failed to mkdir PMU directory\n"); + goto err_out; + } + file = openat(dirfd, "perf-pmu-test/type", O_WRONLY | O_CREAT, 0600); + if (!file) { + pr_err("Failed to open for writing file \"type\"\n"); + goto err_out; + } + len = strlen("9999"); + if (write(file, "9999\n", len) < len) { + close(file); + pr_err("Failed to write to 'type' file\n"); + goto err_out; + } + close(file); - scnprintf(name, PATH_MAX, "%s/%s", dir, format->name); + /* Create format directory and files. */ + if (mkdirat(dirfd, "perf-pmu-test/format", 0755) < 0) { + pr_err("Failed to mkdir PMU format directory\n)"); + goto err_out; + } + for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) { + const struct test_format *format = &test_formats[i]; - file = fopen(name, "w"); - if (!file) - return NULL; + if (scnprintf(name, PATH_MAX, "perf-pmu-test/format/%s", format->name) < 0) { + pr_err("Failure to set up path for \"%s\"\n", format->name); + goto err_out; + } + file = openat(dirfd, name, O_WRONLY | O_CREAT, 0600); + if (!file) { + pr_err("Failed to open for writing file \"%s\"\n", name); + goto err_out; + } - if (1 != fwrite(format->value, strlen(format->value), 1, file)) - break; + if (write(file, format->value, strlen(format->value)) < 0) { + pr_err("Failed to write to file \"%s\"\n", name); + close(file); + goto err_out; + } + close(file); + } - fclose(file); + /* Create test event. */ + if (mkdirat(dirfd, "perf-pmu-test/events", 0755) < 0) { + pr_err("Failed to mkdir PMU events directory\n"); + goto err_out; + } + file = openat(dirfd, "perf-pmu-test/events/test-event", O_WRONLY | O_CREAT, 0600); + if (!file) { + pr_err("Failed to open for writing file \"type\"\n"); + goto err_out; } + len = strlen(test_event); + if (write(file, test_event, len) < len) { + close(file); + pr_err("Failed to write to 'test-event' file\n"); + goto err_out; + } + close(file); + + /* Make the PMU reading the files created above. */ + pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test"); + if (!pmu) + pr_err("Test PMU creation failed\n"); - return dir; +err_out: + if (!pmu) + test_pmu_put(dir, pmu); + if (dirfd >= 0) + close(dirfd); + return pmu; } -/* Cleanup format directory. */ -static int test_format_dir_put(char *dir) +static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - char buf[PATH_MAX + 20]; + char dir[PATH_MAX]; + struct perf_event_attr attr; + struct parse_events_terms terms; + int ret = TEST_FAIL; + struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir)); + + if (!pmu) + return TEST_FAIL; + + parse_events_terms__init(&terms); + if (parse_events_terms(&terms, + "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1," + "krava13=2,krava21=119,krava22=11,krava23=2", + NULL)) { + pr_err("Term parsing failed\n"); + goto err_out; + } - snprintf(buf, sizeof(buf), "rm -f %s/*\n", dir); - if (system(buf)) - return -1; + memset(&attr, 0, sizeof(attr)); + ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, + /*apply_hardcoded=*/false, /*err=*/NULL); + if (ret) { + pr_err("perf_pmu__config_terms failed"); + goto err_out; + } + + if (attr.config != 0xc00000000002a823) { + pr_err("Unexpected config value %llx\n", attr.config); + goto err_out; + } + if (attr.config1 != 0x8000400000000145) { + pr_err("Unexpected config1 value %llx\n", attr.config1); + goto err_out; + } + if (attr.config2 != 0x0400000020041d07) { + pr_err("Unexpected config2 value %llx\n", attr.config2); + goto err_out; + } - snprintf(buf, sizeof(buf), "rmdir %s\n", dir); - return system(buf); + ret = TEST_OK; +err_out: + parse_events_terms__exit(&terms); + test_pmu_put(dir, pmu); + return ret; } -static void add_test_terms(struct parse_events_terms *terms) +static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - unsigned int i; + char dir[PATH_MAX]; + struct parse_events_error err; + struct evlist *evlist; + struct evsel *evsel; + struct perf_event_attr *attr; + int ret = TEST_FAIL; + struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir)); + const char *event = "perf-pmu-test/test-event/"; - for (i = 0; i < ARRAY_SIZE(test_terms); i++) { - struct parse_events_term *clone; - parse_events_term__clone(&clone, &test_terms[i]); - list_add_tail(&clone->list, &terms->terms); + if (!pmu) + return TEST_FAIL; + + evlist = evlist__new(); + if (evlist == NULL) { + pr_err("Failed allocation"); + goto err_out; + } + parse_events_error__init(&err); + ret = parse_events(evlist, event, &err); + if (ret) { + pr_debug("failed to parse event '%s', err %d\n", event, ret); + parse_events_error__print(&err, event); + if (parse_events_error__contains(&err, "can't access trace events")) + ret = TEST_SKIP; + goto err_out; + } + evsel = evlist__first(evlist); + attr = &evsel->core.attr; + if (attr->config != 0xc00000000002a823) { + pr_err("Unexpected config value %llx\n", attr->config); + goto err_out; } + if (attr->config1 != 0x8000400000000145) { + pr_err("Unexpected config1 value %llx\n", attr->config1); + goto err_out; + } + if (attr->config2 != 0x0400000020041d07) { + pr_err("Unexpected config2 value %llx\n", attr->config2); + goto err_out; + } + + ret = TEST_OK; +err_out: + parse_events_error__exit(&err); + evlist__delete(evlist); + test_pmu_put(dir, pmu); + return ret; } -static int test__pmu(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +static bool permitted_event_name(const char *name) { - char dir[PATH_MAX]; - char *format; - struct parse_events_terms terms; - struct perf_event_attr attr; - struct perf_pmu *pmu; - int fd; - int ret; + bool has_lower = false, has_upper = false; + __u64 config; - parse_events_terms__init(&terms); - add_test_terms(&terms); - pmu = zalloc(sizeof(*pmu)); - if (!pmu) { - parse_events_terms__exit(&terms); - return -ENOMEM; - } - - INIT_LIST_HEAD(&pmu->format); - INIT_LIST_HEAD(&pmu->aliases); - INIT_LIST_HEAD(&pmu->caps); - format = test_format_dir_get(dir, sizeof(dir)); - if (!format) { - free(pmu); - parse_events_terms__exit(&terms); - return -EINVAL; + for (size_t i = 0; i < strlen(name); i++) { + char c = name[i]; + + if (islower(c)) { + if (has_upper) + goto check_legacy; + has_lower = true; + continue; + } + if (isupper(c)) { + if (has_lower) + goto check_legacy; + has_upper = true; + continue; + } + if (!isdigit(c) && c != '.' && c != '_' && c != '-') + goto check_legacy; + } + return true; +check_legacy: + /* + * If the event name matches a legacy cache name the legacy encoding + * will still be used. This isn't quite WAI as sysfs events should take + * priority, but this case happens on PowerPC and matches the behavior + * in older perf tools where legacy events were the priority. Be + * permissive and assume later PMU drivers will use all lower or upper + * case names. + */ + if (parse_events__decode_legacy_cache(name, /*extended_pmu_type=*/0, &config) == 0) { + pr_warning("sysfs event '%s' should be all lower/upper case, it will be matched using legacy encoding.", + name); + return true; } + return false; +} - memset(&attr, 0, sizeof(attr)); +static int test__pmu_event_names(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + char path[PATH_MAX]; + DIR *pmu_dir, *event_dir; + struct dirent *pmu_dent, *event_dent; + const char *sysfs = sysfs__mountpoint(); + int ret = TEST_OK; - fd = open(format, O_DIRECTORY); - if (fd < 0) { - ret = fd; - goto out; + if (!sysfs) { + pr_err("Sysfs not mounted\n"); + return TEST_FAIL; } - pmu->name = strdup("perf-pmu-test"); - ret = perf_pmu__format_parse(pmu, fd, /*eager_load=*/true); - if (ret) - goto out; + snprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs); + pmu_dir = opendir(path); + if (!pmu_dir) { + pr_err("Error opening \"%s\"\n", path); + return TEST_FAIL; + } + while ((pmu_dent = readdir(pmu_dir))) { + if (!strcmp(pmu_dent->d_name, ".") || + !strcmp(pmu_dent->d_name, "..")) + continue; - ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL); - if (ret) - goto out; - - ret = -EINVAL; - if (attr.config != 0xc00000000002a823) - goto out; - if (attr.config1 != 0x8000400000000145) - goto out; - if (attr.config2 != 0x0400000020041d07) - goto out; - - ret = 0; -out: - test_format_dir_put(format); - perf_pmu__delete(pmu); - parse_events_terms__exit(&terms); + snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/type", + sysfs, pmu_dent->d_name); + + /* Does it look like a PMU? */ + if (!file_available(path)) + continue; + + /* Process events. */ + snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/events", + sysfs, pmu_dent->d_name); + + event_dir = opendir(path); + if (!event_dir) { + pr_debug("Skipping as no event directory \"%s\"\n", path); + continue; + } + while ((event_dent = readdir(event_dir))) { + const char *event_name = event_dent->d_name; + + if (!strcmp(event_name, ".") || !strcmp(event_name, "..")) + continue; + + if (!permitted_event_name(event_name)) { + pr_err("Invalid sysfs event name: %s/%s\n", + pmu_dent->d_name, event_name); + ret = TEST_FAIL; + } + } + closedir(event_dir); + } + closedir(pmu_dir); return ret; } -DEFINE_SUITE("Parse perf pmu format", pmu); +static const char * const uncore_chas[] = { + "uncore_cha_0", + "uncore_cha_1", + "uncore_cha_2", + "uncore_cha_3", + "uncore_cha_4", + "uncore_cha_5", + "uncore_cha_6", + "uncore_cha_7", + "uncore_cha_8", + "uncore_cha_9", + "uncore_cha_10", + "uncore_cha_11", + "uncore_cha_12", + "uncore_cha_13", + "uncore_cha_14", + "uncore_cha_15", + "uncore_cha_16", + "uncore_cha_17", + "uncore_cha_18", + "uncore_cha_19", + "uncore_cha_20", + "uncore_cha_21", + "uncore_cha_22", + "uncore_cha_23", + "uncore_cha_24", + "uncore_cha_25", + "uncore_cha_26", + "uncore_cha_27", + "uncore_cha_28", + "uncore_cha_29", + "uncore_cha_30", + "uncore_cha_31", +}; + +static const char * const mrvl_ddrs[] = { + "mrvl_ddr_pmu_87e1b0000000", + "mrvl_ddr_pmu_87e1b1000000", + "mrvl_ddr_pmu_87e1b2000000", + "mrvl_ddr_pmu_87e1b3000000", + "mrvl_ddr_pmu_87e1b4000000", + "mrvl_ddr_pmu_87e1b5000000", + "mrvl_ddr_pmu_87e1b6000000", + "mrvl_ddr_pmu_87e1b7000000", + "mrvl_ddr_pmu_87e1b8000000", + "mrvl_ddr_pmu_87e1b9000000", + "mrvl_ddr_pmu_87e1ba000000", + "mrvl_ddr_pmu_87e1bb000000", + "mrvl_ddr_pmu_87e1bc000000", + "mrvl_ddr_pmu_87e1bd000000", + "mrvl_ddr_pmu_87e1be000000", + "mrvl_ddr_pmu_87e1bf000000", +}; + +static int test__name_len(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + TEST_ASSERT_VAL("cpu", pmu_name_len_no_suffix("cpu") == strlen("cpu")); + TEST_ASSERT_VAL("i915", pmu_name_len_no_suffix("i915") == strlen("i915")); + TEST_ASSERT_VAL("cpum_cf", pmu_name_len_no_suffix("cpum_cf") == strlen("cpum_cf")); + for (size_t i = 0; i < ARRAY_SIZE(uncore_chas); i++) { + TEST_ASSERT_VAL("Strips uncore_cha suffix", + pmu_name_len_no_suffix(uncore_chas[i]) == + strlen("uncore_cha")); + } + for (size_t i = 0; i < ARRAY_SIZE(mrvl_ddrs); i++) { + TEST_ASSERT_VAL("Strips mrvl_ddr_pmu suffix", + pmu_name_len_no_suffix(mrvl_ddrs[i]) == + strlen("mrvl_ddr_pmu")); + } + return TEST_OK; +} + +static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + TEST_ASSERT_EQUAL("cpu", pmu_name_cmp("cpu", "cpu"), 0); + TEST_ASSERT_EQUAL("i915", pmu_name_cmp("i915", "i915"), 0); + TEST_ASSERT_EQUAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_cf"), 0); + TEST_ASSERT_VAL("i915", pmu_name_cmp("cpu", "i915") < 0); + TEST_ASSERT_VAL("i915", pmu_name_cmp("i915", "cpu") > 0); + TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_ce") > 0); + TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_d0") < 0); + for (size_t i = 1; i < ARRAY_SIZE(uncore_chas); i++) { + TEST_ASSERT_VAL("uncore_cha suffixes ordered lt", + pmu_name_cmp(uncore_chas[i-1], uncore_chas[i]) < 0); + TEST_ASSERT_VAL("uncore_cha suffixes ordered gt", + pmu_name_cmp(uncore_chas[i], uncore_chas[i-1]) > 0); + } + for (size_t i = 1; i < ARRAY_SIZE(mrvl_ddrs); i++) { + TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered lt", + pmu_name_cmp(mrvl_ddrs[i-1], mrvl_ddrs[i]) < 0); + TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered gt", + pmu_name_cmp(mrvl_ddrs[i], mrvl_ddrs[i-1]) > 0); + } + return TEST_OK; +} + +/** + * Test perf_pmu__match() that's used to search for a PMU given a name passed + * on the command line. The name that's passed may also be a filename type glob + * match. If the name does not match, perf_pmu__match() attempts to match the + * alias of the PMU, if provided. + */ +static int test__pmu_match(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + struct perf_pmu test_pmu = { + .name = "pmuname", + }; + + TEST_ASSERT_EQUAL("Exact match", perf_pmu__match(&test_pmu, "pmuname"), true); + TEST_ASSERT_EQUAL("Longer token", perf_pmu__match(&test_pmu, "longertoken"), false); + TEST_ASSERT_EQUAL("Shorter token", perf_pmu__match(&test_pmu, "pmu"), false); + + test_pmu.name = "pmuname_10"; + TEST_ASSERT_EQUAL("Diff suffix_", perf_pmu__match(&test_pmu, "pmuname_2"), false); + TEST_ASSERT_EQUAL("Sub suffix_", perf_pmu__match(&test_pmu, "pmuname_1"), true); + TEST_ASSERT_EQUAL("Same suffix_", perf_pmu__match(&test_pmu, "pmuname_10"), true); + TEST_ASSERT_EQUAL("No suffix_", perf_pmu__match(&test_pmu, "pmuname"), true); + TEST_ASSERT_EQUAL("Underscore_", perf_pmu__match(&test_pmu, "pmuname_"), true); + TEST_ASSERT_EQUAL("Substring_", perf_pmu__match(&test_pmu, "pmuna"), false); + + test_pmu.name = "pmuname_ab23"; + TEST_ASSERT_EQUAL("Diff suffix hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false); + TEST_ASSERT_EQUAL("Sub suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab"), true); + TEST_ASSERT_EQUAL("Same suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab23"), true); + TEST_ASSERT_EQUAL("No suffix hex_", perf_pmu__match(&test_pmu, "pmuname"), true); + TEST_ASSERT_EQUAL("Underscore hex_", perf_pmu__match(&test_pmu, "pmuname_"), true); + TEST_ASSERT_EQUAL("Substring hex_", perf_pmu__match(&test_pmu, "pmuna"), false); + + test_pmu.name = "pmuname10"; + TEST_ASSERT_EQUAL("Diff suffix", perf_pmu__match(&test_pmu, "pmuname2"), false); + TEST_ASSERT_EQUAL("Sub suffix", perf_pmu__match(&test_pmu, "pmuname1"), true); + TEST_ASSERT_EQUAL("Same suffix", perf_pmu__match(&test_pmu, "pmuname10"), true); + TEST_ASSERT_EQUAL("No suffix", perf_pmu__match(&test_pmu, "pmuname"), true); + TEST_ASSERT_EQUAL("Underscore", perf_pmu__match(&test_pmu, "pmuname_"), false); + TEST_ASSERT_EQUAL("Substring", perf_pmu__match(&test_pmu, "pmuna"), false); + + test_pmu.name = "pmunameab23"; + TEST_ASSERT_EQUAL("Diff suffix hex", perf_pmu__match(&test_pmu, "pmuname2"), false); + TEST_ASSERT_EQUAL("Sub suffix hex", perf_pmu__match(&test_pmu, "pmunameab"), true); + TEST_ASSERT_EQUAL("Same suffix hex", perf_pmu__match(&test_pmu, "pmunameab23"), true); + TEST_ASSERT_EQUAL("No suffix hex", perf_pmu__match(&test_pmu, "pmuname"), true); + TEST_ASSERT_EQUAL("Underscore hex", perf_pmu__match(&test_pmu, "pmuname_"), false); + TEST_ASSERT_EQUAL("Substring hex", perf_pmu__match(&test_pmu, "pmuna"), false); + + /* + * 2 hex chars or less are not considered suffixes so it shouldn't be + * possible to wildcard by skipping the suffix. Therefore there are more + * false results here than above. + */ + test_pmu.name = "pmuname_a3"; + TEST_ASSERT_EQUAL("Diff suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false); + /* + * This one should be false, but because pmuname_a3 ends in 3 which is + * decimal, it's not possible to determine if it's a short hex suffix or + * a normal decimal suffix following text. And we want to match on any + * length of decimal suffix. Run the test anyway and expect the wrong + * result. And slightly fuzzy matching shouldn't do too much harm. + */ + TEST_ASSERT_EQUAL("Sub suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a"), true); + TEST_ASSERT_EQUAL("Same suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a3"), true); + TEST_ASSERT_EQUAL("No suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname"), false); + TEST_ASSERT_EQUAL("Underscore 2 hex_", perf_pmu__match(&test_pmu, "pmuname_"), false); + TEST_ASSERT_EQUAL("Substring 2 hex_", perf_pmu__match(&test_pmu, "pmuna"), false); + + test_pmu.name = "pmuname_5"; + TEST_ASSERT_EQUAL("Glob 1", perf_pmu__match(&test_pmu, "pmu*"), true); + TEST_ASSERT_EQUAL("Glob 2", perf_pmu__match(&test_pmu, "nomatch*"), false); + TEST_ASSERT_EQUAL("Seq 1", perf_pmu__match(&test_pmu, "pmuname_[12345]"), true); + TEST_ASSERT_EQUAL("Seq 2", perf_pmu__match(&test_pmu, "pmuname_[67890]"), false); + TEST_ASSERT_EQUAL("? 1", perf_pmu__match(&test_pmu, "pmuname_?"), true); + TEST_ASSERT_EQUAL("? 2", perf_pmu__match(&test_pmu, "pmuname_1?"), false); + + return TEST_OK; +} + +static struct test_case tests__pmu[] = { + TEST_CASE("Parsing with PMU format directory", pmu_format), + TEST_CASE("Parsing with PMU event", pmu_events), + TEST_CASE("PMU event names", pmu_event_names), + TEST_CASE("PMU name combining", name_len), + TEST_CASE("PMU name comparison", name_cmp), + TEST_CASE("PMU cmdline match", pmu_match), + { .name = NULL, } +}; + +struct test_suite suite__pmu = { + .desc = "Sysfs PMU tests", + .test_cases = tests__pmu, +}; diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh new file mode 100755 index 000000000000..1590a37363de --- /dev/null +++ b/tools/perf/tests/shell/annotate.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# perf annotate basic tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +shelldir=$(dirname "$0") + +# shellcheck source=lib/perf_has_symbol.sh +. "${shelldir}"/lib/perf_has_symbol.sh + +testsym="noploop" + +skip_test_missing_symbol ${testsym} + +err=0 +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) +perfout=$(mktemp /tmp/__perf_test.perf.out.XXXXX) +testprog="perf test -w noploop" +# disassembly format: "percent : offset: instruction (operands ...)" +disasm_regex="[0-9]*\.[0-9]* *: *\w*: *\w*" + +cleanup() { + rm -rf "${perfdata}" "${perfout}" + rm -rf "${perfdata}".old + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +test_basic() { + echo "Basic perf annotate test" + if ! perf record -o "${perfdata}" ${testprog} 2> /dev/null + then + echo "Basic annotate [Failed: perf record]" + err=1 + return + fi + + # Generate the annotated output file + perf annotate --no-demangle -i "${perfdata}" --stdio 2> /dev/null | head -250 > "${perfout}" + + # check if it has the target symbol + if ! grep "${testsym}" "${perfout}" + then + echo "Basic annotate [Failed: missing target symbol]" + err=1 + return + fi + + # check if it has the disassembly lines + if ! grep "${disasm_regex}" "${perfout}" + then + echo "Basic annotate [Failed: missing disasm output from default disassembler]" + err=1 + return + fi + + # check again with a target symbol name + if ! perf annotate --no-demangle -i "${perfdata}" "${testsym}" 2> /dev/null | \ + head -250 | grep -m 3 "${disasm_regex}" + then + echo "Basic annotate [Failed: missing disasm output when specifying the target symbol]" + err=1 + return + fi + + # check one more with external objdump tool (forced by --objdump option) + if ! perf annotate --no-demangle -i "${perfdata}" --objdump=objdump 2> /dev/null | \ + head -250 | grep -m 3 "${disasm_regex}" + then + echo "Basic annotate [Failed: missing disasm output from non default disassembler (using --objdump)]" + err=1 + return + fi + echo "Basic annotate test [Success]" +} + +test_basic + +cleanup +exit $err diff --git a/tools/perf/tests/shell/attr.sh b/tools/perf/tests/shell/attr.sh new file mode 100755 index 000000000000..5a4e43b2471d --- /dev/null +++ b/tools/perf/tests/shell/attr.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Perf attribute expectations test +# SPDX-License-Identifier: GPL-2.0 + +err=0 + +cleanup() { + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +shelldir=$(dirname "$0") +perf_path=$(which perf) +python "${shelldir}"/lib/attr.py -d "${shelldir}"/attr -v -p "$perf_path" +cleanup +exit $err diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/shell/attr/README index 4066fec7180a..67c4ca76b85d 100644 --- a/tools/perf/tests/attr/README +++ b/tools/perf/tests/shell/attr/README @@ -51,6 +51,8 @@ Following tests are defined (with perf commands): perf record --call-graph fp kill (test-record-graph-fp-aarch64) perf record -e '{cycles,instructions}' kill (test-record-group1) perf record -e '{cycles/period=1/,instructions/period=2/}:S' kill (test-record-group2) + perf record -e '{cycles,cache-misses}:S' kill (test-record-group-sampling1) + perf record -c 10000 -e '{cycles,cache-misses}:S' kill (test-record-group-sampling2) perf record -D kill (test-record-no-delay) perf record -i kill (test-record-no-inherit) perf record -n kill (test-record-no-samples) diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/shell/attr/base-record index b44e4e6e4443..b44e4e6e4443 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/shell/attr/base-record diff --git a/tools/perf/tests/attr/base-record-spe b/tools/perf/tests/shell/attr/base-record-spe index 08fa96b59240..08fa96b59240 100644 --- a/tools/perf/tests/attr/base-record-spe +++ b/tools/perf/tests/shell/attr/base-record-spe diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/shell/attr/base-stat index fccd8ec4d1b0..fccd8ec4d1b0 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/shell/attr/base-stat diff --git a/tools/perf/tests/attr/system-wide-dummy b/tools/perf/tests/shell/attr/system-wide-dummy index a1e1d6a263bf..a1e1d6a263bf 100644 --- a/tools/perf/tests/attr/system-wide-dummy +++ b/tools/perf/tests/shell/attr/system-wide-dummy diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/shell/attr/test-record-C0 index 198e8429a1bf..1049ac8b52f2 100644 --- a/tools/perf/tests/attr/test-record-C0 +++ b/tools/perf/tests/shell/attr/test-record-C0 @@ -18,5 +18,7 @@ sample_type=65927 mmap=0 comm=0 task=0 +inherit=0 [event:system-wide-dummy] +inherit=0 diff --git a/tools/perf/tests/attr/test-record-basic b/tools/perf/tests/shell/attr/test-record-basic index b0ca42a5ecc9..b0ca42a5ecc9 100644 --- a/tools/perf/tests/attr/test-record-basic +++ b/tools/perf/tests/shell/attr/test-record-basic diff --git a/tools/perf/tests/attr/test-record-branch-any b/tools/perf/tests/shell/attr/test-record-branch-any index 1a99b3ce6b89..1a99b3ce6b89 100644 --- a/tools/perf/tests/attr/test-record-branch-any +++ b/tools/perf/tests/shell/attr/test-record-branch-any diff --git a/tools/perf/tests/attr/test-record-branch-filter-any b/tools/perf/tests/shell/attr/test-record-branch-filter-any index 709768b508c6..709768b508c6 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-any +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-any diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_call b/tools/perf/tests/shell/attr/test-record-branch-filter-any_call index f943221f7825..f943221f7825 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-any_call +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-any_call diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_ret b/tools/perf/tests/shell/attr/test-record-branch-filter-any_ret index fd4f5b4154a9..fd4f5b4154a9 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-any_ret +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-any_ret diff --git a/tools/perf/tests/attr/test-record-branch-filter-hv b/tools/perf/tests/shell/attr/test-record-branch-filter-hv index 4e52d685ebe1..4e52d685ebe1 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-hv +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-hv diff --git a/tools/perf/tests/attr/test-record-branch-filter-ind_call b/tools/perf/tests/shell/attr/test-record-branch-filter-ind_call index e08c6ab3796e..e08c6ab3796e 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-ind_call +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-ind_call diff --git a/tools/perf/tests/attr/test-record-branch-filter-k b/tools/perf/tests/shell/attr/test-record-branch-filter-k index b4b98f84fc2f..b4b98f84fc2f 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-k +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-k diff --git a/tools/perf/tests/attr/test-record-branch-filter-u b/tools/perf/tests/shell/attr/test-record-branch-filter-u index fb9610edbb0d..fb9610edbb0d 100644 --- a/tools/perf/tests/attr/test-record-branch-filter-u +++ b/tools/perf/tests/shell/attr/test-record-branch-filter-u diff --git a/tools/perf/tests/attr/test-record-count b/tools/perf/tests/shell/attr/test-record-count index 5e9b9019d786..5e9b9019d786 100644 --- a/tools/perf/tests/attr/test-record-count +++ b/tools/perf/tests/shell/attr/test-record-count diff --git a/tools/perf/tests/attr/test-record-data b/tools/perf/tests/shell/attr/test-record-data index a99bb13149c2..a99bb13149c2 100644 --- a/tools/perf/tests/attr/test-record-data +++ b/tools/perf/tests/shell/attr/test-record-data diff --git a/tools/perf/tests/attr/test-record-dummy-C0 b/tools/perf/tests/shell/attr/test-record-dummy-C0 index 576ec48b3aaf..91499405fff4 100644 --- a/tools/perf/tests/attr/test-record-dummy-C0 +++ b/tools/perf/tests/shell/attr/test-record-dummy-C0 @@ -19,7 +19,7 @@ sample_period=4000 sample_type=391 read_format=4|20 disabled=0 -inherit=1 +inherit=0 pinned=0 exclusive=0 exclude_user=0 @@ -37,7 +37,7 @@ precise_ip=0 mmap_data=0 sample_id_all=1 exclude_host=0 -exclude_guest=1 +exclude_guest=0 exclude_callchain_kernel=0 exclude_callchain_user=0 mmap2=1 diff --git a/tools/perf/tests/attr/test-record-freq b/tools/perf/tests/shell/attr/test-record-freq index 89e29f6b2ae0..89e29f6b2ae0 100644 --- a/tools/perf/tests/attr/test-record-freq +++ b/tools/perf/tests/shell/attr/test-record-freq diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/shell/attr/test-record-graph-default index f0a18b4ea4f5..f0a18b4ea4f5 100644 --- a/tools/perf/tests/attr/test-record-graph-default +++ b/tools/perf/tests/shell/attr/test-record-graph-default diff --git a/tools/perf/tests/attr/test-record-graph-default-aarch64 b/tools/perf/tests/shell/attr/test-record-graph-default-aarch64 index e98d62efb6f7..e98d62efb6f7 100644 --- a/tools/perf/tests/attr/test-record-graph-default-aarch64 +++ b/tools/perf/tests/shell/attr/test-record-graph-default-aarch64 diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/shell/attr/test-record-graph-dwarf index ae92061d611d..ae92061d611d 100644 --- a/tools/perf/tests/attr/test-record-graph-dwarf +++ b/tools/perf/tests/shell/attr/test-record-graph-dwarf diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/shell/attr/test-record-graph-fp index a6e60e839205..a6e60e839205 100644 --- a/tools/perf/tests/attr/test-record-graph-fp +++ b/tools/perf/tests/shell/attr/test-record-graph-fp diff --git a/tools/perf/tests/attr/test-record-graph-fp-aarch64 b/tools/perf/tests/shell/attr/test-record-graph-fp-aarch64 index cbeea9971285..cbeea9971285 100644 --- a/tools/perf/tests/attr/test-record-graph-fp-aarch64 +++ b/tools/perf/tests/shell/attr/test-record-graph-fp-aarch64 diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/shell/attr/test-record-group-sampling index 97e7e64a38f0..86a940d7895d 100644 --- a/tools/perf/tests/attr/test-record-group-sampling +++ b/tools/perf/tests/shell/attr/test-record-group-sampling @@ -2,6 +2,7 @@ command = record args = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 ret = 1 +kernel_until = 6.12 [event-1:base-record] fd=1 @@ -18,7 +19,7 @@ group_fd=1 type=0 config=3 -# default | PERF_SAMPLE_READ +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD sample_type=343 # PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling1 b/tools/perf/tests/shell/attr/test-record-group-sampling1 new file mode 100644 index 000000000000..4748ab7bf684 --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group-sampling1 @@ -0,0 +1,50 @@ +[config] +command = record +args = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 + +# cycles +type=0 +config=0 + +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD +sample_type=343 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=1 +mmap=1 +comm=1 +enable_on_exec=1 +disabled=1 + +# inherit is enabled for group sampling +inherit=1 + +[event-2:base-record] +fd=2 +group_fd=1 + +# cache-misses +type=0 +config=3 + +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD +sample_type=343 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=0 +mmap=0 +comm=0 +enable_on_exec=0 +disabled=0 +freq=0 + +# inherit is enabled for group sampling +inherit=1 diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling2 b/tools/perf/tests/shell/attr/test-record-group-sampling2 new file mode 100644 index 000000000000..e0432244a0eb --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group-sampling2 @@ -0,0 +1,61 @@ +[config] +command = record +args = --no-bpf-event -c 10000 -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 + +# cycles +type=0 +config=0 + +# default | PERF_SAMPLE_READ +sample_type=87 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=1 +mmap=1 +comm=1 +enable_on_exec=1 +disabled=1 + +# inherit is enabled for group sampling +inherit=1 + +# sampling disabled +sample_freq=0 +sample_period=10000 +freq=0 +write_backward=0 + +[event-2:base-record] +fd=2 +group_fd=1 + +# cache-misses +type=0 +config=3 + +# default | PERF_SAMPLE_READ +sample_type=87 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=0 +mmap=0 +comm=0 +enable_on_exec=0 +disabled=0 + +# inherit is enabled for group sampling +inherit=1 + +# sampling disabled +sample_freq=0 +sample_period=0 +freq=0 +write_backward=0 diff --git a/tools/perf/tests/attr/test-record-group1 b/tools/perf/tests/shell/attr/test-record-group1 index eeb1db392bc9..eeb1db392bc9 100644 --- a/tools/perf/tests/attr/test-record-group1 +++ b/tools/perf/tests/shell/attr/test-record-group1 diff --git a/tools/perf/tests/attr/test-record-group2 b/tools/perf/tests/shell/attr/test-record-group2 index cebdaa8e64e4..891d41a7bddf 100644 --- a/tools/perf/tests/attr/test-record-group2 +++ b/tools/perf/tests/shell/attr/test-record-group2 @@ -2,6 +2,7 @@ command = record args = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1 ret = 1 +kernel_until = 6.12 [event-1:base-record] fd=1 diff --git a/tools/perf/tests/shell/attr/test-record-group3 b/tools/perf/tests/shell/attr/test-record-group3 new file mode 100644 index 000000000000..249be884959e --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group3 @@ -0,0 +1,31 @@ +[config] +command = record +args = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 +config=0|1 +sample_period=1234000 +sample_type=87 +read_format=28|31 +disabled=1 +inherit=1 +freq=0 + +[event-2:base-record] +fd=2 +group_fd=1 +config=0|1 +sample_period=6789000 +sample_type=87 +read_format=28|31 +disabled=0 +inherit=1 +mmap=0 +comm=0 +freq=0 +enable_on_exec=0 +task=0 diff --git a/tools/perf/tests/attr/test-record-no-buffering b/tools/perf/tests/shell/attr/test-record-no-buffering index 583dcbb078ba..583dcbb078ba 100644 --- a/tools/perf/tests/attr/test-record-no-buffering +++ b/tools/perf/tests/shell/attr/test-record-no-buffering diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/shell/attr/test-record-no-inherit index 15d1dc162e1c..15d1dc162e1c 100644 --- a/tools/perf/tests/attr/test-record-no-inherit +++ b/tools/perf/tests/shell/attr/test-record-no-inherit diff --git a/tools/perf/tests/attr/test-record-no-samples b/tools/perf/tests/shell/attr/test-record-no-samples index 596fbd6d5a2c..596fbd6d5a2c 100644 --- a/tools/perf/tests/attr/test-record-no-samples +++ b/tools/perf/tests/shell/attr/test-record-no-samples diff --git a/tools/perf/tests/attr/test-record-period b/tools/perf/tests/shell/attr/test-record-period index 119101154c5e..119101154c5e 100644 --- a/tools/perf/tests/attr/test-record-period +++ b/tools/perf/tests/shell/attr/test-record-period diff --git a/tools/perf/tests/attr/test-record-pfm-period b/tools/perf/tests/shell/attr/test-record-pfm-period index 368f5b814094..368f5b814094 100644 --- a/tools/perf/tests/attr/test-record-pfm-period +++ b/tools/perf/tests/shell/attr/test-record-pfm-period diff --git a/tools/perf/tests/attr/test-record-raw b/tools/perf/tests/shell/attr/test-record-raw index 13a5f7860c78..13a5f7860c78 100644 --- a/tools/perf/tests/attr/test-record-raw +++ b/tools/perf/tests/shell/attr/test-record-raw diff --git a/tools/perf/tests/attr/test-record-spe-period b/tools/perf/tests/shell/attr/test-record-spe-period index 75f8c9cd8e3f..75f8c9cd8e3f 100644 --- a/tools/perf/tests/attr/test-record-spe-period +++ b/tools/perf/tests/shell/attr/test-record-spe-period diff --git a/tools/perf/tests/attr/test-record-spe-period-term b/tools/perf/tests/shell/attr/test-record-spe-period-term index 8f60a4fec657..8f60a4fec657 100644 --- a/tools/perf/tests/attr/test-record-spe-period-term +++ b/tools/perf/tests/shell/attr/test-record-spe-period-term diff --git a/tools/perf/tests/attr/test-record-spe-physical-address b/tools/perf/tests/shell/attr/test-record-spe-physical-address index 7ebcf5012ce3..7ebcf5012ce3 100644 --- a/tools/perf/tests/attr/test-record-spe-physical-address +++ b/tools/perf/tests/shell/attr/test-record-spe-physical-address diff --git a/tools/perf/tests/attr/test-record-user-regs-no-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-no-sve-aarch64 index bed765450ca9..bed765450ca9 100644 --- a/tools/perf/tests/attr/test-record-user-regs-no-sve-aarch64 +++ b/tools/perf/tests/shell/attr/test-record-user-regs-no-sve-aarch64 diff --git a/tools/perf/tests/attr/test-record-user-regs-old-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-old-sve-aarch64 index 15ebfc3418e3..15ebfc3418e3 100644 --- a/tools/perf/tests/attr/test-record-user-regs-old-sve-aarch64 +++ b/tools/perf/tests/shell/attr/test-record-user-regs-old-sve-aarch64 diff --git a/tools/perf/tests/attr/test-record-user-regs-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-sve-aarch64 index a65113cd7311..a65113cd7311 100644 --- a/tools/perf/tests/attr/test-record-user-regs-sve-aarch64 +++ b/tools/perf/tests/shell/attr/test-record-user-regs-sve-aarch64 diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/shell/attr/test-stat-C0 index a2c76d10b2bb..a2c76d10b2bb 100644 --- a/tools/perf/tests/attr/test-stat-C0 +++ b/tools/perf/tests/shell/attr/test-stat-C0 diff --git a/tools/perf/tests/attr/test-stat-basic b/tools/perf/tests/shell/attr/test-stat-basic index 69867d049fda..69867d049fda 100644 --- a/tools/perf/tests/attr/test-stat-basic +++ b/tools/perf/tests/shell/attr/test-stat-basic diff --git a/tools/perf/tests/attr/test-stat-default b/tools/perf/tests/shell/attr/test-stat-default index a1e2da0a9a6d..e47fb4944679 100644 --- a/tools/perf/tests/attr/test-stat-default +++ b/tools/perf/tests/shell/attr/test-stat-default @@ -88,98 +88,142 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-1 b/tools/perf/tests/shell/attr/test-stat-detailed-1 index 1c52cb05c900..3d500d3e0c5c 100644 --- a/tools/perf/tests/attr/test-stat-detailed-1 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-1 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-2 b/tools/perf/tests/shell/attr/test-stat-detailed-2 index 7e961d24a885..01777a63752f 100644 --- a/tools/perf/tests/attr/test-stat-detailed-2 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-2 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 @@ -230,8 +274,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event29:base-stat] -fd=29 +[event33:base-stat] +fd=33 type=3 config=1 optional=1 @@ -240,8 +284,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event30:base-stat] -fd=30 +[event34:base-stat] +fd=34 type=3 config=65537 optional=1 @@ -250,8 +294,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event31:base-stat] -fd=31 +[event35:base-stat] +fd=35 type=3 config=3 optional=1 @@ -260,8 +304,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event32:base-stat] -fd=32 +[event36:base-stat] +fd=36 type=3 config=65539 optional=1 @@ -270,8 +314,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event33:base-stat] -fd=33 +[event37:base-stat] +fd=37 type=3 config=4 optional=1 @@ -280,8 +324,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event34:base-stat] -fd=34 +[event38:base-stat] +fd=38 type=3 config=65540 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-3 b/tools/perf/tests/shell/attr/test-stat-detailed-3 index e50535f45977..8400abd7e1e4 100644 --- a/tools/perf/tests/attr/test-stat-detailed-3 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-3 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 @@ -230,8 +274,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event29:base-stat] -fd=29 +[event33:base-stat] +fd=33 type=3 config=1 optional=1 @@ -240,8 +284,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event30:base-stat] -fd=30 +[event34:base-stat] +fd=34 type=3 config=65537 optional=1 @@ -250,8 +294,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event31:base-stat] -fd=31 +[event35:base-stat] +fd=35 type=3 config=3 optional=1 @@ -260,8 +304,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event32:base-stat] -fd=32 +[event36:base-stat] +fd=36 type=3 config=65539 optional=1 @@ -270,8 +314,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event33:base-stat] -fd=33 +[event37:base-stat] +fd=37 type=3 config=4 optional=1 @@ -280,8 +324,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event34:base-stat] -fd=34 +[event38:base-stat] +fd=38 type=3 config=65540 optional=1 @@ -290,8 +334,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event35:base-stat] -fd=35 +[event39:base-stat] +fd=39 type=3 config=512 optional=1 @@ -300,8 +344,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event36:base-stat] -fd=36 +[event40:base-stat] +fd=40 type=3 config=66048 optional=1 diff --git a/tools/perf/tests/attr/test-stat-group1 b/tools/perf/tests/shell/attr/test-stat-group1 index 1746751123dc..1746751123dc 100644 --- a/tools/perf/tests/attr/test-stat-group1 +++ b/tools/perf/tests/shell/attr/test-stat-group1 diff --git a/tools/perf/tests/attr/test-stat-no-inherit b/tools/perf/tests/shell/attr/test-stat-no-inherit index 924fbb9300d1..924fbb9300d1 100644 --- a/tools/perf/tests/attr/test-stat-no-inherit +++ b/tools/perf/tests/shell/attr/test-stat-no-inherit diff --git a/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh new file mode 100755 index 000000000000..8226449ac5c3 --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# perf_probe :: Reject blacklisted probes (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_adding_blacklisted of perf_probe test +# Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# Blacklisted functions should not be added successfully as probes, +# they must be skipped. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +# skip if not supported +BLACKFUNC_LIST=`head -n 5 /sys/kernel/debug/kprobes/blacklist 2> /dev/null | cut -f2` +if [ -z "$BLACKFUNC_LIST" ]; then + print_overall_skipped + exit 2 +fi + +# try to find vmlinux with DWARF debug info +VMLINUX_FILE=$(perf probe -v random_probe |& grep "Using.*for symbols" | sed -r 's/^Using (.*) for symbols$/\1/') + +# remove all previously added probes +clear_all_probes + + +### adding blacklisted function +REGEX_SCOPE_FAIL="Failed to find scope of probe point" +REGEX_SKIP_MESSAGE=" is blacklisted function, skip it\." +REGEX_NOT_FOUND_MESSAGE="Probe point \'$RE_EVENT\' not found." +REGEX_ERROR_MESSAGE="Error: Failed to add events." +REGEX_INVALID_ARGUMENT="Failed to write event: Invalid argument" +REGEX_SYMBOL_FAIL="Failed to find symbol at $RE_ADDRESS" +REGEX_OUT_SECTION="$RE_EVENT is out of \.\w+, skip it" +REGEX_MISSING_DECL_LINE="A function DIE doesn't have decl_line. Maybe broken DWARF?" + +BLACKFUNC="" +SKIP_DWARF=0 + +for BLACKFUNC in $BLACKFUNC_LIST; do + echo "Probing $BLACKFUNC" + + # functions from blacklist should be skipped by perf probe + ! $CMD_PERF probe $BLACKFUNC > $LOGS_DIR/adding_blacklisted.log 2> $LOGS_DIR/adding_blacklisted.err + PERF_EXIT_CODE=$? + + # check for bad DWARF polluting the result + ../common/check_all_patterns_found.pl "$REGEX_MISSING_DECL_LINE" >/dev/null < $LOGS_DIR/adding_blacklisted.err + + if [ $? -eq 0 ]; then + SKIP_DWARF=1 + echo "Result polluted by broken DWARF, trying another probe" + + # confirm that the broken DWARF comes from assembler + if [ -n "$VMLINUX_FILE" ]; then + readelf -wi "$VMLINUX_FILE" | + awk -v probe="$BLACKFUNC" '/DW_AT_language/ { comp_lang = $0 } + $0 ~ probe { if (comp_lang) { print comp_lang }; exit }' | + grep -q "MIPS assembler" + + CHECK_EXIT_CODE=$? + if [ $CHECK_EXIT_CODE -ne 0 ]; then + SKIP_DWARF=0 # broken DWARF while available + break + fi + fi + else + ../common/check_all_lines_matched.pl "$REGEX_SKIP_MESSAGE" "$REGEX_NOT_FOUND_MESSAGE" "$REGEX_ERROR_MESSAGE" "$REGEX_SCOPE_FAIL" "$REGEX_INVALID_ARGUMENT" "$REGEX_SYMBOL_FAIL" "$REGEX_OUT_SECTION" < $LOGS_DIR/adding_blacklisted.err + CHECK_EXIT_CODE=$? + + SKIP_DWARF=0 + break + fi +done + +if [ $SKIP_DWARF -eq 1 ]; then + print_testcase_skipped "adding blacklisted function $BLACKFUNC" +else + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "adding blacklisted function $BLACKFUNC" + (( TEST_RESULT += $? )) +fi + +### listing not-added probe + +# blacklisted probes should NOT appear in perf-list output +$CMD_PERF list probe:\* > $LOGS_DIR/adding_blacklisted_list.log +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "List of pre-defined events" "Metric Groups:" < $LOGS_DIR/adding_blacklisted_list.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "listing blacklisted probe (should NOT be listed)" +(( TEST_RESULT += $? )) + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? diff --git a/tools/perf/tests/shell/base_probe/test_adding_kernel.sh b/tools/perf/tests/shell/base_probe/test_adding_kernel.sh new file mode 100755 index 000000000000..df288cf90cd6 --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_adding_kernel.sh @@ -0,0 +1,301 @@ +#!/bin/bash +# perf_probe :: Add probes, list and remove them (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_adding_kernel of perf_probe test +# Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This test tests adding of probes, their correct listing +# and removing. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +# shellcheck source=lib/probe_vfs_getname.sh +. "$(dirname "$0")/../lib/probe_vfs_getname.sh" + +TEST_PROBE=${TEST_PROBE:-"inode_permission"} + +# set NO_DEBUGINFO to skip testcase if debuginfo is not present +# skip_if_no_debuginfo returns 2 if debuginfo is not present +skip_if_no_debuginfo +if [ $? -eq 2 ]; then + NO_DEBUGINFO=1 +fi + +check_kprobes_available +if [ $? -ne 0 ]; then + print_overall_skipped + exit 2 +fi + + +### basic probe adding + +for opt in "" "-a" "--add"; do + clear_all_probes + $CMD_PERF probe $opt $TEST_PROBE 2> $LOGS_DIR/adding_kernel_add$opt.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "Added new events?:" "probe:$TEST_PROBE" "on $TEST_PROBE" < $LOGS_DIR/adding_kernel_add$opt.err + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "adding probe $TEST_PROBE :: $opt" + (( TEST_RESULT += $? )) +done + + +### listing added probe :: perf list + +# any added probes should appear in perf-list output +$CMD_PERF list probe:\* > $LOGS_DIR/adding_kernel_list.log +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "List of pre-defined events" "probe:${TEST_PROBE}(?:_\d+)?\s+\[Tracepoint event\]" "Metric Groups:" < $LOGS_DIR/adding_kernel_list.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "listing added probe :: perf list" +(( TEST_RESULT += $? )) + + +### listing added probe :: perf probe -l + +# '-l' should list all the added probes as well +$CMD_PERF probe -l > $LOGS_DIR/adding_kernel_list-l.log +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "\s*probe:${TEST_PROBE}(?:_\d+)?\s+\(on ${TEST_PROBE}(?:[:\+]$RE_NUMBER_HEX)?@.+\)" < $LOGS_DIR/adding_kernel_list-l.log +CHECK_EXIT_CODE=$? + +if [ $NO_DEBUGINFO ] ; then + print_testcase_skipped $NO_DEBUGINFO $NO_DEBUGINFO "Skipped due to missing debuginfo" +else + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "listing added probe :: perf probe -l" +fi + +(( TEST_RESULT += $? )) + + +### using added probe + +$CMD_PERF stat -e probe:$TEST_PROBE\* -o $LOGS_DIR/adding_kernel_using_probe.log -- cat /proc/uptime > /dev/null +PERF_EXIT_CODE=$? + +REGEX_STAT_HEADER="\s*Performance counter stats for \'cat /proc/uptime\':" +REGEX_STAT_VALUES="\s*\d+\s+probe:$TEST_PROBE" +# the value should be greater than 1 +REGEX_STAT_VALUE_NONZERO="\s*[1-9][0-9]*\s+probe:$TEST_PROBE" +REGEX_STAT_TIME="\s*$RE_NUMBER\s+seconds (?:time elapsed|user|sys)" +../common/check_all_lines_matched.pl "$REGEX_STAT_HEADER" "$REGEX_STAT_VALUES" "$REGEX_STAT_TIME" "$RE_LINE_COMMENT" "$RE_LINE_EMPTY" < $LOGS_DIR/adding_kernel_using_probe.log +CHECK_EXIT_CODE=$? +../common/check_all_patterns_found.pl "$REGEX_STAT_HEADER" "$REGEX_STAT_VALUE_NONZERO" "$REGEX_STAT_TIME" < $LOGS_DIR/adding_kernel_using_probe.log +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "using added probe" +(( TEST_RESULT += $? )) + + +### removing added probe + +# '-d' should remove the probe +$CMD_PERF probe -d $TEST_PROBE\* 2> $LOGS_DIR/adding_kernel_removing.err +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "Removed event: probe:$TEST_PROBE" < $LOGS_DIR/adding_kernel_removing.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "deleting added probe" +(( TEST_RESULT += $? )) + + +### listing removed probe + +# removed probes should NOT appear in perf-list output +$CMD_PERF list probe:\* > $LOGS_DIR/adding_kernel_list_removed.log +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "List of pre-defined events" "Metric Groups:" < $LOGS_DIR/adding_kernel_list_removed.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "listing removed probe (should NOT be listed)" +(( TEST_RESULT += $? )) + + +### dry run + +# the '-n' switch should run it in dry mode +$CMD_PERF probe -n --add $TEST_PROBE 2> $LOGS_DIR/adding_kernel_dryrun.err +PERF_EXIT_CODE=$? + +# check for the output (should be the same as usual) +../common/check_all_patterns_found.pl "Added new events?:" "probe:$TEST_PROBE" "on $TEST_PROBE" < $LOGS_DIR/adding_kernel_dryrun.err +CHECK_EXIT_CODE=$? + +# check that no probe was added in real +! ( $CMD_PERF probe -l | grep "probe:$TEST_PROBE" ) +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "dry run :: adding probe" +(( TEST_RESULT += $? )) + + +### force-adding probes + +# when using '--force' a probe should be added even if it is already there +$CMD_PERF probe --add $TEST_PROBE 2> $LOGS_DIR/adding_kernel_forceadd_01.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "Added new events?:" "probe:$TEST_PROBE" "on $TEST_PROBE" < $LOGS_DIR/adding_kernel_forceadd_01.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "force-adding probes :: first probe adding" +(( TEST_RESULT += $? )) + +# adding existing probe without '--force' should fail +! $CMD_PERF probe --add $TEST_PROBE 2> $LOGS_DIR/adding_kernel_forceadd_02.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "Error: event \"$TEST_PROBE\" already exists." "Error: Failed to add events." < $LOGS_DIR/adding_kernel_forceadd_02.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "force-adding probes :: second probe adding (without force)" +(( TEST_RESULT += $? )) + +# adding existing probe with '--force' should pass +NO_OF_PROBES=`$CMD_PERF probe -l $TEST_PROBE| wc -l` +$CMD_PERF probe --force --add $TEST_PROBE 2> $LOGS_DIR/adding_kernel_forceadd_03.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "Added new events?:" "probe:${TEST_PROBE}_${NO_OF_PROBES}" "on $TEST_PROBE" < $LOGS_DIR/adding_kernel_forceadd_03.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "force-adding probes :: second probe adding (with force)" +(( TEST_RESULT += $? )) + + +### using doubled probe + +# since they are the same, they should produce the same results +$CMD_PERF stat -e probe:$TEST_PROBE -e probe:${TEST_PROBE}_${NO_OF_PROBES} -x';' -o $LOGS_DIR/adding_kernel_using_two.log -- bash -c 'cat /proc/cpuinfo > /dev/null' +PERF_EXIT_CODE=$? + +REGEX_LINE="$RE_NUMBER;+probe:${TEST_PROBE}_?(?:$NO_OF_PROBES)?;$RE_NUMBER;$RE_NUMBER" +../common/check_all_lines_matched.pl "$REGEX_LINE" "$RE_LINE_EMPTY" "$RE_LINE_COMMENT" < $LOGS_DIR/adding_kernel_using_two.log +CHECK_EXIT_CODE=$? + +VALUE_1=`grep "$TEST_PROBE;" $LOGS_DIR/adding_kernel_using_two.log | awk -F';' '{print $1}'` +VALUE_2=`grep "${TEST_PROBE}_${NO_OF_PROBES};" $LOGS_DIR/adding_kernel_using_two.log | awk -F';' '{print $1}'` + +test $VALUE_1 -eq $VALUE_2 +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "using doubled probe" + + +### removing multiple probes + +# using wildcards should remove all matching probes +$CMD_PERF probe --del \* 2> $LOGS_DIR/adding_kernel_removing_wildcard.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "Removed event: probe:$TEST_PROBE" "Removed event: probe:${TEST_PROBE}_1" < $LOGS_DIR/adding_kernel_removing_wildcard.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "removing multiple probes" +(( TEST_RESULT += $? )) + + +### wildcard adding support + +$CMD_PERF probe -nf --max-probes=512 -a 'vfs_* $params' 2> $LOGS_DIR/adding_kernel_adding_wildcard.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "probe:vfs_mknod" "probe:vfs_create" "probe:vfs_rmdir" "probe:vfs_link" "probe:vfs_write" < $LOGS_DIR/adding_kernel_adding_wildcard.err +CHECK_EXIT_CODE=$? + +if [ $NO_DEBUGINFO ] ; then + print_testcase_skipped $NO_DEBUGINFO $NO_DEBUGINFO "Skipped due to missing debuginfo" +else + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "wildcard adding support" +fi + +(( TEST_RESULT += $? )) + + +### non-existing variable + +# perf probe should survive a non-existing variable probing attempt +{ $CMD_PERF probe 'vfs_read somenonexistingrandomstuffwhichisalsoprettylongorevenlongertoexceed64' ; } 2> $LOGS_DIR/adding_kernel_nonexisting.err +PERF_EXIT_CODE=$? + +# the exitcode should not be 0 or segfault +test $PERF_EXIT_CODE -ne 139 -a $PERF_EXIT_CODE -ne 0 +PERF_EXIT_CODE=$? + +# check that the error message is reasonable +../common/check_all_patterns_found.pl "Failed to find" "somenonexistingrandomstuffwhichisalsoprettylongorevenlongertoexceed64" < $LOGS_DIR/adding_kernel_nonexisting.err +CHECK_EXIT_CODE=$? +../common/check_all_patterns_found.pl "in this function|at this address" "Error" "Failed to add events" < $LOGS_DIR/adding_kernel_nonexisting.err +(( CHECK_EXIT_CODE += $? )) +../common/check_all_lines_matched.pl "Failed to find" "Error" "Probe point .+ not found" "optimized out" "Use.+\-\-range option to show.+location range" < $LOGS_DIR/adding_kernel_nonexisting.err +(( CHECK_EXIT_CODE += $? )) +../common/check_no_patterns_found.pl "$RE_SEGFAULT" < $LOGS_DIR/adding_kernel_nonexisting.err +(( CHECK_EXIT_CODE += $? )) + +if [ $NO_DEBUGINFO ]; then + print_testcase_skipped $NO_DEBUGINFO $NO_DEBUGINFO "Skipped due to missing debuginfo" +else + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "non-existing variable" +fi + +(( TEST_RESULT += $? )) + + +### function with return value + +# adding probe with return value +$CMD_PERF probe --add "$TEST_PROBE%return \$retval" 2> $LOGS_DIR/adding_kernel_func_retval_add.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "Added new events?:" "probe:$TEST_PROBE" "on $TEST_PROBE%return with \\\$retval" < $LOGS_DIR/adding_kernel_func_retval_add.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "function with retval :: add" +(( TEST_RESULT += $? )) + +# recording some data +$CMD_PERF record -e probe:$TEST_PROBE\* -o $CURRENT_TEST_DIR/perf.data -- cat /proc/cpuinfo > /dev/null 2> $LOGS_DIR/adding_kernel_func_retval_record.err +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "$RE_LINE_RECORD1" "$RE_LINE_RECORD2" < $LOGS_DIR/adding_kernel_func_retval_record.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "function with retval :: record" +(( TEST_RESULT += $? )) + +# perf script should report the function calls with the correct arg values +$CMD_PERF script -i $CURRENT_TEST_DIR/perf.data > $LOGS_DIR/adding_kernel_func_retval_script.log +PERF_EXIT_CODE=$? + +REGEX_SCRIPT_LINE="\s*cat\s+$RE_NUMBER\s+\[$RE_NUMBER\]\s+$RE_NUMBER:\s+probe:$TEST_PROBE\w*:\s+\($RE_NUMBER_HEX\s+<\-\s+$RE_NUMBER_HEX\)\s+arg1=$RE_NUMBER_HEX" +../common/check_all_lines_matched.pl "$REGEX_SCRIPT_LINE" < $LOGS_DIR/adding_kernel_func_retval_script.log +CHECK_EXIT_CODE=$? +../common/check_all_patterns_found.pl "$REGEX_SCRIPT_LINE" < $LOGS_DIR/adding_kernel_func_retval_script.log +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "function argument probing :: script" +(( TEST_RESULT += $? )) + + +clear_all_probes + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? diff --git a/tools/perf/tests/shell/base_probe/test_basic.sh b/tools/perf/tests/shell/base_probe/test_basic.sh new file mode 100755 index 000000000000..9d8b5afbeddd --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_basic.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# perf_probe :: Basic perf probe functionality (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_basic of perf_probe test +# Author: Michael Petlan <mpetlan@redhat.com> +# Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# +# Description: +# +# This test tests basic functionality of perf probe command. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 2 +fi + + +### help message + +if [ "$PARAM_GENERAL_HELP_TEXT_CHECK" = "y" ]; then + # test that a help message is shown and looks reasonable + $CMD_PERF probe --help > $LOGS_DIR/basic_helpmsg.log 2> $LOGS_DIR/basic_helpmsg.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "PERF-PROBE" "NAME" "SYNOPSIS" "DESCRIPTION" "OPTIONS" "PROBE\s+SYNTAX" "PROBE\s+ARGUMENT" "LINE\s+SYNTAX" < $LOGS_DIR/basic_helpmsg.log + CHECK_EXIT_CODE=$? + ../common/check_all_patterns_found.pl "LAZY\s+MATCHING" "FILTER\s+PATTERN" "EXAMPLES" "SEE\s+ALSO" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "vmlinux" "module=" "source=" "verbose" "quiet" "add=" "del=" "list.*EVENT" "line=" "vars=" "externs" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "no-inlines" "funcs.*FILTER" "filter=FILTER" "force" "dry-run" "max-probes" "exec=" "demangle-kernel" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_no_patterns_found.pl "No manual entry for" < $LOGS_DIR/basic_helpmsg.err + (( CHECK_EXIT_CODE += $? )) + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "help message" + (( TEST_RESULT += $? )) +else + print_testcase_skipped "help message" +fi + + +### usage message + +# without any args perf-probe should print usage +$CMD_PERF probe 2> $LOGS_DIR/basic_usage.log > /dev/null + +../common/check_all_patterns_found.pl "[Uu]sage" "perf probe" "verbose" "quiet" "add" "del" "force" "line" "vars" "externs" "range" < $LOGS_DIR/basic_usage.log +CHECK_EXIT_CODE=$? + +print_results 0 $CHECK_EXIT_CODE "usage message" +(( TEST_RESULT += $? )) + + +### quiet switch + +# '--quiet' should mute all output +$CMD_PERF probe --quiet --add vfs_read > $LOGS_DIR/basic_quiet01.log 2> $LOGS_DIR/basic_quiet01.err +PERF_EXIT_CODE=$? +$CMD_PERF probe --quiet --del vfs_read > $LOGS_DIR/basic_quiet03.log 2> $LOGS_DIR/basic_quiet02.err +(( PERF_EXIT_CODE += $? )) + +test "`cat $LOGS_DIR/basic_quiet*log $LOGS_DIR/basic_quiet*err | wc -l`" -eq 0 +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "quiet switch" +(( TEST_RESULT += $? )) + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? diff --git a/tools/perf/tests/shell/base_probe/test_invalid_options.sh b/tools/perf/tests/shell/base_probe/test_invalid_options.sh new file mode 100755 index 000000000000..92f7254eb32a --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_invalid_options.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# perf_probe :: Reject invalid options (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_invalid_options of perf_probe test +# Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This test checks whether the invalid and incompatible options are reported +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 2 +fi + +# Check for presence of DWARF +$CMD_PERF check feature -q dwarf +[ $? -ne 0 ] && HINT_FAIL="Some of the tests need DWARF to run" + +### missing argument + +# some options require an argument +for opt in '-a' '-d' '-L' '-V'; do + ! $CMD_PERF probe $opt 2> $LOGS_DIR/invalid_options_missing_argument$opt.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "Error: switch .* requires a value" < $LOGS_DIR/invalid_options_missing_argument$opt.err + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "missing argument for $opt" + (( TEST_RESULT += $? )) +done + + +### unnecessary argument + +# some options may omit the argument +for opt in '-F' '-l'; do + $CMD_PERF probe -F > /dev/null 2> $LOGS_DIR/invalid_options_unnecessary_argument$opt.err + PERF_EXIT_CODE=$? + + test ! -s $LOGS_DIR/invalid_options_unnecessary_argument$opt.err + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "unnecessary argument for $opt" + (( TEST_RESULT += $? )) +done + + +### mutually exclusive options + +# some options are mutually exclusive +test -e $LOGS_DIR/invalid_options_mutually_exclusive.log && rm -f $LOGS_DIR/invalid_options_mutually_exclusive.log +for opt in '-a xxx -d xxx' '-a xxx -L foo' '-a xxx -V foo' '-a xxx -l' '-a xxx -F' \ + '-d xxx -L foo' '-d xxx -V foo' '-d xxx -l' '-d xxx -F' \ + '-L foo -V bar' '-L foo -l' '-L foo -F' '-V foo -l' '-V foo -F' '-l -F'; do + ! $CMD_PERF probe $opt > /dev/null 2> $LOGS_DIR/aux.log + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "Error: switch .+ cannot be used with switch .+" < $LOGS_DIR/aux.log + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "mutually exclusive options :: $opt" + (( TEST_RESULT += $? )) + + # gather the logs + cat $LOGS_DIR/aux.log | grep "Error" >> $LOGS_DIR/invalid_options_mutually_exclusive.log +done + + +# print overall results +print_overall_results "$TEST_RESULT" $HINT_FAIL +exit $? diff --git a/tools/perf/tests/shell/base_probe/test_line_semantics.sh b/tools/perf/tests/shell/base_probe/test_line_semantics.sh new file mode 100755 index 000000000000..20435b6bf6bc --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_line_semantics.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# perf_probe :: Check patterns for line semantics (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_line_semantics of perf_probe test +# Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This test checks whether the semantic errors of line option's +# arguments are properly reported. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 2 +fi + +# Check for presence of DWARF +$CMD_PERF check feature -q dwarf +[ $? -ne 0 ] && HINT_FAIL="Some of the tests need DWARF to run" + +### acceptable --line descriptions + +# testing acceptance of valid patterns for the '--line' option +VALID_PATTERNS="func func:10 func:0-10 func:2+10 func@source.c func@source.c:1 source.c:1 source.c:1+1 source.c:1-10" +for desc in $VALID_PATTERNS; do + ! ( $CMD_PERF probe --line $desc 2>&1 | grep -q "Semantic error" ) + CHECK_EXIT_CODE=$? + + print_results 0 $CHECK_EXIT_CODE "acceptable descriptions :: $desc" + (( TEST_RESULT += $? )) +done + + +### unacceptable --line descriptions + +# testing handling of invalid patterns for the '--line' option +INVALID_PATTERNS="func:foo func:1-foo func:1+foo func;lazy\*pattern" +for desc in $INVALID_PATTERNS; do + $CMD_PERF probe --line $desc 2>&1 | grep -q "Semantic error" + CHECK_EXIT_CODE=$? + + print_results 0 $CHECK_EXIT_CODE "unacceptable descriptions :: $desc" + (( TEST_RESULT += $? )) +done + + +# print overall results +print_overall_results "$TEST_RESULT" $HINT_FAIL +exit $? diff --git a/tools/perf/tests/shell/base_report/setup.sh b/tools/perf/tests/shell/base_report/setup.sh new file mode 100755 index 000000000000..b03501b2e8fc --- /dev/null +++ b/tools/perf/tests/shell/base_report/setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# perftool-testsuite :: perf_report +# SPDX-License-Identifier: GPL-2.0 + +# +# setup.sh of perf report test +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# We need some sample data for perf-report testing +# +# + +# include working environment +. ../common/init.sh + +test -d "$HEADER_TAR_DIR" || mkdir -p "$HEADER_TAR_DIR" + +SW_EVENT="cpu-clock" + +$CMD_PERF record -asdg -e $SW_EVENT -o $CURRENT_TEST_DIR/perf.data -- $CMD_LONGER_SLEEP 2> $LOGS_DIR/setup.log +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "$RE_LINE_RECORD1" "$RE_LINE_RECORD2" < $LOGS_DIR/setup.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "prepare the perf.data file" +TEST_RESULT=$? + +print_overall_results $TEST_RESULT +exit $? diff --git a/tools/perf/tests/shell/base_report/stderr-whitelist.txt b/tools/perf/tests/shell/base_report/stderr-whitelist.txt new file mode 100644 index 000000000000..e3341401b47c --- /dev/null +++ b/tools/perf/tests/shell/base_report/stderr-whitelist.txt @@ -0,0 +1,5 @@ +no symbols found in .*, maybe install a debug package +was updated .*is prelink enabled.+ Restart the long running apps that use it +Warning: +\d+ out of order events recorded. +detected invalid bpf_prog_info diff --git a/tools/perf/tests/shell/base_report/test_basic.sh b/tools/perf/tests/shell/base_report/test_basic.sh new file mode 100755 index 000000000000..2398eba4d3fd --- /dev/null +++ b/tools/perf/tests/shell/base_report/test_basic.sh @@ -0,0 +1,190 @@ +#!/bin/bash +# perf_report :: Basic perf report options (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# +# test_basic of perf_report test +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This test tests basic functionality of perf report command. +# +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + + +### help message + +if [ "$PARAM_GENERAL_HELP_TEXT_CHECK" = "y" ]; then + # test that a help message is shown and looks reasonable + $CMD_PERF report --help > $LOGS_DIR/basic_helpmsg.log 2> $LOGS_DIR/basic_helpmsg.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "PERF-REPORT" "NAME" "SYNOPSIS" "DESCRIPTION" "OPTIONS" "OVERHEAD\s+CALCULATION" "SEE ALSO" < $LOGS_DIR/basic_helpmsg.log + CHECK_EXIT_CODE=$? + ../common/check_all_patterns_found.pl "input" "verbose" "show-nr-samples" "show-cpu-utilization" "threads" "comms" "pid" "tid" "dsos" "symbols" "symbol-filter" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "hide-unresolved" "sort" "fields" "parent" "exclude-other" "column-widths" "field-separator" "dump-raw-trace" "children" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "call-graph" "max-stack" "inverted" "ignore-callees" "pretty" "stdio" "tui" "gtk" "vmlinux" "kallsyms" "modules" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "force" "symfs" "cpu" "disassembler-style" "source" "asm-raw" "show-total-period" "show-info" "branch-stack" "group" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "branch-history" "objdump" "demangle" "percent-limit" "percentage" "header" "itrace" "full-source-path" "show-ref-call-graph" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_no_patterns_found.pl "No manual entry for" < $LOGS_DIR/basic_helpmsg.err + (( CHECK_EXIT_CODE += $? )) + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "help message" + (( TEST_RESULT += $? )) +else + print_testcase_skipped "help message" +fi + + +### basic execution + +# test that perf report is even working +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio > $LOGS_DIR/basic_basic.log 2> $LOGS_DIR/basic_basic.err +PERF_EXIT_CODE=$? + +REGEX_LOST_SAMPLES_INFO="#\s*Total Lost Samples:\s+$RE_NUMBER" +REGEX_SAMPLES_INFO="#\s*Samples:\s+(?:$RE_NUMBER)\w?\s+of\s+event\s+'$RE_EVENT_ANY'" +REGEX_LINES_HEADER="#\s*Children\s+Self\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LOST_SAMPLES_INFO" "$REGEX_SAMPLES_INFO" "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_basic.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_basic.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "basic execution" +(( TEST_RESULT += $? )) + + +### number of samples + +# '--show-nr-samples' should show number of samples for each symbol +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --show-nr-samples > $LOGS_DIR/basic_nrsamples.log 2> $LOGS_DIR/basic_nrsamples.err +PERF_EXIT_CODE=$? + +REGEX_LINES_HEADER="#\s*Children\s+Self\s+Samples\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_nrsamples.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_nrsamples.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "number of samples" +(( TEST_RESULT += $? )) + + +### header + +# '--header' and '--header-only' should show perf report header +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio --header-only > $LOGS_DIR/basic_header.log +PERF_EXIT_CODE=$? + +REGEX_LINE_TIMESTAMP="#\s+captured on\s*:\s*$RE_DATE_TIME" +REGEX_LINE_HOSTNAME="#\s+hostname\s*:\s*$MY_HOSTNAME" +REGEX_LINE_KERNEL="#\s+os release\s*:\s*${MY_KERNEL_VERSION//+/\\+}" +REGEX_LINE_PERF="#\s+perf version\s*:\s*" +REGEX_LINE_ARCH="#\s+arch\s*:\s*$MY_ARCH" +REGEX_LINE_CPUS_ONLINE="#\s+nrcpus online\s*:\s*$MY_CPUS_ONLINE" +REGEX_LINE_CPUS_AVAIL="#\s+nrcpus avail\s*:\s*$MY_CPUS_AVAILABLE" +# disable precise check for "nrcpus avail" in BASIC runmode +test $PERFTOOL_TESTSUITE_RUNMODE -lt $RUNMODE_STANDARD && REGEX_LINE_CPUS_AVAIL="#\s+nrcpus avail\s*:\s*$RE_NUMBER" +../common/check_all_patterns_found.pl "$REGEX_LINE_TIMESTAMP" "$REGEX_LINE_HOSTNAME" "$REGEX_LINE_KERNEL" "$REGEX_LINE_PERF" "$REGEX_LINE_ARCH" "$REGEX_LINE_CPUS_ONLINE" "$REGEX_LINE_CPUS_AVAIL" < $LOGS_DIR/basic_header.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "header" +(( TEST_RESULT += $? )) + +# '--header' and '--header-only' should use creation time +OLD_TIMESTAMP=`$CMD_PERF report --stdio --header-only -i $CURRENT_TEST_DIR/perf.data | grep "captured on"` +PERF_EXIT_CODE=$? + +( tar -C $CURRENT_TEST_DIR -c perf.data | xz > $CURRENT_TEST_DIR/perf.data.tar.xz ; xzcat $CURRENT_TEST_DIR/perf.data.tar.xz | tar x -C $HEADER_TAR_DIR ) +(( PERF_EXIT_CODE += $? )) + +NEW_TIMESTAMP=`$CMD_PERF report --stdio --header-only -i $HEADER_TAR_DIR/perf.data | grep "captured on"` +(( PERF_EXIT_CODE += $? )) + +test "$OLD_TIMESTAMP" = "$NEW_TIMESTAMP" +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "header timestamp" +(( TEST_RESULT += $? )) + + +### show CPU utilization + +# '--showcpuutilization' should show percentage for both system and userspace mode +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio --showcpuutilization > $LOGS_DIR/basic_cpuut.log 2> $LOGS_DIR/basic_cpuut.err +PERF_EXIT_CODE=$? + +REGEX_LINES_HEADER="#\s*Children\s+Self\s+sys\s+usr\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER%\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_cpuut.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_cpuut.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "show CPU utilization" +(( TEST_RESULT += $? )) + + +### pid + +# '--pid=' should limit the output for a process with the given pid only +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --pid=1 > $LOGS_DIR/basic_pid.log 2> $LOGS_DIR/basic_pid.err +PERF_EXIT_CODE=$? + +grep -P -v '^#' $LOGS_DIR/basic_pid.log | grep -P '\s+[\d\.]+%' | ../common/check_all_lines_matched.pl "systemd|init" +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_pid.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "pid" +(( TEST_RESULT += $? )) + + +### non-existing symbol + +# '--symbols' should show only the given symbols +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --symbols=dummynonexistingsymbol > $LOGS_DIR/basic_symbols.log 2> $LOGS_DIR/basic_symbols.err +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "$RE_LINE_COMMENT" < $LOGS_DIR/basic_symbols.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_symbols.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "non-existing symbol" +(( TEST_RESULT += $? )) + + +### symbol filter + +# '--symbol-filter' should filter symbols based on substrings +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --symbol-filter=map > $LOGS_DIR/basic_symbolfilter.log 2> $LOGS_DIR/basic_symbolfilter.err +PERF_EXIT_CODE=$? + +grep -P -v '^#' $LOGS_DIR/basic_symbolfilter.log | grep -P '\s+[\d\.]+%' | ../common/check_all_lines_matched.pl "\[[k\.]\]\s+.*map" +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_symbolfilter.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "symbol filter" +(( TEST_RESULT += $? )) + + +# TODO: $CMD_PERF report -n --showcpuutilization -TUxDg 2> 01.log + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? diff --git a/tools/perf/tests/shell/common/check_all_lines_matched.pl b/tools/perf/tests/shell/common/check_all_lines_matched.pl new file mode 100755 index 000000000000..fded48959a3f --- /dev/null +++ b/tools/perf/tests/shell/common/check_all_lines_matched.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 + +@regexps = @ARGV; + +$max_printed_lines = 20; +$max_printed_lines = $ENV{TESTLOG_ERR_MSG_MAX_LINES} if (defined $ENV{TESTLOG_ERR_MSG_MAX_LINES}); + +$quiet = 1; +$quiet = 0 if (defined $ENV{TESTLOG_VERBOSITY} && $ENV{TESTLOG_VERBOSITY} ge 2); + +$passed = 1; +$lines_printed = 0; + +while (<STDIN>) +{ + s/\n//; + + $line_matched = 0; + for $r (@regexps) + { + if (/$r/) + { + $line_matched = 1; + last; + } + } + + unless ($line_matched) + { + if ($lines_printed++ < $max_printed_lines) + { + print "Line did not match any pattern: \"$_\"\n" unless $quiet; + } + $passed = 0; + } +} + +exit ($passed == 0); diff --git a/tools/perf/tests/shell/common/check_all_patterns_found.pl b/tools/perf/tests/shell/common/check_all_patterns_found.pl new file mode 100755 index 000000000000..11bdf1d3460a --- /dev/null +++ b/tools/perf/tests/shell/common/check_all_patterns_found.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 + +@regexps = @ARGV; + +$quiet = 1; +$quiet = 0 if (defined $ENV{TESTLOG_VERBOSITY} && $ENV{TESTLOG_VERBOSITY} ge 2); + +%found = (); +$passed = 1; + +while (<STDIN>) +{ + s/\n//; + + for $r (@regexps) + { + if (/$r/) + { + $found{$r} = 1; # FIXME: maybe add counters -- how many times was the regexp matched + } + } +} + +for $r (@regexps) +{ + unless (exists $found{$r}) + { + print "Regexp not found: \"$r\"\n" unless $quiet; + $passed = 0; + } +} + +exit ($passed == 0); diff --git a/tools/perf/tests/shell/common/check_errors_whitelisted.pl b/tools/perf/tests/shell/common/check_errors_whitelisted.pl new file mode 100755 index 000000000000..c57d355dd76e --- /dev/null +++ b/tools/perf/tests/shell/common/check_errors_whitelisted.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 + +$whitelist_file = shift; + +if (defined $whitelist_file) +{ + open (INFILE, $whitelist_file) or die "Checker error: Unable to open the whitelist file: $whitelist_file\n"; + @regexps = <INFILE>; + close INFILE or die "Checker error: Unable to close the whitelist file: $whitelist_file\n"; +} +else +{ + @regexps = (); +} + +$max_printed_lines = 20; +$max_printed_lines = $ENV{TESTLOG_ERR_MSG_MAX_LINES} if (defined $ENV{TESTLOG_ERR_MSG_MAX_LINES}); + +$quiet = 1; +$quiet = 0 if (defined $ENV{TESTLOG_VERBOSITY} && $ENV{TESTLOG_VERBOSITY} ge 2); + +$passed = 1; +$lines_printed = 0; + +while (<STDIN>) +{ + s/\n//; + + $line_matched = 0; + for $r (@regexps) + { + chomp $r; + if (/$r/) + { + $line_matched = 1; + last; + } + } + + unless ($line_matched) + { + if ($lines_printed++ < $max_printed_lines) + { + print "Line did not match any pattern: \"$_\"\n" unless $quiet; + } + $passed = 0; + } +} + +exit ($passed == 0); diff --git a/tools/perf/tests/shell/common/check_no_patterns_found.pl b/tools/perf/tests/shell/common/check_no_patterns_found.pl new file mode 100755 index 000000000000..770999e87a5f --- /dev/null +++ b/tools/perf/tests/shell/common/check_no_patterns_found.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 + +@regexps = @ARGV; + +$quiet = 1; +$quiet = 0 if (defined $ENV{TESTLOG_VERBOSITY} && $ENV{TESTLOG_VERBOSITY} ge 2); + +%found = (); +$passed = 1; + +while (<STDIN>) +{ + s/\n//; + + for $r (@regexps) + { + if (/$r/) + { + $found{$r} = 1; + } + } +} + +for $r (@regexps) +{ + if (exists $found{$r}) + { + print "Regexp found: \"$r\"\n" unless $quiet; + $passed = 0; + } +} + +exit ($passed == 0); diff --git a/tools/perf/tests/shell/common/init.sh b/tools/perf/tests/shell/common/init.sh new file mode 100644 index 000000000000..26c7525651e0 --- /dev/null +++ b/tools/perf/tests/shell/common/init.sh @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# init.sh +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This file should be used for initialization of basic functions +# for checking, reporting results etc. +# +# + + +. ../common/settings.sh +. ../common/patterns.sh + +THIS_TEST_NAME=`basename $0 .sh` + +_echo() +{ + test "$TESTLOG_VERBOSITY" -ne 0 && echo -e "$@" +} + +print_results() +{ + PERF_RETVAL="$1"; shift + CHECK_RETVAL="$1"; shift + FAILURE_REASON="" + TASK_COMMENT="$*" + if [ $PERF_RETVAL -eq 0 ] && [ $CHECK_RETVAL -eq 0 ]; then + _echo "$MPASS-- [ PASS ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $TASK_COMMENT" + return 0 + else + if [ $PERF_RETVAL -ne 0 ]; then + FAILURE_REASON="command exitcode" + fi + if [ $CHECK_RETVAL -ne 0 ]; then + test -n "$FAILURE_REASON" && FAILURE_REASON="$FAILURE_REASON + " + FAILURE_REASON="$FAILURE_REASON""output regexp parsing" + fi + _echo "$MFAIL-- [ FAIL ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $TASK_COMMENT ($FAILURE_REASON)" + return 1 + fi +} + +print_overall_results() +{ + RETVAL="$1"; shift + TASK_COMMENT="$*" + test -n "$TASK_COMMENT" && TASK_COMMENT=":: $TASK_COMMENT" + + if [ $RETVAL -eq 0 ]; then + _echo "$MALLPASS## [ PASS ] ##$MEND $TEST_NAME :: $THIS_TEST_NAME SUMMARY" + else + _echo "$MALLFAIL## [ FAIL ] ##$MEND $TEST_NAME :: $THIS_TEST_NAME SUMMARY :: $RETVAL failures found $TASK_COMMENT" + fi + return $RETVAL +} + +print_testcase_skipped() +{ + TASK_COMMENT="$*" + _echo "$MSKIP-- [ SKIP ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $TASK_COMMENT :: testcase skipped" + return 0 +} + +print_overall_skipped() +{ + _echo "$MSKIP## [ SKIP ] ##$MEND $TEST_NAME :: $THIS_TEST_NAME :: testcase skipped" + return 0 +} + +print_warning() +{ + WARN_COMMENT="$*" + _echo "$MWARN-- [ WARN ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $WARN_COMMENT" + return 0 +} + +# this function should skip a testcase if the testsuite is not run in +# a runmode that fits the testcase --> if the suite runs in BASIC mode +# all STANDARD and EXPERIMENTAL testcases will be skipped; if the suite +# runs in STANDARD mode, all EXPERIMENTAL testcases will be skipped and +# if the suite runs in EXPERIMENTAL mode, nothing is skipped +consider_skipping() +{ + TESTCASE_RUNMODE="$1" + # the runmode of a testcase needs to be at least the current suite's runmode + if [ $PERFTOOL_TESTSUITE_RUNMODE -lt $TESTCASE_RUNMODE ]; then + print_overall_skipped + exit 2 + fi +} + +detect_baremetal() +{ + # return values: + # 0 = bare metal + # 1 = virtualization detected + # 2 = unknown state + VIRT=`systemd-detect-virt 2>/dev/null` + test $? -eq 127 && return 2 + test "$VIRT" = "none" +} + +detect_intel() +{ + # return values: + # 0 = is Intel + # 1 = is not Intel or unknown + grep "vendor_id" < /proc/cpuinfo | grep -q "GenuineIntel" +} + +detect_amd() +{ + # return values: + # 0 = is AMD + # 1 = is not AMD or unknown + grep "vendor_id" < /proc/cpuinfo | grep -q "AMD" +} + +# base probe utility +check_kprobes_available() +{ + test -e /sys/kernel/debug/tracing/kprobe_events +} + +check_uprobes_available() +{ + test -e /sys/kernel/debug/tracing/uprobe_events +} + +clear_all_probes() +{ + echo 0 > /sys/kernel/debug/tracing/events/enable + check_kprobes_available && echo > /sys/kernel/debug/tracing/kprobe_events + check_uprobes_available && echo > /sys/kernel/debug/tracing/uprobe_events +} + +check_sdt_support() +{ + $CMD_PERF list sdt | grep sdt > /dev/null 2> /dev/null +} diff --git a/tools/perf/tests/shell/common/patterns.sh b/tools/perf/tests/shell/common/patterns.sh new file mode 100644 index 000000000000..21dab25c7b7f --- /dev/null +++ b/tools/perf/tests/shell/common/patterns.sh @@ -0,0 +1,268 @@ +# SPDX-License-Identifier: GPL-2.0 + +export RE_NUMBER="[0-9\.]+" +# Number +# Examples: +# 123.456 + + +export RE_NUMBER_HEX="[0-9A-Fa-f]+" +# Hexadecimal number +# Examples: +# 1234 +# a58d +# aBcD +# deadbeef + + +export RE_DATE_YYYYMMDD="[0-9]{4}-(?:(?:01|03|05|07|08|10|12)-(?:[0-2][0-9]|3[0-1])|02-[0-2][0-9]|(?:(?:04|06|09|11)-(?:[0-2][0-9]|30)))" +# Date in YYYY-MM-DD form +# Examples: +# 1990-02-29 +# 0015-07-31 +# 2456-12-31 +#! 2012-13-01 +#! 1963-09-31 + + +export RE_TIME="(?:[0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]" +# Time +# Examples: +# 15:12:27 +# 23:59:59 +#! 24:00:00 +#! 11:25:60 +#! 17:60:15 + + +export RE_DATE_TIME="\w+\s+\w+\s+$RE_NUMBER\s+$RE_TIME\s+$RE_NUMBER" +# Time and date +# Examples: +# Wed Feb 12 10:46:26 2020 +# Mon Mar 2 13:27:06 2020 +#! St úno 12 10:57:21 CET 2020 +#! Po úno 14 15:17:32 2010 + + +export RE_ADDRESS="0x$RE_NUMBER_HEX" +# Memory address +# Examples: +# 0x123abc +# 0xffffffff9abe8ae8 +# 0x0 + + +export RE_ADDRESS_NOT_NULL="0x[0-9A-Fa-f]*[1-9A-Fa-f]+[0-9A-Fa-f]*" +# Memory address (not NULL) +# Examples: +# 0xffffffff9abe8ae8 +#! 0x0 +#! 0x0000000000000000 + +export RE_PROCESS_PID="[^\/]+\/\d+" +# A process with PID +# Example: +# sleep/4102 +# test_overhead./866185 +# in:imjournal/1096 +# random#$& test/866607 + +export RE_EVENT_ANY="[\w\-\:\/_=,]+" +# Name of any event (universal) +# Examples: +# cpu-cycles +# cpu/event=12,umask=34/ +# r41e1 +# nfs:nfs_getattr_enter + + +export RE_EVENT="[\w\-:_]+" +# Name of an usual event +# Examples: +# cpu-cycles + + +export RE_EVENT_RAW="r$RE_NUMBER_HEX" +# Specification of a raw event +# Examples: +# r41e1 +# r1a + + +export RE_EVENT_CPU="cpu/(\w+=$RE_NUMBER_HEX,?)+/p*" +# Specification of a CPU event +# Examples: +# cpu/event=12,umask=34/pp + + +export RE_EVENT_UNCORE="uncore/[\w_]+/" +# Specification of an uncore event +# Examples: +# uncore/qhl_request_local_reads/ + + +export RE_EVENT_SUBSYSTEM="[\w\-]+:[\w\-]+" +# Name of an event from subsystem +# Examples: +# ext4:ext4_ordered_write_end +# sched:sched_switch + + +export RE_FILE_NAME="[\w\+\.-]+" +# A filename +# Examples: +# libstdc++.so.6 +#! some/path + + +export RE_PATH_ABSOLUTE="(?:\/$RE_FILE_NAME)+" +# A full filepath +# Examples: +# /usr/lib64/somelib.so.5.4.0 +# /lib/modules/4.3.0-rc5/kernel/fs/xfs/xfs.ko +# /usr/bin/mv +#! some/relative/path +#! ./some/relative/path + + +export RE_PATH="(?:$RE_FILE_NAME)?$RE_PATH_ABSOLUTE" +# A filepath +# Examples: +# /usr/lib64/somelib.so.5.4.0 +# /lib/modules/4.3.0-rc5/kernel/fs/xfs/xfs.ko +# ./.emacs +# src/fs/file.c + + +export RE_DSO="(?:$RE_PATH_ABSOLUTE(?: \(deleted\))?|\[kernel\.kallsyms\]|\[unknown\]|\[vdso\]|\[kernel\.vmlinux\][\.\w]*)" +# A DSO name in various result tables +# Examples: +# /usr/lib64/somelib.so.5.4.0 +# /usr/bin/somebinart (deleted) +# /lib/modules/4.3.0-rc5/kernel/fs/xfs/xfs.ko +# [kernel.kallsyms] +# [kernel.vmlinux] +# [vdso] +# [unknown] + + +export RE_LINE_COMMENT="^#.*" +# A comment line +# Examples: +# # Started on Thu Sep 10 11:43:00 2015 + + +export RE_LINE_EMPTY="^\s*$" +# An empty line with possible whitespaces +# Examples: +# + + +export RE_LINE_RECORD1="^\[\s+perf\s+record:\s+Woken up $RE_NUMBER times? to write data\s+\].*$" +# The first line of perf-record "OK" output +# Examples: +# [ perf record: Woken up 1 times to write data ] + + +export RE_LINE_RECORD2="^\[\s+perf\s+record:\s+Captured and wrote $RE_NUMBER\s*MB\s+(?:[\w\+\.-]*(?:$RE_PATH)?\/)?perf\.data(?:\.\d+)?\s*\(~?$RE_NUMBER samples\)\s+\].*$" +# The second line of perf-record "OK" output +# Examples: +# [ perf record: Captured and wrote 0.405 MB perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB perf.data (~109 samples) ] +# [ perf record: Captured and wrote 0.405 MB /some/temp/dir/perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB ./perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB ./perf.data.3 (109 samples) ] + + +export RE_LINE_RECORD2_TOLERANT="^\[\s+perf\s+record:\s+Captured and wrote $RE_NUMBER\s*MB\s+(?:[\w\+\.-]*(?:$RE_PATH)?\/)?perf\.data(?:\.\d+)?\s*(?:\(~?$RE_NUMBER samples\))?\s+\].*$" +# The second line of perf-record "OK" output, even no samples is OK here +# Examples: +# [ perf record: Captured and wrote 0.405 MB perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB perf.data (~109 samples) ] +# [ perf record: Captured and wrote 0.405 MB /some/temp/dir/perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB ./perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB ./perf.data.3 (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB perf.data ] + + +export RE_LINE_RECORD2_TOLERANT_FILENAME="^\[\s+perf\s+record:\s+Captured and wrote $RE_NUMBER\s*MB\s+(?:[\w\+\.-]*(?:$RE_PATH)?\/)?perf\w*\.data(?:\.\d+)?\s*\(~?$RE_NUMBER samples\)\s+\].*$" +# The second line of perf-record "OK" output +# Examples: +# [ perf record: Captured and wrote 0.405 MB perf.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB perf_ls.data (~109 samples) ] +# [ perf record: Captured and wrote 0.405 MB perf_aNyCaSe.data (109 samples) ] +# [ perf record: Captured and wrote 0.405 MB ./perfdata.data.3 (109 samples) ] +#! [ perf record: Captured and wrote 0.405 MB /some/temp/dir/my_own.data (109 samples) ] +#! [ perf record: Captured and wrote 0.405 MB ./UPPERCASE.data (109 samples) ] +#! [ perf record: Captured and wrote 0.405 MB ./aNyKiNDoF.data.3 (109 samples) ] +#! [ perf record: Captured and wrote 0.405 MB perf.data ] + + +export RE_LINE_TRACE_FULL="^\s*$RE_NUMBER\s*\(\s*$RE_NUMBER\s*ms\s*\):\s*$RE_PROCESS_PID\s+.*\)\s+=\s+(:?\-?$RE_NUMBER|0x$RE_NUMBER_HEX).*$" +# A line of perf-trace output +# Examples: +# 0.115 ( 0.005 ms): sleep/4102 open(filename: 0xd09e2ab2, flags: CLOEXEC ) = 3 +# 0.157 ( 0.005 ms): sleep/4102 mmap(len: 3932736, prot: EXEC|READ, flags: PRIVATE|DENYWRITE, fd: 3 ) = 0x7f89d0605000 +#! 0.115 ( 0.005 ms): sleep/4102 open(filename: 0xd09e2ab2, flags: CLOEXEC ) = + +export RE_LINE_TRACE_ONE_PROC="^\s*$RE_NUMBER\s*\(\s*$RE_NUMBER\s*ms\s*\):\s*\w+\(.*\)\s+=\s+(?:\-?$RE_NUMBER|0x$RE_NUMBER_HEX).*$" +# A line of perf-trace output +# Examples: +# 0.115 ( 0.005 ms): open(filename: 0xd09e2ab2, flags: CLOEXEC ) = 3 +# 0.157 ( 0.005 ms): mmap(len: 3932736, prot: EXEC|READ, flags: PRIVATE|DENYWRITE, fd: 3 ) = 0x7f89d0605000 +#! 0.115 ( 0.005 ms): open(filename: 0xd09e2ab2, flags: CLOEXEC ) = + +export RE_LINE_TRACE_CONTINUED="^\s*(:?$RE_NUMBER|\?)\s*\(\s*($RE_NUMBER\s*ms\s*)?\):\s*($RE_PROCESS_PID\s*)?\.\.\.\s*\[continued\]:\s+\w+\(\).*\s+=\s+(?:\-?$RE_NUMBER|0x$RE_NUMBER_HEX).*$" +# A line of perf-trace output +# Examples: +# 0.000 ( 0.000 ms): ... [continued]: nanosleep()) = 0 +# 0.000 ( 0.000 ms): ... [continued]: nanosleep()) = 0x00000000 +# ? ( ): packagekitd/94838 ... [continued]: poll()) = 0 (Timeout) +#! 0.000 ( 0.000 ms): ... [continued]: nanosleep()) = + +export RE_LINE_TRACE_UNFINISHED="^\s*$RE_NUMBER\s*\(\s*\):\s*$RE_PROCESS_PID\s+.*\)\s+\.\.\.\s*$" +# A line of perf-trace output +# Examples: +# 901.040 ( ): in:imjournal/1096 ppoll(ufds: 0x7f701a5adb70, nfds: 1, tsp: 0x7f701a5adaf0, sigsetsize: 8) ... +# 613.727 ( ): gmain/1099 poll(ufds: 0x56248f6b64b0, nfds: 2, timeout_msecs: 3996) ... + +export RE_LINE_TRACE_SUMMARY_HEADER="\s*syscall\s+calls\s+(?:errors\s+)?total\s+min\s+avg\s+max\s+stddev" +# A header of a perf-trace summary table +# Example: +# syscall calls total min avg max stddev +# syscall calls errors total min avg max stddev + + +export RE_LINE_TRACE_SUMMARY_CONTENT="^\s*\w+\s+(?:$RE_NUMBER\s+){5,6}$RE_NUMBER%" +# A line of a perf-trace summary table +# Example: +# open 3 0.017 0.005 0.006 0.007 10.90% +# openat 2 0 0.017 0.008 0.009 0.010 12.29% + + +export RE_LINE_REPORT_CONTENT="^\s+$RE_NUMBER%\s+\w+\s+\S+\s+\S+\s+\S+" # FIXME +# A line from typicap perf report --stdio output +# Example: +# 100.00% sleep [kernel.vmlinux] [k] syscall_return_slowpath + + +export RE_TASK="\s+[\w~\/ \.\+:#-]+(?:\[-1(?:\/\d+)?\]|\[\d+(?:\/\d+)?\])" +# A name of a task used for perf sched timehist -s +# Example: +# sleep[62755] +# runtest.sh[62762] +# gmain[705/682] +# xfsaild/dm-0[495] +# kworker/u8:1-ev[62714] +# :-1[-1/62756] +# :-1[-1] +# :-1[62756] + + +export RE_SEGFAULT=".*(?:Segmentation\sfault|SIGSEGV|\score\s|dumped|segfault).*" +# Possible variations of the segfault message +# Example: +# /bin/bash: line 1: 32 Segmentation fault timeout 15s +# Segmentation fault (core dumped) +# Program terminated with signal SIGSEGV +#! WARNING: 12323431 isn't a 'cpu_core', please use a CPU list in the 'cpu_core' range (0-15) diff --git a/tools/perf/tests/shell/common/settings.sh b/tools/perf/tests/shell/common/settings.sh new file mode 100644 index 000000000000..cba1b338f96f --- /dev/null +++ b/tools/perf/tests/shell/common/settings.sh @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# settings.sh +# Author: Michael Petlan <mpetlan@redhat.com> +# +# Description: +# +# This file contains global settings for the whole testsuite. +# Its purpose is to make it easier when it is necessary i.e. to +# change the usual sample command which is used in all of the tests +# in many files. +# +# This file is intended to be sourced in the tests. +# + +#### which perf to use in the testing +export CMD_PERF=${CMD_PERF:-`which perf`} + +#### basic programs examinated by perf +export CMD_BASIC_SLEEP="sleep 0.1" +export CMD_QUICK_SLEEP="sleep 0.01" +export CMD_LONGER_SLEEP="sleep 2" +export CMD_DOUBLE_LONGER_SLEEP="sleep 4" +export CMD_VERY_LONG_SLEEP="sleep 30" +export CMD_SIMPLE="true" + +#### testsuite run mode +# define constants: +export RUNMODE_BASIC=0 +export RUNMODE_STANDARD=1 +export RUNMODE_EXPERIMENTAL=2 +# default runmode is STANDARD +export PERFTOOL_TESTSUITE_RUNMODE=${PERFTOOL_TESTSUITE_RUNMODE:-$RUNMODE_STANDARD} + +#### common settings +export TESTLOG_VERBOSITY=${TESTLOG_VERBOSITY:-2} +export TESTLOG_FORCE_COLOR=${TESTLOG_FORCE_COLOR:-n} +export TESTLOG_ERR_MSG_MAX_LINES=${TESTLOG_ERR_MSG_MAX_LINES:-20} +export TESTLOG_CLEAN=${TESTLOG_CLEAN:-y} + +#### other environment-related settings +export TEST_IGNORE_MISSING_PMU=${TEST_IGNORE_MISSING_PMU:-n} + +#### clear locale +export LC_ALL=C + +#### colors +if [ -t 1 ] || [ "$TESTLOG_FORCE_COLOR" = "yes" ]; then + export MPASS="\e[32m" + export MALLPASS="\e[1;32m" + export MFAIL="\e[31m" + export MALLFAIL="\e[1;31m" + export MWARN="\e[1;35m" + export MSKIP="\e[33m" + export MHIGH="\e[1;33m" + export MEND="\e[m" +else + export MPASS="" + export MALLPASS="" + export MFAIL="" + export MALLFAIL="" + export MWARN="" + export MSKIP="" + export MHIGH="" + export MEND="" +fi + +### general info +DIR_PATH=`dirname "$(readlink -e "$0")"` + +TEST_NAME=`basename $DIR_PATH | sed 's/base/perf/'`; export TEST_NAME +MY_ARCH=`arch`; export MY_ARCH + +# storing logs and temporary files variables +if [ -n "$PERFSUITE_RUN_DIR" ]; then + # when $PERFSUITE_RUN_DIR is set to something, all the logs and temp files will be placed there + # --> the $PERFSUITE_RUN_DIR/perf_something/examples and $PERFSUITE_RUN_DIR/perf_something/logs + # dirs will be used for that + PERFSUITE_RUN_DIR=`readlink -f $PERFSUITE_RUN_DIR`; export PERFSUITE_RUN_DIR + export CURRENT_TEST_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME" + export MAKE_TARGET_DIR="$CURRENT_TEST_DIR/examples" + export LOGS_DIR="$CURRENT_TEST_DIR/logs" + export HEADER_TAR_DIR="$CURRENT_TEST_DIR/header_tar" + test -d "$CURRENT_TEST_DIR" || mkdir -p "$CURRENT_TEST_DIR" + test -d "$LOGS_DIR" || mkdir -p "$LOGS_DIR" +else + # when $PERFSUITE_RUN_DIR is not set, logs will be placed here + export CURRENT_TEST_DIR="." + export LOGS_DIR="." + export HEADER_TAR_DIR="./header_tar" +fi + + +#### test parametrization +if [ ! -d ./common ]; then + # set parameters based on runmode + if [ -f ../common/parametrization.$PERFTOOL_TESTSUITE_RUNMODE.sh ]; then + # shellcheck source=/dev/null + . ../common/parametrization.$PERFTOOL_TESTSUITE_RUNMODE.sh + fi + # if some parameters haven't been set until now, set them to default + if [ -f ../common/parametrization.sh ]; then + . ../common/parametrization.sh + fi +fi diff --git a/tools/perf/tests/shell/coresight/Makefile b/tools/perf/tests/shell/coresight/Makefile index b070e779703e..fa08fd9a5991 100644 --- a/tools/perf/tests/shell/coresight/Makefile +++ b/tools/perf/tests/shell/coresight/Makefile @@ -24,6 +24,6 @@ CLEANDIRS = $(SUBDIRS:%=clean-%) clean: $(CLEANDIRS) $(CLEANDIRS): - $(call QUIET_CLEAN, test-$(@:clean-%=%)) $(Q)$(MAKE) -C $(@:clean-%=%) clean >/dev/null + $(call QUIET_CLEAN, test-$(@:clean-%=%)) $(MAKE) -C $(@:clean-%=%) clean >/dev/null .PHONY: all clean $(SUBDIRS) $(CLEANDIRS) $(INSTALLDIRS) diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop.sh b/tools/perf/tests/shell/coresight/asm_pure_loop.sh index 2d65defb7e0f..c63bc8c73e26 100755 --- a/tools/perf/tests/shell/coresight/asm_pure_loop.sh +++ b/tools/perf/tests/shell/coresight/asm_pure_loop.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / ASM Pure Loop +# CoreSight / ASM Pure Loop (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 diff --git a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh index ddcc9bb850f5..8e29630957c8 100755 --- a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh +++ b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Memcpy 16k 10 Threads +# CoreSight / Memcpy 16k 10 Threads (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh index 2ce5e139b2fd..0c4c82a1c8e1 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Thread Loop 10 Threads - Check TID +# CoreSight / Thread Loop 10 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh index 3ad9498753d7..d3aea9fc6ced 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Thread Loop 2 Threads - Check TID +# CoreSight / Thread Loop 2 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh index 4fbb4a29aad3..7429d3a2ae43 100755 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh +++ b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Unroll Loop Thread 10 +# CoreSight / Unroll Loop Thread 10 (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh new file mode 100755 index 000000000000..c243731d2fbf --- /dev/null +++ b/tools/perf/tests/shell/ftrace.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# perf ftrace tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +# perf ftrace commands only works for root +if [ "$(id -u)" != 0 ]; then + echo "perf ftrace test [Skipped: no permission]" + exit 2 +fi + +output=$(mktemp /tmp/__perf_test.ftrace.XXXXXX) + +cleanup() { + rm -f "${output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# this will be set in test_ftrace_trace() +target_function= + +test_ftrace_list() { + echo "perf ftrace list test" + perf ftrace -F > "${output}" + # this will be used in test_ftrace_trace() + sleep_functions=$(grep 'sys_.*sleep$' "${output}") + echo "syscalls for sleep:" + echo "${sleep_functions}" + echo "perf ftrace list test [Success]" +} + +test_ftrace_trace() { + echo "perf ftrace trace test" + perf ftrace trace --graph-opts depth=5 sleep 0.1 > "${output}" + # it should have some function name contains 'sleep' + grep "^#" "${output}" + grep -F 'sleep()' "${output}" + # find actual syscall function name + for FN in ${sleep_functions}; do + if grep -q "${FN}" "${output}"; then + target_function="${FN}" + echo "perf ftrace trace test [Success]" + return + fi + done + + echo "perf ftrace trace test [Failure: sleep syscall not found]" + exit 1 +} + +test_ftrace_latency() { + echo "perf ftrace latency test" + echo "target function: ${target_function}" + perf ftrace latency -T "${target_function}" sleep 0.1 > "${output}" + grep "^#" "${output}" + grep "###" "${output}" + echo "perf ftrace latency test [Success]" +} + +test_ftrace_profile() { + echo "perf ftrace profile test" + perf ftrace profile --graph-opts depth=5 sleep 0.1 > "${output}" + grep ^# "${output}" + time_re="[[:space:]]+1[[:digit:]]{5}\.[[:digit:]]{3}" + # 100283.000 100283.000 100283.000 1 __x64_sys_clock_nanosleep + # Check for one *clock_nanosleep line with a Count of just 1 that takes a bit more than 0.1 seconds + # Strip the _x64_sys part to work with other architectures + grep -E "^${time_re}${time_re}${time_re}[[:space:]]+1[[:space:]]+.*clock_nanosleep" "${output}" + echo "perf ftrace profile test [Success]" +} + +test_ftrace_list +test_ftrace_trace +test_ftrace_latency +test_ftrace_profile + +cleanup +exit 0 diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/shell/lib/attr.py index e890c261ad26..3db9a7d78715 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/shell/lib/attr.py @@ -246,6 +246,23 @@ class Test(object): return False return True + def restore_sample_rate(self, value=10000): + try: + # Check value of sample_rate + with open("/proc/sys/kernel/perf_event_max_sample_rate", "r") as fIn: + curr_value = fIn.readline() + # If too low restore to reasonable value + if not curr_value or int(curr_value) < int(value): + with open("/proc/sys/kernel/perf_event_max_sample_rate", "w") as fOut: + fOut.write(str(value)) + + except IOError as e: + log.warning("couldn't restore sample_rate value: I/O error %s" % e) + except ValueError as e: + log.warning("couldn't restore sample_rate value: Value error %s" % e) + except TypeError as e: + log.warning("couldn't restore sample_rate value: Type error %s" % e) + def load_events(self, path, events): parser_event = configparser.ConfigParser() parser_event.read(path) @@ -283,6 +300,7 @@ class Test(object): if self.skip_test_kernel_until(): raise Notest(self, "new kernel skip") + self.restore_sample_rate() cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir, self.perf, self.command, tempdir, self.args) ret = os.WEXITSTATUS(os.system(cmd)) diff --git a/tools/perf/tests/shell/lib/coresight.sh b/tools/perf/tests/shell/lib/coresight.sh index 11ed2c25ed91..184d62e7e5bd 100644 --- a/tools/perf/tests/shell/lib/coresight.sh +++ b/tools/perf/tests/shell/lib/coresight.sh @@ -18,7 +18,7 @@ BIN="$DIR/$TEST" # If the test tool/binary does not exist and is executable then skip the test if ! test -x "$BIN"; then exit 2; fi # If CoreSight is not available, skip the test -perf list cs_etm | grep -q cs_etm || exit 2 +perf list pmu | grep -q cs_etm || exit 2 DATD="." # If the data dir env is set then make the data dir use that instead of ./ if test -n "$PERF_TEST_CORESIGHT_DATADIR"; then diff --git a/tools/perf/tests/shell/lib/perf_has_symbol.sh b/tools/perf/tests/shell/lib/perf_has_symbol.sh index 5d59c32ae3e7..561c93b75d77 100644 --- a/tools/perf/tests/shell/lib/perf_has_symbol.sh +++ b/tools/perf/tests/shell/lib/perf_has_symbol.sh @@ -3,7 +3,7 @@ perf_has_symbol() { - if perf test -vv "Symbols" 2>&1 | grep "[[:space:]]$1$"; then + if perf test -vv -F "Symbols" 2>&1 | grep "[[:space:]]$1$"; then echo "perf does have symbol '$1'" return 0 fi diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index ea55d5ea1ced..b066d721f897 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -15,6 +15,7 @@ ap.add_argument('--event', action='store_true') ap.add_argument('--per-core', action='store_true') ap.add_argument('--per-thread', action='store_true') ap.add_argument('--per-cache', action='store_true') +ap.add_argument('--per-cluster', action='store_true') ap.add_argument('--per-die', action='store_true') ap.add_argument('--per-node', action='store_true') ap.add_argument('--per-socket', action='store_true') @@ -49,12 +50,14 @@ def check_json_output(expected_items): 'cgroup': lambda x: True, 'cpu': lambda x: isint(x), 'cache': lambda x: True, + 'cluster': lambda x: True, 'die': lambda x: True, 'event': lambda x: True, 'event-runtime': lambda x: isfloat(x), 'interval': lambda x: isfloat(x), 'metric-unit': lambda x: True, 'metric-value': lambda x: isfloat(x), + 'metric-threshold': lambda x: x in ['unknown', 'good', 'less good', 'nearly bad', 'bad'], 'metricgroup': lambda x: True, 'node': lambda x: True, 'pcnt-running': lambda x: isfloat(x), @@ -66,14 +69,16 @@ def check_json_output(expected_items): for item in json.loads(input): if expected_items != -1: count = len(item) - if count != expected_items and count >= 1 and count <= 6 and 'metric-value' in item: + if count not in expected_items and count >= 1 and count <= 7 and 'metric-value' in item: # Events that generate >1 metric may have isolated metric # values and possibly other prefixes like interval, core, # aggregate-number, or event-runtime/pcnt-running from multiplexing. pass - elif count != expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: + elif count not in expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: pass - elif count != expected_items: + elif count - 1 in expected_items and 'metric-threshold' in item: + pass + elif count not in expected_items: raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}' f' in \'{item}\'') for key, value in item.items(): @@ -85,11 +90,11 @@ def check_json_output(expected_items): try: if args.no_args or args.system_wide or args.event: - expected_items = 7 + expected_items = [5, 7] elif args.interval or args.per_thread or args.system_wide_no_aggr: - expected_items = 8 - elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cache: - expected_items = 9 + expected_items = [6, 8] + elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cluster or args.per_cache: + expected_items = [7, 9] else: # If no option is specified, don't check the number of items. expected_items = -1 diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py index 50a34a9cc040..0b94216c9c46 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation.py +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -1,4 +1,4 @@ -#SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0 import re import csv import json @@ -6,36 +6,61 @@ import argparse from pathlib import Path import subprocess + +class TestError: + def __init__(self, metric: list[str], wl: str, value: list[float], low: float, up=float('nan'), description=str()): + self.metric: list = metric # multiple metrics in relationship type tests + self.workloads = [wl] # multiple workloads possible + self.collectedValue: list = value + self.valueLowBound = low + self.valueUpBound = up + self.description = description + + def __repr__(self) -> str: + if len(self.metric) > 1: + return "\nMetric Relationship Error: \tThe collected value of metric {0}\n\ + \tis {1} in workload(s): {2} \n\ + \tbut expected value range is [{3}, {4}]\n\ + \tRelationship rule description: \'{5}\'".format(self.metric, self.collectedValue, self.workloads, + self.valueLowBound, self.valueUpBound, self.description) + elif len(self.collectedValue) == 0: + return "\nNo Metric Value Error: \tMetric {0} returns with no value \n\ + \tworkload(s): {1}".format(self.metric, self.workloads) + else: + return "\nWrong Metric Value Error: \tThe collected value of metric {0}\n\ + \tis {1} in workload(s): {2}\n\ + \tbut expected value range is [{3}, {4}]"\ + .format(self.metric, self.collectedValue, self.workloads, + self.valueLowBound, self.valueUpBound) + + class Validator: def __init__(self, rulefname, reportfname='', t=5, debug=False, datafname='', fullrulefname='', workload='true', metrics=''): self.rulefname = rulefname self.reportfname = reportfname self.rules = None - self.collectlist:str = metrics + self.collectlist: str = metrics self.metrics = self.__set_metrics(metrics) self.skiplist = set() self.tolerance = t self.workloads = [x for x in workload.split(",") if x] - self.wlidx = 0 # idx of current workloads - self.allresults = dict() # metric results of all workload - self.allignoremetrics = dict() # metrics with no results or negative results - self.allfailtests = dict() + self.wlidx = 0 # idx of current workloads + self.allresults = dict() # metric results of all workload self.alltotalcnt = dict() self.allpassedcnt = dict() - self.allerrlist = dict() - self.results = dict() # metric results of current workload + self.results = dict() # metric results of current workload # vars for test pass/failure statistics - self.ignoremetrics= set() # metrics with no results or negative results, neg result counts as a failed test - self.failtests = dict() + # metrics with no results or negative results, neg result counts failed tests + self.ignoremetrics = set() self.totalcnt = 0 self.passedcnt = 0 # vars for errors self.errlist = list() # vars for Rule Generator - self.pctgmetrics = set() # Percentage rule + self.pctgmetrics = set() # Percentage rule # vars for debug self.datafname = datafname @@ -69,10 +94,10 @@ class Validator: ensure_ascii=True, indent=4) - def get_results(self, idx:int = 0): - return self.results[idx] + def get_results(self, idx: int = 0): + return self.results.get(idx) - def get_bounds(self, lb, ub, error, alias={}, ridx:int = 0) -> list: + def get_bounds(self, lb, ub, error, alias={}, ridx: int = 0) -> list: """ Get bounds and tolerance from lb, ub, and error. If missing lb, use 0.0; missing ub, use float('inf); missing error, use self.tolerance. @@ -85,7 +110,7 @@ class Validator: tolerance, denormalized base on upper bound value """ # init ubv and lbv to invalid values - def get_bound_value (bound, initval, ridx): + def get_bound_value(bound, initval, ridx): val = initval if isinstance(bound, int) or isinstance(bound, float): val = bound @@ -113,10 +138,10 @@ class Validator: return lbv, ubv, denormerr - def get_value(self, name:str, ridx:int = 0) -> list: + def get_value(self, name: str, ridx: int = 0) -> list: """ Get value of the metric from self.results. - If result of this metric is not provided, the metric name will be added into self.ignoremetics and self.errlist. + If result of this metric is not provided, the metric name will be added into self.ignoremetics. All future test(s) on this metric will fail. @param name: name of the metric @@ -142,38 +167,43 @@ class Validator: Check if metrics value are non-negative. One metric is counted as one test. Failure: when metric value is negative or not provided. - Metrics with negative value will be added into the self.failtests['PositiveValueTest'] and self.ignoremetrics. + Metrics with negative value will be added into self.ignoremetrics. """ negmetric = dict() pcnt = 0 tcnt = 0 rerun = list() - for name, val in self.get_results().items(): + results = self.get_results() + if not results: + return + for name, val in results.items(): if val < 0: negmetric[name] = val rerun.append(name) else: pcnt += 1 tcnt += 1 + # The first round collect_perf() run these metrics with simple workload + # "true". We give metrics a second chance with a longer workload if less + # than 20 metrics failed positive test. if len(rerun) > 0 and len(rerun) < 20: second_results = dict() self.second_test(rerun, second_results) for name, val in second_results.items(): - if name not in negmetric: continue + if name not in negmetric: + continue if val >= 0: del negmetric[name] pcnt += 1 - self.failtests['PositiveValueTest']['Total Tests'] = tcnt - self.failtests['PositiveValueTest']['Passed Tests'] = pcnt if len(negmetric.keys()): self.ignoremetrics.update(negmetric.keys()) - negmessage = ["{0}(={1:.4f})".format(name, val) for name, val in negmetric.items()] - self.failtests['PositiveValueTest']['Failed Tests'].append({'NegativeValue': negmessage}) + self.errlist.extend( + [TestError([m], self.workloads[self.wlidx], negmetric[m], 0) for m in negmetric.keys()]) return - def evaluate_formula(self, formula:str, alias:dict, ridx:int = 0): + def evaluate_formula(self, formula: str, alias: dict, ridx: int = 0): """ Evaluate the value of formula. @@ -187,10 +217,11 @@ class Validator: sign = "+" f = str() - #TODO: support parenthesis? + # TODO: support parenthesis? for i in range(len(formula)): if i+1 == len(formula) or formula[i] in ('+', '-', '*', '/'): - s = alias[formula[b:i]] if i+1 < len(formula) else alias[formula[b:]] + s = alias[formula[b:i]] if i + \ + 1 < len(formula) else alias[formula[b:]] v = self.get_value(s, ridx) if not v: errs.append(s) @@ -228,49 +259,49 @@ class Validator: alias = dict() for m in rule['Metrics']: alias[m['Alias']] = m['Name'] - lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'], alias, ridx=rule['RuleIndex']) - val, f = self.evaluate_formula(rule['Formula'], alias, ridx=rule['RuleIndex']) + lbv, ubv, t = self.get_bounds( + rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold'], alias, ridx=rule['RuleIndex']) + val, f = self.evaluate_formula( + rule['Formula'], alias, ridx=rule['RuleIndex']) + + lb = rule['RangeLower'] + ub = rule['RangeUpper'] + if isinstance(lb, str): + if lb in alias: + lb = alias[lb] + if isinstance(ub, str): + if ub in alias: + ub = alias[ub] + if val == -1: - self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Description':f}) + self.errlist.append(TestError([m['Name'] for m in rule['Metrics']], self.workloads[self.wlidx], [], + lb, ub, rule['Description'])) elif not self.check_bound(val, lbv, ubv, t): - lb = rule['RangeLower'] - ub = rule['RangeUpper'] - if isinstance(lb, str): - if lb in alias: - lb = alias[lb] - if isinstance(ub, str): - if ub in alias: - ub = alias[ub] - self.failtests['RelationshipTest']['Failed Tests'].append({'RuleIndex': rule['RuleIndex'], 'Formula':f, - 'RangeLower': lb, 'LowerBoundValue': self.get_value(lb), - 'RangeUpper': ub, 'UpperBoundValue':self.get_value(ub), - 'ErrorThreshold': t, 'CollectedValue': val}) + self.errlist.append(TestError([m['Name'] for m in rule['Metrics']], self.workloads[self.wlidx], [val], + lb, ub, rule['Description'])) else: self.passedcnt += 1 - self.failtests['RelationshipTest']['Passed Tests'] += 1 self.totalcnt += 1 - self.failtests['RelationshipTest']['Total Tests'] += 1 return - # Single Metric Test - def single_test(self, rule:dict): + def single_test(self, rule: dict): """ Validate if the metrics are in the required value range. eg. lower_bound <= metrics_value <= upper_bound One metric is counted as one test in this type of test. One rule may include one or more metrics. Failure: when the metric value not provided or the value is outside the bounds. - This test updates self.total_cnt and records failed tests in self.failtest['SingleMetricTest']. + This test updates self.total_cnt. @param rule: dict with metrics to validate and the value range requirement """ - lbv, ubv, t = self.get_bounds(rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold']) + lbv, ubv, t = self.get_bounds( + rule['RangeLower'], rule['RangeUpper'], rule['ErrorThreshold']) metrics = rule['Metrics'] passcnt = 0 totalcnt = 0 - faillist = list() failures = dict() rerun = list() for m in metrics: @@ -286,25 +317,20 @@ class Validator: second_results = dict() self.second_test(rerun, second_results) for name, val in second_results.items(): - if name not in failures: continue + if name not in failures: + continue if self.check_bound(val, lbv, ubv, t): passcnt += 1 del failures[name] else: - failures[name] = val + failures[name] = [val] self.results[0][name] = val self.totalcnt += totalcnt self.passedcnt += passcnt - self.failtests['SingleMetricTest']['Total Tests'] += totalcnt - self.failtests['SingleMetricTest']['Passed Tests'] += passcnt if len(failures.keys()) != 0: - faillist = [{'MetricName':name, 'CollectedValue':val} for name, val in failures.items()] - self.failtests['SingleMetricTest']['Failed Tests'].append({'RuleIndex':rule['RuleIndex'], - 'RangeLower': rule['RangeLower'], - 'RangeUpper': rule['RangeUpper'], - 'ErrorThreshold':rule['ErrorThreshold'], - 'Failure':faillist}) + self.errlist.extend([TestError([name], self.workloads[self.wlidx], val, + rule['RangeLower'], rule['RangeUpper']) for name, val in failures.items()]) return @@ -312,19 +338,11 @@ class Validator: """ Create final report and write into a JSON file. """ - alldata = list() - for i in range(0, len(self.workloads)): - reportstas = {"Total Rule Count": self.alltotalcnt[i], "Passed Rule Count": self.allpassedcnt[i]} - data = {"Metric Validation Statistics": reportstas, "Tests in Category": self.allfailtests[i], - "Errors":self.allerrlist[i]} - alldata.append({"Workload": self.workloads[i], "Report": data}) - - json_str = json.dumps(alldata, indent=4) - print("Test validation finished. Final report: ") - print(json_str) + print(self.errlist) if self.debug: - allres = [{"Workload": self.workloads[i], "Results": self.allresults[i]} for i in range(0, len(self.workloads))] + allres = [{"Workload": self.workloads[i], "Results": self.allresults[i]} + for i in range(0, len(self.workloads))] self.json_dump(allres, self.datafname) def check_rule(self, testtype, metric_list): @@ -342,13 +360,13 @@ class Validator: return True # Start of Collector and Converter - def convert(self, data: list, metricvalues:dict): + def convert(self, data: list, metricvalues: dict): """ Convert collected metric data from the -j output to dict of {metric_name:value}. """ for json_string in data: try: - result =json.loads(json_string) + result = json.loads(json_string) if "metric-unit" in result and result["metric-unit"] != "(null)" and result["metric-unit"] != "": name = result["metric-unit"].split(" ")[1] if len(result["metric-unit"].split(" ")) > 1 \ else result["metric-unit"] @@ -365,9 +383,10 @@ class Validator: print(" ".join(command)) cmd = subprocess.run(command, stderr=subprocess.PIPE, encoding='utf-8') data = [x+'}' for x in cmd.stderr.split('}\n') if x] + if data[0][0] != '{': + data[0] = data[0][data[0].find('{'):] return data - def collect_perf(self, workload: str): """ Collect metric data with "perf stat -M" on given workload with -a and -j. @@ -385,14 +404,18 @@ class Validator: if rule["TestType"] == "RelationshipTest": metrics = [m["Name"] for m in rule["Metrics"]] if not any(m not in collectlist[0] for m in metrics): - collectlist[rule["RuleIndex"]] = [",".join(list(set(metrics)))] + collectlist[rule["RuleIndex"]] = [ + ",".join(list(set(metrics)))] for idx, metrics in collectlist.items(): - if idx == 0: wl = "true" - else: wl = workload + if idx == 0: + wl = "true" + else: + wl = workload for metric in metrics: data = self._run_perf(metric, wl) - if idx not in self.results: self.results[idx] = dict() + if idx not in self.results: + self.results[idx] = dict() self.convert(data, self.results[idx]) return @@ -412,7 +435,8 @@ class Validator: 2) create metric name list """ command = ['perf', 'list', '-j', '--details', 'metrics'] - cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8') + cmd = subprocess.run(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, encoding='utf-8') try: data = json.loads(cmd.stdout) for m in data: @@ -453,12 +477,12 @@ class Validator: rules = data['RelationshipRules'] self.skiplist = set([name.lower() for name in data['SkipList']]) self.rules = self.remove_unsupported_rules(rules) - pctgrule = {'RuleIndex':0, - 'TestType':'SingleMetricTest', - 'RangeLower':'0', + pctgrule = {'RuleIndex': 0, + 'TestType': 'SingleMetricTest', + 'RangeLower': '0', 'RangeUpper': '100', 'ErrorThreshold': self.tolerance, - 'Description':'Metrics in percent unit have value with in [0, 100]', + 'Description': 'Metrics in percent unit have value with in [0, 100]', 'Metrics': [{'Name': m.lower()} for m in self.pctgmetrics]} self.rules.append(pctgrule) @@ -469,8 +493,9 @@ class Validator: idx += 1 if self.debug: - #TODO: need to test and generate file name correctly - data = {'RelationshipRules':self.rules, 'SupportedMetrics': [{"MetricName": name} for name in self.metrics]} + # TODO: need to test and generate file name correctly + data = {'RelationshipRules': self.rules, 'SupportedMetrics': [ + {"MetricName": name} for name in self.metrics]} self.json_dump(data, self.fullrulefname) return @@ -482,20 +507,17 @@ class Validator: @param key: key to the dictionaries (index of self.workloads). ''' self.allresults[key] = self.results - self.allignoremetrics[key] = self.ignoremetrics - self.allfailtests[key] = self.failtests self.alltotalcnt[key] = self.totalcnt self.allpassedcnt[key] = self.passedcnt - self.allerrlist[key] = self.errlist - #Initialize data structures before data validation of each workload + # Initialize data structures before data validation of each workload def _init_data(self): - testtypes = ['PositiveValueTest', 'RelationshipTest', 'SingleMetricTest'] + testtypes = ['PositiveValueTest', + 'RelationshipTest', 'SingleMetricTest'] self.results = dict() - self.ignoremetrics= set() + self.ignoremetrics = set() self.errlist = list() - self.failtests = {k:{'Total Tests':0, 'Passed Tests':0, 'Failed Tests':[]} for k in testtypes} self.totalcnt = 0 self.passedcnt = 0 @@ -513,6 +535,9 @@ class Validator: ''' if not self.collectlist: self.parse_perf_metrics() + if not self.metrics: + print("No metric found for testing") + return 0 self.create_rules() for i in range(0, len(self.workloads)): self.wlidx = i @@ -525,32 +550,33 @@ class Validator: testtype = r['TestType'] if not self.check_rule(testtype, r['Metrics']): continue - if testtype == 'RelationshipTest': + if testtype == 'RelationshipTest': self.relationship_test(r) elif testtype == 'SingleMetricTest': self.single_test(r) else: print("Unsupported Test Type: ", testtype) - self.errlist.append("Unsupported Test Type from rule: " + r['RuleIndex']) - self._storewldata(i) print("Workload: ", self.workloads[i]) - print("Total metrics collected: ", self.failtests['PositiveValueTest']['Total Tests']) - print("Non-negative metric count: ", self.failtests['PositiveValueTest']['Passed Tests']) print("Total Test Count: ", self.totalcnt) print("Passed Test Count: ", self.passedcnt) - + self._storewldata(i) self.create_report() - return sum(self.alltotalcnt.values()) != sum(self.allpassedcnt.values()) + return len(self.errlist) > 0 # End of Class Validator def main() -> None: - parser = argparse.ArgumentParser(description="Launch metric value validation") - - parser.add_argument("-rule", help="Base validation rule file", required=True) - parser.add_argument("-output_dir", help="Path for validator output file, report file", required=True) - parser.add_argument("-debug", help="Debug run, save intermediate data to files", action="store_true", default=False) - parser.add_argument("-wl", help="Workload to run while data collection", default="true") + parser = argparse.ArgumentParser( + description="Launch metric value validation") + + parser.add_argument( + "-rule", help="Base validation rule file", required=True) + parser.add_argument( + "-output_dir", help="Path for validator output file, report file", required=True) + parser.add_argument("-debug", help="Debug run, save intermediate data to files", + action="store_true", default=False) + parser.add_argument( + "-wl", help="Workload to run while data collection", default="true") parser.add_argument("-m", help="Metric list to validate", default="") args = parser.parse_args() outpath = Path(args.output_dir) @@ -559,8 +585,8 @@ def main() -> None: datafile = Path.joinpath(outpath, 'perf_data.json') validator = Validator(args.rule, reportf, debug=args.debug, - datafname=datafile, fullrulefname=fullrule, workload=args.wl, - metrics=args.m) + datafname=datafile, fullrulefname=fullrule, workload=args.wl, + metrics=args.m) ret = validator.test() return ret @@ -569,6 +595,3 @@ def main() -> None: if __name__ == "__main__": import sys sys.exit(main()) - - - diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index bf4c1fb71c4b..5c33ec7a5a63 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -13,7 +13,12 @@ cleanup_probe_vfs_getname() { add_probe_vfs_getname() { add_probe_verbose=$1 if [ $had_vfs_getname -eq 1 ] ; then - line=$(perf probe -L getname_flags 2>&1 | grep -E 'result.*=.*filename;' | sed -r 's/[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*/\1/') + result_filename_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*" + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_filename_re" | sed -r "s/$result_filename_re/\1/") + if [ -z "$line" ] ; then + result_aname_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->aname = NULL;" + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_aname_re" | sed -r "s/$result_aname_re/\1/") + fi perf probe -q "vfs_getname=getname_flags:${line} pathname=result->name:string" || \ perf probe $add_probe_verbose "vfs_getname=getname_flags:${line} pathname=filename:ustring" fi @@ -27,7 +32,7 @@ skip_if_no_debuginfo() { # check if perf is compiled with libtraceevent support skip_no_probe_record_support() { if [ $had_vfs_getname -eq 1 ] ; then - perf record --dry-run -e $1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2 - return 1 + perf check feature -q libtraceevent && return 1 + return 2 fi } diff --git a/tools/perf/tests/shell/lib/stat_output.sh b/tools/perf/tests/shell/lib/stat_output.sh index 3cc158a64326..9a176ceae4a3 100644 --- a/tools/perf/tests/shell/lib/stat_output.sh +++ b/tools/perf/tests/shell/lib/stat_output.sh @@ -79,7 +79,7 @@ check_per_thread() echo "[Skip] paranoid and not root" return fi - perf stat --per-thread -a $2 true + perf stat --per-thread -p $$ $2 true commachecker --per-thread echo "[Success]" } @@ -97,6 +97,18 @@ check_per_cache_instance() echo "[Success]" } +check_per_cluster() +{ + echo -n "Checking $1 output: per cluster " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoid and not root" + return + fi + perf stat --per-cluster -a $2 true + echo "[Success]" +} + check_per_die() { echo -n "Checking $1 output: per die " diff --git a/tools/perf/tests/shell/list.sh b/tools/perf/tests/shell/list.sh index 8a868ae64560..76a9846cff22 100755 --- a/tools/perf/tests/shell/list.sh +++ b/tools/perf/tests/shell/list.sh @@ -24,8 +24,11 @@ trap trap_cleanup EXIT TERM INT test_list_json() { echo "Json output test" + # Generate perf list json output into list_output file. perf list -j -o "${list_output}" - $PYTHON -m json.tool "${list_output}" + # Validate the json using python, redirect the json copy to /dev/null as + # otherwise the test may block writing to stdout. + $PYTHON -m json.tool "${list_output}" /dev/null echo "Json output test [Success]" } diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index c1ec5762215b..30d195d4c62f 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -27,7 +27,7 @@ check() { exit fi - if ! perf list | grep -q lock:contention_begin; then + if ! perf list tracepoint | grep -q lock:contention_begin; then echo "[Skip] No lock contention tracepoints" err=2 exit diff --git a/tools/perf/tests/shell/perftool-testsuite_probe.sh b/tools/perf/tests/shell/perftool-testsuite_probe.sh new file mode 100755 index 000000000000..7b1bfd0f888f --- /dev/null +++ b/tools/perf/tests/shell/perftool-testsuite_probe.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# perftool-testsuite_probe (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +test -d "$(dirname "$0")/base_probe" || exit 2 +cd "$(dirname "$0")/base_probe" || exit 2 +status=0 + +PERFSUITE_RUN_DIR=$(mktemp -d /tmp/"$(basename "$0" .sh)".XXX) +export PERFSUITE_RUN_DIR + +for testcase in setup.sh test_*; do # skip setup.sh if not present or not executable + test -x "$testcase" || continue + ./"$testcase" + (( status += $? )) +done + +if ! [ "$PERFTEST_KEEP_LOGS" = "y" ]; then + rm -rf "$PERFSUITE_RUN_DIR" +fi + +test $status -ne 0 && exit 1 +exit 0 diff --git a/tools/perf/tests/shell/perftool-testsuite_report.sh b/tools/perf/tests/shell/perftool-testsuite_report.sh new file mode 100755 index 000000000000..a8cf75b4e77e --- /dev/null +++ b/tools/perf/tests/shell/perftool-testsuite_report.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# perftool-testsuite_report (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +test -d "$(dirname "$0")/base_report" || exit 2 +cd "$(dirname "$0")/base_report" || exit 2 +status=0 + +PERFSUITE_RUN_DIR=$(mktemp -d /tmp/"$(basename "$0" .sh)".XXX) +export PERFSUITE_RUN_DIR + +for testcase in setup.sh test_*; do # skip setup.sh if not present or not executable + test -x "$testcase" || continue + ./"$testcase" + (( status += $? )) +done + +if ! [ "$PERFTEST_KEEP_LOGS" = "y" ]; then + rm -rf "$PERFSUITE_RUN_DIR" +fi + +test $status -ne 0 && exit 1 +exit 0 diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index a78d35d2cff0..e459aa99a951 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf pipe recording and injection test # SPDX-License-Identifier: GPL-2.0 @@ -11,31 +11,117 @@ sym="noploop" skip_test_missing_symbol ${sym} data=$(mktemp /tmp/perf.data.XXXXXX) +data2=$(mktemp /tmp/perf.data2.XXXXXX) prog="perf test -w noploop" -task="perf" +[ "$(uname -m)" = "s390x" ] && prog="$prog 3" +err=0 -if ! perf record -e task-clock:u -o - ${prog} | perf report -i - --task | grep ${task}; then - echo "cannot find the test file in the perf report" - exit 1 -fi +set -e -if ! perf record -e task-clock:u -o - ${prog} | perf inject -b | perf report -i - | grep ${sym}; then - echo "cannot find noploop function in pipe #1" - exit 1 -fi +cleanup() { + rm -rf "${data}" + rm -rf "${data}".old + rm -rf "${data2}" + rm -rf "${data2}".old -perf record -e task-clock:u -o - ${prog} | perf inject -b -o ${data} -if ! perf report -i ${data} | grep ${sym}; then - echo "cannot find noploop function in pipe #2" - exit 1 -fi + trap - EXIT TERM INT +} -perf record -e task-clock:u -o ${data} ${prog} -if ! perf inject -b -i ${data} | perf report -i - | grep ${sym}; then - echo "cannot find noploop function in pipe #3" - exit 1 -fi +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT +test_record_report() { + echo + echo "Record+report pipe test" + + task="perf" + if ! perf record -e task-clock:u -o - ${prog} | perf report -i - --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #1]" + err=1 + return + fi + + if ! perf record -g -e task-clock:u -o - ${prog} | perf report -i - --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #2]" + err=1 + return + fi + + perf record -g -e task-clock:u -o - ${prog} > ${data} + if ! perf report -i ${data} --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #3]" + err=1 + return + fi + + echo "Record+report pipe test [Success]" +} + +test_inject_bids() { + inject_opt=$1 + + echo + echo "Inject ${inject_opt} build-ids test" + + if ! perf record -e task-clock:u -o - ${prog} | perf inject ${inject_opt}| perf report -i - | grep -q ${sym} + then + echo "Inject build-ids test [Failed - cannot find noploop function in pipe #1]" + err=1 + return + fi + + if ! perf record -g -e task-clock:u -o - ${prog} | perf inject ${inject_opt} | perf report -i - | grep -q ${sym} + then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #2]" + err=1 + return + fi + + perf record -e task-clock:u -o - ${prog} | perf inject ${inject_opt} -o ${data} + if ! perf report -i ${data} | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #3]" + err=1 + return + fi + + perf record -e task-clock:u -o ${data} ${prog} + if ! perf inject ${inject_opt} -i ${data} | perf report -i - | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #4]" + err=1 + return + fi + + perf record -e task-clock:u -o - ${prog} > ${data} + if ! perf inject ${inject_opt} -i ${data} | perf report -i - | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #5]" + err=1 + return + fi + + perf record -e task-clock:u -o - ${prog} > ${data} + perf inject ${inject_opt} -i ${data} -o ${data2} + if ! perf report -i ${data2} | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #6]" + err=1 + return + fi + + echo "Inject ${inject_opt} build-ids test [Success]" +} + +test_record_report +test_inject_bids -B +test_inject_bids -b +test_inject_bids --buildid-all +test_inject_bids --mmap2-buildid-all + +cleanup +exit $err -rm -f ${data} ${data}.old -exit 0 diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh index 554e12e83c55..0c5aacc446b3 100755 --- a/tools/perf/tests/shell/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Add vfs_getname probe to get syscall args filenames +# Add vfs_getname probe to get syscall args filenames (exclusive) # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 72c65570db37..d5e5193cceb6 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -1,5 +1,5 @@ #!/bin/sh -# probe libc's inet_pton & backtrace it with ping +# probe libc's inet_pton & backtrace it with ping (exclusive) # Installs a probe on libc's inet_pton function, that will use uprobes, # then use 'perf trace' on a ping to localhost asking for just one packet @@ -40,20 +40,11 @@ trace_libc_inet_pton_backtrace() { case "$(uname -m)" in s390x) eventattr='call-graph=dwarf,max-stack=4' - echo "(__GI_)?getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected - echo "main\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected - ;; - ppc64|ppc64le) - eventattr='max-stack=4' - # Add gaih_inet to expected backtrace only if it is part of libc. - if nm $libc | grep -F -q gaih_inet.; then - echo "gaih_inet.*\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected - fi - echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected - echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected + echo "((__GI_)?getaddrinfo|text_to_binary_address)\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected + echo "(gaih_inet|main)\+0x[[:xdigit:]]+[[:space:]]\(inlined|.*/bin/ping.*\)$" >> $expected ;; *) - eventattr='max-stack=3' + eventattr='max-stack=4' echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected ;; esac @@ -63,7 +54,10 @@ trace_libc_inet_pton_backtrace() { # Check presence of libtraceevent support to run perf record skip_no_probe_record_support "$event_name/$eventattr/" - [ $? -eq 2 ] && return 2 + if [ $? -eq 2 ]; then + echo "WARN: Skipping test trace_libc_inet_pton_backtrace. No libtraceevent support." + return 2 + fi perf record -e $event_name/$eventattr/ -o $perf_data ping -6 -c 1 ::1 > /dev/null 2>&1 # check if perf data file got created in above step. @@ -73,14 +67,25 @@ trace_libc_inet_pton_backtrace() { fi perf script -i $perf_data | tac | grep -m1 ^ping -B9 | tac > $perf_script - exec 3<$perf_script exec 4<$expected - while read line <&3 && read -r pattern <&4; do + while read -r pattern <&4; do + echo "Pattern: $pattern" [ -z "$pattern" ] && break - echo $line - echo "$line" | grep -E -q "$pattern" - if [ $? -ne 0 ] ; then - printf "FAIL: expected backtrace entry \"%s\" got \"%s\"\n" "$pattern" "$line" + + found=0 + + # Search lines in the perf script result + exec 3<$perf_script + while read line <&3; do + [ -z "$line" ] && break + echo " Matching: $line" + ! echo "$line" | grep -E -q "$pattern" + found=$? + [ $found -eq 1 ] && break + done + + if [ $found -ne 1 ] ; then + printf "FAIL: Didn't find the expected backtrace entry \"%s\"\n" "$pattern" return 1 fi done diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh index 5eedbe29bba1..5940fdc1df37 100755 --- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh +++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Use vfs_getname probe to get syscall args filenames +# Use vfs_getname probe to get syscall args filenames (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system # then use it with 'perf record' using 'touch' to write to a temp file, then @@ -21,7 +21,10 @@ record_open_file() { echo "Recording open file:" # Check presence of libtraceevent support to run perf record skip_no_probe_record_support "probe:vfs_getname*" - [ $? -eq 2 ] && return 2 + if [ $? -eq 2 ]; then + echo "WARN: Skipping test record_open_file. No libtraceevent support" + return 2 + fi perf record -o ${perfdata} -e probe:vfs_getname\* touch $file } diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 3d1a7759a7b2..0fc7a909ae9b 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -1,5 +1,5 @@ -#!/bin/sh -# perf record tests +#!/bin/bash +# perf record tests (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e @@ -17,10 +17,21 @@ skip_test_missing_symbol ${testsym} err=0 perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) +script_output=$(mktemp /tmp/__perf_test.perf.data.XXXXX.script) testprog="perf test -w thloop" cpu_pmu_dir="/sys/bus/event_source/devices/cpu*" br_cntr_file="/caps/branch_counter_nr" br_cntr_output="branch stack counters" +br_cntr_script_output="br_cntr: A" + +default_fd_limit=$(ulimit -Sn) +# With option --threads=cpu the number of open file descriptors should be +# equal to sum of: nmb_cpus * nmb_events (2+dummy), +# nmb_threads for perf.data.n (equal to nmb_cpus) and +# 2*nmb_cpus of pipes = 4*nmb_cpus (each pipe has 2 ends) +# All together it needs 8*nmb_cpus file descriptors plus some are also used +# outside of testing, thus raising the limit to 16*nmb_cpus +min_fd_limit=$(($(getconf _NPROCESSORS_ONLN) * 16)) cleanup() { rm -rf "${perfdata}" @@ -83,7 +94,7 @@ test_per_thread() { test_register_capture() { echo "Register capture test" - if ! perf list | grep -q 'br_inst_retired.near_call' + if ! perf list pmu | grep -q 'br_inst_retired.near_call' then echo "Register capture test [Skipped missing event]" return @@ -165,7 +176,7 @@ test_workload() { } test_branch_counter() { - echo "Basic branch counter test" + echo "Branch counter test" # Check if the branch counter feature is supported for dir in $cpu_pmu_dir do @@ -175,26 +186,133 @@ test_branch_counter() { return fi done - if ! perf record -o "${perfdata}" -j any,counter ${testprog} 2> /dev/null + if ! perf record -o "${perfdata}" -e "{branches:p,instructions}" -j any,counter ${testprog} 2> /dev/null then - echo "Basic branch counter test [Failed record]" + echo "Branch counter record test [Failed record]" err=1 return fi if ! perf report -i "${perfdata}" -D -q | grep -q "$br_cntr_output" then - echo "Basic branch record test [Failed missing output]" + echo "Branch counter report test [Failed missing output]" + err=1 + return + fi + if ! perf script -i "${perfdata}" -F +brstackinsn,+brcntr | grep -q "$br_cntr_script_output" + then + echo " Branch counter script test [Failed missing output]" + err=1 + return + fi + echo "Branch counter test [Success]" +} + +test_cgroup() { + echo "Cgroup sampling test" + if ! perf record -aB --synth=cgroup --all-cgroups -o "${perfdata}" ${testprog} 2> /dev/null + then + echo "Cgroup sampling [Skipped not supported]" + return + fi + if ! perf report -i "${perfdata}" -D | grep -q "CGROUP" + then + echo "Cgroup sampling [Failed missing output]" + err=1 + return + fi + if ! perf script -i "${perfdata}" -F cgroup | grep -q -v "unknown" + then + echo "Cgroup sampling [Failed cannot resolve cgroup names]" + err=1 + return + fi + echo "Cgroup sampling test [Success]" +} + +test_leader_sampling() { + echo "Basic leader sampling test" + if ! perf record -o "${perfdata}" -e "{instructions,instructions}:Su" -- \ + perf test -w brstack 2> /dev/null + then + echo "Leader sampling [Failed record]" + err=1 + return + fi + index=0 + perf script -i "${perfdata}" > $script_output + while IFS= read -r line + do + # Check if the two instruction counts are equal in each record + instructions=$(echo $line | awk '{for(i=1;i<=NF;i++) if($i=="instructions:") print $(i-1)}') + if [ $(($index%2)) -ne 0 ] && [ ${instructions}x != ${prev_instructions}x ] + then + echo "Leader sampling [Failed inconsistent instructions count]" + err=1 + return + fi + index=$(($index+1)) + prev_instructions=$instructions + done < $script_output + echo "Basic leader sampling test [Success]" +} + +test_topdown_leader_sampling() { + echo "Topdown leader sampling test" + if ! perf stat -e "{slots,topdown-retiring}" true 2> /dev/null + then + echo "Topdown leader sampling [Skipped event parsing failed]" + return + fi + if ! perf record -o "${perfdata}" -e "{instructions,slots,topdown-retiring}:S" true 2> /dev/null + then + echo "Topdown leader sampling [Failed topdown events not reordered correctly]" + err=1 + return + fi + echo "Topdown leader sampling test [Success]" +} + +test_precise_max() { + echo "precise_max attribute test" + if ! perf stat -e "cycles,instructions" true 2> /dev/null + then + echo "precise_max attribute [Skipped no hardware events]" + return + fi + # Just to make sure it doesn't fail + if ! perf record -o "${perfdata}" -e "cycles:P" true 2> /dev/null + then + echo "precise_max attribute [Failed cycles:P event]" err=1 return fi - echo "Basic branch counter test [Success]" + # On AMD, cycles and instructions events are treated differently + if ! perf record -o "${perfdata}" -e "instructions:P" true 2> /dev/null + then + echo "precise_max attribute [Failed instructions:P event]" + err=1 + return + fi + echo "precise_max attribute test [Success]" } +# raise the limit of file descriptors to minimum +if [[ $default_fd_limit -lt $min_fd_limit ]]; then + ulimit -Sn $min_fd_limit +fi + test_per_thread test_register_capture test_system_wide test_workload test_branch_counter +test_cgroup +test_leader_sampling +test_topdown_leader_sampling +test_precise_max + +# restore the default value +ulimit -Sn $default_fd_limit cleanup exit $err diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh index 31c593966e8c..1b58ccc1fd88 100755 --- a/tools/perf/tests/shell/record_bpf_filter.sh +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -22,15 +22,16 @@ trap trap_cleanup EXIT TERM INT test_bpf_filter_priv() { echo "Checking BPF-filter privilege" - if [ "$(id -u)" != 0 ] - then - echo "bpf-filter test [Skipped permission]" - err=2 - return - fi if ! perf record -e task-clock --filter 'period > 1' \ -o /dev/null --quiet true 2>&1 then + if [ "$(id -u)" != 0 ] + then + echo "try 'sudo perf record --setup-filter pin' first." + echo "bpf-filter test [Skipped permission]" + err=2 + return + fi echo "bpf-filter test [Skipped missing BPF support]" err=2 return @@ -67,7 +68,7 @@ test_bpf_filter_fail() { # 'cpu' requires PERF_SAMPLE_CPU flag if ! perf record -e task-clock --filter 'cpu > 0' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CPU then echo "Failing bpf-filter test [Failed forbidden CPU]" err=1 @@ -97,7 +98,7 @@ test_bpf_filter_group() { fi if ! perf record -e task-clock --filter 'cpu > 0 || ip > 0' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CPU then echo "Group bpf-filter test [Failed forbidden CPU]" err=1 @@ -105,7 +106,7 @@ test_bpf_filter_group() { fi if ! perf record -e task-clock --filter 'period > 0 || code_pgsz > 4096' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CODE_PAGE_SIZE + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CODE_PAGE_SIZE then echo "Group bpf-filter test [Failed forbidden CODE_PAGE_SIZE]" err=1 @@ -115,6 +116,65 @@ test_bpf_filter_group() { echo "Group bpf-filter test [Success]" } +test_bpf_filter_multi() { + echo "Multiple bpf-filter test" + + if ! perf record -e task-clock --filter 'period > 100000' \ + -e page-faults --filter 'ip < 0xffffffff00000000' \ + -o "${perfdata}" true 2> /dev/null + then + echo "Multiple bpf-filter test [Failed record]" + err=1 + return + fi + + if ! perf script -i "${perfdata}" -F period,event | grep task-clock | \ + awk '{ if (int($1) <= 100000) { print $0; exit(1); } }' + then + echo "Multiple bpf-filter test [Failed task-clock period]" + err=1 + return + fi + + if perf script -i "${perfdata}" -F event,ip | grep page-fault | \ + grep 'ffffffff[0-9a-f]*' + then + echo "Multiple bpf-filter test [Failed page-faults ip]" + err=1 + return + fi + + echo "Multiple bpf-filter test [Success]" +} + +test_bpf_filter_cgroup() { + echo "Cgroup bpf-filter test" + + if ! perf record -e task-clock --filter 'cgroup == /' \ + -a --all-cgroups --synth=cgroup -o "${perfdata}" true 2> /dev/null + then + echo "Cgroup bpf-filter test [Skipped cgroup not supported]" + return + fi + + # 'cgroup' requires PERF_SAMPLE_CGROUP flag + if ! perf record -e task-clock --filter 'cgroup == /' \ + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CGROUP + then + echo "Cgroup bpf-filter test [Failed CGROUP requires --all-cgroups]" + err=1 + return + fi + + if ! perf report -i "${perfdata}" -s cgroup -q | grep -q -F '100.00%' + then + echo "Cgroup bpf-filter test [Failed root cgroup does not have 100%]" + err=1 + return + fi + + echo "Cgroup bpf-filter test [Success]" +} test_bpf_filter_priv @@ -130,5 +190,13 @@ if [ $err = 0 ]; then test_bpf_filter_group fi +if [ $err = 0 ]; then + test_bpf_filter_multi +fi + +if [ $err = 0 ]; then + test_bpf_filter_cgroup +fi + cleanup exit $err diff --git a/tools/perf/tests/shell/record_lbr.sh b/tools/perf/tests/shell/record_lbr.sh new file mode 100755 index 000000000000..8d750ee631f8 --- /dev/null +++ b/tools/perf/tests/shell/record_lbr.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# perf record LBR tests (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +set -e + +if [ ! -f /sys/devices/cpu/caps/branches ] && [ ! -f /sys/devices/cpu_core/caps/branches ] +then + echo "Skip: only x86 CPUs support LBR" + exit 2 +fi + +err=0 +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + +cleanup() { + rm -rf "${perfdata}" + rm -rf "${perfdata}".old + rm -rf "${perfdata}".txt + + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + + +lbr_callgraph_test() { + test="LBR callgraph" + + echo "$test" + if ! perf record -e cycles --call-graph lbr -o "${perfdata}" perf test -w thloop + then + echo "$test [Failed support missing]" + if [ $err -eq 0 ] + then + err=2 + fi + return + fi + + if ! perf report --stitch-lbr -i "${perfdata}" > "${perfdata}".txt + then + cat "${perfdata}".txt + echo "$test [Failed in perf report]" + err=1 + return + fi + + echo "$test [Success]" +} + +lbr_test() { + local branch_flags=$1 + local test="LBR $2 test" + local threshold=$3 + local out + local sam_nr + local bs_nr + local zero_nr + local r + + echo "$test" + if ! perf record -e cycles $branch_flags -o "${perfdata}" perf test -w thloop + then + echo "$test [Failed support missing]" + perf record -e cycles $branch_flags -o "${perfdata}" perf test -w thloop || true + if [ $err -eq 0 ] + then + err=2 + fi + return + fi + + out=$(perf report -D -i "${perfdata}" 2> /dev/null | grep -A1 'PERF_RECORD_SAMPLE') + sam_nr=$(echo "$out" | grep -c 'PERF_RECORD_SAMPLE' || true) + if [ $sam_nr -eq 0 ] + then + echo "$test [Failed no samples captured]" + err=1 + return + fi + echo "$test: $sam_nr samples" + + bs_nr=$(echo "$out" | grep -c 'branch stack: nr:' || true) + if [ $sam_nr -ne $bs_nr ] + then + echo "$test [Failed samples missing branch stacks]" + err=1 + return + fi + + zero_nr=$(echo "$out" | grep -c 'branch stack: nr:0' || true) + r=$(($zero_nr * 100 / $bs_nr)) + if [ $r -gt $threshold ]; then + echo "$test [Failed empty br stack ratio exceed $threshold%: $r%]" + err=1 + return + fi + + echo "$test [Success]" +} + +parallel_lbr_test() { + err=0 + perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + lbr_test "$1" "$2" "$3" + cleanup + exit $err +} + +lbr_callgraph_test + +# Sequential +lbr_test "-b" "any branch" 2 +lbr_test "-j any_call" "any call" 2 +lbr_test "-j any_ret" "any ret" 2 +lbr_test "-j ind_call" "any indirect call" 2 +lbr_test "-j ind_jmp" "any indirect jump" 100 +lbr_test "-j call" "direct calls" 2 +lbr_test "-j ind_call,u" "any indirect user call" 100 +lbr_test "-a -b" "system wide any branch" 2 +lbr_test "-a -j any_call" "system wide any call" 2 + +# Parallel +parallel_lbr_test "-b" "parallel any branch" 100 & +pid1=$! +parallel_lbr_test "-j any_call" "parallel any call" 100 & +pid2=$! +parallel_lbr_test "-j any_ret" "parallel any ret" 100 & +pid3=$! +parallel_lbr_test "-j ind_call" "parallel any indirect call" 100 & +pid4=$! +parallel_lbr_test "-j ind_jmp" "parallel any indirect jump" 100 & +pid5=$! +parallel_lbr_test "-j call" "parallel direct calls" 100 & +pid6=$! +parallel_lbr_test "-j ind_call,u" "parallel any indirect user call" 100 & +pid7=$! +parallel_lbr_test "-a -b" "parallel system wide any branch" 100 & +pid8=$! +parallel_lbr_test "-a -j any_call" "parallel system wide any call" 100 & +pid9=$! + +for pid in $pid1 $pid2 $pid3 $pid4 $pid5 $pid6 $pid7 $pid8 $pid9 +do + set +e + wait $pid + child_err=$? + set -e + if ([ $err -eq 2 ] && [ $child_err -eq 1 ]) || [ $err -eq 0 ] + then + err=$child_err + fi +done + +cleanup +exit $err diff --git a/tools/perf/tests/shell/record_offcpu.sh b/tools/perf/tests/shell/record_offcpu.sh index 67c925f3a15a..678947fe69ee 100755 --- a/tools/perf/tests/shell/record_offcpu.sh +++ b/tools/perf/tests/shell/record_offcpu.sh @@ -1,5 +1,5 @@ #!/bin/sh -# perf record offcpu profiling tests +# perf record offcpu profiling tests (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e diff --git a/tools/perf/tests/shell/script.sh b/tools/perf/tests/shell/script.sh index fa4d71e2e72a..d3e2958d2242 100755 --- a/tools/perf/tests/shell/script.sh +++ b/tools/perf/tests/shell/script.sh @@ -17,7 +17,7 @@ cleanup() sane=$(echo "${temp_dir}" | cut -b 1-21) if [ "${sane}" = "/tmp/perf-test-script" ] ; then echo "--- Cleaning up ---" - rm -f "${temp_dir}/"* + rm -rf "${temp_dir:?}/"* rmdir "${temp_dir}" fi } @@ -61,11 +61,38 @@ _end_of_file_ esac perf record $cmd_flags -o "${perfdatafile}" true + # Disable lsan to avoid warnings about python memory leaks. + export ASAN_OPTIONS=detect_leaks=0 perf script -i "${perfdatafile}" -s "${db_test}" + export ASAN_OPTIONS= echo "DB test [Success]" } +test_parallel_perf() +{ + echo "parallel-perf test" + if ! python3 --version >/dev/null 2>&1 ; then + echo "SKIP: no python3" + err=2 + return + fi + pp=$(dirname "$0")/../../scripts/python/parallel-perf.py + if [ ! -f "${pp}" ] ; then + echo "SKIP: parallel-perf.py script not found " + err=2 + return + fi + perf_data="${temp_dir}/pp-perf.data" + output1_dir="${temp_dir}/output1" + output2_dir="${temp_dir}/output2" + perf record -o "${perf_data}" --sample-cpu uname + python3 "${pp}" -o "${output1_dir}" --jobs 4 --verbose -- perf script -i "${perf_data}" + python3 "${pp}" -o "${output2_dir}" --jobs 4 --verbose --per-cpu -- perf script -i "${perf_data}" + echo "parallel-perf test [Success]" +} + test_db +test_parallel_perf cleanup diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh index f1818fa6d9ce..fc2d8cc6e5e0 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -42,6 +42,7 @@ function commachecker() ;; "--per-socket") exp=8 ;; "--per-node") exp=8 ;; "--per-die") exp=8 + ;; "--per-cluster") exp=8 ;; "--per-cache") exp=8 esac @@ -79,6 +80,7 @@ then check_system_wide_no_aggr "CSV" "$perf_cmd" check_per_core "CSV" "$perf_cmd" check_per_cache_instance "CSV" "$perf_cmd" + check_per_cluster "CSV" "$perf_cmd" check_per_die "CSV" "$perf_cmd" check_per_socket "CSV" "$perf_cmd" else diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/shell/stat+json_output.sh index 3bc900533a5d..6b630d33c328 100755 --- a/tools/perf/tests/shell/stat+json_output.sh +++ b/tools/perf/tests/shell/stat+json_output.sh @@ -105,7 +105,7 @@ check_per_thread() echo "[Skip] paranoia and not root" return fi - perf stat -j --per-thread -a -o "${stat_output}" true + perf stat -j --per-thread -p $$ -o "${stat_output}" true $PYTHON $pythonchecker --per-thread --file "${stat_output}" echo "[Success]" } @@ -122,6 +122,18 @@ check_per_cache_instance() echo "[Success]" } +check_per_cluster() +{ + echo -n "Checking json output: per cluster " + if ParanoidAndNotRoot 0 + then + echo "[Skip] paranoia and not root" + return + fi + perf stat -j --per-cluster -a true 2>&1 | $PYTHON $pythonchecker --per-cluster + echo "[Success]" +} + check_per_die() { echo -n "Checking json output: per die " @@ -200,6 +212,7 @@ then check_system_wide_no_aggr check_per_core check_per_cache_instance + check_per_cluster check_per_die check_per_socket else diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh index 4fcdd1a9142c..0f7967be60af 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -13,7 +13,7 @@ stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults stalled-cycles-frontend stalled-cycles-backend cycles instructions branches branch-misses) event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "frontend cycles idle" "backend cycles idle" "GHz" "insn per cycle" "/sec" "of all branches") -skip_metric=("stalled cycles per insn" "tma_") +skip_metric=("stalled cycles per insn" "tma_" "retiring" "frontend_bound" "bad_speculation" "backend_bound" "TopdownL1" "percent of slots") cleanup() { rm -f "${stat_output}" @@ -40,6 +40,7 @@ function commachecker() ;; "--per-node") prefix=3 ;; "--per-die") prefix=3 ;; "--per-cache") prefix=3 + ;; "--per-cluster") prefix=3 esac while read line @@ -99,6 +100,7 @@ then check_system_wide_no_aggr "STD" "$perf_cmd" check_per_core "STD" "$perf_cmd" check_per_cache_instance "STD" "$perf_cmd" + check_per_cluster "STD" "$perf_cmd" check_per_die "STD" "$perf_cmd" check_per_socket "STD" "$perf_cmd" else diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh index 3f1e67795490..68323d636fb7 100755 --- a/tools/perf/tests/shell/stat.sh +++ b/tools/perf/tests/shell/stat.sh @@ -73,9 +73,33 @@ test_topdown_groups() { err=1 return fi - if perf stat -e '{topdown-retiring,slots}' true 2>&1 | grep -E -q "<not supported>" + if perf stat -e 'instructions,topdown-retiring,slots' true 2>&1 | grep -E -q "<not supported>" then - echo "Topdown event group test [Failed slots not reordered first]" + echo "Topdown event group test [Failed slots not reordered first in no-group case]" + err=1 + return + fi + if perf stat -e '{instructions,topdown-retiring,slots}' true 2>&1 | grep -E -q "<not supported>" + then + echo "Topdown event group test [Failed slots not reordered first in single group case]" + err=1 + return + fi + if perf stat -e '{instructions,slots},topdown-retiring' true 2>&1 | grep -E -q "<not supported>" + then + echo "Topdown event group test [Failed topdown metrics event not move into slots group]" + err=1 + return + fi + if perf stat -e '{instructions,slots},{topdown-retiring}' true 2>&1 | grep -E -q "<not supported>" + then + echo "Topdown event group test [Failed topdown metrics group not merge into slots group]" + err=1 + return + fi + if perf stat -e '{instructions,r400,r8000}' true 2>&1 | grep -E -q "<not supported>" + then + echo "Topdown event group test [Failed raw format slots not reordered first]" err=1 return fi @@ -117,16 +141,18 @@ test_cputype() { # Find a known PMU for cputype. pmu="" - for i in cpu cpu_atom armv8_pmuv3_0 + devs="/sys/bus/event_source/devices" + for i in $devs/cpu $devs/cpu_atom $devs/armv8_pmuv3_0 $devs/armv8_cortex_* do - if test -d "/sys/devices/$i" + i_base=$(basename "$i") + if test -d "$i" then - pmu="$i" + pmu="$i_base" break fi - if perf stat -e "$i/instructions/" true > /dev/null 2>&1 + if perf stat -e "$i_base/instructions/" true > /dev/null 2>&1 then - pmu="$i" + pmu="$i_base" break fi done @@ -146,6 +172,34 @@ test_cputype() { echo "cputype test [Success]" } +test_hybrid() { + # Test the default stat command on hybrid devices opens one cycles event for + # each CPU type. + echo "hybrid test" + + # Count the number of core PMUs, assume minimum of 1 + pmus=$(ls /sys/bus/event_source/devices/*/cpus 2>/dev/null | wc -l) + if [ "$pmus" -lt 1 ] + then + pmus=1 + fi + + # Run default Perf stat + cycles_events=$(perf stat -- true 2>&1 | grep -E "/cycles/[uH]*| cycles[:uH]* " -c) + + # The expectation is that default output will have a cycles events on each + # hybrid PMU. In situations with no cycles PMU events, like virtualized, this + # can fall back to task-clock and so the end count may be 0. Fail if neither + # condition holds. + if [ "$pmus" -ne "$cycles_events" ] && [ "0" -ne "$cycles_events" ] + then + echo "hybrid test [Found $pmus PMUs but $cycles_events cycles events. Failed]" + err=1 + return + fi + echo "hybrid test [Success]" +} + test_default_stat test_stat_record_report test_stat_record_script @@ -153,4 +207,5 @@ test_stat_repeat_weak_groups test_topdown_groups test_topdown_weak_groups test_cputype +test_hybrid exit $err diff --git a/tools/perf/tests/shell/stat_all_metricgroups.sh b/tools/perf/tests/shell/stat_all_metricgroups.sh index 55ef9c9ded2d..c6d61a4ac3e7 100755 --- a/tools/perf/tests/shell/stat_all_metricgroups.sh +++ b/tools/perf/tests/shell/stat_all_metricgroups.sh @@ -1,9 +1,7 @@ -#!/bin/sh +#!/bin/bash # perf all metricgroups test # SPDX-License-Identifier: GPL-2.0 -set -e - ParanoidAndNotRoot() { [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] @@ -14,11 +12,37 @@ if ParanoidAndNotRoot 0 then system_wide_flag="" fi - +err=0 for m in $(perf list --raw-dump metricgroups) do echo "Testing $m" - perf stat -M "$m" $system_wide_flag sleep 0.01 + result=$(perf stat -M "$m" $system_wide_flag sleep 0.01 2>&1) + result_err=$? + if [[ $result_err -gt 0 ]] + then + if [[ "$result" =~ \ + "Access to performance monitoring and observability operations is limited" ]] + then + echo "Permission failure" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + elif [[ "$result" =~ "in per-thread mode, enable system wide" ]] + then + echo "Permissions - need system wide mode" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + else + echo "Metric group $m failed" + echo $result + err=1 # Fail + fi + fi done -exit 0 +exit $err diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh index 54774525e18a..73e9347e88a9 100755 --- a/tools/perf/tests/shell/stat_all_metrics.sh +++ b/tools/perf/tests/shell/stat_all_metrics.sh @@ -2,42 +2,87 @@ # perf all metrics test # SPDX-License-Identifier: GPL-2.0 +ParanoidAndNotRoot() +{ + [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] +} + +system_wide_flag="-a" +if ParanoidAndNotRoot 0 +then + system_wide_flag="" +fi + err=0 for m in $(perf list --raw-dump metrics); do echo "Testing $m" - result=$(perf stat -M "$m" true 2>&1) - if [[ "$result" =~ ${m:0:50} ]] || [[ "$result" =~ "<not supported>" ]] + result=$(perf stat -M "$m" $system_wide_flag -- sleep 0.01 2>&1) + result_err=$? + if [[ $result_err -gt 0 ]] then - continue + if [[ "$result" =~ \ + "Access to performance monitoring and observability operations is limited" ]] + then + echo "Permission failure" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "in per-thread mode, enable system wide" ]] + then + echo "Permissions - need system wide mode" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "<not supported>" ]] + then + echo "Not supported events" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]] + then + echo "FP issues" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "PMM" ]] + then + echo "Optane memory issues" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + fi fi - # Failed so try system wide. - result=$(perf stat -M "$m" -a sleep 0.01 2>&1) + if [[ "$result" =~ ${m:0:50} ]] then continue fi - # Failed again, possibly the workload was too small so retry with something - # longer. - result=$(perf stat -M "$m" perf bench internals synthesize 2>&1) + + # Failed, possibly the workload was too small so retry with something longer. + result=$(perf stat -M "$m" $system_wide_flag -- perf bench internals synthesize 2>&1) if [[ "$result" =~ ${m:0:50} ]] then continue fi echo "Metric '$m' not printed in:" echo "$result" - if [[ "$err" != "1" ]] - then - err=2 - if [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]] - then - echo "Skip, not fail, for FP issues" - elif [[ "$result" =~ "PMM" ]] - then - echo "Skip, not fail, for Optane memory issues" - else - err=1 - fi - fi + err=1 done exit "$err" diff --git a/tools/perf/tests/shell/stat_all_pmu.sh b/tools/perf/tests/shell/stat_all_pmu.sh index d2a3506e0d19..8b148b300be1 100755 --- a/tools/perf/tests/shell/stat_all_pmu.sh +++ b/tools/perf/tests/shell/stat_all_pmu.sh @@ -1,23 +1,51 @@ -#!/bin/sh -# perf all PMU test +#!/bin/bash +# perf all PMU test (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e +err=0 +result="" + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + echo "$result" + exit 1 +} +trap trap_cleanup EXIT TERM INT # Test all PMU events; however exclude parameterized ones (name contains '?') -for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g'); do +for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g') +do echo "Testing $p" result=$(perf stat -e "$p" true 2>&1) - if ! echo "$result" | grep -q "$p" && ! echo "$result" | grep -q "<not supported>" ; then - # We failed to see the event and it is supported. Possibly the workload was - # too small so retry with something longer. - result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) - if ! echo "$result" | grep -q "$p" ; then - echo "Event '$p' not printed in:" - echo "$result" - exit 1 - fi + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue + fi + if echo "$result" | grep -q "<not supported>" + then + # Event not supported, so ignore. + continue + fi + if echo "$result" | grep -q "Access to performance monitoring and observability operations is limited." + then + # Access is limited, so ignore. + continue + fi + + # We failed to see the event and it is supported. Possibly the workload was + # too small so retry with something longer. + result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue fi + echo "Error: event '$p' not printed in:" + echo "$result" + err=1 done -exit 0 +trap - EXIT TERM INT +exit $err diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh index a87bb2814b4c..95d2ad5d17c6 100755 --- a/tools/perf/tests/shell/stat_bpf_counters.sh +++ b/tools/perf/tests/shell/stat_bpf_counters.sh @@ -1,45 +1,74 @@ #!/bin/sh -# perf stat --bpf-counters test +# perf stat --bpf-counters test (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e -# check whether $2 is within +/- 10% of $1 +workload="perf test -w sqrtloop" + +# check whether $2 is within +/- 20% of $1 compare_number() { - first_num=$1 - second_num=$2 - - # upper bound is first_num * 110% - upper=$(expr $first_num + $first_num / 10 ) - # lower bound is first_num * 90% - lower=$(expr $first_num - $first_num / 10 ) - - if [ $second_num -gt $upper ] || [ $second_num -lt $lower ]; then - echo "The difference between $first_num and $second_num are greater than 10%." - exit 1 - fi + first_num=$1 + second_num=$2 + + # upper bound is first_num * 120% + upper=$(expr $first_num + $first_num / 5 ) + # lower bound is first_num * 80% + lower=$(expr $first_num - $first_num / 5 ) + + if [ $second_num -gt $upper ] || [ $second_num -lt $lower ]; then + echo "The difference between $first_num and $second_num are greater than 20%." + exit 1 + fi +} + +check_counts() +{ + base_instructions=$1 + bpf_instructions=$2 + + if [ "$base_instructions" = "<not" ]; then + echo "Skipping: instructions event not counted" + exit 2 + fi + if [ "$bpf_instructions" = "<not" ]; then + echo "Failed: instructions not counted with --bpf-counters" + exit 1 + fi +} + +test_bpf_counters() +{ + printf "Testing --bpf-counters " + base_instructions=$(perf stat --no-big-num -e instructions -- $workload 2>&1 | awk '/instructions/ {print $1}') + bpf_instructions=$(perf stat --no-big-num --bpf-counters -e instructions -- $workload 2>&1 | awk '/instructions/ {print $1}') + check_counts $base_instructions $bpf_instructions + compare_number $base_instructions $bpf_instructions + echo "[Success]" +} + +test_bpf_modifier() +{ + printf "Testing bpf event modifier " + stat_output=$(perf stat --no-big-num -e instructions/name=base_instructions/,instructions/name=bpf_instructions/b -- $workload 2>&1) + base_instructions=$(echo "$stat_output"| awk '/base_instructions/ {print $1}') + bpf_instructions=$(echo "$stat_output"| awk '/bpf_instructions/ {print $1}') + check_counts $base_instructions $bpf_instructions + compare_number $base_instructions $bpf_instructions + echo "[Success]" } # skip if --bpf-counters is not supported -if ! perf stat -e cycles --bpf-counters true > /dev/null 2>&1; then +if ! perf stat -e instructions --bpf-counters true > /dev/null 2>&1; then if [ "$1" = "-v" ]; then echo "Skipping: --bpf-counters not supported" - perf --no-pager stat -e cycles --bpf-counters true || true + perf --no-pager stat -e instructions --bpf-counters true || true fi exit 2 fi -base_cycles=$(perf stat --no-big-num -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}') -if [ "$base_cycles" = "<not" ]; then - echo "Skipping: cycles event not counted" - exit 2 -fi -bpf_cycles=$(perf stat --no-big-num --bpf-counters -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}') -if [ "$bpf_cycles" = "<not" ]; then - echo "Failed: cycles not counted with --bpf-counters" - exit 1 -fi +test_bpf_counters +test_bpf_modifier -compare_number $base_cycles $bpf_cycles exit 0 diff --git a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh index e75d0780dc78..2ec69060c42f 100755 --- a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh +++ b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh @@ -58,22 +58,9 @@ check_system_wide_counted() fi } -check_cpu_list_counted() -{ - check_cpu_list_counted_output=$(perf stat -C 0,1 --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, taskset -c 1 sleep 1 2>&1) - if echo ${check_cpu_list_counted_output} | grep -q -F "<not "; then - echo "Some CPU events are not counted" - if [ "${verbose}" = "1" ]; then - echo ${check_cpu_list_counted_output} - fi - exit 1 - fi -} - check_bpf_counter find_cgroups check_system_wide_counted -check_cpu_list_counted exit 0 diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tests/shell/stat_metrics_values.sh index 7ca172599aa6..279f19c5919a 100755 --- a/tools/perf/tests/shell/stat_metrics_values.sh +++ b/tools/perf/tests/shell/stat_metrics_values.sh @@ -19,6 +19,8 @@ echo "Output will be stored in: $tmpdir" $PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workload}" ret=$? rm -rf $tmpdir - +if [ $ret -ne 0 ]; then + echo "Metric validation return with erros. Please check metrics reported with errors." +fi exit $ret diff --git a/tools/perf/tests/shell/test_arm_callgraph_fp.sh b/tools/perf/tests/shell/test_arm_callgraph_fp.sh index e342e6c8aa50..9caa36130175 100755 --- a/tools/perf/tests/shell/test_arm_callgraph_fp.sh +++ b/tools/perf/tests/shell/test_arm_callgraph_fp.sh @@ -6,7 +6,15 @@ shelldir=$(dirname "$0") # shellcheck source=lib/perf_has_symbol.sh . "${shelldir}"/lib/perf_has_symbol.sh -lscpu | grep -q "aarch64" || exit 2 +if [ "$(uname -m)" != "aarch64" ]; then + exit 2 +fi + +if perf version --build-options | grep HAVE_DWARF_UNWIND_SUPPORT | grep -q OFF +then + echo "Skipping, no dwarf unwind support" + exit 2 +fi skip_test_missing_symbol leafloop @@ -20,28 +28,21 @@ cleanup_files() trap cleanup_files EXIT TERM INT -# Add a 1 second delay to skip samples that are not in the leaf() function # shellcheck disable=SC2086 -perf record -o "$PERF_DATA" --call-graph fp -e cycles//u -D 1000 --user-callchains -- $TEST_PROGRAM 2> /dev/null & -PID=$! - -echo " + Recording (PID=$PID)..." -sleep 2 -echo " + Stopping perf-record..." +perf record -o "$PERF_DATA" --call-graph fp -e cycles//u --user-callchains -- $TEST_PROGRAM -kill $PID -wait $PID +# Try opening the file so any immediate errors are visible in the log +perf script -i "$PERF_DATA" -F comm,ip,sym | head -n4 -# expected perf-script output: +# expected perf-script output if 'leaf' has been inserted correctly: # -# program +# perf # 728 leaf # 753 parent # 76c leafloop -# ... +# ... remaining stack to main() ... -perf script -i "$PERF_DATA" -F comm,ip,sym | head -n4 -perf script -i "$PERF_DATA" -F comm,ip,sym | head -n4 | \ - awk '{ if ($2 != "") sym[i++] = $2 } END { if (sym[0] != "leaf" || - sym[1] != "parent" || - sym[2] != "leafloop") exit 1 }' +# Each frame is separated by a tab, some spaces and an address +SEP="[[:space:]]+ [[:xdigit:]]+" +perf script -i "$PERF_DATA" -F comm,ip,sym | tr '\n' ' ' | \ + grep -E -q "perf $SEP leaf $SEP parent $SEP leafloop" diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index 65dd85207125..573af9235b72 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Check Arm CoreSight trace data recording and synthesized samples +# Check Arm CoreSight trace data recording and synthesized samples (exclusive) # Uses the 'perf record' to record trace data with Arm CoreSight sinks; # then verify if there have any branch samples and instruction samples @@ -12,7 +12,7 @@ glb_err=0 skip_if_no_cs_etm_event() { - perf list | grep -q 'cs_etm//' && return 0 + perf list pmu | grep -q 'cs_etm//' && return 0 # cs_etm event doesn't exist return 2 @@ -188,7 +188,7 @@ arm_cs_etm_snapshot_test() { arm_cs_etm_basic_test() { echo "Recording trace with '$*'" - perf record -o ${perfdata} "$@" -- ls > /dev/null 2>&1 + perf record -o ${perfdata} "$@" -m,8M -- ls > /dev/null 2>&1 perf_script_branch_samples ls && perf_report_branch_samples ls && diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh new file mode 100755 index 000000000000..be2d26303f94 --- /dev/null +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Check Arm CoreSight disassembly script completes without errors (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +# The disassembly script reconstructs ranges of instructions and gives these to objdump to +# decode. objdump doesn't like ranges that go backwards, but these are a good indication +# that decoding has gone wrong either in OpenCSD, Perf or in the range reconstruction in +# the script. Test all 3 parts are working correctly by running the script. + +skip_if_no_cs_etm_event() { + perf list pmu | grep -q 'cs_etm//' && return 0 + + # cs_etm event doesn't exist + return 2 +} + +skip_if_no_cs_etm_event || exit 2 + +# Assume an error unless we reach the very end +set -e +glb_err=1 + +perfdata_dir=$(mktemp -d /tmp/__perf_test.perf.data.XXXXX) +perfdata=${perfdata_dir}/perf.data +file=$(mktemp /tmp/temporary_file.XXXXX) +# Relative path works whether it's installed or running from repo +script_path=$(dirname "$0")/../../scripts/python/arm-cs-trace-disasm.py + +cleanup_files() +{ + set +e + rm -rf ${perfdata_dir} + rm -f ${file} + trap - EXIT TERM INT + exit $glb_err +} + +trap cleanup_files EXIT TERM INT + +# Ranges start and end on branches, so check for some likely branch instructions +sep="\s\|\s" +branch_search="\sbl${sep}b${sep}b.ne${sep}b.eq${sep}cbz\s" + +## Test kernel ## +if [ -e /proc/kcore ]; then + echo "Testing kernel disassembly" + perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/null 2>&1 + perf script -i ${perfdata} -s python:${script_path} -- \ + -d --stop-sample=30 2> /dev/null > ${file} + grep -q -e ${branch_search} ${file} + echo "Found kernel branches" +else + # kcore is required for correct kernel decode due to runtime code patching + echo "No kcore, skipping kernel test" +fi + +## Test user ## +echo "Testing userspace disassembly" +perf record -o ${perfdata} -e cs_etm//u -- touch $file > /dev/null 2>&1 +perf script -i ${perfdata} -s python:${script_path} -- \ + -d --stop-sample=30 2> /dev/null > ${file} +grep -q -e ${branch_search} ${file} +echo "Found userspace branches" + +glb_err=0 diff --git a/tools/perf/tests/shell/test_arm_spe.sh b/tools/perf/tests/shell/test_arm_spe.sh index 03d5c7d12ee5..a69aab70dd8a 100755 --- a/tools/perf/tests/shell/test_arm_spe.sh +++ b/tools/perf/tests/shell/test_arm_spe.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Check Arm SPE trace data recording and synthesized samples +# Check Arm SPE trace data recording and synthesized samples (exclusive) # Uses the 'perf record' to record trace data of Arm SPE events; # then verify if any SPE event samples are generated by SPE with @@ -9,7 +9,7 @@ # German Gomez <german.gomez@arm.com>, 2021 skip_if_no_arm_spe_event() { - perf list | grep -E -q 'arm_spe_[0-9]+//' && return 0 + perf list pmu | grep -E -q 'arm_spe_[0-9]+//' && return 0 # arm_spe event doesn't exist return 2 @@ -107,7 +107,37 @@ arm_spe_system_wide_test() { arm_spe_report "SPE system-wide testing" $err } +arm_spe_discard_test() { + echo "SPE discard mode" + + for f in /sys/bus/event_source/devices/arm_spe_*; do + if [ -e "$f/format/discard" ]; then + cpu=$(cut -c -1 "$f/cpumask") + break + fi + done + + if [ -z $cpu ]; then + arm_spe_report "SPE discard mode not present" 2 + return + fi + + # Test can use wildcard SPE instance and Perf will only open the event + # on instances that have that format flag. But make sure the target + # runs on an instance with discard mode otherwise we're not testing + # anything. + perf record -o ${perfdata} -e arm_spe/discard/ -N -B --no-bpf-event \ + -- taskset --cpu-list $cpu true + + if perf report -i ${perfdata} --stats | grep 'AUX events\|AUXTRACE events'; then + arm_spe_report "SPE discard mode found unexpected data" 1 + else + arm_spe_report "SPE discard mode" 0 + fi +} + arm_spe_snapshot_test arm_spe_system_wide_test +arm_spe_discard_test exit $glb_err diff --git a/tools/perf/tests/shell/test_arm_spe_fork.sh b/tools/perf/tests/shell/test_arm_spe_fork.sh index 1a7e6a82d0e3..8efeef9fb956 100755 --- a/tools/perf/tests/shell/test_arm_spe_fork.sh +++ b/tools/perf/tests/shell/test_arm_spe_fork.sh @@ -5,7 +5,7 @@ # German Gomez <german.gomez@arm.com>, 2022 skip_if_no_arm_spe_event() { - perf list | grep -E -q 'arm_spe_[0-9]+//' && return 0 + perf list pmu | grep -E -q 'arm_spe_[0-9]+//' && return 0 return 2 } diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh index 5f14d0cb013f..e01df7581393 100755 --- a/tools/perf/tests/shell/test_brstack.sh +++ b/tools/perf/tests/shell/test_brstack.sh @@ -30,7 +30,7 @@ test_user_branches() { echo "Testing user branch stack sampling" perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u -- ${TESTPROG} > /dev/null 2>&1 - perf script -i $TMPDIR/perf.data --fields brstacksym | xargs -n1 > $TMPDIR/perf.script + perf script -i $TMPDIR/perf.data --fields brstacksym | tr -s ' ' '\n' > $TMPDIR/perf.script # example of branch entries: # brstack_foo+0x14/brstack_bar+0x40/P/-/-/0/CALL @@ -59,7 +59,7 @@ test_filter() { echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)" perf record -o $TMPDIR/perf.data --branch-filter $test_filter_filter,save_type,u -- ${TESTPROG} > /dev/null 2>&1 - perf script -i $TMPDIR/perf.data --fields brstack | xargs -n1 > $TMPDIR/perf.script + perf script -i $TMPDIR/perf.data --fields brstack | tr -s ' ' '\n' | grep '.' > $TMPDIR/perf.script # fail if we find any branch type that doesn't match any of the expected ones # also consider UNKNOWN branch types (-) diff --git a/tools/perf/tests/shell/test_data_symbol.sh b/tools/perf/tests/shell/test_data_symbol.sh index 3dfa91832aa8..c86da0235059 100755 --- a/tools/perf/tests/shell/test_data_symbol.sh +++ b/tools/perf/tests/shell/test_data_symbol.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Test data symbol +# Test data symbol (exclusive) # SPDX-License-Identifier: GPL-2.0 # Leo Yan <leo.yan@linaro.org>, 2022 diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/shell/test_intel_pt.sh index 723ec501f99a..f3a9a040bacc 100755 --- a/tools/perf/tests/shell/test_intel_pt.sh +++ b/tools/perf/tests/shell/test_intel_pt.sh @@ -1,11 +1,11 @@ #!/bin/sh -# Miscellaneous Intel PT testing +# Miscellaneous Intel PT testing (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e # Skip if no Intel PT -perf list | grep -q 'intel_pt//' || exit 2 +perf list pmu | grep -q 'intel_pt//' || exit 2 shelldir=$(dirname "$0") # shellcheck source=lib/waiting.sh @@ -644,6 +644,33 @@ test_pipe() return 0 } +test_pause_resume() +{ + echo "--- Test with pause / resume ---" + if ! perf_record_no_decode -o "${perfdatafile}" -e intel_pt/aux-action=start-paused/u uname ; then + echo "SKIP: pause / resume is not supported" + return 2 + fi + if ! perf_record_no_bpf -o "${perfdatafile}" \ + -e intel_pt/aux-action=start-paused/u \ + -e instructions/period=50000,aux-action=resume,name=Resume/u \ + -e instructions/period=100000,aux-action=pause,name=Pause/u uname ; then + echo "perf record with pause / resume failed" + return 1 + fi + if ! perf script -i "${perfdatafile}" --itrace=b -Fperiod,event | \ + awk 'BEGIN {paused=1;branches=0} + /Resume/ {paused=0} + /branches/ {if (paused) exit 1;branches=1} + /Pause/ {paused=1} + END {if (!branches) exit 1}' ; then + echo "perf record with pause / resume failed" + return 1 + fi + echo OK + return 0 +} + count_result() { if [ "$1" -eq 2 ] ; then @@ -672,6 +699,7 @@ test_power_event || ret=$? ; count_result $ret ; ret=0 test_no_tnt || ret=$? ; count_result $ret ; ret=0 test_event_trace || ret=$? ; count_result $ret ; ret=0 test_pipe || ret=$? ; count_result $ret ; ret=0 +test_pause_resume || ret=$? ; count_result $ret ; ret=0 cleanup diff --git a/tools/perf/tests/shell/test_stat_intel_tpebs.sh b/tools/perf/tests/shell/test_stat_intel_tpebs.sh new file mode 100755 index 000000000000..f95fc64bf0a7 --- /dev/null +++ b/tools/perf/tests/shell/test_stat_intel_tpebs.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# test Intel TPEBS counting mode (exclusive) +# SPDX-License-Identifier: GPL-2.0 + +set -e +grep -q GenuineIntel /proc/cpuinfo || { echo Skipping non-Intel; exit 2; } + +# Use this event for testing because it should exist in all platforms +event=cache-misses:R + +# Hybrid platforms output like "cpu_atom/cache-misses/R", rather than as above +alt_name=/cache-misses/R + +# Without this cmd option, default value or zero is returned +#echo "Testing without --record-tpebs" +#result=$(perf stat -e "$event" true 2>&1) +#[[ "$result" =~ $event || "$result" =~ $alt_name ]] || exit 1 + +# In platforms that do not support TPEBS, it should execute without error. +echo "Testing with --record-tpebs" +result=$(perf stat -e "$event" --record-tpebs -a sleep 0.01 2>&1) +[[ "$result" =~ "perf record" && "$result" =~ $event || "$result" =~ $alt_name ]] || exit 1 diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 92d15154ba79..e194fcf61df3 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -1,5 +1,5 @@ #!/bin/bash -# perf script task-analyzer tests +# perf script task-analyzer tests (exclusive) # SPDX-License-Identifier: GPL-2.0 tmpdir=$(mktemp -d /tmp/perf-script-task-analyzer-XXXXX) @@ -11,6 +11,9 @@ if [ -e "$perfdir/scripts/python/Perf-Trace-Util" ]; then export PERF_EXEC_PATH=$perfdir fi +# Disable lsan to avoid warnings about python memory leaks. +export ASAN_OPTIONS=detect_leaks=0 + cleanup() { rm -f perf.data rm -f perf.data.old @@ -52,8 +55,8 @@ find_str_or_fail() { # check if perf is compiled with libtraceevent support skip_no_probe_record_support() { - perf version --build-options | grep -q " OFF .* HAVE_LIBTRACEEVENT" && return 2 - return 0 + perf check feature -q libtraceevent && return 0 + return 2 } prepare_perf_data() { diff --git a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh index 319f36ebb9a4..33387c329f92 100755 --- a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh +++ b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh @@ -4,6 +4,13 @@ set -e +# Skip if there's no probe command. +if ! perf | grep probe +then + echo "Skip: probe command isn't present" + exit 2 +fi + # skip if there's no gcc if ! [ -x "$(command -v gcc)" ]; then echo "failed: no gcc compiler" @@ -77,7 +84,7 @@ gcc -g -Og -flto -c ${temp_dir}/testfile-foo.c -o ${temp_dir}/testfile-foo.o gcc -g -Og -c ${temp_dir}/testfile-main.c -o ${temp_dir}/testfile-main.o gcc -g -Og -o ${temp_dir}/testfile ${temp_dir}/testfile-foo.o ${temp_dir}/testfile-main.o -perf probe -x ${temp_dir}/testfile --funcs foo +perf probe -x ${temp_dir}/testfile --funcs foo | grep "foo" perf probe -x ${temp_dir}/testfile foo cleanup diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh index 3146a1eece07..708a13f00635 100755 --- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh +++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Check open filename arg using perf trace + vfs_getname +# Check open filename arg using perf trace + vfs_getname (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system # then use it with 'perf trace' using 'touch' to write to a temp file, then @@ -19,7 +19,7 @@ skip_if_no_perf_trace || exit 2 . "$(dirname $0)"/lib/probe_vfs_getname.sh trace_open_vfs_getname() { - evts="$(echo "$(perf list syscalls:sys_enter_open* 2>/dev/null | grep -E 'open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/')" | sed ':a;N;s:\n:,:g')" + evts="$(echo "$(perf list tracepoint 2>/dev/null | grep -E 'syscalls:sys_enter_open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/')" | sed ':a;N;s:\n:,:g')" perf trace -e $evts touch $file 2>&1 | \ grep -E " +[0-9]+\.[0-9]+ +\( +[0-9]+\.[0-9]+ ms\): +touch/[0-9]+ open(at)?\((dfd: +CWD, +)?filename: +\"?${file}\"?, +flags: CREAT\|NOCTTY\|NONBLOCK\|WRONLY, +mode: +IRUGO\|IWUGO\) += +[0-9]+$" } diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh new file mode 100755 index 000000000000..8d1e6bbeac90 --- /dev/null +++ b/tools/perf/tests/shell/trace_btf_enum.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# perf trace enum augmentation tests +# SPDX-License-Identifier: GPL-2.0 + +err=0 +set -e + +syscall="landlock_add_rule" +non_syscall="timer:hrtimer_init,timer:hrtimer_start" + +TESTPROG="perf test -w landlock" + +# shellcheck source=lib/probe.sh +. "$(dirname $0)"/lib/probe.sh +skip_if_no_perf_trace || exit 2 + +check_vmlinux() { + echo "Checking if vmlinux exists" + if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1 + then + echo "trace+enum test [Skipped missing vmlinux BTF support]" + err=2 + fi +} + +trace_landlock() { + echo "Tracing syscall ${syscall}" + + # test flight just to see if landlock_add_rule is available + if ! perf trace $TESTPROG 2>&1 | grep -q landlock + then + echo "No landlock system call found, skipping to non-syscall tracing." + return + fi + + if perf trace -e $syscall $TESTPROG 2>&1 | \ + grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -1.*" + then + err=0 + else + err=1 + fi +} + +trace_non_syscall() { + echo "Tracing non-syscall tracepoint ${non-syscall}" + if perf trace -e $non_syscall --max-events=1 2>&1 | \ + grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$' + then + err=0 + else + err=1 + fi +} + +check_vmlinux + +if [ $err = 0 ]; then + trace_landlock +fi + +if [ $err = 0 ]; then + trace_non_syscall +fi + +exit $err diff --git a/tools/perf/tests/shell/trace_btf_general.sh b/tools/perf/tests/shell/trace_btf_general.sh new file mode 100755 index 000000000000..e9ee727f3433 --- /dev/null +++ b/tools/perf/tests/shell/trace_btf_general.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# perf trace BTF general tests +# SPDX-License-Identifier: GPL-2.0 + +err=0 +set -e + +# shellcheck source=lib/probe.sh +. "$(dirname $0)"/lib/probe.sh + +file1=$(mktemp /tmp/file1_XXXX) +file2=$(echo $file1 | sed 's/file1/file2/g') + +buffer="buffer content" +perf_config_tmp=$(mktemp /tmp/.perfconfig_XXXXX) + +trap cleanup EXIT TERM INT HUP + +check_vmlinux() { + echo "Checking if vmlinux BTF exists" + if [ ! -f /sys/kernel/btf/vmlinux ] + then + echo "Skipped due to missing vmlinux BTF" + return 2 + fi + return 0 +} + +trace_test_string() { + echo "Testing perf trace's string augmentation" + if ! perf trace -e renameat* --max-events=1 -- mv ${file1} ${file2} 2>&1 | \ + grep -q -E "^mv/[0-9]+ renameat(2)?\(.*, \"${file1}\", .*, \"${file2}\", .*\) += +[0-9]+$" + then + echo "String augmentation test failed" + err=1 + fi +} + +trace_test_buffer() { + echo "Testing perf trace's buffer augmentation" + # echo will insert a newline (\10) at the end of the buffer + if ! perf trace -e write --max-events=1 -- echo "${buffer}" 2>&1 | \ + grep -q -E "^echo/[0-9]+ write\([0-9]+, ${buffer}.*, [0-9]+\) += +[0-9]+$" + then + echo "Buffer augmentation test failed" + err=1 + fi +} + +trace_test_struct_btf() { + echo "Testing perf trace's struct augmentation" + if ! perf trace -e clock_nanosleep --force-btf --max-events=1 -- sleep 1 2>&1 | \ + grep -q -E "^sleep/[0-9]+ clock_nanosleep\(0, 0, \{1,\}, 0x[0-9a-f]+\) += +[0-9]+$" + then + echo "BTF struct augmentation test failed" + err=1 + fi +} + +cleanup() { + rm -rf ${file1} ${file2} ${perf_config_tmp} +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} + +# don't overwrite user's perf config +trace_config() { + export PERF_CONFIG=${perf_config_tmp} + perf config trace.show_arg_names=false trace.show_duration=false \ + trace.show_timestamp=false trace.args_alignment=0 +} + +skip_if_no_perf_trace || exit 2 +check_vmlinux || exit 2 + +trace_config + +trace_test_string + +if [ $err = 0 ]; then + trace_test_buffer +fi + +if [ $err = 0 ]; then + trace_test_struct_btf +fi + +cleanup + +exit $err diff --git a/tools/perf/tests/shell/trace_exit_race.sh b/tools/perf/tests/shell/trace_exit_race.sh new file mode 100755 index 000000000000..fbb0adc33a88 --- /dev/null +++ b/tools/perf/tests/shell/trace_exit_race.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# perf trace exit race +# SPDX-License-Identifier: GPL-2.0 + +# Check that the last events of a perf trace'd subprocess are not +# lost. Specifically, trace the exiting syscall of "true" 10 times and ensure +# the output contains 10 correct lines. + +# shellcheck source=lib/probe.sh +. "$(dirname $0)"/lib/probe.sh + +skip_if_no_perf_trace || exit 2 + +if [ "$1" = "-v" ]; then + verbose="1" +fi + +iter=10 +regexp=" +[0-9]+\.[0-9]+ [0-9]+ syscalls:sys_enter_exit_group\(\)$" + +trace_shutdown_race() { + for _ in $(seq $iter); do + perf trace --no-comm -e syscalls:sys_enter_exit_group true 2>>$file + done + result="$(grep -c -E "$regexp" $file)" + [ $result = $iter ] +} + + +file=$(mktemp /tmp/temporary_file.XXXXX) + +# Do not use whatever ~/.perfconfig file, it may change the output +# via trace.{show_timestamp,show_prefix,etc} +export PERF_CONFIG=/dev/null + +trace_shutdown_race +err=$? + +if [ $err != 0 ] && [ "${verbose}" = "1" ]; then + lines_not_matching=$(mktemp /tmp/temporary_file.XXXXX) + if grep -v -E "$regexp" $file > $lines_not_matching ; then + echo "Lines not matching the expected regexp: '$regexp':" + cat $lines_not_matching + else + echo "Missing output, expected $iter but only got $result" + fi + rm -f $lines_not_matching +fi + +rm -f ${file} +exit $err diff --git a/tools/perf/tests/sigtrap.c b/tools/perf/tests/sigtrap.c index e6fd934b027a..a67c756f90b8 100644 --- a/tools/perf/tests/sigtrap.c +++ b/tools/perf/tests/sigtrap.c @@ -56,6 +56,7 @@ static struct perf_event_attr make_event_attr(void) #ifdef HAVE_BPF_SKEL #include <bpf/btf.h> +#include <util/btf.h> static struct btf *btf; @@ -73,21 +74,6 @@ static void btf__exit(void) btf = NULL; } -static const struct btf_member *__btf_type__find_member_by_name(int type_id, const char *member_name) -{ - const struct btf_type *t = btf__type_by_id(btf, type_id); - const struct btf_member *m; - int i; - - for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) { - const char *current_member_name = btf__name_by_offset(btf, m->name_off); - if (!strcmp(current_member_name, member_name)) - return m; - } - - return NULL; -} - static bool attr_has_sigtrap(void) { int id; @@ -101,7 +87,7 @@ static bool attr_has_sigtrap(void) if (id < 0) return false; - return __btf_type__find_member_by_name(id, "sigtrap") != NULL; + return __btf_type__find_member_by_name(btf, id, "sigtrap") != NULL; } static bool kernel_with_sleepable_spinlocks(void) @@ -119,7 +105,7 @@ static bool kernel_with_sleepable_spinlocks(void) return false; // Only RT has a "lock" member for "struct spinlock" - member = __btf_type__find_member_by_name(id, "lock"); + member = __btf_type__find_member_by_name(btf, id, "lock"); if (member == NULL) return false; diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index 706780fb5695..d60983657bad 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -21,13 +21,13 @@ static bool has_term(struct perf_record_stat_config *config, return false; } -static int process_stat_config_event(struct perf_tool *tool __maybe_unused, +static int process_stat_config_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { struct perf_record_stat_config *config = &event->stat_config; - struct perf_stat_config stat_config = {}; + struct perf_stat_config test_stat_config = {}; #define HAS(term, val) \ has_term(config, PERF_STAT_CONFIG_TERM__##term, val) @@ -39,30 +39,32 @@ static int process_stat_config_event(struct perf_tool *tool __maybe_unused, #undef HAS - perf_event__read_stat_config(&stat_config, config); + perf_event__read_stat_config(&test_stat_config, config); - TEST_ASSERT_VAL("wrong aggr_mode", stat_config.aggr_mode == AGGR_CORE); - TEST_ASSERT_VAL("wrong scale", stat_config.scale == 1); - TEST_ASSERT_VAL("wrong interval", stat_config.interval == 1); + TEST_ASSERT_VAL("wrong aggr_mode", test_stat_config.aggr_mode == AGGR_CORE); + TEST_ASSERT_VAL("wrong scale", test_stat_config.scale == 1); + TEST_ASSERT_VAL("wrong interval", test_stat_config.interval == 1); return 0; } static int test__synthesize_stat_config(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - struct perf_stat_config stat_config = { + struct perf_stat_config test_stat_config = { .aggr_mode = AGGR_CORE, .scale = 1, .interval = 1, }; TEST_ASSERT_VAL("failed to synthesize stat_config", - !perf_event__synthesize_stat_config(NULL, &stat_config, process_stat_config_event, NULL)); + !perf_event__synthesize_stat_config(NULL, &test_stat_config, + process_stat_config_event, + NULL)); return 0; } -static int process_stat_event(struct perf_tool *tool __maybe_unused, +static int process_stat_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -93,7 +95,7 @@ static int test__synthesize_stat(struct test_suite *test __maybe_unused, int sub return 0; } -static int process_stat_round_event(struct perf_tool *tool __maybe_unused, +static int process_stat_round_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 5cab17a1942e..576f82a15015 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -583,4 +583,4 @@ out_err: goto out; } -DEFINE_SUITE("Track with sched_switch", switch_tracking); +DEFINE_SUITE_EXCLUSIVE("Track with sched_switch", switch_tracking); diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c index 16e1c5502b09..ee20a366f32f 100644 --- a/tools/perf/tests/symbols.c +++ b/tools/perf/tests/symbols.c @@ -41,6 +41,30 @@ static void exit_test_info(struct test_info *ti) machine__delete(ti->machine); } +struct dso_map { + struct dso *dso; + struct map *map; +}; + +static int find_map_cb(struct map *map, void *d) +{ + struct dso_map *data = d; + + if (map__dso(map) != data->dso) + return 0; + data->map = map; + return 1; +} + +static struct map *find_module_map(struct machine *machine, struct dso *dso) +{ + struct dso_map data = { .dso = dso }; + + machine__for_each_kernel_map(machine, find_map_cb, &data); + + return data.map; +} + static void get_test_dso_filename(char *filename, size_t max_sz) { if (dso_to_test) @@ -51,6 +75,26 @@ static void get_test_dso_filename(char *filename, size_t max_sz) static int create_map(struct test_info *ti, char *filename, struct map **map_p) { + struct dso *dso = machine__findnew_dso(ti->machine, filename); + + /* + * If 'filename' matches a current kernel module, must use a kernel + * map. Find the one that already exists. + */ + if (dso && dso__kernel(dso) != DSO_SPACE__USER) { + *map_p = find_module_map(ti->machine, dso); + dso__put(dso); + if (!*map_p) { + pr_debug("Failed to find map for current kernel module %s", + filename); + return TEST_FAIL; + } + map__get(*map_p); + return TEST_OK; + } + + dso__put(dso); + /* Create a dummy map at 0x100000 */ *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL, PROT_EXEC, 0, NULL, filename, ti->thread); @@ -72,7 +116,7 @@ static int test_dso(struct dso *dso) if (verbose > 1) dso__fprintf(dso, stderr); - for (nd = rb_first_cached(&dso->symbols); nd; nd = rb_next(nd)) { + for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) { struct symbol *sym = rb_entry(nd, struct symbol, rb_node); if (sym->type != STT_FUNC && sym->type != STT_GNU_IFUNC) @@ -97,6 +141,26 @@ static int test_dso(struct dso *dso) return ret; } +static int subdivided_dso_cb(struct dso *dso, struct machine *machine __maybe_unused, void *d) +{ + struct dso *text_dso = d; + + if (dso != text_dso && strstarts(dso__short_name(dso), dso__short_name(text_dso))) + if (test_dso(dso) != TEST_OK) + return -1; + + return 0; +} + +static int process_subdivided_dso(struct machine *machine, struct dso *dso) +{ + int ret; + + ret = machine__for_each_dso(machine, subdivided_dso_cb, dso); + + return ret < 0 ? TEST_FAIL : TEST_OK; +} + static int test_file(struct test_info *ti, char *filename) { struct map *map = NULL; @@ -124,6 +188,10 @@ static int test_file(struct test_info *ti, char *filename) } ret = test_dso(dso); + + /* Module dso is split into many dsos by section */ + if (ret == TEST_OK && dso__kernel(dso) != DSO_SPACE__USER) + ret = process_subdivided_dso(ti->machine, dso); out_put: map__put(map); diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index d33d0952025c..8e328bbd509d 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -152,4 +152,11 @@ out_delete_evlist: return err; } -DEFINE_SUITE("Number of exit events of a simple workload", task_exit); +struct test_case tests__task_exit[] = { + TEST_CASE_EXCLUSIVE("Number of exit events of a simple workload", task_exit), + { .name = NULL, } +}; +struct test_suite suite__task_exit = { + .desc = "Number of exit events of a simple workload", + .test_cases = tests__task_exit, +}; diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c new file mode 100644 index 000000000000..1d5759d08141 --- /dev/null +++ b/tools/perf/tests/tests-scripts.c @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/zalloc.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <subcmd/exec-cmd.h> +#include <subcmd/parse-options.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <api/io.h> +#include "builtin.h" +#include "tests-scripts.h" +#include "color.h" +#include "debug.h" +#include "hist.h" +#include "intlist.h" +#include "string2.h" +#include "symbol.h" +#include "tests.h" +#include "util/rlimit.h" +#include "util/util.h" + +static int shell_tests__dir_fd(void) +{ + struct stat st; + char path[PATH_MAX], path2[PATH_MAX], *exec_path; + static const char * const devel_dirs[] = { + "./tools/perf/tests/shell", + "./tests/shell", + "./source/tests/shell" + }; + int fd; + char *p; + + for (size_t i = 0; i < ARRAY_SIZE(devel_dirs); ++i) { + fd = open(devel_dirs[i], O_PATH); + + if (fd >= 0) + return fd; + } + + /* Use directory of executable */ + if (readlink("/proc/self/exe", path2, sizeof path2) < 0) + return -1; + /* Follow another level of symlink if there */ + if (lstat(path2, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) { + scnprintf(path, sizeof(path), path2); + if (readlink(path, path2, sizeof path2) < 0) + return -1; + } + /* Get directory */ + p = strrchr(path2, '/'); + if (p) + *p = 0; + scnprintf(path, sizeof(path), "%s/tests/shell", path2); + fd = open(path, O_PATH); + if (fd >= 0) + return fd; + scnprintf(path, sizeof(path), "%s/source/tests/shell", path2); + fd = open(path, O_PATH); + if (fd >= 0) + return fd; + + /* Then installed path. */ + exec_path = get_argv_exec_path(); + scnprintf(path, sizeof(path), "%s/tests/shell", exec_path); + free(exec_path); + return open(path, O_PATH); +} + +static char *shell_test__description(int dir_fd, const char *name) +{ + struct io io; + char buf[128], desc[256]; + int ch, pos = 0; + + io__init(&io, openat(dir_fd, name, O_RDONLY), buf, sizeof(buf)); + if (io.fd < 0) + return NULL; + + /* Skip first line - should be #!/bin/sh Shebang */ + if (io__get_char(&io) != '#') + goto err_out; + if (io__get_char(&io) != '!') + goto err_out; + do { + ch = io__get_char(&io); + if (ch < 0) + goto err_out; + } while (ch != '\n'); + + do { + ch = io__get_char(&io); + if (ch < 0) + goto err_out; + } while (ch == '#' || isspace(ch)); + while (ch > 0 && ch != '\n') { + desc[pos++] = ch; + if (pos >= (int)sizeof(desc) - 1) + break; + ch = io__get_char(&io); + } + while (pos > 0 && isspace(desc[--pos])) + ; + desc[++pos] = '\0'; + close(io.fd); + return strdup(desc); +err_out: + close(io.fd); + return NULL; +} + +/* Is this full file path a shell script */ +static bool is_shell_script(int dir_fd, const char *path) +{ + const char *ext; + + ext = strrchr(path, '.'); + if (!ext) + return false; + if (!strcmp(ext, ".sh")) { /* Has .sh extension */ + if (faccessat(dir_fd, path, R_OK | X_OK, 0) == 0) /* Is executable */ + return true; + } + return false; +} + +/* Is this file in this dir a shell script (for test purposes) */ +static bool is_test_script(int dir_fd, const char *name) +{ + return is_shell_script(dir_fd, name); +} + +/* Duplicate a string and fall over and die if we run out of memory */ +static char *strdup_check(const char *str) +{ + char *newstr; + + newstr = strdup(str); + if (!newstr) { + pr_err("Out of memory while duplicating test script string\n"); + abort(); + } + return newstr; +} + +static int shell_test__run(struct test_suite *test, int subtest __maybe_unused) +{ + const char *file = test->priv; + int err; + char *cmd = NULL; + + if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0) + return TEST_FAIL; + err = system(cmd); + free(cmd); + if (!err) + return TEST_OK; + + return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL; +} + +static void append_script(int dir_fd, const char *name, char *desc, + struct test_suite ***result, + size_t *result_sz) +{ + char filename[PATH_MAX], link[128]; + struct test_suite *test_suite, **result_tmp; + struct test_case *tests; + ssize_t len; + char *exclusive; + + snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd); + len = readlink(link, filename, sizeof(filename)); + if (len < 0) { + pr_err("Failed to readlink %s", link); + return; + } + filename[len++] = '/'; + strcpy(&filename[len], name); + + tests = calloc(2, sizeof(*tests)); + if (!tests) { + pr_err("Out of memory while building script test suite list\n"); + return; + } + tests[0].name = strdup_check(name); + exclusive = strstr(desc, " (exclusive)"); + if (exclusive != NULL) { + tests[0].exclusive = true; + exclusive[0] = '\0'; + } + tests[0].desc = strdup_check(desc); + tests[0].run_case = shell_test__run; + test_suite = zalloc(sizeof(*test_suite)); + if (!test_suite) { + pr_err("Out of memory while building script test suite list\n"); + free(tests); + return; + } + test_suite->desc = desc; + test_suite->test_cases = tests; + test_suite->priv = strdup_check(filename); + /* Realloc is good enough, though we could realloc by chunks, not that + * anyone will ever measure performance here */ + result_tmp = realloc(*result, (*result_sz + 1) * sizeof(*result_tmp)); + if (result_tmp == NULL) { + pr_err("Out of memory while building script test suite list\n"); + free(tests); + free(test_suite); + return; + } + /* Add file to end and NULL terminate the struct array */ + *result = result_tmp; + (*result)[*result_sz] = test_suite; + (*result_sz)++; +} + +static void append_scripts_in_dir(int dir_fd, + struct test_suite ***result, + size_t *result_sz) +{ + struct dirent **entlist; + struct dirent *ent; + int n_dirs, i; + + /* List files, sorted by alpha */ + n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort); + if (n_dirs == -1) + return; + for (i = 0; i < n_dirs && (ent = entlist[i]); i++) { + int fd; + + if (ent->d_name[0] == '.') + continue; /* Skip hidden files */ + if (is_test_script(dir_fd, ent->d_name)) { /* It's a test */ + char *desc = shell_test__description(dir_fd, ent->d_name); + + if (desc) /* It has a desc line - valid script */ + append_script(dir_fd, ent->d_name, desc, result, result_sz); + continue; + } + if (ent->d_type != DT_DIR) { + struct stat st; + + if (ent->d_type != DT_UNKNOWN) + continue; + fstatat(dir_fd, ent->d_name, &st, 0); + if (!S_ISDIR(st.st_mode)) + continue; + } + if (strncmp(ent->d_name, "base_", 5) == 0) + continue; /* Skip scripts that have a separate driver. */ + fd = openat(dir_fd, ent->d_name, O_PATH); + append_scripts_in_dir(fd, result, result_sz); + } + for (i = 0; i < n_dirs; i++) /* Clean up */ + zfree(&entlist[i]); + free(entlist); +} + +struct test_suite **create_script_test_suites(void) +{ + struct test_suite **result = NULL, **result_tmp; + size_t result_sz = 0; + int dir_fd = shell_tests__dir_fd(); /* Walk dir */ + + /* + * Append scripts if fd is good, otherwise return a NULL terminated zero + * length array. + */ + if (dir_fd >= 0) + append_scripts_in_dir(dir_fd, &result, &result_sz); + + result_tmp = realloc(result, (result_sz + 1) * sizeof(*result_tmp)); + if (result_tmp == NULL) { + pr_err("Out of memory while building script test suite list\n"); + abort(); + } + /* NULL terminate the test suite array. */ + result = result_tmp; + result[result_sz] = NULL; + if (dir_fd >= 0) + close(dir_fd); + return result; +} diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h new file mode 100644 index 000000000000..b553ad26ea17 --- /dev/null +++ b/tools/perf/tests/tests-scripts.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef TESTS_SCRIPTS_H +#define TESTS_SCRIPTS_H + +#include "tests.h" + +struct test_suite **create_script_test_suites(void); + +#endif /* TESTS_SCRIPTS_H */ diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index dad3d7414142..8aea344536b8 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -4,11 +4,17 @@ #include <stdbool.h> +enum { + TEST_OK = 0, + TEST_FAIL = -1, + TEST_SKIP = -2, +}; + #define TEST_ASSERT_VAL(text, cond) \ do { \ if (!(cond)) { \ pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ - return -1; \ + return TEST_FAIL; \ } \ } while (0) @@ -17,16 +23,10 @@ do { \ if (val != expected) { \ pr_debug("FAILED %s:%d %s (%d != %d)\n", \ __FILE__, __LINE__, text, val, expected); \ - return -1; \ + return TEST_FAIL; \ } \ } while (0) -enum { - TEST_OK = 0, - TEST_FAIL = -1, - TEST_SKIP = -2, -}; - struct test_suite; typedef int (*test_fnptr)(struct test_suite *, int); @@ -36,6 +36,7 @@ struct test_case { const char *desc; const char *skip_reason; test_fnptr run_case; + bool exclusive; }; struct test_suite { @@ -62,6 +63,14 @@ struct test_suite { .skip_reason = _reason, \ } +#define TEST_CASE_EXCLUSIVE(description, _name) \ + { \ + .name = #_name, \ + .desc = description, \ + .run_case = test__##_name, \ + .exclusive = true, \ + } + #define DEFINE_SUITE(description, _name) \ struct test_case tests__##_name[] = { \ TEST_CASE(description, _name), \ @@ -72,6 +81,16 @@ struct test_suite { .test_cases = tests__##_name, \ } +#define DEFINE_SUITE_EXCLUSIVE(description, _name) \ + struct test_case tests__##_name[] = { \ + TEST_CASE_EXCLUSIVE(description, _name),\ + { .name = NULL, } \ + }; \ + struct test_suite suite__##_name = { \ + .desc = description, \ + .test_cases = tests__##_name, \ + } + /* Tests */ DECLARE_SUITE(vmlinux_matches_kallsyms); DECLARE_SUITE(openat_syscall_event); @@ -83,6 +102,8 @@ DECLARE_SUITE(perf_evsel__tp_sched_test); DECLARE_SUITE(syscall_openat_tp_fields); DECLARE_SUITE(pmu); DECLARE_SUITE(pmu_events); +DECLARE_SUITE(hwmon_pmu); +DECLARE_SUITE(tool_pmu); DECLARE_SUITE(attr); DECLARE_SUITE(dso_data); DECLARE_SUITE(dso_data_cache); @@ -205,6 +226,7 @@ DECLARE_WORKLOAD(leafloop); DECLARE_WORKLOAD(sqrtloop); DECLARE_WORKLOAD(brstack); DECLARE_WORKLOAD(datasym); +DECLARE_WORKLOAD(landlock); extern const char *dso_to_test; extern const char *test_objdump_path; diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 74308c1368fe..1fe521466bf4 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -60,7 +60,7 @@ static int test__thread_map(struct test_suite *test __maybe_unused, int subtest return 0; } -static int process_event(struct perf_tool *tool __maybe_unused, +static int process_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/tests/thread-maps-share.c b/tools/perf/tests/thread-maps-share.c index 7fa6f7c568e2..e9ecd30a5c05 100644 --- a/tools/perf/tests/thread-maps-share.c +++ b/tools/perf/tests/thread-maps-share.c @@ -46,9 +46,9 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s TEST_ASSERT_EQUAL("wrong refcnt", refcount_read(maps__refcnt(maps)), 4); /* test the maps pointer is shared */ - TEST_ASSERT_VAL("maps don't match", RC_CHK_EQUAL(maps, thread__maps(t1))); - TEST_ASSERT_VAL("maps don't match", RC_CHK_EQUAL(maps, thread__maps(t2))); - TEST_ASSERT_VAL("maps don't match", RC_CHK_EQUAL(maps, thread__maps(t3))); + TEST_ASSERT_VAL("maps don't match", maps__equal(maps, thread__maps(t1))); + TEST_ASSERT_VAL("maps don't match", maps__equal(maps, thread__maps(t2))); + TEST_ASSERT_VAL("maps don't match", maps__equal(maps, thread__maps(t3))); /* * Verify the other leader was created by previous call. @@ -73,7 +73,7 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s other_maps = thread__maps(other); TEST_ASSERT_EQUAL("wrong refcnt", refcount_read(maps__refcnt(other_maps)), 2); - TEST_ASSERT_VAL("maps don't match", RC_CHK_EQUAL(other_maps, thread__maps(other_leader))); + TEST_ASSERT_VAL("maps don't match", maps__equal(other_maps, thread__maps(other_leader))); /* release thread group */ thread__put(t3); diff --git a/tools/perf/tests/tool_pmu.c b/tools/perf/tests/tool_pmu.c new file mode 100644 index 000000000000..187942b749b7 --- /dev/null +++ b/tools/perf/tests/tool_pmu.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "tests.h" +#include "tool_pmu.h" + +static int do_test(enum tool_pmu_event ev, bool with_pmu) +{ + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + struct parse_events_error err; + int ret; + char str[128]; + bool found = false; + + if (!evlist) { + pr_err("evlist allocation failed\n"); + return TEST_FAIL; + } + + if (with_pmu) + snprintf(str, sizeof(str), "tool/%s/", tool_pmu__event_to_str(ev)); + else + snprintf(str, sizeof(str), "%s", tool_pmu__event_to_str(ev)); + + parse_events_error__init(&err); + ret = parse_events(evlist, str, &err); + if (ret) { + if (tool_pmu__skip_event(tool_pmu__event_to_str(ev))) { + ret = TEST_OK; + goto out; + } + + pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n", + __FILE__, __LINE__, str, ret); + parse_events_error__print(&err, str); + ret = TEST_FAIL; + goto out; + } + + ret = TEST_OK; + if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) { + pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", + __FILE__, __LINE__, str, evlist->core.nr_entries); + ret = TEST_FAIL; + goto out; + } + + evlist__for_each_entry(evlist, evsel) { + if (perf_pmu__is_tool(evsel->pmu)) { + if (evsel->core.attr.config != ev) { + pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %d\n", + __FILE__, __LINE__, str, evsel->core.attr.config, ev); + ret = TEST_FAIL; + goto out; + } + found = true; + } + } + + if (!found && !tool_pmu__skip_event(tool_pmu__event_to_str(ev))) { + pr_debug("FAILED %s:%d Didn't find tool event '%s' in parsed evsels\n", + __FILE__, __LINE__, str); + ret = TEST_FAIL; + } + +out: + parse_events_error__exit(&err); + evlist__delete(evlist); + return ret; +} + +static int test__tool_pmu_without_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int i; + + tool_pmu__for_each_event(i) { + int ret = do_test(i, /*with_pmu=*/false); + + if (ret != TEST_OK) + return ret; + } + return TEST_OK; +} + +static int test__tool_pmu_with_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int i; + + tool_pmu__for_each_event(i) { + int ret = do_test(i, /*with_pmu=*/true); + + if (ret != TEST_OK) + return ret; + } + return TEST_OK; +} + +static struct test_case tests__tool_pmu[] = { + TEST_CASE("Parsing without PMU name", tool_pmu_without_pmu), + TEST_CASE("Parsing with PMU name", tool_pmu_with_pmu), + { .name = NULL, } +}; + +struct test_suite suite__tool_pmu = { + .desc = "Tool PMU", + .test_cases = tests__tool_pmu, +}; diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index 2a842f53fbb5..a8cb5ba898ab 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -68,6 +68,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) }; int i; struct aggr_cpu_id id; + struct perf_cpu cpu; session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); @@ -113,8 +114,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) TEST_ASSERT_VAL("Session header CPU map not set", session->header.env.cpu); for (i = 0; i < session->header.env.nr_cpus_avail; i++) { - struct perf_cpu cpu = { .cpu = i }; - + cpu.cpu = i; if (!perf_cpu_map__has(map, cpu)) continue; pr_debug("CPU %d, core %d, socket %d\n", i, @@ -123,48 +123,48 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) } // Test that CPU ID contains socket, die, core and CPU - for (i = 0; i < perf_cpu_map__nr(map); i++) { - id = aggr_cpu_id__cpu(perf_cpu_map__cpu(map, i), NULL); + perf_cpu_map__for_each_cpu(cpu, i, map) { + id = aggr_cpu_id__cpu(cpu, NULL); TEST_ASSERT_VAL("Cpu map - CPU ID doesn't match", - perf_cpu_map__cpu(map, i).cpu == id.cpu.cpu); + cpu.cpu == id.cpu.cpu); TEST_ASSERT_VAL("Cpu map - Core ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].core_id == id.core); + session->header.env.cpu[cpu.cpu].core_id == id.core); TEST_ASSERT_VAL("Cpu map - Socket ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == + session->header.env.cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Cpu map - Die ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); + session->header.env.cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Cpu map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Cpu map - Thread IDX is set", id.thread_idx == -1); } // Test that core ID contains socket, die and core - for (i = 0; i < perf_cpu_map__nr(map); i++) { - id = aggr_cpu_id__core(perf_cpu_map__cpu(map, i), NULL); + perf_cpu_map__for_each_cpu(cpu, i, map) { + id = aggr_cpu_id__core(cpu, NULL); TEST_ASSERT_VAL("Core map - Core ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].core_id == id.core); + session->header.env.cpu[cpu.cpu].core_id == id.core); TEST_ASSERT_VAL("Core map - Socket ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == + session->header.env.cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Core map - Die ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); + session->header.env.cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Core map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Core map - Thread IDX is set", id.thread_idx == -1); } // Test that die ID contains socket and die - for (i = 0; i < perf_cpu_map__nr(map); i++) { - id = aggr_cpu_id__die(perf_cpu_map__cpu(map, i), NULL); + perf_cpu_map__for_each_cpu(cpu, i, map) { + id = aggr_cpu_id__die(cpu, NULL); TEST_ASSERT_VAL("Die map - Socket ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == + session->header.env.cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Die map - Die ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); + session->header.env.cpu[cpu.cpu].die_id == id.die); TEST_ASSERT_VAL("Die map - Node ID is set", id.node == -1); TEST_ASSERT_VAL("Die map - Core is set", id.core == -1); @@ -173,10 +173,10 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) } // Test that socket ID contains only socket - for (i = 0; i < perf_cpu_map__nr(map); i++) { - id = aggr_cpu_id__socket(perf_cpu_map__cpu(map, i), NULL); + perf_cpu_map__for_each_cpu(cpu, i, map) { + id = aggr_cpu_id__socket(cpu, NULL); TEST_ASSERT_VAL("Socket map - Socket ID doesn't match", - session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == + session->header.env.cpu[cpu.cpu].socket_id == id.socket); TEST_ASSERT_VAL("Socket map - Node ID is set", id.node == -1); @@ -187,10 +187,10 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) } // Test that node ID contains only node - for (i = 0; i < perf_cpu_map__nr(map); i++) { - id = aggr_cpu_id__node(perf_cpu_map__cpu(map, i), NULL); + perf_cpu_map__for_each_cpu(cpu, i, map) { + id = aggr_cpu_id__node(cpu, NULL); TEST_ASSERT_VAL("Node map - Node ID doesn't match", - cpu__get_node(perf_cpu_map__cpu(map, i)) == id.node); + cpu__get_node(cpu) == id.node); TEST_ASSERT_VAL("Node map - Socket is set", id.socket == -1); TEST_ASSERT_VAL("Node map - Die ID is set", id.die == -1); TEST_ASSERT_VAL("Node map - Core is set", id.core == -1); diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 822f893e67d5..74cdbd2ce9d0 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -26,7 +26,6 @@ static bool is_ignored_symbol(const char *name, char type) * when --all-symbols is specified so exclude them to get a * stable symbol list. */ - "kallsyms_addresses", "kallsyms_offsets", "kallsyms_relative_base", "kallsyms_num_syms", @@ -129,11 +128,12 @@ static int test__vmlinux_matches_kallsyms_cb1(struct map *map, void *data) * cases. */ struct map *pair = maps__find_by_name(args->kallsyms.kmaps, - (dso->kernel ? dso->short_name : dso->name)); + (dso__kernel(dso) ? dso__short_name(dso) : dso__name(dso))); - if (pair) - map__set_priv(pair, 1); - else { + if (pair) { + map__set_priv(pair); + map__put(pair); + } else { if (!args->header_printed) { pr_info("WARN: Maps only in vmlinux:\n"); args->header_printed = true; @@ -151,10 +151,8 @@ static int test__vmlinux_matches_kallsyms_cb2(struct map *map, void *data) u64 mem_end = map__unmap_ip(args->vmlinux_map, map__end(map)); pair = maps__find(args->kallsyms.kmaps, mem_start); - if (pair == NULL || map__priv(pair)) - return 0; - if (map__start(pair) == mem_start) { + if (pair != NULL && !map__priv(pair) && map__start(pair) == mem_start) { struct dso *dso = map__dso(map); if (!args->header_printed) { @@ -163,13 +161,14 @@ static int test__vmlinux_matches_kallsyms_cb2(struct map *map, void *data) } pr_info("WARN: %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", - map__start(map), map__end(map), map__pgoff(map), dso->name); + map__start(map), map__end(map), map__pgoff(map), dso__name(dso)); if (mem_end != map__end(pair)) pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64, map__start(pair), map__end(pair), map__pgoff(pair)); - pr_info(" %s\n", dso->name); - map__set_priv(pair, 1); + pr_info(" %s\n", dso__name(dso)); + map__set_priv(pair); } + map__put(pair); return 0; } diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index a1f34d5861e3..5af17206f04d 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -1,11 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 -perf-y += noploop.o -perf-y += thloop.o -perf-y += leafloop.o -perf-y += sqrtloop.o -perf-y += brstack.o -perf-y += datasym.o +perf-test-y += noploop.o +perf-test-y += thloop.o +perf-test-y += leafloop.o +perf-test-y += sqrtloop.o +perf-test-y += brstack.o +perf-test-y += datasym.o +perf-test-y += landlock.o CFLAGS_sqrtloop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_leafloop.o = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE diff --git a/tools/perf/tests/workloads/datasym.c b/tools/perf/tests/workloads/datasym.c index ddd40bc63448..8e08fc75a973 100644 --- a/tools/perf/tests/workloads/datasym.c +++ b/tools/perf/tests/workloads/datasym.c @@ -16,6 +16,22 @@ static int datasym(int argc __maybe_unused, const char **argv __maybe_unused) { for (;;) { buf1.data1++; + if (buf1.data1 == 123) { + /* + * Add some 'noise' in the loop to work around errata + * 1694299 on Arm N1. + * + * Bias exists in SPE sampling which can cause the load + * and store instructions to be skipped entirely. This + * comes and goes randomly depending on the offset the + * linker places the datasym loop at in the Perf binary. + * With an extra branch in the middle of the loop that + * isn't always taken, the instruction stream is no + * longer a continuous repeating pattern that interacts + * badly with the bias. + */ + buf1.data1++; + } buf1.data2 += buf1.data1; } return 0; diff --git a/tools/perf/tests/workloads/landlock.c b/tools/perf/tests/workloads/landlock.c new file mode 100644 index 000000000000..1f285b7b6236 --- /dev/null +++ b/tools/perf/tests/workloads/landlock.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/compiler.h> +#include <linux/types.h> +#include <unistd.h> +#include "../tests.h" + +/* This workload was initially added to test enum augmentation with BTF in perf + * trace because its the only syscall that has an enum argument. Since it is + * a recent addition to the Linux kernel (at the time of the introduction of this + * 'perf test' workload) we just add the required types and defines here instead + * of including linux/landlock, that isn't available in older systems. + * + * We are not interested in the result of the syscall, just in intercepting + * its arguments. + */ + +#ifndef __NR_landlock_add_rule +#define __NR_landlock_add_rule 445 +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_FILE +#define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) + +#define LANDLOCK_RULE_PATH_BENEATH 1 + +struct landlock_path_beneath_attr { + __u64 allowed_access; + __s32 parent_fd; +}; +#endif + +#ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP +#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) + +#define LANDLOCK_RULE_NET_PORT 2 + +struct landlock_net_port_attr { + __u64 allowed_access; + __u64 port; +}; +#endif + +static int landlock(int argc __maybe_unused, const char **argv __maybe_unused) +{ + int fd = 11, flags = 45; + + struct landlock_path_beneath_attr path_beneath_attr = { + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, + .parent_fd = 14, + }; + + struct landlock_net_port_attr net_port_attr = { + .port = 19, + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + + syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath_attr, flags); + + syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT, + &net_port_attr, flags); + + return 0; +} + +DEFINE_WORKLOAD(landlock); diff --git a/tools/perf/tests/workloads/leafloop.c b/tools/perf/tests/workloads/leafloop.c index 1bf5cc97649b..f7561767e32c 100644 --- a/tools/perf/tests/workloads/leafloop.c +++ b/tools/perf/tests/workloads/leafloop.c @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include <signal.h> #include <stdlib.h> #include <linux/compiler.h> +#include <unistd.h> #include "../tests.h" /* We want to check these symbols in perf script */ @@ -8,10 +10,16 @@ noinline void leaf(volatile int b); noinline void parent(volatile int b); static volatile int a; +static volatile sig_atomic_t done; + +static void sighandler(int sig __maybe_unused) +{ + done = 1; +} noinline void leaf(volatile int b) { - for (;;) + while (!done) a += b; } @@ -22,12 +30,16 @@ noinline void parent(volatile int b) static int leafloop(int argc, const char **argv) { - int c = 1; + int sec = 1; if (argc > 0) - c = atoi(argv[0]); + sec = atoi(argv[0]); + + signal(SIGINT, sighandler); + signal(SIGALRM, sighandler); + alarm(sec); - parent(c); + parent(sec); return 0; } diff --git a/tools/perf/tests/wp.c b/tools/perf/tests/wp.c index cc8719609b19..6c178985e37f 100644 --- a/tools/perf/tests/wp.c +++ b/tools/perf/tests/wp.c @@ -20,7 +20,12 @@ do { \ TEST_ASSERT_VAL(text, count == val); \ } while (0) +#ifdef __i386__ +/* Only breakpoint length less-than 8 has hardware support on i386. */ +volatile u32 data1; +#else volatile u64 data1; +#endif volatile u8 data2[3]; #ifndef __s390x__ |