aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/stacktrace.c
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2009-02-11 07:07:53 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-02-12 08:21:17 -0500
commit2d7c11bfc91637e5f9bc5f8c9a82aaffcc0e97aa (patch)
tree0bb67dae38b1185089b6c9813769689cb79c5ee3 /arch/arm/kernel/stacktrace.c
parent67a94c23bb7338c321ae71aa33f8d398c94e1d0c (diff)
[ARM] 5382/1: unwind: Reorganise the stacktrace support
This patch changes the walk_stacktrace and its callers for easier integration of stack unwinding. The arch/arm/kernel/stacktrace.h file is also moved to arch/arm/include/asm/stacktrace.h. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/stacktrace.c')
-rw-r--r--arch/arm/kernel/stacktrace.c88
1 files changed, 60 insertions, 28 deletions
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
index fc650f64df43..9f444e5cc165 100644
--- a/arch/arm/kernel/stacktrace.c
+++ b/arch/arm/kernel/stacktrace.c
@@ -2,35 +2,60 @@
2#include <linux/sched.h> 2#include <linux/sched.h>
3#include <linux/stacktrace.h> 3#include <linux/stacktrace.h>
4 4
5#include "stacktrace.h" 5#include <asm/stacktrace.h>
6 6
7int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, 7#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
8 int (*fn)(struct stackframe *, void *), void *data) 8/*
9 * Unwind the current stack frame and store the new register values in the
10 * structure passed as argument. Unwinding is equivalent to a function return,
11 * hence the new PC value rather than LR should be used for backtrace.
12 *
13 * With framepointer enabled, a simple function prologue looks like this:
14 * mov ip, sp
15 * stmdb sp!, {fp, ip, lr, pc}
16 * sub fp, ip, #4
17 *
18 * A simple function epilogue looks like this:
19 * ldm sp, {fp, sp, pc}
20 *
21 * Note that with framepointer enabled, even the leaf functions have the same
22 * prologue and epilogue, therefore we can ignore the LR value in this case.
23 */
24int unwind_frame(struct stackframe *frame)
9{ 25{
10 struct stackframe *frame; 26 unsigned long high, low;
11 27 unsigned long fp = frame->fp;
12 do {
13 /*
14 * Check current frame pointer is within bounds
15 */
16 if (fp < (low + 12) || fp + 4 >= high)
17 break;
18 28
19 frame = (struct stackframe *)(fp - 12); 29 /* only go to a higher address on the stack */
30 low = frame->sp;
31 high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE;
20 32
21 if (fn(frame, data)) 33 /* check current frame pointer is within bounds */
22 break; 34 if (fp < (low + 12) || fp + 4 >= high)
35 return -EINVAL;
23 36
24 /* 37 /* restore the registers from the stack frame */
25 * Update the low bound - the next frame must always 38 frame->fp = *(unsigned long *)(fp - 12);
26 * be at a higher address than the current frame. 39 frame->sp = *(unsigned long *)(fp - 8);
27 */ 40 frame->pc = *(unsigned long *)(fp - 4);
28 low = fp + 4;
29 fp = frame->fp;
30 } while (fp);
31 41
32 return 0; 42 return 0;
33} 43}
44#endif
45
46void walk_stackframe(struct stackframe *frame,
47 int (*fn)(struct stackframe *, void *), void *data)
48{
49 while (1) {
50 int ret;
51
52 if (fn(frame, data))
53 break;
54 ret = unwind_frame(frame);
55 if (ret < 0)
56 break;
57 }
58}
34EXPORT_SYMBOL(walk_stackframe); 59EXPORT_SYMBOL(walk_stackframe);
35 60
36#ifdef CONFIG_STACKTRACE 61#ifdef CONFIG_STACKTRACE
@@ -44,7 +69,7 @@ static int save_trace(struct stackframe *frame, void *d)
44{ 69{
45 struct stack_trace_data *data = d; 70 struct stack_trace_data *data = d;
46 struct stack_trace *trace = data->trace; 71 struct stack_trace *trace = data->trace;
47 unsigned long addr = frame->lr; 72 unsigned long addr = frame->pc;
48 73
49 if (data->no_sched_functions && in_sched_functions(addr)) 74 if (data->no_sched_functions && in_sched_functions(addr))
50 return 0; 75 return 0;
@@ -61,11 +86,10 @@ static int save_trace(struct stackframe *frame, void *d)
61void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 86void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
62{ 87{
63 struct stack_trace_data data; 88 struct stack_trace_data data;
64 unsigned long fp, base; 89 struct stackframe frame;
65 90
66 data.trace = trace; 91 data.trace = trace;
67 data.skip = trace->skip; 92 data.skip = trace->skip;
68 base = (unsigned long)task_stack_page(tsk);
69 93
70 if (tsk != current) { 94 if (tsk != current) {
71#ifdef CONFIG_SMP 95#ifdef CONFIG_SMP
@@ -76,14 +100,22 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
76 BUG(); 100 BUG();
77#else 101#else
78 data.no_sched_functions = 1; 102 data.no_sched_functions = 1;
79 fp = thread_saved_fp(tsk); 103 frame.fp = thread_saved_fp(tsk);
104 frame.sp = thread_saved_sp(tsk);
105 frame.lr = 0; /* recovered from the stack */
106 frame.pc = thread_saved_pc(tsk);
80#endif 107#endif
81 } else { 108 } else {
109 register unsigned long current_sp asm ("sp");
110
82 data.no_sched_functions = 0; 111 data.no_sched_functions = 0;
83 asm("mov %0, fp" : "=r" (fp)); 112 frame.fp = (unsigned long)__builtin_frame_address(0);
113 frame.sp = current_sp;
114 frame.lr = (unsigned long)__builtin_return_address(0);
115 frame.pc = (unsigned long)save_stack_trace_tsk;
84 } 116 }
85 117
86 walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); 118 walk_stackframe(&frame, save_trace, &data);
87 if (trace->nr_entries < trace->max_entries) 119 if (trace->nr_entries < trace->max_entries)
88 trace->entries[trace->nr_entries++] = ULONG_MAX; 120 trace->entries[trace->nr_entries++] = ULONG_MAX;
89} 121}