[PATCH 2/4] LoongArch: Add prologue unwinder support

Qing Zhang posted 4 patches 3 years, 8 months ago
[PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Qing Zhang 3 years, 8 months ago
It unwind the stack frame based on prologue code analyze.
CONFIG_KALLSYMS is needed, at least the address and length
of each function.

Three stages when we do unwind,
  (1)unwind_start(), the prapare of unwinding, fill unwind_state.
  (2)unwind_done(), judge whether the unwind process is finished or not.
  (3)unwind_next_frame(), unwind the next frame.

Dividing unwinder helps to add new unwinders in the future, eg:
unwind_frame, unwind_orc .etc

Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
---
 arch/loongarch/Kconfig.debug            |  19 +++
 arch/loongarch/include/asm/inst.h       |  52 +++++++
 arch/loongarch/include/asm/unwind.h     |   8 ++
 arch/loongarch/kernel/Makefile          |   1 +
 arch/loongarch/kernel/traps.c           |   5 +
 arch/loongarch/kernel/unwind_prologue.c | 172 ++++++++++++++++++++++++
 6 files changed, 257 insertions(+)
 create mode 100644 arch/loongarch/kernel/unwind_prologue.c

diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug
index 68634d4fa27b..57cdbe0cfd98 100644
--- a/arch/loongarch/Kconfig.debug
+++ b/arch/loongarch/Kconfig.debug
@@ -1,3 +1,11 @@
+choice
+	prompt "Choose kernel unwinder"
+	default UNWINDER_PROLOGUE if KALLSYMS
+	help
+	  This determines which method will be used for unwinding kernel stack
+	  traces for panics, oopses, bugs, warnings, perf, /proc/<pid>/stack,
+	  lockdep, and more.
+
 config UNWINDER_GUESS
 	bool "Guess unwinder"
 	help
@@ -7,3 +15,14 @@ config UNWINDER_GUESS
 
 	  While this option often produces false positives, it can still be
 	  useful in many cases.
+
+config UNWINDER_PROLOGUE
+	bool "Prologue unwinder"
+	depends on KALLSYMS
+	help
+	  This option enables the "prologue" unwinder for unwinding kernel stack
+	  traces.  It unwind the stack frame based on prologue code analyze.  Symbol
+	  information is needed, at least the address and length of each function.
+	  Some of the addresses it reports may be incorrect.
+
+endchoice
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 575d1bb66ffb..b876907ca65a 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -23,12 +23,33 @@ enum reg1i20_op {
 	lu32id_op	= 0x0b,
 };
 
+enum reg1i21_op {
+	beqz_op		= 0x10,
+	bnez_op		= 0x11,
+};
+
 enum reg2i12_op {
+	addiw_op	= 0x0a,
+	addid_op	= 0x0b,
 	lu52id_op	= 0x0c,
+	ldb_op		= 0xa0,
+	ldh_op		= 0xa1,
+	ldw_op		= 0xa2,
+	ldd_op		= 0xa3,
+	stb_op		= 0xa4,
+	sth_op		= 0xa5,
+	stw_op		= 0xa6,
+	std_op		= 0xa7,
 };
 
 enum reg2i16_op {
 	jirl_op		= 0x13,
+	beq_op		= 0x16,
+	bne_op		= 0x17,
+	blt_op		= 0x18,
+	bge_op		= 0x19,
+	bltu_op		= 0x1a,
+	bgeu_op		= 0x1b,
 };
 
 struct reg0i26_format {
@@ -110,6 +131,37 @@ enum loongarch_gpr {
 	LOONGARCH_GPR_MAX
 };
 
+#define is_imm12_negative(val)	is_imm_negative(val, 12)
+
+static inline bool is_imm_negative(unsigned long val, unsigned int bit)
+{
+	return val & (1UL << (bit - 1));
+}
+
+static inline bool is_stack_alloc_ins(union loongarch_instruction *ip)
+{
+	/* addi.d $sp, $sp, -imm */
+	return ip->reg2i12_format.opcode == addid_op &&
+		ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
+		ip->reg2i12_format.rd == LOONGARCH_GPR_SP &&
+		is_imm12_negative(ip->reg2i12_format.immediate);
+}
+
+static inline bool is_ra_save_ins(union loongarch_instruction *ip)
+{
+	/* st.d $ra, $sp, offset */
+	return ip->reg2i12_format.opcode == std_op &&
+		ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
+		ip->reg2i12_format.rd == LOONGARCH_GPR_RA &&
+		!is_imm12_negative(ip->reg2i12_format.immediate);
+}
+
+static inline bool is_branch_insn(union loongarch_instruction insn)
+{
+	return insn.reg1i21_format.opcode >= beqz_op &&
+		insn.reg1i21_format.opcode <= bgeu_op;
+}
+
 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
index 243330b39d0d..f9f73a26504e 100644
--- a/arch/loongarch/include/asm/unwind.h
+++ b/arch/loongarch/include/asm/unwind.h
@@ -14,6 +14,14 @@
 struct unwind_state {
 	struct stack_info stack_info;
 	struct task_struct *task;
+#if defined(CONFIG_UNWINDER_PROLOGUE)
+	unsigned long ra;
+	bool enable;
+	/*
+	 * Enable is the prologue analysis method
+	 * otherwise is the way to guess.
+	 */
+#endif
 	unsigned long sp, pc;
 	bool first;
 	bool error;
diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
index c5fa4adb23b6..918600e7b30f 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -23,5 +23,6 @@ obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_NUMA)		+= numa.o
 
 obj-$(CONFIG_UNWINDER_GUESS)	+= unwind_guess.o
+obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
 
 CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
index ef2c3aeb1dab..3e904fa12d48 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -73,6 +73,11 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
 
 	unwind_start(&state, task, pregs);
 
+#ifdef CONFIG_UNWINDER_PROLOGUE
+	if (user_mode(regs))
+		state.enable = false;
+#endif
+
 	printk("%sCall Trace:", loglvl);
 	for (; !unwind_done(&state); unwind_next_frame(&state)) {
 		addr = unwind_get_return_address(&state);
diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c
new file mode 100644
index 000000000000..072d1f7bf4ac
--- /dev/null
+++ b/arch/loongarch/kernel/unwind_prologue.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+#include <linux/kallsyms.h>
+
+#include <asm/inst.h>
+#include <asm/ptrace.h>
+#include <asm/unwind.h>
+
+unsigned long unwind_get_return_address(struct unwind_state *state)
+{
+
+	if (unwind_done(state))
+		return 0;
+	else if (state->enable)
+		return state->pc;
+	else if (state->first)
+		return state->pc;
+
+	return *(unsigned long *)(state->sp);
+
+}
+EXPORT_SYMBOL_GPL(unwind_get_return_address);
+
+static bool unwind_by_prologue(struct unwind_state *state)
+{
+	struct stack_info *info = &state->stack_info;
+	union loongarch_instruction *ip, *ip_end;
+	unsigned long frame_size = 0, frame_ra = -1;
+	unsigned long size, offset, pc = state->pc;
+
+	if (state->sp >= info->end || state->sp < info->begin)
+		return false;
+
+	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
+		return false;
+
+	ip = (union loongarch_instruction *)(pc - offset);
+	ip_end = (union loongarch_instruction *)pc;
+
+	while (ip < ip_end) {
+		if (is_stack_alloc_ins(ip)) {
+			frame_size = (1 << 12) - ip->reg2i12_format.immediate;
+			ip++;
+			break;
+		}
+		ip++;
+	}
+
+	if (!frame_size) {
+		if (state->first)
+			goto first;
+
+		return false;
+	}
+
+	while (ip < ip_end) {
+		if (is_ra_save_ins(ip)) {
+			frame_ra = ip->reg2i12_format.immediate;
+			break;
+		}
+		if (is_branch_insn(*ip))
+			break;
+		ip++;
+	}
+
+	if (frame_ra < 0) {
+		if (state->first) {
+			state->sp = state->sp + frame_size;
+			goto first;
+		}
+		return false;
+	}
+
+	if (state->first)
+		state->first = false;
+
+	state->pc = *(unsigned long *)(state->sp + frame_ra);
+	state->sp = state->sp + frame_size;
+	return !!__kernel_text_address(state->pc);
+
+first:
+	state->first = false;
+	if (state->pc == state->ra)
+		return false;
+
+	state->pc = state->ra;
+
+	return !!__kernel_text_address(state->ra);
+}
+
+static bool unwind_by_guess(struct unwind_state *state)
+{
+	struct stack_info *info = &state->stack_info;
+	unsigned long addr;
+
+	for (state->sp += sizeof(unsigned long);
+	     state->sp < info->end;
+	     state->sp += sizeof(unsigned long)) {
+		addr = *(unsigned long *)(state->sp);
+		if (__kernel_text_address(addr))
+			return true;
+	}
+
+	return false;
+}
+
+bool unwind_next_frame(struct unwind_state *state)
+{
+	struct stack_info *info = &state->stack_info;
+	struct pt_regs *regs;
+	unsigned long pc;
+
+	if (unwind_done(state))
+		return false;
+
+	do {
+		if (state->enable) {
+			if (unwind_by_prologue(state))
+				return true;
+
+			if (info->type == STACK_TYPE_IRQ &&
+				info->end == state->sp) {
+				regs = (struct pt_regs *)info->next_sp;
+				pc = regs->csr_era;
+				if (user_mode(regs) || !__kernel_text_address(pc))
+					return false;
+
+				state->pc = pc;
+				state->sp = regs->regs[3];
+				state->ra = regs->regs[1];
+				state->first = true;
+				get_stack_info(state->sp, state->task, info);
+
+				return true;
+			}
+		} else {
+			if (state->first)
+				state->first = false;
+			else if (unwind_by_guess(state))
+				return true;
+		}
+
+		state->sp = info->next_sp;
+
+	} while (!get_stack_info(state->sp, state->task, info));
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(unwind_next_frame);
+
+void unwind_start(struct unwind_state *state, struct task_struct *task,
+		    struct pt_regs *regs)
+{
+	memset(state, 0, sizeof(*state));
+
+	if (__kernel_text_address(regs->csr_era))
+		state->enable = true;
+
+	state->task = task;
+	state->pc = regs->csr_era;
+	state->sp = regs->regs[3];
+	state->ra = regs->regs[1];
+	state->first = true;
+
+	get_stack_info(state->sp, state->task, &state->stack_info);
+
+	if (!unwind_done(state) && !__kernel_text_address(state->pc))
+		unwind_next_frame(state);
+}
+EXPORT_SYMBOL_GPL(unwind_start);
-- 
2.20.1
Re: [PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Huacai Chen 3 years, 8 months ago
Hi, Qing,

On Mon, Aug 1, 2022 at 8:17 PM Qing Zhang <zhangqing@loongson.cn> wrote:
>
> It unwind the stack frame based on prologue code analyze.
> CONFIG_KALLSYMS is needed, at least the address and length
> of each function.
>
> Three stages when we do unwind,
>   (1)unwind_start(), the prapare of unwinding, fill unwind_state.
>   (2)unwind_done(), judge whether the unwind process is finished or not.
>   (3)unwind_next_frame(), unwind the next frame.
>
> Dividing unwinder helps to add new unwinders in the future, eg:
> unwind_frame, unwind_orc .etc
>
> Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
> ---
>  arch/loongarch/Kconfig.debug            |  19 +++
>  arch/loongarch/include/asm/inst.h       |  52 +++++++
>  arch/loongarch/include/asm/unwind.h     |   8 ++
>  arch/loongarch/kernel/Makefile          |   1 +
>  arch/loongarch/kernel/traps.c           |   5 +
>  arch/loongarch/kernel/unwind_prologue.c | 172 ++++++++++++++++++++++++
>  6 files changed, 257 insertions(+)
>  create mode 100644 arch/loongarch/kernel/unwind_prologue.c
>
> diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug
> index 68634d4fa27b..57cdbe0cfd98 100644
> --- a/arch/loongarch/Kconfig.debug
> +++ b/arch/loongarch/Kconfig.debug
> @@ -1,3 +1,11 @@
> +choice
> +       prompt "Choose kernel unwinder"
> +       default UNWINDER_PROLOGUE if KALLSYMS
> +       help
> +         This determines which method will be used for unwinding kernel stack
> +         traces for panics, oopses, bugs, warnings, perf, /proc/<pid>/stack,
> +         lockdep, and more.
> +
>  config UNWINDER_GUESS
>         bool "Guess unwinder"
>         help
> @@ -7,3 +15,14 @@ config UNWINDER_GUESS
>
>           While this option often produces false positives, it can still be
>           useful in many cases.
> +
> +config UNWINDER_PROLOGUE
> +       bool "Prologue unwinder"
> +       depends on KALLSYMS
> +       help
> +         This option enables the "prologue" unwinder for unwinding kernel stack
> +         traces.  It unwind the stack frame based on prologue code analyze.  Symbol
> +         information is needed, at least the address and length of each function.
> +         Some of the addresses it reports may be incorrect.
> +
> +endchoice
> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
> index 575d1bb66ffb..b876907ca65a 100644
> --- a/arch/loongarch/include/asm/inst.h
> +++ b/arch/loongarch/include/asm/inst.h
> @@ -23,12 +23,33 @@ enum reg1i20_op {
>         lu32id_op       = 0x0b,
>  };
>
> +enum reg1i21_op {
> +       beqz_op         = 0x10,
> +       bnez_op         = 0x11,
> +};
> +
>  enum reg2i12_op {
> +       addiw_op        = 0x0a,
> +       addid_op        = 0x0b,
>         lu52id_op       = 0x0c,
> +       ldb_op          = 0xa0,
> +       ldh_op          = 0xa1,
> +       ldw_op          = 0xa2,
> +       ldd_op          = 0xa3,
> +       stb_op          = 0xa4,
> +       sth_op          = 0xa5,
> +       stw_op          = 0xa6,
> +       std_op          = 0xa7,
>  };
>
>  enum reg2i16_op {
>         jirl_op         = 0x13,
> +       beq_op          = 0x16,
> +       bne_op          = 0x17,
> +       blt_op          = 0x18,
> +       bge_op          = 0x19,
> +       bltu_op         = 0x1a,
> +       bgeu_op         = 0x1b,
>  };
>
>  struct reg0i26_format {
> @@ -110,6 +131,37 @@ enum loongarch_gpr {
>         LOONGARCH_GPR_MAX
>  };
>
> +#define is_imm12_negative(val) is_imm_negative(val, 12)
> +
> +static inline bool is_imm_negative(unsigned long val, unsigned int bit)
> +{
> +       return val & (1UL << (bit - 1));
> +}
> +
> +static inline bool is_stack_alloc_ins(union loongarch_instruction *ip)
> +{
> +       /* addi.d $sp, $sp, -imm */
> +       return ip->reg2i12_format.opcode == addid_op &&
> +               ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
> +               ip->reg2i12_format.rd == LOONGARCH_GPR_SP &&
> +               is_imm12_negative(ip->reg2i12_format.immediate);
> +}
> +
> +static inline bool is_ra_save_ins(union loongarch_instruction *ip)
> +{
> +       /* st.d $ra, $sp, offset */
> +       return ip->reg2i12_format.opcode == std_op &&
> +               ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
> +               ip->reg2i12_format.rd == LOONGARCH_GPR_RA &&
> +               !is_imm12_negative(ip->reg2i12_format.immediate);
> +}
> +
> +static inline bool is_branch_insn(union loongarch_instruction insn)
> +{
> +       return insn.reg1i21_format.opcode >= beqz_op &&
> +               insn.reg1i21_format.opcode <= bgeu_op;
> +}
> +
>  u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
>  u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
>  u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
> diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
> index 243330b39d0d..f9f73a26504e 100644
> --- a/arch/loongarch/include/asm/unwind.h
> +++ b/arch/loongarch/include/asm/unwind.h
> @@ -14,6 +14,14 @@
>  struct unwind_state {
>         struct stack_info stack_info;
>         struct task_struct *task;
> +#if defined(CONFIG_UNWINDER_PROLOGUE)
> +       unsigned long ra;
> +       bool enable;
> +       /*
> +        * Enable is the prologue analysis method
> +        * otherwise is the way to guess.
> +        */
> +#endif
>         unsigned long sp, pc;
>         bool first;
>         bool error;
This data struct makes me very uncomfortable, especially "enable" and
the #ifdef, maybe we can rework it like this?

#define UNWINDER_GUESS     0
#define UNWINDER_PROLOGURE 1
struct unwind_state {
        char type; /* UNWINDER_xxx */
        bool first, error;
        unsigned long sp, pc, ra;
        struct task_struct *task;
        struct stack_info stack_info;
};

Huacai

> diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
> index c5fa4adb23b6..918600e7b30f 100644
> --- a/arch/loongarch/kernel/Makefile
> +++ b/arch/loongarch/kernel/Makefile
> @@ -23,5 +23,6 @@ obj-$(CONFIG_SMP)             += smp.o
>  obj-$(CONFIG_NUMA)             += numa.o
>
>  obj-$(CONFIG_UNWINDER_GUESS)   += unwind_guess.o
> +obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
>
>  CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
> diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
> index ef2c3aeb1dab..3e904fa12d48 100644
> --- a/arch/loongarch/kernel/traps.c
> +++ b/arch/loongarch/kernel/traps.c
> @@ -73,6 +73,11 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
>
>         unwind_start(&state, task, pregs);
>
> +#ifdef CONFIG_UNWINDER_PROLOGUE
> +       if (user_mode(regs))
> +               state.enable = false;
> +#endif
> +
>         printk("%sCall Trace:", loglvl);
>         for (; !unwind_done(&state); unwind_next_frame(&state)) {
>                 addr = unwind_get_return_address(&state);
> diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c
> new file mode 100644
> index 000000000000..072d1f7bf4ac
> --- /dev/null
> +++ b/arch/loongarch/kernel/unwind_prologue.c
> @@ -0,0 +1,172 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +#include <linux/kallsyms.h>
> +
> +#include <asm/inst.h>
> +#include <asm/ptrace.h>
> +#include <asm/unwind.h>
> +
> +unsigned long unwind_get_return_address(struct unwind_state *state)
> +{
> +
> +       if (unwind_done(state))
> +               return 0;
> +       else if (state->enable)
> +               return state->pc;
> +       else if (state->first)
> +               return state->pc;
> +
> +       return *(unsigned long *)(state->sp);
> +
> +}
> +EXPORT_SYMBOL_GPL(unwind_get_return_address);
> +
> +static bool unwind_by_prologue(struct unwind_state *state)
> +{
> +       struct stack_info *info = &state->stack_info;
> +       union loongarch_instruction *ip, *ip_end;
> +       unsigned long frame_size = 0, frame_ra = -1;
> +       unsigned long size, offset, pc = state->pc;
> +
> +       if (state->sp >= info->end || state->sp < info->begin)
> +               return false;
> +
> +       if (!kallsyms_lookup_size_offset(pc, &size, &offset))
> +               return false;
> +
> +       ip = (union loongarch_instruction *)(pc - offset);
> +       ip_end = (union loongarch_instruction *)pc;
> +
> +       while (ip < ip_end) {
> +               if (is_stack_alloc_ins(ip)) {
> +                       frame_size = (1 << 12) - ip->reg2i12_format.immediate;
> +                       ip++;
> +                       break;
> +               }
> +               ip++;
> +       }
> +
> +       if (!frame_size) {
> +               if (state->first)
> +                       goto first;
> +
> +               return false;
> +       }
> +
> +       while (ip < ip_end) {
> +               if (is_ra_save_ins(ip)) {
> +                       frame_ra = ip->reg2i12_format.immediate;
> +                       break;
> +               }
> +               if (is_branch_insn(*ip))
> +                       break;
> +               ip++;
> +       }
> +
> +       if (frame_ra < 0) {
> +               if (state->first) {
> +                       state->sp = state->sp + frame_size;
> +                       goto first;
> +               }
> +               return false;
> +       }
> +
> +       if (state->first)
> +               state->first = false;
> +
> +       state->pc = *(unsigned long *)(state->sp + frame_ra);
> +       state->sp = state->sp + frame_size;
> +       return !!__kernel_text_address(state->pc);
> +
> +first:
> +       state->first = false;
> +       if (state->pc == state->ra)
> +               return false;
> +
> +       state->pc = state->ra;
> +
> +       return !!__kernel_text_address(state->ra);
> +}
> +
> +static bool unwind_by_guess(struct unwind_state *state)
> +{
> +       struct stack_info *info = &state->stack_info;
> +       unsigned long addr;
> +
> +       for (state->sp += sizeof(unsigned long);
> +            state->sp < info->end;
> +            state->sp += sizeof(unsigned long)) {
> +               addr = *(unsigned long *)(state->sp);
> +               if (__kernel_text_address(addr))
> +                       return true;
> +       }
> +
> +       return false;
> +}
> +
> +bool unwind_next_frame(struct unwind_state *state)
> +{
> +       struct stack_info *info = &state->stack_info;
> +       struct pt_regs *regs;
> +       unsigned long pc;
> +
> +       if (unwind_done(state))
> +               return false;
> +
> +       do {
> +               if (state->enable) {
> +                       if (unwind_by_prologue(state))
> +                               return true;
> +
> +                       if (info->type == STACK_TYPE_IRQ &&
> +                               info->end == state->sp) {
> +                               regs = (struct pt_regs *)info->next_sp;
> +                               pc = regs->csr_era;
> +                               if (user_mode(regs) || !__kernel_text_address(pc))
> +                                       return false;
> +
> +                               state->pc = pc;
> +                               state->sp = regs->regs[3];
> +                               state->ra = regs->regs[1];
> +                               state->first = true;
> +                               get_stack_info(state->sp, state->task, info);
> +
> +                               return true;
> +                       }
> +               } else {
> +                       if (state->first)
> +                               state->first = false;
> +                       else if (unwind_by_guess(state))
> +                               return true;
> +               }
> +
> +               state->sp = info->next_sp;
> +
> +       } while (!get_stack_info(state->sp, state->task, info));
> +
> +       return false;
> +}
> +EXPORT_SYMBOL_GPL(unwind_next_frame);
> +
> +void unwind_start(struct unwind_state *state, struct task_struct *task,
> +                   struct pt_regs *regs)
> +{
> +       memset(state, 0, sizeof(*state));
> +
> +       if (__kernel_text_address(regs->csr_era))
> +               state->enable = true;
> +
> +       state->task = task;
> +       state->pc = regs->csr_era;
> +       state->sp = regs->regs[3];
> +       state->ra = regs->regs[1];
> +       state->first = true;
> +
> +       get_stack_info(state->sp, state->task, &state->stack_info);
> +
> +       if (!unwind_done(state) && !__kernel_text_address(state->pc))
> +               unwind_next_frame(state);
> +}
> +EXPORT_SYMBOL_GPL(unwind_start);
> --
> 2.20.1
>
Re: [PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Youling Tang 3 years, 8 months ago

On 08/01/2022 11:26 PM, Huacai Chen wrote:
>> diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
>> index 243330b39d0d..f9f73a26504e 100644
>> --- a/arch/loongarch/include/asm/unwind.h
>> +++ b/arch/loongarch/include/asm/unwind.h
>> @@ -14,6 +14,14 @@
>>  struct unwind_state {
>>         struct stack_info stack_info;
>>         struct task_struct *task;
>> +#if defined(CONFIG_UNWINDER_PROLOGUE)
>> +       unsigned long ra;
>> +       bool enable;
>> +       /*
>> +        * Enable is the prologue analysis method
>> +        * otherwise is the way to guess.
>> +        */
>> +#endif
>>         unsigned long sp, pc;
>>         bool first;
>>         bool error;
> This data struct makes me very uncomfortable, especially "enable" and
> the #ifdef, maybe we can rework it like this?
>
> #define UNWINDER_GUESS     0
> #define UNWINDER_PROLOGURE 1

Maybe it's better to define with enum type?
enum unwind_type {
	UNWINDER_GUESS,
	UNWINDER_PROLOGURE,
};

Youling

> struct unwind_state {
>         char type; /* UNWINDER_xxx */
>         bool first, error;
>         unsigned long sp, pc, ra;
>         struct task_struct *task;
>         struct stack_info stack_info;
> };
>
> Huacai
Re: [PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Huacai Chen 3 years, 8 months ago
Hi, Youling,

On Tue, Aug 2, 2022 at 9:30 AM Youling Tang <tangyouling@loongson.cn> wrote:
>
>
>
> On 08/01/2022 11:26 PM, Huacai Chen wrote:
> >> diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
> >> index 243330b39d0d..f9f73a26504e 100644
> >> --- a/arch/loongarch/include/asm/unwind.h
> >> +++ b/arch/loongarch/include/asm/unwind.h
> >> @@ -14,6 +14,14 @@
> >>  struct unwind_state {
> >>         struct stack_info stack_info;
> >>         struct task_struct *task;
> >> +#if defined(CONFIG_UNWINDER_PROLOGUE)
> >> +       unsigned long ra;
> >> +       bool enable;
> >> +       /*
> >> +        * Enable is the prologue analysis method
> >> +        * otherwise is the way to guess.
> >> +        */
> >> +#endif
> >>         unsigned long sp, pc;
> >>         bool first;
> >>         bool error;
> > This data struct makes me very uncomfortable, especially "enable" and
> > the #ifdef, maybe we can rework it like this?
> >
> > #define UNWINDER_GUESS     0
> > #define UNWINDER_PROLOGURE 1
>
> Maybe it's better to define with enum type?
> enum unwind_type {
>         UNWINDER_GUESS,
>         UNWINDER_PROLOGURE,
> };
Both macro and enum are acceptable, but enum is essentially "int",
while the "type" member is "char" here.

Huacai
>
> Youling
>
> > struct unwind_state {
> >         char type; /* UNWINDER_xxx */
> >         bool first, error;
> >         unsigned long sp, pc, ra;
> >         struct task_struct *task;
> >         struct stack_info stack_info;
> > };
> >
> > Huacai
>
>
Re: [PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Xi Ruoyao 3 years, 8 months ago
On Tue, 2022-08-02 at 09:34 +0800, Huacai Chen wrote:
> > > #define UNWINDER_GUESS     0
> > > #define UNWINDER_PROLOGURE 1
> > 
> > Maybe it's better to define with enum type?
> > enum unwind_type {
> >          UNWINDER_GUESS,
> >          UNWINDER_PROLOGURE,
> > };
> Both macro and enum are acceptable, but enum is essentially "int",
> while the "type" member is "char" here.

"0" or "1" will also be "int" (C99 6.4.4.1 para 5).

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University
Re: [PATCH 2/4] LoongArch: Add prologue unwinder support
Posted by Huacai Chen 3 years, 8 months ago
Hi, Ruoyao,

On Tue, Aug 2, 2022 at 9:39 AM Xi Ruoyao <xry111@xry111.site> wrote:
>
> On Tue, 2022-08-02 at 09:34 +0800, Huacai Chen wrote:
> > > > #define UNWINDER_GUESS     0
> > > > #define UNWINDER_PROLOGURE 1
> > >
> > > Maybe it's better to define with enum type?
> > > enum unwind_type {
> > >          UNWINDER_GUESS,
> > >          UNWINDER_PROLOGURE,
> > > };
> > Both macro and enum are acceptable, but enum is essentially "int",
> > while the "type" member is "char" here.
>
> "0" or "1" will also be "int" (C99 6.4.4.1 para 5).
OK, then Zhang Qing has her rights to choose. :)

Huacai
>
> --
> Xi Ruoyao <xry111@xry111.site>
> School of Aerospace Science and Technology, Xidian University