[PATCH] lockdep: stacktrace subsystem, i386 support
authorIngo Molnar <mingo@elte.hu>
Mon, 3 Jul 2006 07:24:39 +0000 (00:24 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 3 Jul 2006 22:27:02 +0000 (15:27 -0700)
Framework to generate and save stacktraces quickly, without printing anything
to the console.  i386 support.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/Kconfig
arch/i386/kernel/Makefile
arch/i386/kernel/stacktrace.c [new file with mode: 0644]

index 27d8dddbaa471658d838b539618f6b267e3ce0db..076a72c7a41f7125a2a04903d9b41f59672545d4 100644 (file)
@@ -18,6 +18,10 @@ config GENERIC_TIME
        bool
        default y
 
+config STACKTRACE_SUPPORT
+       bool
+       default y
+
 config SEMAPHORE_SLEEPERS
        bool
        default y
index cbc1184e947386fcf712a7846120879eda63e918..1b452a1665c4ffe92a05f3a5fa80704b4e1738ec 100644 (file)
@@ -9,6 +9,7 @@ obj-y   := process.o semaphore.o signal.o entry.o traps.o irq.o \
                pci-dma.o i386_ksyms.o i387.o bootflag.o \
                quirks.o i8237.o topology.o alternative.o i8253.o tsc.o
 
+obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-y                          += cpu/
 obj-y                          += acpi/
 obj-$(CONFIG_X86_BIOS_REBOOT)  += reboot.o
diff --git a/arch/i386/kernel/stacktrace.c b/arch/i386/kernel/stacktrace.c
new file mode 100644 (file)
index 0000000..e62a037
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * arch/i386/kernel/stacktrace.c
+ *
+ * Stack trace management functions
+ *
+ *  Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
+ */
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+
+static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
+{
+       return  p > (void *)tinfo &&
+               p < (void *)tinfo + THREAD_SIZE - 3;
+}
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer:
+ */
+static inline unsigned long
+save_context_stack(struct stack_trace *trace, unsigned int skip,
+                  struct thread_info *tinfo, unsigned long *stack,
+                  unsigned long ebp)
+{
+       unsigned long addr;
+
+#ifdef CONFIG_FRAME_POINTER
+       while (valid_stack_ptr(tinfo, (void *)ebp)) {
+               addr = *(unsigned long *)(ebp + 4);
+               if (!skip)
+                       trace->entries[trace->nr_entries++] = addr;
+               else
+                       skip--;
+               if (trace->nr_entries >= trace->max_entries)
+                       break;
+               /*
+                * break out of recursive entries (such as
+                * end_of_stack_stop_unwind_function):
+                */
+               if (ebp == *(unsigned long *)ebp)
+                       break;
+
+               ebp = *(unsigned long *)ebp;
+       }
+#else
+       while (valid_stack_ptr(tinfo, stack)) {
+               addr = *stack++;
+               if (__kernel_text_address(addr)) {
+                       if (!skip)
+                               trace->entries[trace->nr_entries++] = addr;
+                       else
+                               skip--;
+                       if (trace->nr_entries >= trace->max_entries)
+                               break;
+               }
+       }
+#endif
+
+       return ebp;
+}
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer.
+ * If all_contexts is set, all contexts (hardirq, softirq and process)
+ * are saved. If not set then only the current context is saved.
+ */
+void save_stack_trace(struct stack_trace *trace,
+                     struct task_struct *task, int all_contexts,
+                     unsigned int skip)
+{
+       unsigned long ebp;
+       unsigned long *stack = &ebp;
+
+       WARN_ON(trace->nr_entries || !trace->max_entries);
+
+       if (!task || 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 = (struct thread_info *)
+                               ((unsigned long)stack & (~(THREAD_SIZE - 1)));
+
+               ebp = save_context_stack(trace, skip, context, stack, ebp);
+               stack = (unsigned long *)context->previous_esp;
+               if (!all_contexts || !stack ||
+                               trace->nr_entries >= trace->max_entries)
+                       break;
+               trace->entries[trace->nr_entries++] = ULONG_MAX;
+               if (trace->nr_entries >= trace->max_entries)
+                       break;
+       }
+}
+