diff options
Diffstat (limited to 'arch/arm/kernel/stacktrace.c')
-rw-r--r-- | arch/arm/kernel/stacktrace.c | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index af4e8c8a5422..f065eb05d254 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,16 +83,39 @@ 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 | ||
86 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | 108 | /* This must be noinline to so that our skip calculation works correctly */ |
109 | static noinline void __save_stack_trace(struct task_struct *tsk, | ||
110 | struct stack_trace *trace, unsigned int nosched) | ||
87 | { | 111 | { |
88 | struct stack_trace_data data; | 112 | struct stack_trace_data data; |
89 | struct stackframe frame; | 113 | struct stackframe frame; |
90 | 114 | ||
91 | data.trace = trace; | 115 | data.trace = trace; |
116 | data.last_pc = ULONG_MAX; | ||
92 | data.skip = trace->skip; | 117 | data.skip = trace->skip; |
118 | data.no_sched_functions = nosched; | ||
93 | 119 | ||
94 | if (tsk != current) { | 120 | if (tsk != current) { |
95 | #ifdef CONFIG_SMP | 121 | #ifdef CONFIG_SMP |
@@ -102,7 +128,6 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
102 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 128 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
103 | return; | 129 | return; |
104 | #else | 130 | #else |
105 | data.no_sched_functions = 1; | ||
106 | frame.fp = thread_saved_fp(tsk); | 131 | frame.fp = thread_saved_fp(tsk); |
107 | frame.sp = thread_saved_sp(tsk); | 132 | frame.sp = thread_saved_sp(tsk); |
108 | frame.lr = 0; /* recovered from the stack */ | 133 | frame.lr = 0; /* recovered from the stack */ |
@@ -111,11 +136,12 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
111 | } else { | 136 | } else { |
112 | register unsigned long current_sp asm ("sp"); | 137 | register unsigned long current_sp asm ("sp"); |
113 | 138 | ||
114 | data.no_sched_functions = 0; | 139 | /* We don't want this function nor the caller */ |
140 | data.skip += 2; | ||
115 | frame.fp = (unsigned long)__builtin_frame_address(0); | 141 | frame.fp = (unsigned long)__builtin_frame_address(0); |
116 | frame.sp = current_sp; | 142 | frame.sp = current_sp; |
117 | frame.lr = (unsigned long)__builtin_return_address(0); | 143 | frame.lr = (unsigned long)__builtin_return_address(0); |
118 | frame.pc = (unsigned long)save_stack_trace_tsk; | 144 | frame.pc = (unsigned long)__save_stack_trace; |
119 | } | 145 | } |
120 | 146 | ||
121 | walk_stackframe(&frame, save_trace, &data); | 147 | walk_stackframe(&frame, save_trace, &data); |
@@ -123,9 +149,33 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
123 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 149 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
124 | } | 150 | } |
125 | 151 | ||
152 | void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) | ||
153 | { | ||
154 | struct stack_trace_data data; | ||
155 | struct stackframe frame; | ||
156 | |||
157 | data.trace = trace; | ||
158 | data.skip = trace->skip; | ||
159 | data.no_sched_functions = 0; | ||
160 | |||
161 | frame.fp = regs->ARM_fp; | ||
162 | frame.sp = regs->ARM_sp; | ||
163 | frame.lr = regs->ARM_lr; | ||
164 | frame.pc = regs->ARM_pc; | ||
165 | |||
166 | walk_stackframe(&frame, save_trace, &data); | ||
167 | if (trace->nr_entries < trace->max_entries) | ||
168 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
169 | } | ||
170 | |||
171 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
172 | { | ||
173 | __save_stack_trace(tsk, trace, 1); | ||
174 | } | ||
175 | |||
126 | void save_stack_trace(struct stack_trace *trace) | 176 | void save_stack_trace(struct stack_trace *trace) |
127 | { | 177 | { |
128 | save_stack_trace_tsk(current, trace); | 178 | __save_stack_trace(current, trace, 0); |
129 | } | 179 | } |
130 | EXPORT_SYMBOL_GPL(save_stack_trace); | 180 | EXPORT_SYMBOL_GPL(save_stack_trace); |
131 | #endif | 181 | #endif |