diff options
author | Jan Beulich <jbeulich@novell.com> | 2006-06-26 07:57:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 13:48:17 -0400 |
commit | 176a2718f408ce92788b29127050b04dfd6e4f68 (patch) | |
tree | bb637b13098f821551d07d3a13d140a908e7bc04 /arch/i386/kernel/traps.c | |
parent | dffead4e421e289c8434351400d24fd35723e874 (diff) |
[PATCH] i386: reliable stack trace support (i386)
These are the i386-specific pieces to enable reliable stack traces. This is
going to be even more useful once CFI annotations get added to he assembly
code, namely to entry.S.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/traps.c')
-rw-r--r-- | arch/i386/kernel/traps.c | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index dcc14477af1f..286584667865 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/utsname.h> | 28 | #include <linux/utsname.h> |
29 | #include <linux/kprobes.h> | 29 | #include <linux/kprobes.h> |
30 | #include <linux/kexec.h> | 30 | #include <linux/kexec.h> |
31 | #include <linux/unwind.h> | ||
31 | 32 | ||
32 | #ifdef CONFIG_EISA | 33 | #ifdef CONFIG_EISA |
33 | #include <linux/ioport.h> | 34 | #include <linux/ioport.h> |
@@ -47,7 +48,7 @@ | |||
47 | #include <asm/desc.h> | 48 | #include <asm/desc.h> |
48 | #include <asm/i387.h> | 49 | #include <asm/i387.h> |
49 | #include <asm/nmi.h> | 50 | #include <asm/nmi.h> |
50 | 51 | #include <asm/unwind.h> | |
51 | #include <asm/smp.h> | 52 | #include <asm/smp.h> |
52 | #include <asm/arch_hooks.h> | 53 | #include <asm/arch_hooks.h> |
53 | #include <asm/kdebug.h> | 54 | #include <asm/kdebug.h> |
@@ -170,14 +171,43 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo, | |||
170 | return ebp; | 171 | return ebp; |
171 | } | 172 | } |
172 | 173 | ||
173 | static void show_trace_log_lvl(struct task_struct *task, | 174 | static asmlinkage void show_trace_unwind(struct unwind_frame_info *info, void *log_lvl) |
175 | { | ||
176 | int printed = 0; /* nr of entries already printed on current line */ | ||
177 | |||
178 | while (unwind(info) == 0 && UNW_PC(info)) { | ||
179 | printed = print_addr_and_symbol(UNW_PC(info), log_lvl, printed); | ||
180 | if (arch_unw_user_mode(info)) | ||
181 | break; | ||
182 | } | ||
183 | if (printed) | ||
184 | printk("\n"); | ||
185 | } | ||
186 | |||
187 | static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | ||
174 | unsigned long *stack, char *log_lvl) | 188 | unsigned long *stack, char *log_lvl) |
175 | { | 189 | { |
176 | unsigned long ebp; | 190 | unsigned long ebp; |
191 | struct unwind_frame_info info; | ||
177 | 192 | ||
178 | if (!task) | 193 | if (!task) |
179 | task = current; | 194 | task = current; |
180 | 195 | ||
196 | if (regs) { | ||
197 | if (unwind_init_frame_info(&info, task, regs) == 0) { | ||
198 | show_trace_unwind(&info, log_lvl); | ||
199 | return; | ||
200 | } | ||
201 | } else if (task == current) { | ||
202 | if (unwind_init_running(&info, show_trace_unwind, log_lvl) == 0) | ||
203 | return; | ||
204 | } else { | ||
205 | if (unwind_init_blocked(&info, task) == 0) { | ||
206 | show_trace_unwind(&info, log_lvl); | ||
207 | return; | ||
208 | } | ||
209 | } | ||
210 | |||
181 | if (task == current) { | 211 | if (task == current) { |
182 | /* Grab ebp right from our regs */ | 212 | /* Grab ebp right from our regs */ |
183 | asm ("movl %%ebp, %0" : "=r" (ebp) : ); | 213 | asm ("movl %%ebp, %0" : "=r" (ebp) : ); |
@@ -198,13 +228,13 @@ static void show_trace_log_lvl(struct task_struct *task, | |||
198 | } | 228 | } |
199 | } | 229 | } |
200 | 230 | ||
201 | void show_trace(struct task_struct *task, unsigned long * stack) | 231 | void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long * stack) |
202 | { | 232 | { |
203 | show_trace_log_lvl(task, stack, ""); | 233 | show_trace_log_lvl(task, regs, stack, ""); |
204 | } | 234 | } |
205 | 235 | ||
206 | static void show_stack_log_lvl(struct task_struct *task, unsigned long *esp, | 236 | static void show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, |
207 | char *log_lvl) | 237 | unsigned long *esp, char *log_lvl) |
208 | { | 238 | { |
209 | unsigned long *stack; | 239 | unsigned long *stack; |
210 | int i; | 240 | int i; |
@@ -225,13 +255,13 @@ static void show_stack_log_lvl(struct task_struct *task, unsigned long *esp, | |||
225 | printk("%08lx ", *stack++); | 255 | printk("%08lx ", *stack++); |
226 | } | 256 | } |
227 | printk("\n%sCall Trace:\n", log_lvl); | 257 | printk("\n%sCall Trace:\n", log_lvl); |
228 | show_trace_log_lvl(task, esp, log_lvl); | 258 | show_trace_log_lvl(task, regs, esp, log_lvl); |
229 | } | 259 | } |
230 | 260 | ||
231 | void show_stack(struct task_struct *task, unsigned long *esp) | 261 | void show_stack(struct task_struct *task, unsigned long *esp) |
232 | { | 262 | { |
233 | printk(" "); | 263 | printk(" "); |
234 | show_stack_log_lvl(task, esp, ""); | 264 | show_stack_log_lvl(task, NULL, esp, ""); |
235 | } | 265 | } |
236 | 266 | ||
237 | /* | 267 | /* |
@@ -241,7 +271,7 @@ void dump_stack(void) | |||
241 | { | 271 | { |
242 | unsigned long stack; | 272 | unsigned long stack; |
243 | 273 | ||
244 | show_trace(current, &stack); | 274 | show_trace(current, NULL, &stack); |
245 | } | 275 | } |
246 | 276 | ||
247 | EXPORT_SYMBOL(dump_stack); | 277 | EXPORT_SYMBOL(dump_stack); |
@@ -285,7 +315,7 @@ void show_registers(struct pt_regs *regs) | |||
285 | u8 __user *eip; | 315 | u8 __user *eip; |
286 | 316 | ||
287 | printk("\n" KERN_EMERG "Stack: "); | 317 | printk("\n" KERN_EMERG "Stack: "); |
288 | show_stack_log_lvl(NULL, (unsigned long *)esp, KERN_EMERG); | 318 | show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG); |
289 | 319 | ||
290 | printk(KERN_EMERG "Code: "); | 320 | printk(KERN_EMERG "Code: "); |
291 | 321 | ||