diff options
author | Steven Rostedt <srostedt@redhat.com> | 2012-07-20 13:08:05 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2012-07-31 10:29:54 -0400 |
commit | ea701f11da44b44907af226fe5a5f57d2f26eeb2 (patch) | |
tree | bed777e80fb81b28903fe4a64cb90164e499a178 /kernel/trace | |
parent | 47239c4d8d6a24796039cada69d477a2b8cac9d6 (diff) |
ftrace: Add selftest to test function trace recursion protection
Add selftests to test the function tracing recursion protection actually
does work. It also tests if a ftrace_ops states it will perform its own
protection. Although, even if the ftrace_ops states it will protect itself,
the ftrace infrastructure may still provide protection if the arch does
not support all features or another ftrace_ops is registered.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 21 | ||||
-rw-r--r-- | kernel/trace/trace_selftest.c | 136 |
2 files changed, 157 insertions, 0 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index ad765b4ba426..528d997c7f99 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -111,6 +111,27 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); | |||
111 | #define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) | 111 | #define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) |
112 | #endif | 112 | #endif |
113 | 113 | ||
114 | /** | ||
115 | * ftrace_nr_registered_ops - return number of ops registered | ||
116 | * | ||
117 | * Returns the number of ftrace_ops registered and tracing functions | ||
118 | */ | ||
119 | int ftrace_nr_registered_ops(void) | ||
120 | { | ||
121 | struct ftrace_ops *ops; | ||
122 | int cnt = 0; | ||
123 | |||
124 | mutex_lock(&ftrace_lock); | ||
125 | |||
126 | for (ops = ftrace_ops_list; | ||
127 | ops != &ftrace_list_end; ops = ops->next) | ||
128 | cnt++; | ||
129 | |||
130 | mutex_unlock(&ftrace_lock); | ||
131 | |||
132 | return cnt; | ||
133 | } | ||
134 | |||
114 | /* | 135 | /* |
115 | * Traverse the ftrace_global_list, invoking all entries. The reason that we | 136 | * Traverse the ftrace_global_list, invoking all entries. The reason that we |
116 | * can use rcu_dereference_raw() is that elements removed from this list | 137 | * can use rcu_dereference_raw() is that elements removed from this list |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 1fb6da85ff8b..86422f91dbe1 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
@@ -406,8 +406,141 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | |||
406 | 406 | ||
407 | return ret; | 407 | return ret; |
408 | } | 408 | } |
409 | |||
410 | static int trace_selftest_recursion_cnt; | ||
411 | static void trace_selftest_test_recursion_func(unsigned long ip, | ||
412 | unsigned long pip, | ||
413 | struct ftrace_ops *op, | ||
414 | struct pt_regs *pt_regs) | ||
415 | { | ||
416 | /* | ||
417 | * This function is registered without the recursion safe flag. | ||
418 | * The ftrace infrastructure should provide the recursion | ||
419 | * protection. If not, this will crash the kernel! | ||
420 | */ | ||
421 | trace_selftest_recursion_cnt++; | ||
422 | DYN_FTRACE_TEST_NAME(); | ||
423 | } | ||
424 | |||
425 | static void trace_selftest_test_recursion_safe_func(unsigned long ip, | ||
426 | unsigned long pip, | ||
427 | struct ftrace_ops *op, | ||
428 | struct pt_regs *pt_regs) | ||
429 | { | ||
430 | /* | ||
431 | * We said we would provide our own recursion. By calling | ||
432 | * this function again, we should recurse back into this function | ||
433 | * and count again. But this only happens if the arch supports | ||
434 | * all of ftrace features and nothing else is using the function | ||
435 | * tracing utility. | ||
436 | */ | ||
437 | if (trace_selftest_recursion_cnt++) | ||
438 | return; | ||
439 | DYN_FTRACE_TEST_NAME(); | ||
440 | } | ||
441 | |||
442 | static struct ftrace_ops test_rec_probe = { | ||
443 | .func = trace_selftest_test_recursion_func, | ||
444 | }; | ||
445 | |||
446 | static struct ftrace_ops test_recsafe_probe = { | ||
447 | .func = trace_selftest_test_recursion_safe_func, | ||
448 | .flags = FTRACE_OPS_FL_RECURSION_SAFE, | ||
449 | }; | ||
450 | |||
451 | static int | ||
452 | trace_selftest_function_recursion(void) | ||
453 | { | ||
454 | int save_ftrace_enabled = ftrace_enabled; | ||
455 | int save_tracer_enabled = tracer_enabled; | ||
456 | char *func_name; | ||
457 | int len; | ||
458 | int ret; | ||
459 | int cnt; | ||
460 | |||
461 | /* The previous test PASSED */ | ||
462 | pr_cont("PASSED\n"); | ||
463 | pr_info("Testing ftrace recursion: "); | ||
464 | |||
465 | |||
466 | /* enable tracing, and record the filter function */ | ||
467 | ftrace_enabled = 1; | ||
468 | tracer_enabled = 1; | ||
469 | |||
470 | /* Handle PPC64 '.' name */ | ||
471 | func_name = "*" __stringify(DYN_FTRACE_TEST_NAME); | ||
472 | len = strlen(func_name); | ||
473 | |||
474 | ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1); | ||
475 | if (ret) { | ||
476 | pr_cont("*Could not set filter* "); | ||
477 | goto out; | ||
478 | } | ||
479 | |||
480 | ret = register_ftrace_function(&test_rec_probe); | ||
481 | if (ret) { | ||
482 | pr_cont("*could not register callback* "); | ||
483 | goto out; | ||
484 | } | ||
485 | |||
486 | DYN_FTRACE_TEST_NAME(); | ||
487 | |||
488 | unregister_ftrace_function(&test_rec_probe); | ||
489 | |||
490 | ret = -1; | ||
491 | if (trace_selftest_recursion_cnt != 1) { | ||
492 | pr_cont("*callback not called once (%d)* ", | ||
493 | trace_selftest_recursion_cnt); | ||
494 | goto out; | ||
495 | } | ||
496 | |||
497 | trace_selftest_recursion_cnt = 1; | ||
498 | |||
499 | pr_cont("PASSED\n"); | ||
500 | pr_info("Testing ftrace recursion safe: "); | ||
501 | |||
502 | ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1); | ||
503 | if (ret) { | ||
504 | pr_cont("*Could not set filter* "); | ||
505 | goto out; | ||
506 | } | ||
507 | |||
508 | ret = register_ftrace_function(&test_recsafe_probe); | ||
509 | if (ret) { | ||
510 | pr_cont("*could not register callback* "); | ||
511 | goto out; | ||
512 | } | ||
513 | |||
514 | DYN_FTRACE_TEST_NAME(); | ||
515 | |||
516 | unregister_ftrace_function(&test_recsafe_probe); | ||
517 | |||
518 | /* | ||
519 | * If arch supports all ftrace features, and no other task | ||
520 | * was on the list, we should be fine. | ||
521 | */ | ||
522 | if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC) | ||
523 | cnt = 2; /* Should have recursed */ | ||
524 | else | ||
525 | cnt = 1; | ||
526 | |||
527 | ret = -1; | ||
528 | if (trace_selftest_recursion_cnt != cnt) { | ||
529 | pr_cont("*callback not called expected %d times (%d)* ", | ||
530 | cnt, trace_selftest_recursion_cnt); | ||
531 | goto out; | ||
532 | } | ||
533 | |||
534 | ret = 0; | ||
535 | out: | ||
536 | ftrace_enabled = save_ftrace_enabled; | ||
537 | tracer_enabled = save_tracer_enabled; | ||
538 | |||
539 | return ret; | ||
540 | } | ||
409 | #else | 541 | #else |
410 | # define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) | 542 | # define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) |
543 | # define trace_selftest_function_recursion() ({ 0; }) | ||
411 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 544 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
412 | 545 | ||
413 | /* | 546 | /* |
@@ -455,7 +588,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
455 | 588 | ||
456 | ret = trace_selftest_startup_dynamic_tracing(trace, tr, | 589 | ret = trace_selftest_startup_dynamic_tracing(trace, tr, |
457 | DYN_FTRACE_TEST_NAME); | 590 | DYN_FTRACE_TEST_NAME); |
591 | if (ret) | ||
592 | goto out; | ||
458 | 593 | ||
594 | ret = trace_selftest_function_recursion(); | ||
459 | out: | 595 | out: |
460 | ftrace_enabled = save_ftrace_enabled; | 596 | ftrace_enabled = save_ftrace_enabled; |
461 | tracer_enabled = save_tracer_enabled; | 597 | tracer_enabled = save_tracer_enabled; |