[PATCH] i386/x86-64: fall back to old-style call trace if no unwinding
authorJan Beulich <jbeulich@novell.com>
Mon, 26 Jun 2006 11:57:47 +0000 (13:57 +0200)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 26 Jun 2006 17:48:18 +0000 (10:48 -0700)
If no unwinding is possible at all for a certain exception instance,
fall back to the old style call trace instead of not showing any trace
at all.

Also, allow setting the stack trace mode at the command line.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/kernel/traps.c
arch/x86_64/kernel/traps.c
include/asm-i386/unwind.h
include/asm-x86_64/unwind.h
include/linux/unwind.h
kernel/unwind.c

index 2865846678652d2eb0fc7a6d229f7b4c99e13c36..78464097470a282a8064e8b873b1edcdfa5a1da0 100644 (file)
@@ -93,6 +93,7 @@ asmlinkage void spurious_interrupt_bug(void);
 asmlinkage void machine_check(void);
 
 static int kstack_depth_to_print = 24;
+static int call_trace = 1;
 ATOMIC_NOTIFIER_HEAD(i386die_chain);
 
 int register_die_notifier(struct notifier_block *nb)
@@ -171,40 +172,47 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo,
        return ebp;
 }
 
-static asmlinkage void show_trace_unwind(struct unwind_frame_info *info, void *log_lvl)
+static asmlinkage int show_trace_unwind(struct unwind_frame_info *info, void *log_lvl)
 {
+       int n = 0;
        int printed = 0; /* nr of entries already printed on current line */
 
        while (unwind(info) == 0 && UNW_PC(info)) {
+               ++n;
                printed = print_addr_and_symbol(UNW_PC(info), log_lvl, printed);
                if (arch_unw_user_mode(info))
                        break;
        }
        if (printed)
                printk("\n");
+       return n;
 }
 
 static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
                               unsigned long *stack, char *log_lvl)
 {
        unsigned long ebp;
-       struct unwind_frame_info info;
 
        if (!task)
                task = current;
 
-       if (regs) {
-               if (unwind_init_frame_info(&info, task, regs) == 0) {
-                       show_trace_unwind(&info, log_lvl);
-                       return;
+       if (call_trace >= 0) {
+               int unw_ret = 0;
+               struct unwind_frame_info info;
+
+               if (regs) {
+                       if (unwind_init_frame_info(&info, task, regs) == 0)
+                               unw_ret = show_trace_unwind(&info, log_lvl);
+               } else if (task == current)
+                       unw_ret = unwind_init_running(&info, show_trace_unwind, log_lvl);
+               else {
+                       if (unwind_init_blocked(&info, task) == 0)
+                               unw_ret = show_trace_unwind(&info, log_lvl);
                }
-       } else if (task == current) {
-               if (unwind_init_running(&info, show_trace_unwind, log_lvl) == 0)
-                       return;
-       } else {
-               if (unwind_init_blocked(&info, task) == 0) {
-                       show_trace_unwind(&info, log_lvl);
-                       return;
+               if (unw_ret > 0) {
+                       if (call_trace > 0)
+                               return;
+                       printk("%sLegacy call trace:\n", log_lvl);
                }
        }
 
@@ -1245,3 +1253,15 @@ static int __init kstack_setup(char *s)
        return 1;
 }
 __setup("kstack=", kstack_setup);
+
+static int __init call_trace_setup(char *s)
+{
+       if (strcmp(s, "old") == 0)
+               call_trace = -1;
+       else if (strcmp(s, "both") == 0)
+               call_trace = 0;
+       else if (strcmp(s, "new") == 0)
+               call_trace = 1;
+       return 1;
+}
+__setup("call_trace=", call_trace_setup);
index eb1534ff1f5f0cbe527f1689c4a76ed4a657a0b9..bd0891f4c2c7d2a509f14c29e5655597c822d523 100644 (file)
@@ -107,6 +107,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
 }
 
 static int kstack_depth_to_print = 10;
+static int call_trace = 1;
 
 #ifdef CONFIG_KALLSYMS
 #include <linux/kallsyms.h> 
@@ -190,11 +191,12 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
        return NULL;
 }
 
-static void show_trace_unwind(struct unwind_frame_info *info, void *context)
+static int show_trace_unwind(struct unwind_frame_info *info, void *context)
 {
-       int i = 11;
+       int i = 11, n = 0;
 
        while (unwind(info) == 0 && UNW_PC(info)) {
+               ++n;
                if (i > 50) {
                        printk("\n       ");
                        i = 7;
@@ -205,6 +207,7 @@ static void show_trace_unwind(struct unwind_frame_info *info, void *context)
                        break;
        }
        printk("\n");
+       return n;
 }
 
 /*
@@ -218,27 +221,32 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s
 {
        const unsigned cpu = safe_smp_processor_id();
        unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
-       int i;
+       int i = 11;
        unsigned used = 0;
-       struct unwind_frame_info info;
 
        printk("\nCall Trace:");
 
        if (!tsk)
                tsk = current;
 
-       if (regs) {
-               if (unwind_init_frame_info(&info, tsk, regs) == 0) {
-                       show_trace_unwind(&info, NULL);
-                       return;
+       if (call_trace >= 0) {
+               int unw_ret = 0;
+               struct unwind_frame_info info;
+
+               if (regs) {
+                       if (unwind_init_frame_info(&info, tsk, regs) == 0)
+                               unw_ret = show_trace_unwind(&info, NULL);
+               } else if (tsk == current)
+                       unw_ret = unwind_init_running(&info, show_trace_unwind, NULL);
+               else {
+                       if (unwind_init_blocked(&info, tsk) == 0)
+                               unw_ret = show_trace_unwind(&info, NULL);
                }
-       } else if (tsk == current) {
-               if (unwind_init_running(&info, show_trace_unwind, NULL) == 0)
-                       return;
-       } else {
-               if (unwind_init_blocked(&info, tsk) == 0) {
-                       show_trace_unwind(&info, NULL);
-                       return;
+               if (unw_ret > 0) {
+                       if (call_trace > 0)
+                               return;
+                       printk("Legacy call trace:");
+                       i = 18;
                }
        }
 
@@ -264,7 +272,7 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s
                } \
        } while (0)
 
-       for(i = 11; ; ) {
+       for(; ; ) {
                const char *id;
                unsigned long *estack_end;
                estack_end = in_exception_stack(cpu, (unsigned long)stack,
@@ -1052,3 +1060,14 @@ static int __init kstack_setup(char *s)
 }
 __setup("kstack=", kstack_setup);
 
+static int __init call_trace_setup(char *s)
+{
+       if (strcmp(s, "old") == 0)
+               call_trace = -1;
+       else if (strcmp(s, "both") == 0)
+               call_trace = 0;
+       else if (strcmp(s, "new") == 0)
+               call_trace = 1;
+       return 1;
+}
+__setup("call_trace=", call_trace_setup);
index 1c076897ac21f10ca0e1255c07bbbf49c3a970e6..d480f2e38215d69df898f89e11519f3c00b9dc85 100644 (file)
@@ -66,10 +66,10 @@ static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
        info->regs.xes = __USER_DS;
 }
 
-extern asmlinkage void arch_unwind_init_running(struct unwind_frame_info *,
-                                                asmlinkage void (*callback)(struct unwind_frame_info *,
-                                                                            void *arg),
-                                                void *arg);
+extern asmlinkage int arch_unwind_init_running(struct unwind_frame_info *,
+                                               asmlinkage int (*callback)(struct unwind_frame_info *,
+                                                                          void *arg),
+                                               void *arg);
 
 static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
 {
index 4f61de246179b5e908dee14be3e499dd378dcc25..f3e7124effe3102a69e7fb0b5e81ae78b0092d76 100644 (file)
@@ -75,10 +75,10 @@ static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
        info->regs.ss = __KERNEL_DS;
 }
 
-extern void arch_unwind_init_running(struct unwind_frame_info *,
-                                     void (*callback)(struct unwind_frame_info *,
-                                                      void *arg),
-                                     void *arg);
+extern int arch_unwind_init_running(struct unwind_frame_info *,
+                                    int (*callback)(struct unwind_frame_info *,
+                                                    void *arg),
+                                    void *arg);
 
 static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
 {
index 0295aa789ab43bc7b064936a539dbb662c13525e..13c7b2cd87ce1db338346ac8ce1ce53f3341cff7 100644 (file)
@@ -49,8 +49,8 @@ extern int unwind_init_blocked(struct unwind_frame_info *,
  * Prepare to unwind the currently running thread.
  */
 extern int unwind_init_running(struct unwind_frame_info *,
-                               asmlinkage void (*callback)(struct unwind_frame_info *,
-                                                           void *arg),
+                               asmlinkage int (*callback)(struct unwind_frame_info *,
+                                                          void *arg),
                                void *arg);
 
 /*
@@ -97,8 +97,8 @@ static inline int unwind_init_blocked(struct unwind_frame_info *info,
 }
 
 static inline int unwind_init_running(struct unwind_frame_info *info,
-                                      asmlinkage void (*cb)(struct unwind_frame_info *,
-                                                            void *arg),
+                                      asmlinkage int (*cb)(struct unwind_frame_info *,
+                                                           void *arg),
                                       void *arg)
 {
        return -ENOSYS;
index d36bcd3ad3b5d5877f45713ab5b4710432ffc337..0421035272d99238c890c20945dcf6cb4b1a16cf 100644 (file)
@@ -885,14 +885,13 @@ EXPORT_SYMBOL(unwind_init_blocked);
  * Prepare to unwind the currently running thread.
  */
 int unwind_init_running(struct unwind_frame_info *info,
-                        asmlinkage void (*callback)(struct unwind_frame_info *,
-                                                    void *arg),
+                        asmlinkage int (*callback)(struct unwind_frame_info *,
+                                                   void *arg),
                         void *arg)
 {
        info->task = current;
-       arch_unwind_init_running(info, callback, arg);
 
-       return 0;
+       return arch_unwind_init_running(info, callback, arg);
 }
 EXPORT_SYMBOL(unwind_init_running);