ARM: 9259/1: stacktrace: Convert stacktrace to generic ARCH_STACKWALK

Historically architectures have had duplicated code in their stack trace
implementations for filtering what gets traced. In order to avoid this
duplication some generic code has been provided using a new interface
arch_stack_walk(), enabled by selecting ARCH_STACKWALK in Kconfig, which
factors all this out into the generic stack trace code. Convert ARM to
use this common infrastructure.

When initializing the stack frame of the current task, arm64 uses
__builtin_frame_address(1) to initialize the frame pointer, skipping
arch_stack_walk(), see the commit c607ab4f91 ("arm64: stacktrace:
don't trace arch_stack_walk()"). Since __builtin_frame_address(1) does
not work on ARM, unwind_frame() is used to unwind the stack one layer
forward before calling walk_stackframe().

Signed-off-by: Li Huafei <lihuafei1@huawei.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
This commit is contained in:
Li Huafei 2022-10-18 13:59:02 +01:00 коммит произвёл Russell King (Oracle)
Родитель 70ccc7c066
Коммит 9fbed16c3f
2 изменённых файлов: 33 добавлений и 82 удалений

Просмотреть файл

@ -17,6 +17,7 @@ config ARM
select ARCH_HAS_PTE_SPECIAL if ARM_LPAE
select ARCH_HAS_SETUP_DMA_OPS
select ARCH_HAS_SET_MEMORY
select ARCH_STACKWALK
select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
select ARCH_HAS_STRICT_MODULE_RWX if MMU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE

Просмотреть файл

@ -142,40 +142,32 @@ void notrace walk_stackframe(struct stackframe *frame,
EXPORT_SYMBOL(walk_stackframe);
#ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace *trace;
unsigned int no_sched_functions;
unsigned int skip;
};
static bool save_trace(void *d, unsigned long addr)
static void start_stack_trace(struct stackframe *frame, struct task_struct *task,
unsigned long fp, unsigned long sp,
unsigned long lr, unsigned long pc)
{
struct stack_trace_data *data = d;
struct stack_trace *trace = data->trace;
if (data->no_sched_functions && in_sched_functions(addr))
return true;
if (data->skip) {
data->skip--;
return true;
}
trace->entries[trace->nr_entries++] = addr;
return trace->nr_entries < trace->max_entries;
frame->fp = fp;
frame->sp = sp;
frame->lr = lr;
frame->pc = pc;
#ifdef CONFIG_KRETPROBES
frame->kr_cur = NULL;
frame->tsk = task;
#endif
#ifdef CONFIG_UNWINDER_FRAME_POINTER
frame->ex_frame = in_entry_text(frame->pc);
#endif
}
/* This must be noinline to so that our skip calculation works correctly */
static noinline void __save_stack_trace(struct task_struct *tsk,
struct stack_trace *trace, unsigned int nosched)
void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
struct task_struct *task, struct pt_regs *regs)
{
struct stack_trace_data data;
struct stackframe frame;
data.trace = trace;
data.skip = trace->skip;
data.no_sched_functions = nosched;
if (tsk != current) {
if (regs) {
start_stack_trace(&frame, NULL, regs->ARM_fp, regs->ARM_sp,
regs->ARM_lr, regs->ARM_pc);
} else if (task != current) {
#ifdef CONFIG_SMP
/*
* What guarantees do we have here that 'tsk' is not
@ -184,64 +176,22 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
*/
return;
#else
frame.fp = thread_saved_fp(tsk);
frame.sp = thread_saved_sp(tsk);
frame.lr = 0; /* recovered from the stack */
frame.pc = thread_saved_pc(tsk);
start_stack_trace(&frame, task, thread_saved_fp(task),
thread_saved_sp(task), 0,
thread_saved_pc(task));
#endif
} else {
/* We don't want this function nor the caller */
data.skip += 2;
frame.fp = (unsigned long)__builtin_frame_address(0);
frame.sp = current_stack_pointer;
frame.lr = (unsigned long)__builtin_return_address(0);
here:
frame.pc = (unsigned long)&&here;
start_stack_trace(&frame, task,
(unsigned long)__builtin_frame_address(0),
current_stack_pointer,
(unsigned long)__builtin_return_address(0),
(unsigned long)&&here);
/* skip this function */
if (unwind_frame(&frame))
return;
}
#ifdef CONFIG_KRETPROBES
frame.kr_cur = NULL;
frame.tsk = tsk;
#endif
#ifdef CONFIG_UNWINDER_FRAME_POINTER
frame.ex_frame = false;
#endif
walk_stackframe(&frame, save_trace, &data);
walk_stackframe(&frame, consume_entry, cookie);
}
void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
{
struct stack_trace_data data;
struct stackframe frame;
data.trace = trace;
data.skip = trace->skip;
data.no_sched_functions = 0;
frame.fp = regs->ARM_fp;
frame.sp = regs->ARM_sp;
frame.lr = regs->ARM_lr;
frame.pc = regs->ARM_pc;
#ifdef CONFIG_KRETPROBES
frame.kr_cur = NULL;
frame.tsk = current;
#endif
#ifdef CONFIG_UNWINDER_FRAME_POINTER
frame.ex_frame = in_entry_text(frame.pc);
#endif
walk_stackframe(&frame, save_trace, &data);
}
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
__save_stack_trace(tsk, trace, 1);
}
EXPORT_SYMBOL(save_stack_trace_tsk);
void save_stack_trace(struct stack_trace *trace)
{
__save_stack_trace(current, trace, 0);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
#endif