From: Adam Litke <agl@us.ibm.com>

(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).

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
 [<c01079b2>] __down+0x76/0xe0
              00000400 00000000 0804c038 f29a2160 00000001 f29a2160
              c0118c8c f296ff6c f296ff6c f296ff2c
 [<c0107b7f>] __down_failed+0xb/0x14
              f296ff60 f296ff68 f296ff6c f296ff74
 [<c017887e>] .text.lock.proc_misc+0xf/0x21
              00000000 f2a851e0 f2a85200 000011b4 00000000 40869be4
              15b4a440 40869be4 15b4a440 40869be4 00000004 ffffffff
              00000001 00000001 f296ff10 f296ff10 f296ff98
 [<c014a62e>] vfs_read+0x9e/0xd0
              f2a851e0 0804c038 00000400 f2a85200 f2a851e0 fffffff7
              0804c038 f296ffbc
 [<c014a810>] sys_read+0x30/0x50
              f2a851e0 0804c038 00000400 f2a85200 00000003 00000400
              00000000 f296e000
 [<c0108aff>] 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 |   68 ++++++++++++++++++++++++++++++---------
 1 files changed, 53 insertions(+), 15 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	Thu Apr 29 16:35:49 2004
+++ 25-akpm/arch/i386/kernel/traps.c	Thu Apr 29 16:35:49 2004
@@ -94,27 +94,65 @@ asmlinkage void machine_check(void);
 
 static int kstack_depth_to_print = 24;
 
-void show_trace(struct task_struct *task, unsigned long * stack)
+#define valid_stack_ptr(task, p) \
+	((struct thread_info*)p > task->thread_info) && \
+	 !kstack_end((unsigned long*)p)
+
+#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, 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 +181,7 @@ void show_stack(struct task_struct *task
 			printk("\n       ");
 		printk("%08lx ", *stack++);
 	}
-	printk("\n");
+	printk("\nCall Trace:\n");
 	show_trace(task, esp);
 }
 

_