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; |
