From: Adam Litke Teach the x86 stack tracing code to use frame pointers, if they are available. The show_trace() for the CONFIG_FRAME_POINTER case will now be called the same way as the existing code. This brings up a question though. It doesn't appear to me that anyone is actually calling show_trace_task() yet. Am I missing something or should we change all the callers of show_trace() to use show_trace_task()? I hacked read_profile() a bit so I could catch it inside down(). This looks good to me. In fact, even the inline asm functions were recognized. cat D 00000246 0 215 212 (NOTLB) Call Trace: f296feec 00000086 f296ff60 00000246 f296ff10 f681dd60 00000004 f2b59ce8 f2b59d6c f296fed0 c04b4c80 c04b6680 f296fee4 c01d7f2a bffffafc f296ff08 002e58e9 08848a79 00000018 c1a0cbe0 f29a2160 f29a2328 f296ff18 [] __down+0x76/0xe0 00000400 00000000 0804c038 f29a2160 00000001 f29a2160 c0118c8c f296ff6c f296ff6c f296ff2c [] __down_failed+0xb/0x14 f296ff60 f296ff68 f296ff6c f296ff74 [] .text.lock.proc_misc+0xf/0x21 00000000 f2a851e0 f2a85200 000011b4 00000000 40869be4 15b4a440 40869be4 15b4a440 40869be4 00000004 ffffffff 00000001 00000001 f296ff10 f296ff10 f296ff98 [] vfs_read+0x9e/0xd0 f2a851e0 0804c038 00000400 f2a85200 f2a851e0 fffffff7 0804c038 f296ffbc [] sys_read+0x30/0x50 f2a851e0 0804c038 00000400 f2a85200 00000003 00000400 00000000 f296e000 [] syscall_call+0x7/0xb The top stack frame is defined to be all values from esp to ebp. Subsequent frames are defined as ebp to *ebp (if ebp is a valid stack address). Therefore in the worst case, a stack frame may include data from two "logical functions" if a new frame was not defined (but we will not lose any data). GCC seems to be generating frame-pointer enabled code even for inline asm calls so we should only see this "worst-case" behavior around actual assembly functions. --- 25-akpm/arch/i386/kernel/traps.c | 110 +++++++++++++++++++++++++++++---------- 1 files changed, 84 insertions(+), 26 deletions(-) diff -puN arch/i386/kernel/traps.c~frame-pointer-based-stack-dumps arch/i386/kernel/traps.c --- 25/arch/i386/kernel/traps.c~frame-pointer-based-stack-dumps 2004-04-22 16:19:15.327239656 -0700 +++ 25-akpm/arch/i386/kernel/traps.c 2004-04-22 16:19:15.331239048 -0700 @@ -92,29 +92,84 @@ asmlinkage void alignment_check(void); asmlinkage void spurious_interrupt_bug(void); asmlinkage void machine_check(void); -static int kstack_depth_to_print = 24; +#define valid_stack_ptr(task, p) \ + ((struct thread_info*)p > task->thread_info) && \ + !kstack_end((unsigned long*)p) -void show_trace(struct task_struct *task, unsigned long * stack) +#ifdef CONFIG_FRAME_POINTER +void show_stack_frame(unsigned long start, unsigned long end) +{ + unsigned long i; + + printk(" "); + for (i = start; i < end; i += 4) { + if ((i - start) && ((i - start) % 24 == 0)) + printk("\n "); + printk("%08lx ", *(unsigned long *) i); + } + printk("\n"); +} + +void print_context_stack(struct task_struct *task, unsigned long *stack, + unsigned long ebp) { unsigned long addr; - if (!stack) - stack = (unsigned long*)&stack; + show_stack_frame((unsigned long) stack, ebp + 4); + while (valid_stack_ptr(task, ebp)) { + addr = *(unsigned long *)(ebp + 4); + printk(" [<%08lx>] ", addr); + print_symbol("%s", addr); + printk("\n"); - printk("Call Trace:"); -#ifdef CONFIG_KALLSYMS - printk("\n"); + /* Show the stack frame (excluding the frame pointer) */ + show_stack_frame(ebp + 8, (*(unsigned long *)ebp) + 4); + ebp = *(unsigned long *) ebp; + } +} +#else +int kstack_depth_to_print = 24; + +void print_context_stack(struct task_struct *task, unsigned long *stack, + unsigned long ebp) +{ + unsigned long addr; + + 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; @@ -125,9 +180,6 @@ void show_trace(struct task_struct *task void show_stack(struct task_struct *task, unsigned long *esp) { - unsigned long *stack; - int i; - if (esp == NULL) { if (task) esp = (unsigned long*)task->thread.esp; @@ -135,15 +187,21 @@ void show_stack(struct task_struct *task esp = (unsigned long *)&esp; } - stack = esp; - for(i = 0; i < kstack_depth_to_print; i++) { - if (kstack_end(stack)) - break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", *stack++); +#ifndef CONFIG_FRAME_POINTER + { + unsigned long *stack = esp; + int i; + + for (i = 0; i < kstack_depth_to_print; i++) { + if (kstack_end(stack)) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + printk("\n"); } - printk("\n"); +#endif show_trace(task, esp); } _