On riscv64, ddb's stack unwinder performs poorly. The main problem is
that the exception handlers use a frame structure (trapframe) that
differs from the typical call frame.
The following patch does several adjustments, including:
* Detect and handle exception frames. (Alternatively, the relevant part
of struct trapframe could be adjusted to match with callframe, as has
been done on amd64 and i386.)
* Don't offset ra with -4 when printing function addresses. It is safer
to print the original address and let the user decipher the meaning,
in part because the fixed offset is wrong with compressed instructions.
* Stop if frame pointer is badly misaligned.
* Print the frame address where unwinding stopped.
OK?
Index: arch/riscv64/riscv64/db_trace.c
===================================================================
RCS file: src/sys/arch/riscv64/riscv64/db_trace.c,v
retrieving revision 1.4
diff -u -p -r1.4 db_trace.c
--- arch/riscv64/riscv64/db_trace.c 9 Jul 2021 20:59:51 -0000 1.4
+++ arch/riscv64/riscv64/db_trace.c 21 Feb 2022 17:22:43 -0000
@@ -43,6 +43,9 @@
#include <ddb/db_sym.h>
#include <ddb/db_output.h>
+extern unsigned char cpu_exception_handler_supervisor[];
+extern unsigned char cpu_exception_handler_user[];
+
db_regs_t ddb_regs;
#define INKERNEL(va) (((vaddr_t)(va)) & (1ULL << 63))
@@ -51,7 +54,7 @@ void
db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
char *modif, int (*pr)(const char *, ...))
{
- vaddr_t frame, lastframe, ra, lastra, sp;
+ vaddr_t frame, lastframe, ra, sp, subr;
char c, *cp = modif;
db_expr_t offset;
Elf_Sym * sym;
@@ -68,60 +71,73 @@ db_stack_trace_print(db_expr_t addr, int
}
if (!have_addr) {
- sp = ddb_regs.tf_sp;
ra = ddb_regs.tf_ra;
- lastra = ddb_regs.tf_ra;
frame = ddb_regs.tf_s[0];
} else {
sp = addr;
db_read_bytes(sp - 16, sizeof(vaddr_t), (char *)&frame);
db_read_bytes(sp - 8, sizeof(vaddr_t), (char *)&ra);
- lastra = 0;
}
- while (count-- && frame != 0) {
- lastframe = frame;
-
- sym = db_search_symbol(lastra, DB_STGY_ANY, &offset);
- db_symbol_values(sym, &name, NULL);
+ while (count != 0 && frame != 0) {
+ if (INKERNEL(frame)) {
+ sym = db_search_symbol(ra, DB_STGY_ANY, &offset);
+ db_symbol_values(sym, &name, NULL);
+ } else {
+ sym = NULL;
+ name = NULL;
+ }
if (name == NULL || strcmp(name, "end") == 0) {
- (*pr)("%llx at 0x%lx", lastra, ra - 4);
+ (*pr)("%llx() at 0x%lx", ra, ra);
} else {
(*pr)("%s() at ", name);
- db_printsym(ra - 4, DB_STGY_PROC, pr);
+ db_printsym(ra, DB_STGY_PROC, pr);
}
(*pr)("\n");
- // can we detect traps ?
- db_read_bytes(frame - 16, sizeof(vaddr_t), (char *)&frame);
- if (frame == 0)
+ if ((frame & 0x7) != 0) {
+ (*pr)("bad frame pointer: 0x%lx\n", frame);
break;
- lastra = ra;
- db_read_bytes(frame - 8, sizeof(vaddr_t), (char *)&ra);
+ }
+
+ subr = 0;
+ if (sym != NULL)
+ subr = ra - (vaddr_t)offset;
-#if 0
- if (name != NULL) {
- if ((strcmp (name, "handle_el0_irq") == 0) ||
- (strcmp (name, "handle_el1_irq") == 0)) {
- (*pr)("--- interrupt ---\n");
- } else if (
- (strcmp (name, "handle_el0_sync") == 0) ||
- (strcmp (name, "handle_el1_sync") == 0)) {
- (*pr)("--- trap ---\n");
+ lastframe = frame;
+ if (subr == (vaddr_t)cpu_exception_handler_supervisor ||
+ subr == (vaddr_t)cpu_exception_handler_user) {
+ struct trapframe *tf = (struct trapframe *)frame;
+
+ db_read_bytes((vaddr_t)&tf->tf_ra, sizeof(ra),
+ (char *)&ra);
+ db_read_bytes((vaddr_t)&tf->tf_s[0], sizeof(frame),
+ (char *)&frame);
+ } else {
+ db_read_bytes(frame - 16, sizeof(frame),
+ (char *)&frame);
+ if (frame == 0)
+ break;
+ if ((frame & 0x7) != 0) {
+ (*pr)("bad frame pointer: 0x%lx\n", frame);
+ break;
}
+ db_read_bytes(frame - 8, sizeof(ra), (char *)&ra);
}
-#endif
+
if (INKERNEL(frame)) {
if (frame <= lastframe) {
- (*pr)("Bad frame pointer: 0x%lx\n", frame);
+ (*pr)("bad frame pointer: 0x%lx\n", frame);
break;
}
} else {
- if (kernel_only)
+ if (kernel_only) {
+ (*pr)("end of kernel\n");
break;
+ }
}
-
--count;
}
+ (*pr)("end trace frame: 0x%lx, count: %d\n", frame, count);
}