mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] perf riscv: Add perf_regs_load and enable the dwarf unwind test
@ 2026-09-14 12:41 Chen Pei
       [not found] ` <20260914125814.C92251F000FF@smtp.kernel.org>
  0 siblings, 1 reply; 2+ messages in thread
From: Chen Pei @ 2026-09-14 12:41 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, irogers, mark.rutland,
	alexander.shishkin, jolsa, adrian.hunter, james.clark, pjw,
	palmer, aou, alex, guoren
  Cc: linux-perf-users, linux-riscv, linux-kernel

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.

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---

Tested on riscv64 (QEMU virt, kernel 6.18, rv64gcv userspace) with both
the libdw and the libunwind unwinder, and in both callchain orders. All
eight expected frames resolve, including the one in libc:

  # ./perf test -v "dwarf unwind"
   71: Test dwarf unwind                                     : Ok

  got: test__dwarf_unwind 0x555593375c45, expecting test__dwarf_unwind
  got: test_dwarf_unwind__krava_1 0x555593375b7f, expecting test_dwarf_unwind__krava_1
  got: test_dwarf_unwind__krava_2 0x555593375b43, expecting test_dwarf_unwind__krava_2
  got: test_dwarf_unwind__krava_3 0x555593375aff, expecting test_dwarf_unwind__krava_3
  got: bsearch 0x7fff9aad575b, expecting bsearch
  got: test_dwarf_unwind__compare 0x555593375a5d, expecting test_dwarf_unwind__compare
  got: test_dwarf_unwind__thread 0x555593375939, expecting test_dwarf_unwind__thread
  got: test__arch_unwind_sample 0x5555933788a0, expecting test__arch_unwind_sample

The libunwind build links libunwind-riscv.so.8, so the riscv specific
remote unwinder in util/libunwind-arch/libunwind-riscv.c is what runs.

A build with DWARF unwinding disabled was checked too: it still links,
arch_tests[] degrades to the NULL only array, and the test is simply
absent from "perf test list".

 tools/perf/arch/riscv/Build                |  1 +
 tools/perf/arch/riscv/include/arch-tests.h |  9 +++
 tools/perf/arch/riscv/include/perf_regs.h  |  2 +
 tools/perf/arch/riscv/tests/Build          |  4 ++
 tools/perf/arch/riscv/tests/arch-tests.c   | 10 ++++
 tools/perf/arch/riscv/tests/dwarf-unwind.c | 64 ++++++++++++++++++++++
 tools/perf/arch/riscv/tests/regs_load.S    | 58 ++++++++++++++++++++
 tools/perf/tests/Build                     |  2 +-
 tools/perf/tests/builtin-test.c            |  3 +-
 9 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100644 tools/perf/arch/riscv/include/arch-tests.h
 create mode 100644 tools/perf/arch/riscv/tests/Build
 create mode 100644 tools/perf/arch/riscv/tests/arch-tests.c
 create mode 100644 tools/perf/arch/riscv/tests/dwarf-unwind.c
 create mode 100644 tools/perf/arch/riscv/tests/regs_load.S

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 66944a4f4968..c457504e052d 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -72,7 +72,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 4d0784b16723..a0478dd3e9cc 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -67,7 +67,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[] = {
-- 
2.50.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] perf riscv: Add perf_regs_load and enable the dwarf unwind test
       [not found] ` <20260914125814.C92251F000FF@smtp.kernel.org>
@ 2026-09-15  1:54   ` Chen Pei
  0 siblings, 0 replies; 2+ messages in thread
From: Chen Pei @ 2026-09-15  1:54 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, irogers, mark.rutland,
	alexander.shishkin, jolsa, adrian.hunter, james.clark, pjw,
	palmer, aou, alex, guoren
  Cc: linux-perf-users, linux-riscv, linux-kernel

Both items checked. Neither needs a v2 of this patch.

> Does this file need to explicitly include <stdlib.h>? Since musl libc
> enforces strict POSIX header inclusions, missing the declaration for
> malloc could lead to a build failure regression on musl libc systems.

It is not missing. "perf_regs.h" three lines above resolves to
arch/riscv/include/perf_regs.h, which has #include <stdlib.h> and is
also where PERF_REGS_MAX comes from, so this file cannot be built
without it. arm64 relies on the same transitive include. Checked with
riscv64 gcc 15.3, where an implicit declaration is a hard error:
including only that header is enough to call malloc() and calloc().

> Can perf_sample__user_regs() return NULL here?

It can, but that is not introduced here and cannot be fixed from this
file. arch/x86, arch/arm, arch/arm64 and arch/powerpc all dereference
it the same way, and tests/dwarf-unwind.c:118 does
zfree(&sample.user_regs->regs) unconditionally, so an early return here
would still crash in the caller. A fix has to cover the whole "Test
dwarf unwind" path, or the contract of perf_sample__user_regs() itself.
Either is a separate series, which I can send if wanted.

Best regards,
Pei

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-15  1:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 12:41 [PATCH] perf riscv: Add perf_regs_load and enable the dwarf unwind test Chen Pei
     [not found] ` <20260914125814.C92251F000FF@smtp.kernel.org>
2026-09-15  1:54   ` Chen Pei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®