diff options
author | Török Edwin <edwintorok@gmail.com> | 2008-11-22 06:28:47 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-23 03:25:15 -0500 |
commit | 02b67518e2b1c490787dac7f35e1204e74fe21ba (patch) | |
tree | a3d92846e1a09a829f300ab15726ee9c288cb49e /arch/x86/kernel/stacktrace.c | |
parent | a0a70c735ef714fe1b6777b571630c3d50c7b008 (diff) |
tracing: add support for userspace stacktraces in tracing/iter_ctrl
Impact: add new (default-off) tracing visualization feature
Usage example:
mount -t debugfs nodev /sys/kernel/debug
cd /sys/kernel/debug/tracing
echo userstacktrace >iter_ctrl
echo sched_switch >current_tracer
echo 1 >tracing_enabled
.... run application ...
echo 0 >tracing_enabled
Then read one of 'trace','latency_trace','trace_pipe'.
To get the best output you can compile your userspace programs with
frame pointers (at least glibc + the app you are tracing).
Signed-off-by: Török Edwin <edwintorok@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/stacktrace.c')
-rw-r--r-- | arch/x86/kernel/stacktrace.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index a03e7f6d90c3..b15153060417 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/stacktrace.h> | 7 | #include <linux/stacktrace.h> |
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/uaccess.h> | ||
9 | #include <asm/stacktrace.h> | 10 | #include <asm/stacktrace.h> |
10 | 11 | ||
11 | static void save_stack_warning(void *data, char *msg) | 12 | static void save_stack_warning(void *data, char *msg) |
@@ -83,3 +84,59 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
83 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 84 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
84 | } | 85 | } |
85 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | 86 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |
87 | |||
88 | /* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */ | ||
89 | |||
90 | struct stack_frame { | ||
91 | const void __user *next_fp; | ||
92 | unsigned long return_address; | ||
93 | }; | ||
94 | |||
95 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | ||
96 | { | ||
97 | int ret; | ||
98 | |||
99 | if (!access_ok(VERIFY_READ, fp, sizeof(*frame))) | ||
100 | return 0; | ||
101 | |||
102 | ret = 1; | ||
103 | pagefault_disable(); | ||
104 | if (__copy_from_user_inatomic(frame, fp, sizeof(*frame))) | ||
105 | ret = 0; | ||
106 | pagefault_enable(); | ||
107 | |||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | void save_stack_trace_user(struct stack_trace *trace) | ||
112 | { | ||
113 | /* | ||
114 | * Trace user stack if we are not a kernel thread | ||
115 | */ | ||
116 | if (current->mm) { | ||
117 | const struct pt_regs *regs = task_pt_regs(current); | ||
118 | const void __user *fp = (const void __user *)regs->bp; | ||
119 | |||
120 | if (trace->nr_entries < trace->max_entries) | ||
121 | trace->entries[trace->nr_entries++] = regs->ip; | ||
122 | |||
123 | while (trace->nr_entries < trace->max_entries) { | ||
124 | struct stack_frame frame; | ||
125 | frame.next_fp = NULL; | ||
126 | frame.return_address = 0; | ||
127 | if (!copy_stack_frame(fp, &frame)) | ||
128 | break; | ||
129 | if ((unsigned long)fp < regs->sp) | ||
130 | break; | ||
131 | if (frame.return_address) | ||
132 | trace->entries[trace->nr_entries++] = | ||
133 | frame.return_address; | ||
134 | if (fp == frame.next_fp) | ||
135 | break; | ||
136 | fp = frame.next_fp; | ||
137 | } | ||
138 | } | ||
139 | if (trace->nr_entries < trace->max_entries) | ||
140 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
141 | } | ||
142 | |||