Add user stack into /proc/xxx/stack.
authorxiaobing tu <xiaobing.tu@intel.com>
Fri, 13 Apr 2012 02:37:00 +0000 (10:37 +0800)
committerbuildbot <buildbot@intel.com>
Thu, 26 Apr 2012 23:48:41 +0000 (16:48 -0700)
BZ 32868

We often need user space stack call-chain information when debugging hard issues.
Here is a prototype and it could work well on X86 architecture.
The idea is to output user space stack call-chain from /proc/xxx/stack, currently,
/proc/xxx/stack only output kernel stack call chain. We extend it to output user
space call chain With hex format.In user space, we implement a backtrace tool, it
read kernel space and user space stack call chain from /proc/xxx/stack, and converts
 the hex format into symbols., we tested it on i386/i686 and x86-64. It works well.

kernel patch for this tool
Signed-off-by: xiaobing tu <xiaobing.tu@intel.com>
Change-Id: I009c46282a7abc7fdf3f7cd34d4849095f79d50d
Reviewed-on: http://android.intel.com:8080/43335
Reviewed-by: Tu, Xiaobing <xiaobing.tu@intel.com>
Reviewed-by: Zhang, Yanmin <yanmin.zhang@intel.com>
Reviewed-by: Gross, Mark <mark.gross@intel.com>
Reviewed-by: Kuppuswamy, Sathyanarayanan <sathyanarayanan.kuppuswamy@intel.com>
Tested-by: Ng, Cheon-woei <cheon-woei.ng@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
arch/x86/kernel/stacktrace.c
fs/proc/base.c
include/linux/stacktrace.h

index 55d9bc0..1831558 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/stacktrace.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
+#include <linux/mm.h>
 #include <asm/stacktrace.h>
 
 static int save_stack_stack(void *data, char *name)
@@ -144,3 +145,57 @@ void save_stack_trace_user(struct stack_trace *trace)
                trace->entries[trace->nr_entries++] = ULONG_MAX;
 }
 
+static inline void __save_stack_trace_user_task(struct task_struct *task,
+               struct stack_trace *trace)
+{
+       const struct pt_regs *regs = task_pt_regs(task);
+       const void __user *fp;
+       unsigned long addr;
+#ifdef CONFIG_SMP
+       if (task != current && task->state == TASK_RUNNING && task->on_cpu) {
+               /* To trap into kernel at least once */
+               smp_send_reschedule(task_cpu(task));
+       }
+#endif
+       fp = (const void __user *)regs->bp;
+       if (trace->nr_entries < trace->max_entries)
+               trace->entries[trace->nr_entries++] = regs->ip;
+
+       while (trace->nr_entries < trace->max_entries) {
+               struct stack_frame_user frame;
+
+               frame.next_fp = NULL;
+               frame.ret_addr = 0;
+
+               addr = (unsigned long)fp;
+               if (!access_process_vm(task, addr, (void *)&frame,
+                               sizeof(frame), 0))
+                       break;
+               if ((unsigned long)fp < regs->sp)
+                       break;
+               if (frame.ret_addr) {
+                       trace->entries[trace->nr_entries++] =
+                               frame.ret_addr;
+               }
+               if (fp == frame.next_fp)
+                       break;
+               fp = frame.next_fp;
+       }
+}
+
+void save_stack_trace_user_task(struct task_struct *task,
+               struct stack_trace *trace)
+{
+       if (task == current || !task) {
+               save_stack_trace_user(trace);
+               return;
+       }
+
+       if (task->mm)
+               __save_stack_trace_user_task(task, trace);
+
+       if (trace->nr_entries < trace->max_entries)
+               trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_user_task);
+
index efb3048..bf92e3f 100644 (file)
@@ -403,8 +403,25 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
                        seq_printf(m, "[<%pK>] %pS\n",
                                   (void *)entries[i], (void *)entries[i]);
                }
-               unlock_trace(task);
+       } else
+               goto out;
+
+       trace.nr_entries        = 0;
+       trace.max_entries       = MAX_STACK_TRACE_DEPTH;
+       trace.entries           = entries;
+       trace.skip              = 0;
+
+       seq_printf(m, "userspace\n");
+
+       save_stack_trace_user_task(task, &trace);
+
+       for (i = 0; i < trace.nr_entries; i++) {
+               if (entries[i] != ULONG_MAX)
+                       seq_printf(m, "%p\n", (void *)entries[i]);
        }
+       unlock_trace(task);
+
+out:
        kfree(entries);
 
        return err;
index 25310f1..05fe571 100644 (file)
@@ -23,8 +23,11 @@ extern void print_stack_trace(struct stack_trace *trace, int spaces);
 
 #ifdef CONFIG_USER_STACKTRACE_SUPPORT
 extern void save_stack_trace_user(struct stack_trace *trace);
+extern void save_stack_trace_user_task(struct task_struct *task,
+               struct stack_trace *trace);
 #else
-# define save_stack_trace_user(trace)              do { } while (0)
+# define save_stack_trace_user(trace)                  do { } while (0)
+# define save_stack_trace_user_task(task, trace)       do { } while (0)
 #endif
 
 #else