diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-05-03 11:17:16 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-05-22 11:33:24 -0400 |
commit | 07b403415884e961920f55e6db462dff15d9df5a (patch) | |
tree | b29b4cc10756b984ec30b48b6f7783e91f3f6c79 /arch/arm/kernel | |
parent | 3683f44c42e991d313dc301504ee0fca1aeb8580 (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.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 @@ | |||
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 |
62 | struct stack_trace_data { | 63 | struct 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 | ||