Commit 5bb8d344 authored by Jinyang He's avatar Jinyang He Committed by Huacai Chen
Browse files

LoongArch: Use correct sp value to get graph addr in stack unwinders



The stack frame when function_graph enable like follows,

---------  <- function sp_on_entry
    |
    |
    |
 FAKE_RA   <- sp_on_entry - sizeof(pt_regs) + PT_R1
    |
---------  <- sp_on_entry - sizeof(pt_regs)

So if we want to get the &FAKE_RA we should get sp_on_entry first. In
the unwinder_prologue case, we can get the sp_on_entry as state->sp,
because we try to calculate each CFA and the ra saved address. But in
the unwinder_guess case, we cannot get it because we do not try to
calculate the CFA. Although LoongArch have not fixed frame, the $ra is
saved at CFA - 8 in most cases, we can try guess, too. As we store the
pc in state, we not need to dereference state->sp, too.

Signed-off-by: default avatarJinyang He <hejinyang@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 429a9671
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -10,8 +10,6 @@
#define FTRACE_REGS_PLT_IDX	1
#define NR_FTRACE_PLTS		2

#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))

#ifdef CONFIG_FUNCTION_TRACER

#define MCOUNT_INSN_SIZE 4		/* sizeof mcount call */
+10 −0
Original line number Diff line number Diff line
@@ -8,7 +8,9 @@
#define _ASM_UNWIND_H

#include <linux/sched.h>
#include <linux/ftrace.h>

#include <asm/ptrace.h>
#include <asm/stacktrace.h>

enum unwinder_type {
@@ -40,4 +42,12 @@ static inline bool unwind_error(struct unwind_state *state)
	return state->error;
}

#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))

static inline unsigned long unwind_graph_addr(struct unwind_state *state,
					unsigned long pc, unsigned long cfa)
{
	return ftrace_graph_ret_addr(state->task, &state->graph_idx,
				     pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
}
#endif /* _ASM_UNWIND_H */
+4 −7
Original line number Diff line number Diff line
@@ -11,10 +11,8 @@ unsigned long unwind_get_return_address(struct unwind_state *state)
{
	if (unwind_done(state))
		return 0;
	else if (state->first)
		return state->pc;

	return *(unsigned long *)(state->sp);
	return state->pc;
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);

@@ -36,7 +34,7 @@ void unwind_start(struct unwind_state *state, struct task_struct *task,

	state->task = task;
	state->first = true;

	state->pc = unwind_graph_addr(state, state->pc, state->sp);
	get_stack_info(state->sp, state->task, &state->stack_info);

	if (!unwind_done(state) && !__kernel_text_address(state->pc))
@@ -60,9 +58,8 @@ bool unwind_next_frame(struct unwind_state *state)
		     state->sp < info->end;
		     state->sp += sizeof(unsigned long)) {
			addr = *(unsigned long *)(state->sp);
			state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
					addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
			if (__kernel_text_address(addr))
			state->pc = unwind_graph_addr(state, addr, state->sp + 8);
			if (__kernel_text_address(state->pc))
				return true;
		}

+6 −15
Original line number Diff line number Diff line
@@ -21,16 +21,10 @@ static inline void unwind_state_fixup(struct unwind_state *state)

unsigned long unwind_get_return_address(struct unwind_state *state)
{

	if (unwind_done(state))
		return 0;
	else if (state->type)
		return state->pc;
	else if (state->first)
		return state->pc;

	return *(unsigned long *)(state->sp);

	return state->pc;
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);

@@ -43,9 +37,8 @@ static bool unwind_by_guess(struct unwind_state *state)
	     state->sp < info->end;
	     state->sp += sizeof(unsigned long)) {
		addr = *(unsigned long *)(state->sp);
		state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
				addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
		if (__kernel_text_address(addr))
		state->pc = unwind_graph_addr(state, addr, state->sp + 8);
		if (__kernel_text_address(state->pc))
			return true;
	}

@@ -166,7 +159,7 @@ void unwind_start(struct unwind_state *state, struct task_struct *task,

	state->task = task;
	state->first = true;

	state->pc = unwind_graph_addr(state, state->pc, state->sp);
	get_stack_info(state->sp, state->task, &state->stack_info);

	if (!unwind_done(state) && !__kernel_text_address(state->pc))
@@ -193,8 +186,7 @@ bool unwind_next_frame(struct unwind_state *state)

		case UNWINDER_PROLOGUE:
			if (unwind_by_prologue(state)) {
				state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
						state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
				state->pc = unwind_graph_addr(state, state->pc, state->sp);
				return true;
			}

@@ -209,8 +201,7 @@ bool unwind_next_frame(struct unwind_state *state)
				state->first = true;
				state->ra = regs->regs[1];
				state->sp = regs->regs[3];
				state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
						pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
				state->pc = pc;
				get_stack_info(state->sp, state->task, info);

				return true;