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 | |
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>
-rw-r--r-- | include/linux/ftrace_event.h | 4 | ||||
-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 |
4 files changed, 46 insertions, 7 deletions
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 59d3ef100eb9..b1e69eefc203 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
@@ -129,6 +129,10 @@ void trace_current_buffer_unlock_commit(struct ring_buffer *buffer, | |||
129 | void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer, | 129 | void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer, |
130 | struct ring_buffer_event *event, | 130 | struct ring_buffer_event *event, |
131 | unsigned long flags, int pc); | 131 | unsigned long flags, int pc); |
132 | void trace_nowake_buffer_unlock_commit_regs(struct ring_buffer *buffer, | ||
133 | struct ring_buffer_event *event, | ||
134 | unsigned long flags, int pc, | ||
135 | struct pt_regs *regs); | ||
132 | void trace_current_buffer_discard_commit(struct ring_buffer *buffer, | 136 | void trace_current_buffer_discard_commit(struct ring_buffer *buffer, |
133 | struct ring_buffer_event *event); | 137 | struct ring_buffer_event *event); |
134 | 138 | ||
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 */ |