diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2013-03-13 10:17:50 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-03-15 00:36:05 -0400 |
commit | dd42cd3ea96d687f15525c4f14fa582702db223f (patch) | |
tree | 135eb362cc41f54392d10af19fc4e10ef7060081 /kernel/trace/trace_functions.c | |
parent | c142be8ebe0b7bf73c8a0063925623f3e4b980c0 (diff) |
tracing: Add function probe to trigger stack traces
Add a function probe that will cause a stack trace to be traced in
the ring buffer when the given function(s) are called.
format is:
<function>:stacktrace[:<count>]
echo 'schedule:stacktrace' > /debug/tracing/set_ftrace_filter
cat /debug/tracing/trace_pipe
kworker/2:0-4329 [002] ...2 2933.558007: <stack trace>
=> kthread
=> ret_from_fork
<idle>-0 [000] .N.2 2933.558019: <stack trace>
=> rest_init
=> start_kernel
=> x86_64_start_reservations
=> x86_64_start_kernel
kworker/2:0-4329 [002] ...2 2933.558109: <stack trace>
=> kthread
=> ret_from_fork
[...]
This can be set to only trace a specific amount of times:
echo 'schedule:stacktrace:3' > /debug/tracing/set_ftrace_filter
cat /debug/tracing/trace_pipe
<...>-58 [003] ...2 841.801694: <stack trace>
=> kthread
=> ret_from_fork
<idle>-0 [001] .N.2 841.801697: <stack trace>
=> start_secondary
<...>-2059 [001] ...2 841.801736: <stack trace>
=> wait_for_common
=> wait_for_completion
=> flush_work
=> tty_flush_to_ldisc
=> input_available_p
=> n_tty_poll
=> tty_poll
=> do_select
=> core_sys_select
=> sys_select
=> system_call_fastpath
To remove these:
echo '!schedule:stacktrace' > /debug/tracing/set_ftrace_filter
echo '!schedule:stacktrace:0' > /debug/tracing/set_ftrace_filter
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/trace_functions.c')
-rw-r--r-- | kernel/trace/trace_functions.c | 150 |
1 files changed, 115 insertions, 35 deletions
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 043b2425ae73..c4d6d7191988 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
@@ -265,56 +265,103 @@ ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data) | |||
265 | tracing_off(); | 265 | tracing_off(); |
266 | } | 266 | } |
267 | 267 | ||
268 | /* | ||
269 | * Skip 4: | ||
270 | * ftrace_stacktrace() | ||
271 | * function_trace_probe_call() | ||
272 | * ftrace_ops_list_func() | ||
273 | * ftrace_call() | ||
274 | */ | ||
275 | #define STACK_SKIP 4 | ||
276 | |||
277 | static void | ||
278 | ftrace_stacktrace(unsigned long ip, unsigned long parent_ip, void **data) | ||
279 | { | ||
280 | trace_dump_stack(STACK_SKIP); | ||
281 | } | ||
282 | |||
283 | static void | ||
284 | ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, void **data) | ||
285 | { | ||
286 | if (!tracing_is_on()) | ||
287 | return; | ||
288 | |||
289 | if (update_count(data)) | ||
290 | trace_dump_stack(STACK_SKIP); | ||
291 | } | ||
292 | |||
268 | static int | 293 | static int |
269 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | 294 | ftrace_probe_print(const char *name, struct seq_file *m, |
270 | struct ftrace_probe_ops *ops, void *data); | 295 | unsigned long ip, void *data) |
296 | { | ||
297 | long count = (long)data; | ||
298 | |||
299 | seq_printf(m, "%ps:%s", (void *)ip, name); | ||
300 | |||
301 | if (count == -1) | ||
302 | seq_printf(m, ":unlimited\n"); | ||
303 | else | ||
304 | seq_printf(m, ":count=%ld\n", count); | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int | ||
310 | ftrace_traceon_print(struct seq_file *m, unsigned long ip, | ||
311 | struct ftrace_probe_ops *ops, void *data) | ||
312 | { | ||
313 | return ftrace_probe_print("traceon", m, ip, data); | ||
314 | } | ||
315 | |||
316 | static int | ||
317 | ftrace_traceoff_print(struct seq_file *m, unsigned long ip, | ||
318 | struct ftrace_probe_ops *ops, void *data) | ||
319 | { | ||
320 | return ftrace_probe_print("traceoff", m, ip, data); | ||
321 | } | ||
322 | |||
323 | static int | ||
324 | ftrace_stacktrace_print(struct seq_file *m, unsigned long ip, | ||
325 | struct ftrace_probe_ops *ops, void *data) | ||
326 | { | ||
327 | return ftrace_probe_print("stacktrace", m, ip, data); | ||
328 | } | ||
271 | 329 | ||
272 | static struct ftrace_probe_ops traceon_count_probe_ops = { | 330 | static struct ftrace_probe_ops traceon_count_probe_ops = { |
273 | .func = ftrace_traceon_count, | 331 | .func = ftrace_traceon_count, |
274 | .print = ftrace_trace_onoff_print, | 332 | .print = ftrace_traceon_print, |
275 | }; | 333 | }; |
276 | 334 | ||
277 | static struct ftrace_probe_ops traceoff_count_probe_ops = { | 335 | static struct ftrace_probe_ops traceoff_count_probe_ops = { |
278 | .func = ftrace_traceoff_count, | 336 | .func = ftrace_traceoff_count, |
279 | .print = ftrace_trace_onoff_print, | 337 | .print = ftrace_traceoff_print, |
338 | }; | ||
339 | |||
340 | static struct ftrace_probe_ops stacktrace_count_probe_ops = { | ||
341 | .func = ftrace_stacktrace_count, | ||
342 | .print = ftrace_stacktrace_print, | ||
280 | }; | 343 | }; |
281 | 344 | ||
282 | static struct ftrace_probe_ops traceon_probe_ops = { | 345 | static struct ftrace_probe_ops traceon_probe_ops = { |
283 | .func = ftrace_traceon, | 346 | .func = ftrace_traceon, |
284 | .print = ftrace_trace_onoff_print, | 347 | .print = ftrace_traceon_print, |
285 | }; | 348 | }; |
286 | 349 | ||
287 | static struct ftrace_probe_ops traceoff_probe_ops = { | 350 | static struct ftrace_probe_ops traceoff_probe_ops = { |
288 | .func = ftrace_traceoff, | 351 | .func = ftrace_traceoff, |
289 | .print = ftrace_trace_onoff_print, | 352 | .print = ftrace_traceoff_print, |
290 | }; | 353 | }; |
291 | 354 | ||
292 | static int | 355 | static struct ftrace_probe_ops stacktrace_probe_ops = { |
293 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | 356 | .func = ftrace_stacktrace, |
294 | struct ftrace_probe_ops *ops, void *data) | 357 | .print = ftrace_stacktrace_print, |
295 | { | 358 | }; |
296 | long count = (long)data; | ||
297 | |||
298 | seq_printf(m, "%ps:", (void *)ip); | ||
299 | |||
300 | if (ops == &traceon_probe_ops || ops == &traceon_count_probe_ops) | ||
301 | seq_printf(m, "traceon"); | ||
302 | else | ||
303 | seq_printf(m, "traceoff"); | ||
304 | |||
305 | if (count == -1) | ||
306 | seq_printf(m, ":unlimited\n"); | ||
307 | else | ||
308 | seq_printf(m, ":count=%ld\n", count); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | 359 | ||
313 | static int | 360 | static int |
314 | ftrace_trace_onoff_callback(struct ftrace_hash *hash, | 361 | ftrace_trace_probe_callback(struct ftrace_probe_ops *ops, |
315 | char *glob, char *cmd, char *param, int enable) | 362 | struct ftrace_hash *hash, char *glob, |
363 | char *cmd, char *param, int enable) | ||
316 | { | 364 | { |
317 | struct ftrace_probe_ops *ops; | ||
318 | void *count = (void *)-1; | 365 | void *count = (void *)-1; |
319 | char *number; | 366 | char *number; |
320 | int ret; | 367 | int ret; |
@@ -323,12 +370,6 @@ ftrace_trace_onoff_callback(struct ftrace_hash *hash, | |||
323 | if (!enable) | 370 | if (!enable) |
324 | return -EINVAL; | 371 | return -EINVAL; |
325 | 372 | ||
326 | /* we register both traceon and traceoff to this callback */ | ||
327 | if (strcmp(cmd, "traceon") == 0) | ||
328 | ops = param ? &traceon_count_probe_ops : &traceon_probe_ops; | ||
329 | else | ||
330 | ops = param ? &traceoff_count_probe_ops : &traceoff_probe_ops; | ||
331 | |||
332 | if (glob[0] == '!') { | 373 | if (glob[0] == '!') { |
333 | unregister_ftrace_function_probe_func(glob+1, ops); | 374 | unregister_ftrace_function_probe_func(glob+1, ops); |
334 | return 0; | 375 | return 0; |
@@ -356,6 +397,34 @@ ftrace_trace_onoff_callback(struct ftrace_hash *hash, | |||
356 | return ret < 0 ? ret : 0; | 397 | return ret < 0 ? ret : 0; |
357 | } | 398 | } |
358 | 399 | ||
400 | static int | ||
401 | ftrace_trace_onoff_callback(struct ftrace_hash *hash, | ||
402 | char *glob, char *cmd, char *param, int enable) | ||
403 | { | ||
404 | struct ftrace_probe_ops *ops; | ||
405 | |||
406 | /* we register both traceon and traceoff to this callback */ | ||
407 | if (strcmp(cmd, "traceon") == 0) | ||
408 | ops = param ? &traceon_count_probe_ops : &traceon_probe_ops; | ||
409 | else | ||
410 | ops = param ? &traceoff_count_probe_ops : &traceoff_probe_ops; | ||
411 | |||
412 | return ftrace_trace_probe_callback(ops, hash, glob, cmd, | ||
413 | param, enable); | ||
414 | } | ||
415 | |||
416 | static int | ||
417 | ftrace_stacktrace_callback(struct ftrace_hash *hash, | ||
418 | char *glob, char *cmd, char *param, int enable) | ||
419 | { | ||
420 | struct ftrace_probe_ops *ops; | ||
421 | |||
422 | ops = param ? &stacktrace_count_probe_ops : &stacktrace_probe_ops; | ||
423 | |||
424 | return ftrace_trace_probe_callback(ops, hash, glob, cmd, | ||
425 | param, enable); | ||
426 | } | ||
427 | |||
359 | static struct ftrace_func_command ftrace_traceon_cmd = { | 428 | static struct ftrace_func_command ftrace_traceon_cmd = { |
360 | .name = "traceon", | 429 | .name = "traceon", |
361 | .func = ftrace_trace_onoff_callback, | 430 | .func = ftrace_trace_onoff_callback, |
@@ -366,6 +435,11 @@ static struct ftrace_func_command ftrace_traceoff_cmd = { | |||
366 | .func = ftrace_trace_onoff_callback, | 435 | .func = ftrace_trace_onoff_callback, |
367 | }; | 436 | }; |
368 | 437 | ||
438 | static struct ftrace_func_command ftrace_stacktrace_cmd = { | ||
439 | .name = "stacktrace", | ||
440 | .func = ftrace_stacktrace_callback, | ||
441 | }; | ||
442 | |||
369 | static int __init init_func_cmd_traceon(void) | 443 | static int __init init_func_cmd_traceon(void) |
370 | { | 444 | { |
371 | int ret; | 445 | int ret; |
@@ -377,6 +451,12 @@ static int __init init_func_cmd_traceon(void) | |||
377 | ret = register_ftrace_command(&ftrace_traceon_cmd); | 451 | ret = register_ftrace_command(&ftrace_traceon_cmd); |
378 | if (ret) | 452 | if (ret) |
379 | unregister_ftrace_command(&ftrace_traceoff_cmd); | 453 | unregister_ftrace_command(&ftrace_traceoff_cmd); |
454 | |||
455 | ret = register_ftrace_command(&ftrace_stacktrace_cmd); | ||
456 | if (ret) { | ||
457 | unregister_ftrace_command(&ftrace_traceoff_cmd); | ||
458 | unregister_ftrace_command(&ftrace_traceon_cmd); | ||
459 | } | ||
380 | return ret; | 460 | return ret; |
381 | } | 461 | } |
382 | #else | 462 | #else |