[Crash-utility] [PATCH V2 5/9] RISCV64: Add 'bt' command support

lijiang lijiang at redhat.com
Wed Aug 10 06:46:24 UTC 2022


On Mon, Aug 1, 2022 at 12:31 PM Xianting Tian <
xianting.tian at linux.alibaba.com> wrote:

> 1, Add the implementation to get stack frame from active & inactive
>    task's stack.
> 2, Add 'bt -l' command support get a line number associated with a
>    current pc address.
> 3, Add 'bt -f' command support to display all stack data contained
>    in a frame
>
> With the patch, we can get the backtrace,
> crash> bt
> PID: 113      TASK: ff6000000226c200  CPU: 0    COMMAND: "sh"
>  #0 [ff20000010333b90] riscv_crash_save_regs at ffffffff800078f8
>  #1 [ff20000010333cf0] panic at ffffffff806578c6
>  #2 [ff20000010333d50] sysrq_reset_seq_param_set at ffffffff8038c03c
>  #3 [ff20000010333da0] __handle_sysrq at ffffffff8038c604
>  #4 [ff20000010333e00] write_sysrq_trigger at ffffffff8038cae4
>  #5 [ff20000010333e20] proc_reg_write at ffffffff801b7ee8
>  #6 [ff20000010333e40] vfs_write at ffffffff80152bb2
>  #7 [ff20000010333e80] ksys_write at ffffffff80152eda
>  #8 [ff20000010333ed0] sys_write at ffffffff80152f52
>
> crash> bt -l
> PID: 113      TASK: ff6000000226c200  CPU: 0    COMMAND: "sh"
>  #0 [ff20000010333b90] riscv_crash_save_regs at ffffffff800078f8
>
> /buildroot/qemu_riscv64_virt_defconfig/build/linux-custom/arch/riscv/kernel/crash_save_regs.S:
> 47
>  #1 [ff20000010333cf0] panic at ffffffff806578c6
>
> /buildroot/qemu_riscv64_virt_defconfig/build/linux-custom/kernel/panic.c:
> 276
>  ... ...
>
> crash> bt -f
> PID: 113      TASK: ff6000000226c200  CPU: 0    COMMAND: "sh"
>  #0 [ff20000010333b90] riscv_crash_save_regs at ffffffff800078f8
>     [PC: ffffffff800078f8 RA: ffffffff806578c6 SP: ff20000010333b90 SIZE:
> 352]
>     ff20000010333b90: ff20000010333bb0 ffffffff800078f8
>     ff20000010333ba0: ffffffff8008862c ff20000010333b90
>     ff20000010333bb0: ffffffff810dde38 ff6000000226c200
>     ff20000010333bc0: ffffffff8032be68 0720072007200720
>  ... ...
>
> Signed-off-by: Xianting Tian <xianting.tian at linux.alibaba.com>
> ---
>  netdump.c |  13 +++
>  riscv64.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 300 insertions(+)
>
> diff --git a/netdump.c b/netdump.c
> index 4ec12a0..1f418e6 100644
> --- a/netdump.c
> +++ b/netdump.c
> @@ -42,6 +42,7 @@ static void get_netdump_regs_ppc64(struct bt_info *,
> ulong *, ulong *);
>  static void get_netdump_regs_arm(struct bt_info *, ulong *, ulong *);
>  static void get_netdump_regs_arm64(struct bt_info *, ulong *, ulong *);
>  static void get_netdump_regs_mips(struct bt_info *, ulong *, ulong *);
> +static void get_netdump_regs_riscv(struct bt_info *, ulong *, ulong *);
>  static void check_dumpfile_size(char *);
>  static int proc_kcore_init_32(FILE *, int);
>  static int proc_kcore_init_64(FILE *, int);
> @@ -2675,6 +2676,10 @@ get_netdump_regs(struct bt_info *bt, ulong *eip,
> ulong *esp)
>                 return get_netdump_regs_mips(bt, eip, esp);
>                 break;
>
> +       case EM_RISCV:
> +               return get_netdump_regs_riscv(bt, eip, esp);
> +               break;
> +
>

This looks weird. I guess that you are still following the old code
changes. I would suggest using a better
code style as below, unless you have specific reasons.

+       case EM_RISCV:
+               get_netdump_regs_riscv(bt, eip, esp);
+               break;
+

        default:
