> Date: Mon, 21 Feb 2022 17:34:14 +0000
> From: Visa Hankala <[email protected]>
> 
> 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?

looks reasonable to me

ok kettenis@


> 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