- Update to 3.3-rc3.
[linux-flexiantxendom0-3.2.10.git] / arch / x86 / kernel / dumpstack.c
index 4025fe4..c2265f7 100644 (file)
 #include <linux/sysfs.h>
 
 #include <asm/stacktrace.h>
+#include <linux/unwind.h>
 
 
 int panic_on_unrecovered_nmi;
 int panic_on_io_nmi;
 unsigned int code_bytes = 64;
 int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
+#ifdef CONFIG_STACK_UNWIND
+static int call_trace = 1;
+#else
+#define call_trace (-1)
+#endif
 static int die_counter;
 
 void printk_address(unsigned long address, int reliable)
@@ -62,6 +68,69 @@ print_ftrace_graph_addr(unsigned long addr, void *data,
 { }
 #endif
 
+int asmlinkage dump_trace_unwind(struct unwind_frame_info *info,
+                     const struct stacktrace_ops *ops, void *data)
+{
+       int n = 0;
+#ifdef CONFIG_STACK_UNWIND
+       unsigned long sp = UNW_SP(info);
+
+       if (arch_unw_user_mode(info))
+               return -1;
+       while (unwind(info) == 0 && UNW_PC(info)) {
+               n++;
+               ops->address(data, UNW_PC(info), 1);
+               if (arch_unw_user_mode(info))
+                       break;
+               if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1))
+                   && sp > UNW_SP(info))
+                       break;
+               sp = UNW_SP(info);
+       }
+#endif
+       return n;
+}
+
+int try_stack_unwind(struct task_struct *task, struct pt_regs *regs,
+                    unsigned long **stack, unsigned long *bp,
+                    const struct stacktrace_ops *ops, void *data)
+{
+#ifdef CONFIG_STACK_UNWIND
+       int unw_ret = 0;
+       struct unwind_frame_info info;
+       if (call_trace < 0)
+               return 0;
+
+       if (regs) {
+               if (unwind_init_frame_info(&info, task, regs) == 0)
+                       unw_ret = dump_trace_unwind(&info, ops, data);
+       } else if (task == current)
+               unw_ret = unwind_init_running(&info, dump_trace_unwind, ops, data);
+#ifdef CONFIG_SMP
+       else if (task->on_cpu)
+               /* nothing */;
+#endif
+       else if (unwind_init_blocked(&info, task) == 0)
+               unw_ret = dump_trace_unwind(&info, ops, data);
+       if (unw_ret > 0) {
+               if (call_trace == 1 && !arch_unw_user_mode(&info)) {
+                       ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n",
+                                           UNW_PC(&info));
+                       if (UNW_SP(&info) >= PAGE_OFFSET) {
+                               ops->warning(data, "Leftover inexact backtrace:\n");
+                               *stack = (void *)UNW_SP(&info);
+                               *bp = UNW_FP(&info);
+                               return 0;
+                       }
+               } else if (call_trace >= 1)
+                       return -1;
+               ops->warning(data, "Full inexact backtrace again:\n");
+       } else
+               ops->warning(data, "Inexact backtrace:\n");
+#endif
+       return 0;
+}
+
 /*
  * x86-64 can have up to three kernel stacks:
  * process stack
@@ -135,6 +204,20 @@ print_context_stack_bp(struct thread_info *tinfo,
 }
 EXPORT_SYMBOL_GPL(print_context_stack_bp);
 
+
+static void
+print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+       printk(data);
+       print_symbol(msg, symbol);
+       printk("\n");
+}
+
+static void print_trace_warning(void *data, char *msg)
+{
+       printk("%s%s\n", (char *)data, msg);
+}
+
 static int print_trace_stack(void *data, char *name)
 {
        printk("%s <%s> ", (char *)data, name);
@@ -152,6 +235,8 @@ static void print_trace_address(void *data, unsigned long addr, int reliable)
 }
 
 static const struct stacktrace_ops print_trace_ops = {
+       .warning                = print_trace_warning,
+       .warning_symbol         = print_trace_warning_symbol,
        .stack                  = print_trace_stack,
        .address                = print_trace_address,
        .walk_stack             = print_context_stack,
@@ -324,3 +409,21 @@ static int __init code_bytes_setup(char *s)
        return 1;
 }
 __setup("code_bytes=", code_bytes_setup);
+
+#ifdef CONFIG_STACK_UNWIND
+static int __init call_trace_setup(char *s)
+{
+       if (!s)
+               return -EINVAL;
+       if (strcmp(s, "old") == 0)
+               call_trace = -1;
+       else if (strcmp(s, "both") == 0)
+               call_trace = 0;
+       else if (strcmp(s, "newfallback") == 0)
+               call_trace = 1;
+       else if (strcmp(s, "new") == 0)
+               call_trace = 2;
+       return 0;
+}
+early_param("call_trace", call_trace_setup);
+#endif