Browse Source

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>
Russell King 11 years ago
parent
commit
07b4034158
1 changed files with 23 additions and 0 deletions
  1. 23 0
      arch/arm/kernel/stacktrace.c

+ 23 - 0
arch/arm/kernel/stacktrace.c

@@ -3,6 +3,7 @@
 #include <linux/stacktrace.h>
 #include <linux/stacktrace.h>
 
 
 #include <asm/stacktrace.h>
 #include <asm/stacktrace.h>
+#include <asm/traps.h>
 
 
 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
 /*
 /*
@@ -61,6 +62,7 @@ EXPORT_SYMBOL(walk_stackframe);
 #ifdef CONFIG_STACKTRACE
 #ifdef CONFIG_STACKTRACE
 struct stack_trace_data {
 struct stack_trace_data {
 	struct stack_trace *trace;
 	struct stack_trace *trace;
+	unsigned long last_pc;
 	unsigned int no_sched_functions;
 	unsigned int no_sched_functions;
 	unsigned int skip;
 	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_data *data = d;
 	struct stack_trace *trace = data->trace;
 	struct stack_trace *trace = data->trace;
+	struct pt_regs *regs;
 	unsigned long addr = frame->pc;
 	unsigned long addr = frame->pc;
 
 
 	if (data->no_sched_functions && in_sched_functions(addr))
 	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;
 	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;
 	return trace->nr_entries >= trace->max_entries;
 }
 }
 
 
@@ -91,6 +113,7 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
 	struct stackframe frame;
 	struct stackframe frame;
 
 
 	data.trace = trace;
 	data.trace = trace;
+	data.last_pc = ULONG_MAX;
 	data.skip = trace->skip;
 	data.skip = trace->skip;
 	data.no_sched_functions = nosched;
 	data.no_sched_functions = nosched;