aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2014-05-03 11:17:16 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2014-05-22 11:33:24 -0400
commit07b403415884e961920f55e6db462dff15d9df5a (patch)
treeb29b4cc10756b984ec30b48b6f7783e91f3f6c79 /arch/arm/kernel
parent3683f44c42e991d313dc301504ee0fca1aeb8580 (diff)
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>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/stacktrace.c23
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 @@
3#include <linux/stacktrace.h> 3#include <linux/stacktrace.h>
4 4
5#include <asm/stacktrace.h> 5#include <asm/stacktrace.h>
6#include <asm/traps.h>
6 7
7#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 8#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
8/* 9/*
@@ -61,6 +62,7 @@ EXPORT_SYMBOL(walk_stackframe);
61#ifdef CONFIG_STACKTRACE 62#ifdef CONFIG_STACKTRACE
62struct stack_trace_data { 63struct stack_trace_data {
63 struct stack_trace *trace; 64 struct stack_trace *trace;
65 unsigned long last_pc;
64 unsigned int no_sched_functions; 66 unsigned int no_sched_functions;
65 unsigned int skip; 67 unsigned int skip;
66}; 68};
@@ -69,6 +71,7 @@ static int save_trace(struct stackframe *frame, void *d)
69{ 71{
70 struct stack_trace_data *data = d; 72 struct stack_trace_data *data = d;
71 struct stack_trace *trace = data->trace; 73 struct stack_trace *trace = data->trace;
74 struct pt_regs *regs;
72 unsigned long addr = frame->pc; 75 unsigned long addr = frame->pc;
73 76
74 if (data->no_sched_functions && in_sched_functions(addr)) 77 if (data->no_sched_functions && in_sched_functions(addr))
@@ -80,6 +83,25 @@ static int save_trace(struct stackframe *frame, void *d)
80 83
81 trace->entries[trace->nr_entries++] = addr; 84 trace->entries[trace->nr_entries++] = addr;
82 85
86 if (trace->nr_entries >= trace->max_entries)
87 return 1;
88
89 /*
90 * in_exception_text() is designed to test if the PC is one of
91 * the functions which has an exception stack above it, but
92 * unfortunately what is in frame->pc is the return LR value,
93 * not the saved PC value. So, we need to track the previous
94 * frame PC value when doing this.
95 */
96 addr = data->last_pc;
97 data->last_pc = frame->pc;
98 if (!in_exception_text(addr))
99 return 0;
100
101 regs = (struct pt_regs *)frame->sp;
102
103 trace->entries[trace->nr_entries++] = regs->ARM_pc;
104
83 return trace->nr_entries >= trace->max_entries; 105 return trace->nr_entries >= trace->max_entries;
84} 106}
85 107
@@ -91,6 +113,7 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
91 struct stackframe frame; 113 struct stackframe frame;
92 114
93 data.trace = trace; 115 data.trace = trace;
116 data.last_pc = ULONG_MAX;
94 data.skip = trace->skip; 117 data.skip = trace->skip;
95 data.no_sched_functions = nosched; 118 data.no_sched_functions = nosched;
96 119