>                 error(FATAL,
>                    "support for ELF machine type %d not available\n",
> @@ -2931,6 +2936,8 @@ display_regs_from_elf_notes(int cpu, FILE *ofp)
>                 mips_display_regs_from_elf_notes(cpu, ofp);
>         } else if (machine_type("MIPS64")) {
>                 mips64_display_regs_from_elf_notes(cpu, ofp);
> +       } else if (machine_type("RISCV64")) {
> +               riscv64_display_regs_from_elf_notes(cpu, ofp);
>         }
>  }
>
> @@ -3877,6 +3884,12 @@ get_netdump_regs_mips(struct bt_info *bt, ulong
> *eip, ulong *esp)
>         machdep->get_stack_frame(bt, eip, esp);
>  }
>
> +static void
> +get_netdump_regs_riscv(struct bt_info *bt, ulong *eip, ulong *esp)
> +{
> +       machdep->get_stack_frame(bt, eip, esp);
> +}
> +
>  int
>  is_partial_netdump(void)
>  {
> diff --git a/riscv64.c b/riscv64.c
> index db37123..8bd35f7 100644
> --- a/riscv64.c
> +++ b/riscv64.c
> @@ -33,6 +33,17 @@ static int riscv64_uvtop(struct task_context *tc, ulong
> vaddr,
>  static int riscv64_kvtop(struct task_context *tc, ulong kvaddr,
>                           physaddr_t *paddr, int verbose);
>  static void riscv64_cmd_mach(void);
> +static void riscv64_stackframe_init(void);
> +static void riscv64_back_trace_cmd(struct bt_info *bt);
> +static int riscv64_get_dumpfile_stack_frame(struct bt_info *bt,
> +                                            ulong *nip, ulong *ksp);
> +static void riscv64_get_stack_frame(struct bt_info *bt, ulong *pcp,
> +                                    ulong *spp);
> +static int riscv64_get_frame(struct bt_info *bt, ulong *pcp,
> +                             ulong *spp);
> +static void riscv64_display_full_frame(struct bt_info *bt,
> +                                       struct riscv64_unwind_frame
> *current,
> +                                       struct riscv64_unwind_frame
> *previous);
>  static int riscv64_translate_pte(ulong, void *, ulonglong);
>  static int riscv64_init_active_task_regs(void);
>  static int riscv64_get_crash_notes(void);
> @@ -503,6 +514,279 @@ no_page:
>         return FALSE;
>  }
>
> +/*
> + * 'bt -f' command output
> + * Display all stack data contained in a frame
> + */
> +static void
> +riscv64_display_full_frame(struct bt_info *bt, struct
> riscv64_unwind_frame *current,
> +                         struct riscv64_unwind_frame *previous)
> +{
> +       int i, u_idx;
> +       ulong *up;
> +       ulong words, addr;
> +       char buf[BUFSIZE];
> +
> +       if (previous->sp < current->sp)
> +               return;
> +
> +       if (!(INSTACK(previous->sp, bt) && INSTACK(current->sp, bt)))
> +               return;
> +
> +       words = (previous->sp - current->sp) / sizeof(ulong) + 1;
> +       addr = current->sp;
> +       u_idx = (current->sp - bt->stackbase) / sizeof(ulong);
> +
> +       for (i = 0; i < words; i++, u_idx++) {
> +               if (!(i & 1))
> +                       fprintf(fp, "%s    %lx: ", i ? "\n" : "", addr);
> +
> +               up = (ulong *)(&bt->stackbuf[u_idx*sizeof(ulong)]);
> +               fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0));
> +               addr += sizeof(ulong);
> +       }
> +       fprintf(fp, "\n");
> +
> +       return;
>
            ^^^^^^^
Is it redundant? Could it be removed?


