From 2cc00dd9d550b5c97a9cb65ef9621eea3adbc4db Mon Sep 17 00:00:00 2001 From: xiaobing tu Date: Fri, 13 Apr 2012 10:37:00 +0800 Subject: [PATCH] Add user stack into /proc/xxx/stack. 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 Change-Id: I009c46282a7abc7fdf3f7cd34d4849095f79d50d Reviewed-on: http://android.intel.com:8080/43335 Reviewed-by: Tu, Xiaobing Reviewed-by: Zhang, Yanmin Reviewed-by: Gross, Mark Reviewed-by: Kuppuswamy, Sathyanarayanan Tested-by: Ng, Cheon-woei Reviewed-by: buildbot Tested-by: buildbot --- arch/x86/kernel/stacktrace.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 19 ++++++++++++++- include/linux/stacktrace.h | 5 +++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index 55d9bc0..1831558 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c @@ -7,6 +7,7 @@ #include #include #include +#include #include 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); + diff --git a/fs/proc/base.c b/fs/proc/base.c index efb3048..bf92e3f 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -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; diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index 25310f1..05fe571 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -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 -- 2.7.4