diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2011-06-08 03:09:34 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2011-06-14 22:48:53 -0400 |
| commit | 1fd8df2c3970c9e7e4e262354154ee39e58bdd7c (patch) | |
| tree | 640fbc584af8870917ea6d1fab4da49d299038ad /kernel | |
| parent | c624d33f61cd05241e85b906311f0b712fdb0f32 (diff) | |
tracing/kprobes: Fix kprobe-tracer to support stack trace
Fix to support kernel stack trace correctly on kprobe-tracer.
Since the execution path of kprobe-based dynamic events is different
from other tracepoint-based events, normal ftrace_trace_stack() doesn't
work correctly. To fix that, this introduces ftrace_trace_stack_regs()
which traces stack via pt_regs instead of current stack register.
e.g.
# echo p schedule+4 > /sys/kernel/debug/tracing/kprobe_events
# echo 1 > /sys/kernel/debug/tracing/options/stacktrace
# echo 1 > /sys/kernel/debug/tracing/events/kprobes/enable
# head -n 20 /sys/kernel/debug/tracing/trace
bash-2968 [000] 10297.050245: p_schedule_4: (schedule+0x4/0x4ca)
bash-2968 [000] 10297.050247: <stack trace>
=> schedule_timeout
=> n_tty_read
=> tty_read
=> vfs_read
=> sys_read
=> system_call_fastpath
kworker/0:1-2940 [000] 10297.050265: p_schedule_4: (schedule+0x4/0x4ca)
kworker/0:1-2940 [000] 10297.050266: <stack trace>
=> worker_thread
=> kthread
=> kernel_thread_helper
sshd-1132 [000] 10297.050365: p_schedule_4: (schedule+0x4/0x4ca)
sshd-1132 [000] 10297.050365: <stack trace>
=> sysret_careful
Note: Even with this fix, the first entry will be skipped
if the probe is put on the function entry area before
the frame pointer is set up (usually, that is 4 bytes
(push %bp; mov %sp %bp) on x86), because stack unwinder
depends on the frame pointer.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: yrl.pp-manager.tt@hitachi.com
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Namhyung Kim <namhyung@gmail.com>
Link: http://lkml.kernel.org/r/20110608070934.17777.17116.stgit@fedora15
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/trace/trace.c | 34 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 9 | ||||
| -rw-r--r-- | kernel/trace/trace_kprobe.c | 6 |
3 files changed, 42 insertions, 7 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c977018e87c2..d9c16123f6e2 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -1193,6 +1193,18 @@ void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer, | |||
| 1193 | } | 1193 | } |
| 1194 | EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit); | 1194 | EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit); |
| 1195 | 1195 | ||
| 1196 | void trace_nowake_buffer_unlock_commit_regs(struct ring_buffer *buffer, | ||
| 1197 | struct ring_buffer_event *event, | ||
| 1198 | unsigned long flags, int pc, | ||
| 1199 | struct pt_regs *regs) | ||
| 1200 | { | ||
| 1201 | ring_buffer_unlock_commit(buffer, event); | ||
| 1202 | |||
| 1203 | ftrace_trace_stack_regs(buffer, flags, 0, pc, regs); | ||
| 1204 | ftrace_trace_userstack(buffer, flags, pc); | ||
| 1205 | } | ||
| 1206 | EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit_regs); | ||
| 1207 | |||
| 1196 | void trace_current_buffer_discard_commit(struct ring_buffer *buffer, | 1208 | void trace_current_buffer_discard_commit(struct ring_buffer *buffer, |
| 1197 | struct ring_buffer_event *event) | 1209 | struct ring_buffer_event *event) |
| 1198 | { | 1210 | { |
| @@ -1238,7 +1250,7 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, | |||
| 1238 | #ifdef CONFIG_STACKTRACE | 1250 | #ifdef CONFIG_STACKTRACE |
| 1239 | static void __ftrace_trace_stack(struct ring_buffer *buffer, | 1251 | static void __ftrace_trace_stack(struct ring_buffer *buffer, |
| 1240 | unsigned long flags, | 1252 | unsigned long flags, |
| 1241 | int skip, int pc) | 1253 | int skip, int pc, struct pt_regs *regs) |
| 1242 | { | 1254 | { |
| 1243 | struct ftrace_event_call *call = &event_kernel_stack; | 1255 | struct ftrace_event_call *call = &event_kernel_stack; |
| 1244 | struct ring_buffer_event *event; | 1256 | struct ring_buffer_event *event; |
| @@ -1257,24 +1269,36 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer, | |||
| 1257 | trace.skip = skip; | 1269 | trace.skip = skip; |
| 1258 | trace.entries = entry->caller; | 1270 | trace.entries = entry->caller; |
| 1259 | 1271 | ||
| 1260 | save_stack_trace(&trace); | 1272 | if (regs) |
| 1273 | save_stack_trace_regs(regs, &trace); | ||
| 1274 | else | ||
| 1275 | save_stack_trace(&trace); | ||
| 1261 | if (!filter_check_discard(call, entry, buffer, event)) | 1276 | if (!filter_check_discard(call, entry, buffer, event)) |
| 1262 | ring_buffer_unlock_commit(buffer, event); | 1277 | ring_buffer_unlock_commit(buffer, event); |
| 1263 | } | 1278 | } |
| 1264 | 1279 | ||
| 1280 | void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags, | ||
| 1281 | int skip, int pc, struct pt_regs *regs) | ||
| 1282 | { | ||
| 1283 | if (!(trace_flags & TRACE_ITER_STACKTRACE)) | ||
| 1284 | return; | ||
| 1285 | |||
| 1286 | __ftrace_trace_stack(buffer, flags, skip, pc, regs); | ||
| 1287 | } | ||
| 1288 | |||
| 1265 | void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, | 1289 | void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, |
| 1266 | int skip, int pc) | 1290 | int skip, int pc) |
| 1267 | { | 1291 | { |
| 1268 | if (!(trace_flags & TRACE_ITER_STACKTRACE)) | 1292 | if (!(trace_flags & TRACE_ITER_STACKTRACE)) |
| 1269 | return; | 1293 | return; |
| 1270 | 1294 | ||
| 1271 | __ftrace_trace_stack(buffer, flags, skip, pc); | 1295 | __ftrace_trace_stack(buffer, flags, skip, pc, NULL); |
| 1272 | } | 1296 | } |
| 1273 | 1297 | ||
| 1274 | void __trace_stack(struct trace_array *tr, unsigned long flags, int skip, | 1298 | void __trace_stack(struct trace_array *tr, unsigned long flags, int skip, |
| 1275 | int pc) | 1299 | int pc) |
| 1276 | { | 1300 | { |
| 1277 | __ftrace_trace_stack(tr->buffer, flags, skip, pc); | 1301 | __ftrace_trace_stack(tr->buffer, flags, skip, pc, NULL); |
| 1278 | } | 1302 | } |
| 1279 | 1303 | ||
| 1280 | /** | 1304 | /** |
| @@ -1290,7 +1314,7 @@ void trace_dump_stack(void) | |||
| 1290 | local_save_flags(flags); | 1314 | local_save_flags(flags); |
| 1291 | 1315 | ||
| 1292 | /* skipping 3 traces, seems to get us at the caller of this function */ | 1316 | /* skipping 3 traces, seems to get us at the caller of this function */ |
| 1293 | __ftrace_trace_stack(global_trace.buffer, flags, 3, preempt_count()); | 1317 | __ftrace_trace_stack(global_trace.buffer, flags, 3, preempt_count(), NULL); |
| 1294 | } | 1318 | } |
| 1295 | 1319 | ||
| 1296 | static DEFINE_PER_CPU(int, user_stack_count); | 1320 | static DEFINE_PER_CPU(int, user_stack_count); |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 742f545ae185..a3e2db708072 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -389,6 +389,9 @@ void update_max_tr_single(struct trace_array *tr, | |||
| 389 | void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, | 389 | void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, |
| 390 | int skip, int pc); | 390 | int skip, int pc); |
| 391 | 391 | ||
| 392 | void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags, | ||
| 393 | int skip, int pc, struct pt_regs *regs); | ||
| 394 | |||
| 392 | void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, | 395 | void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, |
| 393 | int pc); | 396 | int pc); |
| 394 | 397 | ||
| @@ -400,6 +403,12 @@ static inline void ftrace_trace_stack(struct ring_buffer *buffer, | |||
| 400 | { | 403 | { |
| 401 | } | 404 | } |
| 402 | 405 | ||
| 406 | static inline void ftrace_trace_stack_regs(struct ring_buffer *buffer, | ||
| 407 | unsigned long flags, int skip, | ||
| 408 | int pc, struct pt_regs *regs) | ||
| 409 | { | ||
| 410 | } | ||
| 411 | |||
| 403 | static inline void ftrace_trace_userstack(struct ring_buffer *buffer, | 412 | static inline void ftrace_trace_userstack(struct ring_buffer *buffer, |
| 404 | unsigned long flags, int pc) | 413 | unsigned long flags, int pc) |
| 405 | { | 414 | { |
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index f925c45f0afa..7053ef3d73d2 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
| @@ -1397,7 +1397,8 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) | |||
| 1397 | store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); | 1397 | store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); |
| 1398 | 1398 | ||
| 1399 | if (!filter_current_check_discard(buffer, call, entry, event)) | 1399 | if (!filter_current_check_discard(buffer, call, entry, event)) |
| 1400 | trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); | 1400 | trace_nowake_buffer_unlock_commit_regs(buffer, event, |
| 1401 | irq_flags, pc, regs); | ||
| 1401 | } | 1402 | } |
| 1402 | 1403 | ||
| 1403 | /* Kretprobe handler */ | 1404 | /* Kretprobe handler */ |
| @@ -1429,7 +1430,8 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, | |||
| 1429 | store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); | 1430 | store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); |
| 1430 | 1431 | ||
| 1431 | if (!filter_current_check_discard(buffer, call, entry, event)) | 1432 | if (!filter_current_check_discard(buffer, call, entry, event)) |
| 1432 | trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); | 1433 | trace_nowake_buffer_unlock_commit_regs(buffer, event, |
| 1434 | irq_flags, pc, regs); | ||
| 1433 | } | 1435 | } |
| 1434 | 1436 | ||
| 1435 | /* Event entry printers */ | 1437 | /* Event entry printers */ |
