diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-05-03 16:17:16 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-05-22 16:33:24 +0100 |
commit | 07b403415884e961920f55e6db462dff15d9df5a (patch) | |
tree | b29b4cc10756b984ec30b48b6f7783e91f3f6c79 | |
parent | 3683f44c42e991d313dc301504ee0fca1aeb8580 (diff) | |
download | linux-07b403415884e961920f55e6db462dff15d9df5a.tar.gz linux-07b403415884e961920f55e6db462dff15d9df5a.tar.bz2 linux-07b403415884e961920f55e6db462dff15d9df5a.zip |
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):
[<c03f4424>] fec_enet_interrupt+0xa0/0xe8
[<c0066c0c>] handle_irq_event_percpu+0x68/0x228
[<c0066e18>] handle_irq_event+0x4c/0x6c
[<c006a024>] handle_fasteoi_irq+0xac/0x198
[<c00664b0>] generic_handle_irq+0x4c/0x60
[<c000f014>] handle_IRQ+0x40/0x98
[<c0008554>] gic_handle_irq+0x30/0x64
[<c0012900>] __irq_svc+0x40/0x50
[<c0029030>] __do_softirq+0xe0/0x2fc <====
[<c0029500>] irq_exit+0xb0/0x100
[<c000f018>] handle_IRQ+0x44/0x98
[<c0008554>] gic_handle_irq+0x30/0x64
[<c0012900>] __irq_svc+0x40/0x50
[<c000f34c>] arch_cpu_idle+0x30/0x38 <====
[<c005e1e4>] cpu_startup_entry+0xac/0x214
[<c066297c>] rest_init+0x68/0x80
[<c08ccb10>] start_kernel+0x2fc/0x358
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/kernel/stacktrace.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 6582c4adc182..5a80ddfe7031 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -3,6 +3,7 @@ #include <linux/stacktrace.h> #include <asm/stacktrace.h> +#include <asm/traps.h> #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; |