Index: linux-2.4.20/kernel/ksyms.c =================================================================== --- linux-2.4.20.orig/kernel/ksyms.c 2004-10-21 21:30:14.000000000 -0400 +++ linux-2.4.20/kernel/ksyms.c 2004-10-21 21:32:00.000000000 -0400 @@ -75,6 +75,7 @@ extern spinlock_t dma_spin_lock; extern int panic_timeout; +extern void show_task(task_t *); #ifdef CONFIG_MODVERSIONS const struct module_symbol __export_Using_Versions @@ -636,3 +637,4 @@ extern void check_tasklist_locked(void); EXPORT_SYMBOL_GPL(check_tasklist_locked); EXPORT_SYMBOL(dump_stack); +EXPORT_SYMBOL(show_task); Index: linux-2.4.20/arch/i386/kernel/traps.c =================================================================== --- linux-2.4.20.orig/arch/i386/kernel/traps.c 2004-10-21 21:30:15.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/traps.c 2004-10-25 14:34:41.000000000 -0400 @@ -137,29 +137,141 @@ #endif -void show_trace(unsigned long * stack) +void scan_stack (unsigned long *stack) { int i; unsigned long addr; - /* static to not take up stackspace; if we race here too bad */ - static char buffer[512]; + /* static to not take up stackspace */ + static char buffer[NR_CPUS][512], *bufp; - if (!stack) - stack = (unsigned long*)&stack; + bufp = buffer[smp_processor_id()]; - printk("Call Trace: "); + /* + * If we have frame pointers then use them to get + * a 100% exact backtrace, up until the entry frame: + */ i = 1; while (((long) stack & (THREAD_SIZE-1)) != 0) { addr = *stack++; if (kernel_text_address(addr)) { - lookup_symbol(addr, buffer, 512); - printk("[<%08lx>] %s (0x%x))\n", addr,buffer,stack-1); + lookup_symbol(addr, bufp, 512); + printk("[<%08lx>] %s (0x%p)\n", addr,bufp,stack-1); i++; } } +} + +#if CONFIG_FRAME_POINTER +void show_stack_frame_params (int param_count, unsigned long params[]) +{ + int i; + unsigned long *p, task_addr, stack_base; + + if (param_count <= 0) + return; + + task_addr = (unsigned long) current; + stack_base = task_addr + THREAD_SIZE - 1; + + printk(" ("); + + for (i = 0, p = params; + ((param_count - i) > 1) && (p >= task_addr) && (p <= stack_base); + i++, p++) { + printk("0x%x, ", *p); + + if ((i % 4) == 3) + printk("\n "); + } + + if ((p >= task_addr) && (p <= stack_base)) + printk("0x%x)\n", *p); +} + +/* Display a stack trace for the currently executing task. The 'dummy' + * parameter serves a purpose although its value is unused. We use the + * address of 'dummy' as a reference point for finding the saved %ebp register + * value on the stack. + */ +void frame_pointer_walk (void *dummy) +{ + int i; + unsigned long addr, task_addr, *frame_ptr, *next_frame_ptr, *eip_ptr, + eip, stack_base; + /* static to not take up stackspace */ + static char buffer[NR_CPUS][512], *bufp; + + bufp = buffer[smp_processor_id()]; + task_addr = (unsigned long) current; + stack_base = task_addr + THREAD_SIZE - 1; + frame_ptr = (unsigned long *) (&dummy - 2); + + for (; ; ) { + next_frame_ptr = (unsigned long *) (*frame_ptr); + addr = (unsigned long) next_frame_ptr; + + /* Stop when we reach a frame pointer that points to a + * location clearly outside our own kernel stack. + */ + if ((addr < task_addr) || (addr > stack_base)) + break; + + eip_ptr = frame_ptr + 1; + eip = *eip_ptr; + + if (kernel_text_address(eip)) { + lookup_symbol(eip, bufp, 512); + show_stack_frame_params(4, frame_ptr + 2); + printk("[<%08lx>] %s (0x%x)\n", eip, bufp, + eip_ptr); + } + + frame_ptr = next_frame_ptr; + } +} + +typedef void (*stack_trace_fn_t) (unsigned long *stack); + +void show_trace(unsigned long * stack) +{ + static const stack_trace_fn_t trace_fn_vector[] = + { scan_stack, frame_pointer_walk }; + unsigned long addr, task_addr, stack_base; + int task_is_current; + + if (!stack) + stack = (unsigned long*)&stack; + + printk("Call Trace:\n"); + addr = (unsigned long) stack; + task_addr = (unsigned long) current; + stack_base = task_addr + THREAD_SIZE - 1; + task_is_current = (addr >= task_addr) && (addr <= stack_base); + + /* We may use frame pointers to do a stack trace only if the current + * task is being traced. Tracing some other task in this manner + * would require a saved %ebp register value. Perhaps in the future + * I'll consider providing a means of obtaining this. + */ + trace_fn_vector[task_is_current](stack); + printk("\n"); } +#else /* CONFIG_FRAME_POINTER */ + +void show_trace(unsigned long * stack) +{ + if (!stack) + stack = (unsigned long*)&stack; + + printk("Call Trace:\n"); + scan_stack(stack); + printk("\n"); +} + +#endif /* CONFIG_FRAME_POINTER */ + void show_trace_task(struct task_struct *tsk) { unsigned long esp = tsk->thread.esp;