From 07b403415884e961920f55e6db462dff15d9df5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 16:17:16 +0100 Subject: ARM: stacktrace: include exception PC value in stacktrace output When we unwind through an exception stack, include the saved PC value into the stack trace: this fills in an otherwise missed functions from the trace (as indicated below): [] fec_enet_interrupt+0xa0/0xe8 [] handle_irq_event_percpu+0x68/0x228 [] handle_irq_event+0x4c/0x6c [] handle_fasteoi_irq+0xac/0x198 [] generic_handle_irq+0x4c/0x60 [] handle_IRQ+0x40/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] __do_softirq+0xe0/0x2fc <==== [] irq_exit+0xb0/0x100 [] handle_IRQ+0x44/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] arch_cpu_idle+0x30/0x38 <==== [] cpu_startup_entry+0xac/0x214 [] rest_init+0x68/0x80 [] start_kernel+0x2fc/0x358 Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 6582c4adc..5a80ddf 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -3,6 +3,7 @@ #include #include +#include #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) /* @@ -61,6 +62,7 @@ EXPORT_SYMBOL(walk_stackframe); #ifdef CONFIG_STACKTRACE struct stack_trace_data { struct stack_trace *trace; + unsigned long last_pc; unsigned int no_sched_functions; unsigned int skip; }; @@ -69,6 +71,7 @@ static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; + struct pt_regs *regs; unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) @@ -80,6 +83,25 @@ static int save_trace(struct stackframe *frame, void *d) trace->entries[trace->nr_entries++] = addr; + if (trace->nr_entries >= trace->max_entries) + return 1; + + /* + * in_exception_text() is designed to test if the PC is one of + * the functions which has an exception stack above it, but + * unfortunately what is in frame->pc is the return LR value, + * not the saved PC value. So, we need to track the previous + * frame PC value when doing this. + */ + addr = data->last_pc; + data->last_pc = frame->pc; + if (!in_exception_text(addr)) + return 0; + + regs = (struct pt_regs *)frame->sp; + + trace->entries[trace->nr_entries++] = regs->ARM_pc; + return trace->nr_entries >= trace->max_entries; } @@ -91,6 +113,7 @@ static noinline void __save_stack_trace(struct task_struct *tsk, struct stackframe frame; data.trace = trace; + data.last_pc = ULONG_MAX; data.skip = trace->skip; data.no_sched_functions = nosched; -- cgit v1.1