summary refs log tree commit diff
path: root/arch/microblaze/kernel/traps.c
diff options
context:
space:
mode:
authorSteven J. Magnani <steve@digidescorp.com>2010-04-27 12:37:54 -0500
committerMichal Simek <monstr@monstr.eu>2010-08-04 10:22:35 +0200
commitce3266c047389443d5f433d605c769e878cbe46e (patch)
treee638a255d5d0f1b000a81b512dc605b92d0b8701 /arch/microblaze/kernel/traps.c
parentba9c4f88d747836bf35c3eee36aa18d2e164f493 (diff)
downloadlinux-ce3266c047389443d5f433d605c769e878cbe46e.tar.gz
microblaze: Add stack unwinder
Implement intelligent backtracing by searching for stack frame creation,
and emitting only return addresses. Use print_hex_dump() to display the
entire binary kernel stack.

Limitation: MMU kernels are not currently able to trace beyond a system trap
(interrupt, syscall, etc.). It is the intent of this patch to provide
infrastructure that can be extended to add this capability later.

Changes from V1:
* Removed checks in find_frame_creation() that prevented location of the frame
  creation instruction in heavily optimized code
* Various formatting/commenting/file location tweaks per review comments
* Dropped Kconfig option to enable STACKTRACE as something logically separate

Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Diffstat (limited to 'arch/microblaze/kernel/traps.c')
-rw-r--r--arch/microblaze/kernel/traps.c91
1 files changed, 40 insertions, 51 deletions
diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c
index 75e49202a5ed..ba034d421ec2 100644
--- a/arch/microblaze/kernel/traps.c
+++ b/arch/microblaze/kernel/traps.c
@@ -16,13 +16,14 @@
 
 #include <asm/exceptions.h>
 #include <asm/system.h>
+#include <asm/unwind.h>
 
 void trap_init(void)
 {
 	__enable_hw_exceptions();
 }
 
-static unsigned long kstack_depth_to_print = 24;
+static unsigned long kstack_depth_to_print;	/* 0 == entire stack */
 
 static int __init kstack_setup(char *s)
 {
@@ -30,31 +31,47 @@ static int __init kstack_setup(char *s)
 }
 __setup("kstack=", kstack_setup);
 
-void show_trace(struct task_struct *task, unsigned long *stack)
+void show_stack(struct task_struct *task, unsigned long *sp)
 {
-	unsigned long addr;
-
-	if (!stack)
-		stack = (unsigned long *)&stack;
+	unsigned long words_to_show;
+	u32 fp = (u32) sp;
+
+	if (fp == 0) {
+		if (task) {
+			fp = ((struct thread_info *)
+				(task->stack))->cpu_context.r1;
+		} else {
+			/* Pick up caller of dump_stack() */
+			fp = (u32)&sp - 8;
+		}
+	}
 
-	printk(KERN_NOTICE "Call Trace: ");
-#ifdef CONFIG_KALLSYMS
-	printk(KERN_NOTICE "\n");
-#endif
-	while (!kstack_end(stack)) {
-		addr = *stack++;
-		/*
-		 * If the address is either in the text segment of the
-		 * kernel, or in the region which contains vmalloc'ed
-		 * memory, it *may* be the address of a calling
-		 * routine; if so, print it so that someone tracing
-		 * down the cause of the crash will be able to figure
-		 * out the call path that was taken.
-		 */
-		if (kernel_text_address(addr))
-			print_ip_sym(addr);
+	words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE - 1))) >> 2;
+	if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print))
+		words_to_show = kstack_depth_to_print;
+
+	pr_info("Kernel Stack:\n");
+
+	/*
+	 * Make the first line an 'odd' size if necessary to get
+	 * remaining lines to start at an address multiple of 0x10
+	 */
+	if (fp & 0xF) {
+		unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2;
+		if (line1_words < words_to_show) {
+			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32,
+				       4, (void *)fp, line1_words << 2, 0);
+			fp += line1_words << 2;
+			words_to_show -= line1_words;
+		}
 	}
-	printk(KERN_NOTICE "\n");
+	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp,
+		       words_to_show << 2, 0);
+	printk(KERN_INFO "\n\n");
+
+	pr_info("Call Trace:\n");
+	microblaze_unwind(task, NULL);
+	pr_info("\n");
 
 	if (!task)
 		task = current;
@@ -62,34 +79,6 @@ void show_trace(struct task_struct *task, unsigned long *stack)
 	debug_show_held_locks(task);
 }
 
-void show_stack(struct task_struct *task, unsigned long *sp)
-{
-	unsigned long *stack;
-	int i;
-
-	if (sp == NULL) {
-		if (task)
-			sp = (unsigned long *) ((struct thread_info *)
-						(task->stack))->cpu_context.r1;
-		else
-			sp = (unsigned long *)&sp;
-	}
-
-	stack = sp;
-
-	printk(KERN_INFO "\nStack:\n  ");
-
-	for (i = 0; i < kstack_depth_to_print; i++) {
-		if (kstack_end(sp))
-			break;
-		if (i && ((i % 8) == 0))
-			printk("\n  ");
-		printk("%08lx ", *sp++);
-	}
-	printk("\n");
-	show_trace(task, stack);
-}
-
 void dump_stack(void)
 {
 	show_stack(NULL, NULL);