> +}
> +
> +static void
> +riscv64_stackframe_init(void)
> +{
> +       long task_struct_thread = MEMBER_OFFSET("task_struct", "thread");
> +
> +       /* from arch/riscv/include/asm/processor.h */
> +       long thread_reg_ra = MEMBER_OFFSET("thread_struct", "ra");
> +       long thread_reg_sp = MEMBER_OFFSET("thread_struct", "sp");
> +       long thread_reg_fp = MEMBER_OFFSET("thread_struct", "s");
> +
> +       if ((task_struct_thread == INVALID_OFFSET) ||
> +           (thread_reg_ra == INVALID_OFFSET) ||
> +           (thread_reg_sp == INVALID_OFFSET) ||
> +           (thread_reg_fp == INVALID_OFFSET) ) {
> +               error(FATAL,
> +                     "cannot determine thread_struct offsets\n");
> +               return;
>
                     ^^^^^^
The "return" has no chance to execute due to the "error(FATAL, ...)".

Thanks.
Lianbo

+       }
> +
> +       ASSIGN_OFFSET(task_struct_thread_context_pc) =
> +               task_struct_thread + thread_reg_ra;
> +       ASSIGN_OFFSET(task_struct_thread_context_sp) =
> +               task_struct_thread + thread_reg_sp;
> +       ASSIGN_OFFSET(task_struct_thread_context_fp) =
> +               task_struct_thread + thread_reg_fp;
> +}
> +
> +static void
> +riscv64_dump_backtrace_entry(struct bt_info *bt, struct syment *sym,
> +                            struct riscv64_unwind_frame *current,
> +                            struct riscv64_unwind_frame *previous, int
> level)
> +{
> +       const char *name = sym ? sym->name : "(invalid)";
> +       struct load_module *lm;
> +       char *name_plus_offset = NULL;
> +       struct syment *symp;
> +       ulong symbol_offset;
> +       char buf[BUFSIZE];
> +
> +       if (bt->flags & BT_SYMBOL_OFFSET) {
> +               symp = value_search(current->pc, &symbol_offset);
> +
> +               if (symp && symbol_offset)
> +                       name_plus_offset =
> +                               value_to_symstr(current->pc, buf,
> bt->radix);
> +       }
> +
> +       fprintf(fp, "%s#%d [%016lx] %s at %016lx",
> +               level < 10 ? " " : "",
> +               level,
> +               current->sp,
> +               name_plus_offset ? name_plus_offset : name,
> +               current->pc);
> +
> +       if (module_symbol(current->pc, NULL, &lm, NULL, 0))
> +               fprintf(fp, " [%s]", lm->mod_name);
> +
> +       fprintf(fp, "\n");
> +
> +       /*
> +        * 'bt -l', get a line number associated with a current pc address.
> +        */
> +       if (bt->flags & BT_LINE_NUMBERS) {
> +               get_line_number(current->pc, buf, FALSE);
> +               if (strlen(buf))
> +                       fprintf(fp, "    %s\n", buf);
> +       }
> +
> +       /* bt -f */
> +       if (bt->flags & BT_FULL) {
> +               fprintf(fp, "    "
> +                       "[PC: %016lx RA: %016lx SP: %016lx SIZE: %ld]\n",
> +                       current->pc,
> +                       previous->pc,
> +                       current->sp,
> +                       previous->sp - current->sp);
> +               riscv64_display_full_frame(bt, current, previous);
> +       }
> +}
> +
> +/*
> + * Unroll a kernel stack.
> + */
> +static void
> +riscv64_back_trace_cmd(struct bt_info *bt)
> +{
> +       struct riscv64_unwind_frame current, previous;
> +       struct stackframe curr_frame;
> +       int level = 0;
> +
> +       if (bt->flags & BT_REGS_NOT_FOUND)
> +               return;
> +
> +       current.pc = bt->instptr;
> +       current.sp = bt->stkptr;
> +       current.fp = bt->frameptr;
> +
> +       if (!INSTACK(current.sp, bt))
> +               return;
> +
> +       for (;;) {
> +               struct syment *symbol = NULL;
> +               struct stackframe *frameptr;
> +               ulong low, high;
> +               ulong offset;
> +
> +               if (CRASHDEBUG(8))
> +                       fprintf(fp, "level %d pc %#lx sp %lx fp 0x%lx\n",
> +                               level, current.pc, current.sp, current.fp);
> +
> +               /* Validate frame pointer */
> +               low = current.sp + sizeof(struct stackframe);
> +               high = bt->stacktop;
> +               if (current.fp < low || current.fp > high || current.fp &
> 0x7) {
> +                       if (CRASHDEBUG(8))
> +                               fprintf(fp, "fp 0x%lx sp 0x%lx low 0x%lx
> high 0x%lx\n",
> +                                       current.fp, current.sp, low, high);
> +                       return;
> +               }
> +
> +               symbol = value_search(current.pc, &offset);
> +               if (!symbol)
> +                       return;
> +
> +               frameptr = (struct stackframe *)current.fp - 1;
> +               if (!readmem((ulong)frameptr, KVADDR, &curr_frame,
> +                   sizeof(curr_frame), "get stack frame",
> RETURN_ON_ERROR))
> +                       return;
> +
> +               previous.pc = curr_frame.ra;
> +               previous.fp = curr_frame.fp;
> +               previous.sp = current.fp;
> +
> +               riscv64_dump_backtrace_entry(bt, symbol, &current,
> &previous, level++);
> +
> +               current.pc = previous.pc;
> +               current.fp = previous.fp;
> +               current.sp = previous.sp;
> +
> +               if (CRASHDEBUG(8))
> +                       fprintf(fp, "next %d pc %#lx sp %#lx fp %lx\n",
> +                               level, current.pc, current.sp, current.fp);
> +       }
> +}
> +
> +/*
> + * Get a stack frame combination of pc and ra from the most relevant spot.
> + */
> +static void
> +riscv64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
> +{
> +       ulong ksp = 0, nip = 0;
> +       int ret = 0;
> +
> +       if (DUMPFILE() && is_task_active(bt->task))
> +               ret = riscv64_get_dumpfile_stack_frame(bt, &nip, &ksp);
> +       else
> +               ret = riscv64_get_frame(bt, &nip, &ksp);
> +
> +       if (!ret)
> +               error(WARNING, "cannot determine starting stack frame for
> task %lx\n",
> +                       bt->task);
> +
> +       if (pcp)
> +               *pcp = nip;
> +       if (spp)
> +               *spp = ksp;
> +}
> +
> +/*
> + * Get the starting point for the active cpu in a diskdump.
> + */
> +static int
> +riscv64_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong
> *ksp)
> +{
> +       const struct machine_specific *ms = machdep->machspec;
> +       struct riscv64_register *regs;
> +       ulong epc, sp;
> +
> +       if (!ms->crash_task_regs) {
> +               bt->flags |= BT_REGS_NOT_FOUND;
> +               return FALSE;
> +       }
> +
> +       /*
> +        * We got registers for panic task from crash_notes. Just return
> them.
> +        */
> +       regs = &ms->crash_task_regs[bt->tc->processor];
> +       epc = regs->regs[RISCV64_REGS_EPC];
> +       sp = regs->regs[RISCV64_REGS_SP];
> +
> +       /*
> +        * Set stack frame ptr.
> +        */
> +       bt->frameptr = regs->regs[RISCV64_REGS_FP];
> +
> +       if (nip)
> +               *nip = epc;
> +       if (ksp)
> +               *ksp = sp;
> +
> +       bt->machdep = regs;
> +
> +       return TRUE;
> +}
> +
> +/*
> + * Do the work for riscv64_get_stack_frame() for non-active tasks.
> + * Get SP and PC values for idle tasks.
> + */
> +static int
> +riscv64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
> +{
> +       if (!bt->tc || !(tt->flags & THREAD_INFO))
> +               return FALSE;
> +
> +       if (!readmem(bt->task + OFFSET(task_struct_thread_context_pc),
> +                    KVADDR, pcp, sizeof(*pcp),
> +                    "thread_struct.ra",
> +                    RETURN_ON_ERROR))
> +               return FALSE;
> +
> +       if (!readmem(bt->task + OFFSET(task_struct_thread_context_sp),
> +                    KVADDR, spp, sizeof(*spp),
> +                    "thread_struct.sp",
> +                    RETURN_ON_ERROR))
> +               return FALSE;
> +
> +       if (!readmem(bt->task + OFFSET(task_struct_thread_context_fp),
> +                    KVADDR, &bt->frameptr, sizeof(bt->frameptr),
> +                    "thread_struct.fp",
> +                    RETURN_ON_ERROR))
> +               return FALSE;
> +
> +       return TRUE;
> +}
> +
>  static int
>  riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int
> verbose)
>  {
> @@ -983,6 +1267,8 @@ riscv64_init(int when)
>                 machdep->uvtop = riscv64_uvtop;
>                 machdep->kvtop = riscv64_kvtop;
>                 machdep->cmd_mach = riscv64_cmd_mach;
> +               machdep->get_stack_frame = riscv64_get_stack_frame;
> +               machdep->back_trace = riscv64_back_trace_cmd;
>
>                 machdep->vmalloc_start = riscv64_vmalloc_start;
>                 machdep->processor_speed = riscv64_processor_speed;
> @@ -1003,6 +1289,7 @@ riscv64_init(int when)
>         case POST_GDB:
>                 machdep->section_size_bits = _SECTION_SIZE_BITS;
>                 machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
> +               riscv64_stackframe_init();
>                 riscv64_page_type_init();
>
>                 if (!machdep->hz)
> --
> 2.17.1
>
>
> _______________________________________________
> kexec mailing list
> kexec at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/crash-utility/attachments/20220810/c47e71f1/attachment-0001.htm>


More information about the Crash-utility mailing list