diff options
| author | Chen Pei <cp0613@linux.alibaba.com> | 2026-09-14 20:41:45 +0800 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-09-22 14:42:08 +0200 |
| commit | 0b26b346a8a2bdcb3a7b6ab6f4e6450ee300eec3 (patch) | |
| tree | 882a40e76a707bd0014ddde3b697c83c68388851 | |
| parent | 1622c9ae39e2882fcbbae7c9d27dda13f1880b93 (diff) | |
| download | linux-next-0b26b346a8a2bdcb3a7b6ab6f4e6450ee300eec3.tar.gz linux-next-0b26b346a8a2bdcb3a7b6ab6f4e6450ee300eec3.zip | |
perf riscv: Add perf_regs_load and enable the dwarf unwind test
riscv has no arch level perf test: tests/builtin-test.c only picks up
arch_tests[] for x86, arm64 and powerpc64, tests/Build only builds the
generic dwarf unwind test driver for x86, arm, arm64 and powerpc, and
arch/riscv/tests/ did not exist. "Test dwarf unwind" was therefore never
registered on riscv, even though DWARF is the only reliable way to walk a
riscv user stack -- the arch callchain code walks frame pointers only --
and both unwinders already support riscv, elfutils libdw generically and
libunwind through util/libunwind-arch/libunwind-riscv.c.
Add perf_regs_load() and the arch unwind sample helper modelled on the
arm64 ones, and wire riscv into the two gates. The x1..x31 slots of enum
perf_event_riscv_regs match the hardware registers one to one, and
PERF_REG_RISCV_PC is filled with ra so that unwinding starts at the
caller.
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
| -rw-r--r-- | tools/perf/arch/riscv/Build | 1 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/include/arch-tests.h | 9 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/include/perf_regs.h | 2 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/tests/Build | 4 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/tests/arch-tests.c | 10 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/tests/dwarf-unwind.c | 64 | ||||
| -rw-r--r-- | tools/perf/arch/riscv/tests/regs_load.S | 58 | ||||
| -rw-r--r-- | tools/perf/tests/Build | 2 | ||||
| -rw-r--r-- | tools/perf/tests/builtin-test.c | 3 |
9 files changed, 151 insertions, 2 deletions
diff --git a/tools/perf/arch/riscv/Build b/tools/perf/arch/riscv/Build index e63eabc2c8f4..12ebc65ea7a3 100644 --- a/tools/perf/arch/riscv/Build +++ b/tools/perf/arch/riscv/Build @@ -1 +1,2 @@ perf-util-y += util/ +perf-test-y += tests/ diff --git a/tools/perf/arch/riscv/include/arch-tests.h b/tools/perf/arch/riscv/include/arch-tests.h new file mode 100644 index 000000000000..2b3834a5ddbb --- /dev/null +++ b/tools/perf/arch/riscv/include/arch-tests.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_TESTS_H +#define ARCH_TESTS_H + +struct test_suite; + +extern struct test_suite *arch_tests[]; + +#endif diff --git a/tools/perf/arch/riscv/include/perf_regs.h b/tools/perf/arch/riscv/include/perf_regs.h index af7a1b47bf66..30cfd3f63eb5 100644 --- a/tools/perf/arch/riscv/include/perf_regs.h +++ b/tools/perf/arch/riscv/include/perf_regs.h @@ -8,6 +8,8 @@ #include <linux/types.h> #include "../../../../arch/riscv/include/uapi/asm/perf_regs.h" +void perf_regs_load(u64 *regs); + #define PERF_REGS_MASK ((1ULL << PERF_REG_RISCV_MAX) - 1) #define PERF_REGS_MAX PERF_REG_RISCV_MAX diff --git a/tools/perf/arch/riscv/tests/Build b/tools/perf/arch/riscv/tests/Build new file mode 100644 index 000000000000..bd15c6bbc1b2 --- /dev/null +++ b/tools/perf/arch/riscv/tests/Build @@ -0,0 +1,4 @@ +perf-test-y += regs_load.o +perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o + +perf-test-y += arch-tests.o diff --git a/tools/perf/arch/riscv/tests/arch-tests.c b/tools/perf/arch/riscv/tests/arch-tests.c new file mode 100644 index 000000000000..756706a09609 --- /dev/null +++ b/tools/perf/arch/riscv/tests/arch-tests.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "tests/tests.h" +#include "arch-tests.h" + +struct test_suite *arch_tests[] = { +#ifdef HAVE_DWARF_UNWIND_SUPPORT + &suite__dwarf_unwind, +#endif + NULL, +}; diff --git a/tools/perf/arch/riscv/tests/dwarf-unwind.c b/tools/perf/arch/riscv/tests/dwarf-unwind.c new file mode 100644 index 000000000000..e981b21f918d --- /dev/null +++ b/tools/perf/arch/riscv/tests/dwarf-unwind.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <string.h> +#include "perf_regs.h" +#include "thread.h" +#include "map.h" +#include "maps.h" +#include "event.h" +#include "debug.h" +#include "tests/tests.h" + +#define STACK_SIZE 8192 + +static int sample_ustack(struct perf_sample *sample, + struct thread *thread, u64 *regs) +{ + struct stack_dump *stack = &sample->user_stack; + struct map *map; + unsigned long sp; + u64 stack_size, *buf; + + buf = malloc(STACK_SIZE); + if (!buf) { + pr_debug("failed to allocate sample uregs data\n"); + return -1; + } + + sp = (unsigned long) regs[PERF_REG_RISCV_SP]; + + map = maps__find(thread__maps(thread), (u64)sp); + if (!map) { + pr_debug("failed to get stack map\n"); + free(buf); + return -1; + } + + stack_size = map__end(map) - sp; + map__put(map); + stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size; + + memcpy(buf, (void *) sp, stack_size); + stack->data = (char *) buf; + stack->size = stack_size; + return 0; +} + +int test__arch_unwind_sample(struct perf_sample *sample, + struct thread *thread) +{ + struct regs_dump *regs = perf_sample__user_regs(sample); + u64 *buf; + + buf = calloc(PERF_REGS_MAX, sizeof(u64)); + if (!buf) { + pr_debug("failed to allocate sample uregs data\n"); + return -1; + } + + perf_regs_load(buf); + regs->abi = PERF_SAMPLE_REGS_ABI; + regs->regs = buf; + regs->mask = PERF_REGS_MASK; + + return sample_ustack(sample, thread, buf); +} diff --git a/tools/perf/arch/riscv/tests/regs_load.S b/tools/perf/arch/riscv/tests/regs_load.S new file mode 100644 index 000000000000..703bbf3374a6 --- /dev/null +++ b/tools/perf/arch/riscv/tests/regs_load.S @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/linkage.h> + +/* + * Fill the u64 array 'regs' at the slots of enum perf_event_riscv_regs: + * x1..x31 map one-to-one onto PERF_REG_RISCV_RA..PERF_REG_RISCV_T6, and the + * stride stays 8 bytes on RV32 because the slots are u64 and the caller + * zeroes them. + * + * PERF_REG_RISCV_PC is filled with ra, the return address of the call to + * this function, so that unwinding starts at the caller rather than here. + */ + +#if __riscv_xlen == 64 +#define REG_S sd +#else +#define REG_S sw +#endif + +#define STR_REG(r) REG_S x##r, 8 * r(a0) + + .text + .type perf_regs_load,%function +SYM_FUNC_START(perf_regs_load) + STR_REG(1) + STR_REG(2) + STR_REG(3) + STR_REG(4) + STR_REG(5) + STR_REG(6) + STR_REG(7) + STR_REG(8) + STR_REG(9) + STR_REG(10) + STR_REG(11) + STR_REG(12) + STR_REG(13) + STR_REG(14) + STR_REG(15) + STR_REG(16) + STR_REG(17) + STR_REG(18) + STR_REG(19) + STR_REG(20) + STR_REG(21) + STR_REG(22) + STR_REG(23) + STR_REG(24) + STR_REG(25) + STR_REG(26) + STR_REG(27) + STR_REG(28) + STR_REG(29) + STR_REG(30) + STR_REG(31) + REG_S ra, 0(a0) + ret +SYM_FUNC_END(perf_regs_load) diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 406e48eed1c8..81c311b131b7 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -73,7 +73,7 @@ perf-test-y += tool_pmu.o perf-test-y += subcmd-help.o perf-test-y += kallsyms-split.o -ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc)) +ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc riscv)) perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 2d53414d8271..d2f594921e25 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -69,7 +69,8 @@ static const char *workload_control; * dependent on the initialization, as such GCC with LTO complains of * conflicting definitions with a weak symbol. */ -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + defined(__powerpc64__) || defined(__riscv) extern struct test_suite *arch_tests[]; #else static struct test_suite *arch_tests[] = { |
