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);
 }

Reply via email to