On Mon, Aug 1, 2022 at 12:31 PM Xianting Tian <xianting.tian@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@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@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec