diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2013-04-30 15:46:14 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-06-11 18:38:46 -0400 |
commit | ad71d889b88055e61e3970a6744a271a51a94f42 (patch) | |
tree | 716f6e043847000a82449270e3b2d5dcdc9c4f79 /kernel | |
parent | 317ddd256b9c24b0d78fa8018f80f1e495481a10 (diff) |
tracing: Add function probe to trigger a ftrace dump to console
Add the "dump" command to have the ftrace buffer dumped to console if
a function is hit. This is useful when debugging a tripple fault,
where you have an idea of a function that is called just before the
tripple fault occurs, and can tell ftrace to dump its content out
to the console before it continues.
Format is:
<function>:dump
echo 'bad_address:dump' > /debug/tracing/set_ftrace_filter
To remove this:
echo '!bad_address:dump' > /debug/tracing/set_ftrace_filter
Requested-by: Luis Claudio R. Goncalves <lclaudio@uudg.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/trace_functions.c | 59 |
1 files changed, 54 insertions, 5 deletions
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index c4d6d7191988..d7c8719734b8 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
@@ -290,6 +290,13 @@ ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, void **data) | |||
290 | trace_dump_stack(STACK_SKIP); | 290 | trace_dump_stack(STACK_SKIP); |
291 | } | 291 | } |
292 | 292 | ||
293 | static void | ||
294 | ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, void **data) | ||
295 | { | ||
296 | if (update_count(data)) | ||
297 | ftrace_dump(DUMP_ALL); | ||
298 | } | ||
299 | |||
293 | static int | 300 | static int |
294 | ftrace_probe_print(const char *name, struct seq_file *m, | 301 | ftrace_probe_print(const char *name, struct seq_file *m, |
295 | unsigned long ip, void *data) | 302 | unsigned long ip, void *data) |
@@ -327,6 +334,13 @@ ftrace_stacktrace_print(struct seq_file *m, unsigned long ip, | |||
327 | return ftrace_probe_print("stacktrace", m, ip, data); | 334 | return ftrace_probe_print("stacktrace", m, ip, data); |
328 | } | 335 | } |
329 | 336 | ||
337 | static int | ||
338 | ftrace_dump_print(struct seq_file *m, unsigned long ip, | ||
339 | struct ftrace_probe_ops *ops, void *data) | ||
340 | { | ||
341 | return ftrace_probe_print("dump", m, ip, data); | ||
342 | } | ||
343 | |||
330 | static struct ftrace_probe_ops traceon_count_probe_ops = { | 344 | static struct ftrace_probe_ops traceon_count_probe_ops = { |
331 | .func = ftrace_traceon_count, | 345 | .func = ftrace_traceon_count, |
332 | .print = ftrace_traceon_print, | 346 | .print = ftrace_traceon_print, |
@@ -342,6 +356,11 @@ static struct ftrace_probe_ops stacktrace_count_probe_ops = { | |||
342 | .print = ftrace_stacktrace_print, | 356 | .print = ftrace_stacktrace_print, |
343 | }; | 357 | }; |
344 | 358 | ||
359 | static struct ftrace_probe_ops dump_probe_ops = { | ||
360 | .func = ftrace_dump_probe, | ||
361 | .print = ftrace_dump_print, | ||
362 | }; | ||
363 | |||
345 | static struct ftrace_probe_ops traceon_probe_ops = { | 364 | static struct ftrace_probe_ops traceon_probe_ops = { |
346 | .func = ftrace_traceon, | 365 | .func = ftrace_traceon, |
347 | .print = ftrace_traceon_print, | 366 | .print = ftrace_traceon_print, |
@@ -425,6 +444,19 @@ ftrace_stacktrace_callback(struct ftrace_hash *hash, | |||
425 | param, enable); | 444 | param, enable); |
426 | } | 445 | } |
427 | 446 | ||
447 | static int | ||
448 | ftrace_dump_callback(struct ftrace_hash *hash, | ||
449 | char *glob, char *cmd, char *param, int enable) | ||
450 | { | ||
451 | struct ftrace_probe_ops *ops; | ||
452 | |||
453 | ops = &dump_probe_ops; | ||
454 | |||
455 | /* Only dump once. */ | ||
456 | return ftrace_trace_probe_callback(ops, hash, glob, cmd, | ||
457 | "1", enable); | ||
458 | } | ||
459 | |||
428 | static struct ftrace_func_command ftrace_traceon_cmd = { | 460 | static struct ftrace_func_command ftrace_traceon_cmd = { |
429 | .name = "traceon", | 461 | .name = "traceon", |
430 | .func = ftrace_trace_onoff_callback, | 462 | .func = ftrace_trace_onoff_callback, |
@@ -440,6 +472,11 @@ static struct ftrace_func_command ftrace_stacktrace_cmd = { | |||
440 | .func = ftrace_stacktrace_callback, | 472 | .func = ftrace_stacktrace_callback, |
441 | }; | 473 | }; |
442 | 474 | ||
475 | static struct ftrace_func_command ftrace_dump_cmd = { | ||
476 | .name = "dump", | ||
477 | .func = ftrace_dump_callback, | ||
478 | }; | ||
479 | |||
443 | static int __init init_func_cmd_traceon(void) | 480 | static int __init init_func_cmd_traceon(void) |
444 | { | 481 | { |
445 | int ret; | 482 | int ret; |
@@ -450,13 +487,25 @@ static int __init init_func_cmd_traceon(void) | |||
450 | 487 | ||
451 | ret = register_ftrace_command(&ftrace_traceon_cmd); | 488 | ret = register_ftrace_command(&ftrace_traceon_cmd); |
452 | if (ret) | 489 | if (ret) |
453 | unregister_ftrace_command(&ftrace_traceoff_cmd); | 490 | goto out_free_traceoff; |
454 | 491 | ||
455 | ret = register_ftrace_command(&ftrace_stacktrace_cmd); | 492 | ret = register_ftrace_command(&ftrace_stacktrace_cmd); |
456 | if (ret) { | 493 | if (ret) |
457 | unregister_ftrace_command(&ftrace_traceoff_cmd); | 494 | goto out_free_traceon; |
458 | unregister_ftrace_command(&ftrace_traceon_cmd); | 495 | |
459 | } | 496 | ret = register_ftrace_command(&ftrace_dump_cmd); |
497 | if (ret) | ||
498 | goto out_free_stacktrace; | ||
499 | |||
500 | return 0; | ||
501 | |||
502 | out_free_stacktrace: | ||
503 | unregister_ftrace_command(&ftrace_stacktrace_cmd); | ||
504 | out_free_traceon: | ||
505 | unregister_ftrace_command(&ftrace_traceon_cmd); | ||
506 | out_free_traceoff: | ||
507 | unregister_ftrace_command(&ftrace_traceoff_cmd); | ||
508 | |||
460 | return ret; | 509 | return ret; |
461 | } | 510 | } |
462 | #else | 511 | #else |