summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen Pei <cp0613@linux.alibaba.com>2026-09-14 20:41:45 +0800
committerArnaldo Carvalho de Melo <acme@redhat.com>2026-09-22 14:42:08 +0200
commit0b26b346a8a2bdcb3a7b6ab6f4e6450ee300eec3 (patch)
tree882a40e76a707bd0014ddde3b697c83c68388851
parent1622c9ae39e2882fcbbae7c9d27dda13f1880b93 (diff)
downloadlinux-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/Build1
-rw-r--r--tools/perf/arch/riscv/include/arch-tests.h9
-rw-r--r--tools/perf/arch/riscv/include/perf_regs.h2
-rw-r--r--tools/perf/arch/riscv/tests/Build4
-rw-r--r--tools/perf/arch/riscv/tests/arch-tests.c10
-rw-r--r--tools/perf/arch/riscv/tests/dwarf-unwind.c64
-rw-r--r--tools/perf/arch/riscv/tests/regs_load.S58
-rw-r--r--tools/perf/tests/Build2
-rw-r--r--tools/perf/tests/builtin-test.c3
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[] = {