diff options
author | Andrew Morton <akpm@osdl.org> | 2004-05-14 20:12:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-05-14 20:12:23 -0700 |
commit | 066479e379f387e5b1da0f1149fe0b97bac58888 (patch) | |
tree | c562c2ae309e3e3e8bbb41c91dbecf198948926b | |
parent | fc4c3ad209fdbb5f1f76b7a396fefd1fac107564 (diff) | |
download | history-066479e379f387e5b1da0f1149fe0b97bac58888.tar.gz |
[PATCH] x86: stack dumps using frame pointers
From: Adam Litke <agl@us.ibm.com>
Teach the x86 stack tracing code to use frame pointers, if they are available.
It eliminates all the false-positives in the normal stack traces.
This is a big improvement, and -fomit-frame-pointer seems to make no
difference at all to generated code size. Maybe we should kill off
-fomit-frame-pointer.
-rw-r--r-- | arch/i386/kernel/traps.c | 73 |
1 files changed, 58 insertions, 15 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index c770d878cbcfaf..4a6226309b49e1 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -94,27 +94,70 @@ asmlinkage void machine_check(void); static int kstack_depth_to_print = 24; -void show_trace(struct task_struct *task, unsigned long * stack) +static int valid_stack_ptr(struct task_struct *task, void *p) +{ + if (p <= (void *)task->thread_info) + return 0; + if (kstack_end(p)) + return 0; + return 1; +} + +#ifdef CONFIG_FRAME_POINTER +void print_context_stack(struct task_struct *task, unsigned long *stack, + unsigned long ebp) { unsigned long addr; - if (!stack) - stack = (unsigned long*)&stack; + while (valid_stack_ptr(task, (void *)ebp)) { + addr = *(unsigned long *)(ebp + 4); + printk(" [<%08lx>] ", addr); + print_symbol("%s", addr); + printk("\n"); + ebp = *(unsigned long *)ebp; + } +} +#else +void print_context_stack(struct task_struct *task, unsigned long *stack, + unsigned long ebp) +{ + unsigned long addr; - printk("Call Trace:"); -#ifdef CONFIG_KALLSYMS - printk("\n"); + while (!kstack_end(stack)) { + addr = *stack++; + if (kernel_text_address(addr)) { + printk(" [<%08lx>] ", addr); + print_symbol("%s\n", addr); + } + } +} #endif + +void show_trace(struct task_struct *task, unsigned long * stack) +{ + unsigned long ebp; + + if (!task) + task = current; + + if (!valid_stack_ptr(task, stack)) { + printk("Stack pointer is garbage, not printing trace\n"); + return; + } + + if (task == current) { + /* Grab ebp right from our regs */ + asm ("movl %%ebp, %0" : "=r" (ebp) : ); + } else { + /* ebp is the last reg pushed by switch_to */ + ebp = *(unsigned long *) task->thread.esp; + } + while (1) { struct thread_info *context; - context = (struct thread_info*) ((unsigned long)stack & (~(THREAD_SIZE - 1))); - while (!kstack_end(stack)) { - addr = *stack++; - if (kernel_text_address(addr)) { - printk(" [<%08lx>] ", addr); - print_symbol("%s\n", addr); - } - } + context = (struct thread_info *) + ((unsigned long)stack & (~(THREAD_SIZE - 1))); + print_context_stack(task, stack, ebp); stack = (unsigned long*)context->previous_esp; if (!stack) break; @@ -143,7 +186,7 @@ void show_stack(struct task_struct *task, unsigned long *esp) printk("\n "); printk("%08lx ", *stack++); } - printk("\n"); + printk("\nCall Trace:\n"); show_trace(task, esp); } |