| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stddef.h> | 5 #include <stddef.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <cstdlib> | 8 #include <cstdlib> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 #endif | 41 #endif |
| 42 | 42 |
| 43 #if defined(OS_WIN) | 43 #if defined(OS_WIN) |
| 44 #pragma intrinsic(_ReturnAddress) | 44 #pragma intrinsic(_ReturnAddress) |
| 45 #endif | 45 #endif |
| 46 | 46 |
| 47 namespace base { | 47 namespace base { |
| 48 | 48 |
| 49 using SamplingParams = StackSamplingProfiler::SamplingParams; | 49 using SamplingParams = StackSamplingProfiler::SamplingParams; |
| 50 using Frame = StackSamplingProfiler::Frame; | 50 using Frame = StackSamplingProfiler::Frame; |
| 51 using Frames = std::vector<StackSamplingProfiler::Frame>; |
| 51 using Module = StackSamplingProfiler::Module; | 52 using Module = StackSamplingProfiler::Module; |
| 52 using Sample = StackSamplingProfiler::Sample; | 53 using Sample = StackSamplingProfiler::Sample; |
| 53 using CallStackProfile = StackSamplingProfiler::CallStackProfile; | 54 using CallStackProfile = StackSamplingProfiler::CallStackProfile; |
| 54 using CallStackProfiles = StackSamplingProfiler::CallStackProfiles; | 55 using CallStackProfiles = StackSamplingProfiler::CallStackProfiles; |
| 55 | 56 |
| 56 namespace { | 57 namespace { |
| 57 | 58 |
| 58 // Configuration for the frames that appear on the stack. | 59 // Configuration for the frames that appear on the stack. |
| 59 struct StackConfiguration { | 60 struct StackConfiguration { |
| 60 enum Config { NORMAL, WITH_ALLOCA, WITH_OTHER_LIBRARY }; | 61 enum Config { NORMAL, WITH_ALLOCA, WITH_OTHER_LIBRARY }; |
| (...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 388 reinterpret_cast<const unsigned char*>(offset + 1); | 389 reinterpret_cast<const unsigned char*>(offset + 1); |
| 389 return next_instruction + *offset; | 390 return next_instruction + *offset; |
| 390 } | 391 } |
| 391 #endif | 392 #endif |
| 392 return function_address; | 393 return function_address; |
| 393 } | 394 } |
| 394 | 395 |
| 395 // Searches through the frames in |sample|, returning an iterator to the first | 396 // Searches through the frames in |sample|, returning an iterator to the first |
| 396 // frame that has an instruction pointer within |target_function|. Returns | 397 // frame that has an instruction pointer within |target_function|. Returns |
| 397 // sample.end() if no such frames are found. | 398 // sample.end() if no such frames are found. |
| 398 Sample::const_iterator FindFirstFrameWithinFunction( | 399 Frames::const_iterator FindFirstFrameWithinFunction( |
| 399 const Sample& sample, | 400 const Sample& sample, |
| 400 TargetFunction target_function) { | 401 TargetFunction target_function) { |
| 401 uintptr_t function_start = reinterpret_cast<uintptr_t>( | 402 uintptr_t function_start = reinterpret_cast<uintptr_t>( |
| 402 MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 403 MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 403 target_function))); | 404 target_function))); |
| 404 uintptr_t function_end = | 405 uintptr_t function_end = |
| 405 reinterpret_cast<uintptr_t>(target_function(nullptr, nullptr, nullptr)); | 406 reinterpret_cast<uintptr_t>(target_function(nullptr, nullptr, nullptr)); |
| 406 for (auto it = sample.begin(); it != sample.end(); ++it) { | 407 for (auto it = sample.frames.begin(); it != sample.frames.end(); ++it) { |
| 407 if ((it->instruction_pointer >= function_start) && | 408 if ((it->instruction_pointer >= function_start) && |
| 408 (it->instruction_pointer <= function_end)) | 409 (it->instruction_pointer <= function_end)) |
| 409 return it; | 410 return it; |
| 410 } | 411 } |
| 411 return sample.end(); | 412 return sample.frames.end(); |
| 412 } | 413 } |
| 413 | 414 |
| 414 // Formats a sample into a string that can be output for test diagnostics. | 415 // Formats a sample into a string that can be output for test diagnostics. |
| 415 std::string FormatSampleForDiagnosticOutput( | 416 std::string FormatSampleForDiagnosticOutput( |
| 416 const Sample& sample, | 417 const Sample& sample, |
| 417 const std::vector<Module>& modules) { | 418 const std::vector<Module>& modules) { |
| 418 std::string output; | 419 std::string output; |
| 419 for (const Frame& frame : sample) { | 420 for (const Frame& frame : sample.frames) { |
| 420 output += StringPrintf( | 421 output += StringPrintf( |
| 421 "0x%p %s\n", reinterpret_cast<const void*>(frame.instruction_pointer), | 422 "0x%p %s\n", reinterpret_cast<const void*>(frame.instruction_pointer), |
| 422 modules[frame.module_index].filename.AsUTF8Unsafe().c_str()); | 423 modules[frame.module_index].filename.AsUTF8Unsafe().c_str()); |
| 423 } | 424 } |
| 424 return output; | 425 return output; |
| 425 } | 426 } |
| 426 | 427 |
| 427 // Returns a duration that is longer than the test timeout. We would use | 428 // Returns a duration that is longer than the test timeout. We would use |
| 428 // TimeDelta::Max() but https://crbug.com/465948. | 429 // TimeDelta::Max() but https://crbug.com/465948. |
| 429 TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); } | 430 TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 sampling_thread_completed.Wait(); | 511 sampling_thread_completed.Wait(); |
| 511 | 512 |
| 512 // Look up the sample. | 513 // Look up the sample. |
| 513 ASSERT_EQ(1u, profiles.size()); | 514 ASSERT_EQ(1u, profiles.size()); |
| 514 const CallStackProfile& profile = profiles[0]; | 515 const CallStackProfile& profile = profiles[0]; |
| 515 ASSERT_EQ(1u, profile.samples.size()); | 516 ASSERT_EQ(1u, profile.samples.size()); |
| 516 const Sample& sample = profile.samples[0]; | 517 const Sample& sample = profile.samples[0]; |
| 517 | 518 |
| 518 // Check that the stack contains a frame for | 519 // Check that the stack contains a frame for |
| 519 // TargetThread::SignalAndWaitUntilSignaled(). | 520 // TargetThread::SignalAndWaitUntilSignaled(). |
| 520 Sample::const_iterator end_frame = FindFirstFrameWithinFunction( | 521 Frames::const_iterator end_frame = FindFirstFrameWithinFunction( |
| 521 sample, | 522 sample, |
| 522 &TargetThread::SignalAndWaitUntilSignaled); | 523 &TargetThread::SignalAndWaitUntilSignaled); |
| 523 ASSERT_TRUE(end_frame != sample.end()) | 524 ASSERT_TRUE(end_frame != sample.frames.end()) |
| 524 << "Function at " | 525 << "Function at " |
| 525 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 526 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 526 &TargetThread::SignalAndWaitUntilSignaled)) | 527 &TargetThread::SignalAndWaitUntilSignaled)) |
| 527 << " was not found in stack:\n" | 528 << " was not found in stack:\n" |
| 528 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 529 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 529 | 530 |
| 530 if (wait_until_unloaded) { | 531 if (wait_until_unloaded) { |
| 531 // The stack should look like this, resulting one frame after | 532 // The stack should look like this, resulting one frame after |
| 532 // SignalAndWaitUntilSignaled. The frame in the now-unloaded library is not | 533 // SignalAndWaitUntilSignaled. The frame in the now-unloaded library is not |
| 533 // recorded since we can't get module information. | 534 // recorded since we can't get module information. |
| 534 // | 535 // |
| 535 // ... WaitableEvent and system frames ... | 536 // ... WaitableEvent and system frames ... |
| 536 // TargetThread::SignalAndWaitUntilSignaled | 537 // TargetThread::SignalAndWaitUntilSignaled |
| 537 // TargetThread::OtherLibraryCallback | 538 // TargetThread::OtherLibraryCallback |
| 538 EXPECT_EQ(2, sample.end() - end_frame) | 539 EXPECT_EQ(2, sample.frames.end() - end_frame) |
| 539 << "Stack:\n" | 540 << "Stack:\n" |
| 540 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 541 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 541 } else { | 542 } else { |
| 542 // We didn't wait for the asynchonous unloading to complete, so the results | 543 // We didn't wait for the asynchonous unloading to complete, so the results |
| 543 // are non-deterministic: if the library finished unloading we should have | 544 // are non-deterministic: if the library finished unloading we should have |
| 544 // the same stack as |wait_until_unloaded|, if not we should have the full | 545 // the same stack as |wait_until_unloaded|, if not we should have the full |
| 545 // stack. The important thing is that we should not crash. | 546 // stack. The important thing is that we should not crash. |
| 546 | 547 |
| 547 if ((sample.end() - 1) - end_frame == 2) { | 548 if ((sample.frames.end() - 1) - end_frame == 2) { |
| 548 // This is the same case as |wait_until_unloaded|. | 549 // This is the same case as |wait_until_unloaded|. |
| 549 return; | 550 return; |
| 550 } | 551 } |
| 551 | 552 |
| 552 // Check that the stack contains a frame for | 553 // Check that the stack contains a frame for |
| 553 // TargetThread::CallThroughOtherLibrary(). | 554 // TargetThread::CallThroughOtherLibrary(). |
| 554 Sample::const_iterator other_library_frame = FindFirstFrameWithinFunction( | 555 Frames::const_iterator other_library_frame = FindFirstFrameWithinFunction( |
| 555 sample, | 556 sample, |
| 556 &TargetThread::CallThroughOtherLibrary); | 557 &TargetThread::CallThroughOtherLibrary); |
| 557 ASSERT_TRUE(other_library_frame != sample.end()) | 558 ASSERT_TRUE(other_library_frame != sample.frames.end()) |
| 558 << "Function at " | 559 << "Function at " |
| 559 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 560 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 560 &TargetThread::CallThroughOtherLibrary)) | 561 &TargetThread::CallThroughOtherLibrary)) |
| 561 << " was not found in stack:\n" | 562 << " was not found in stack:\n" |
| 562 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 563 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 563 | 564 |
| 564 // The stack should look like this, resulting in three frames between | 565 // The stack should look like this, resulting in three frames between |
| 565 // SignalAndWaitUntilSignaled and CallThroughOtherLibrary: | 566 // SignalAndWaitUntilSignaled and CallThroughOtherLibrary: |
| 566 // | 567 // |
| 567 // ... WaitableEvent and system frames ... | 568 // ... WaitableEvent and system frames ... |
| (...skipping 24 matching lines...) Expand all Loading... |
| 592 std::vector<CallStackProfile> profiles; | 593 std::vector<CallStackProfile> profiles; |
| 593 CaptureProfiles(params, AVeryLongTimeDelta(), &profiles); | 594 CaptureProfiles(params, AVeryLongTimeDelta(), &profiles); |
| 594 | 595 |
| 595 // Check that the profile and samples sizes are correct, and the module | 596 // Check that the profile and samples sizes are correct, and the module |
| 596 // indices are in range. | 597 // indices are in range. |
| 597 ASSERT_EQ(1u, profiles.size()); | 598 ASSERT_EQ(1u, profiles.size()); |
| 598 const CallStackProfile& profile = profiles[0]; | 599 const CallStackProfile& profile = profiles[0]; |
| 599 ASSERT_EQ(1u, profile.samples.size()); | 600 ASSERT_EQ(1u, profile.samples.size()); |
| 600 EXPECT_EQ(params.sampling_interval, profile.sampling_period); | 601 EXPECT_EQ(params.sampling_interval, profile.sampling_period); |
| 601 const Sample& sample = profile.samples[0]; | 602 const Sample& sample = profile.samples[0]; |
| 602 for (const auto& frame : sample) { | 603 for (const auto& frame : sample.frames) { |
| 603 ASSERT_GE(frame.module_index, 0u); | 604 ASSERT_GE(frame.module_index, 0u); |
| 604 ASSERT_LT(frame.module_index, profile.modules.size()); | 605 ASSERT_LT(frame.module_index, profile.modules.size()); |
| 605 } | 606 } |
| 606 | 607 |
| 607 // Check that the stack contains a frame for | 608 // Check that the stack contains a frame for |
| 608 // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this | 609 // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this |
| 609 // executable's module. | 610 // executable's module. |
| 610 Sample::const_iterator loc = FindFirstFrameWithinFunction( | 611 Frames::const_iterator loc = FindFirstFrameWithinFunction( |
| 611 sample, | 612 sample, |
| 612 &TargetThread::SignalAndWaitUntilSignaled); | 613 &TargetThread::SignalAndWaitUntilSignaled); |
| 613 ASSERT_TRUE(loc != sample.end()) | 614 ASSERT_TRUE(loc != sample.frames.end()) |
| 614 << "Function at " | 615 << "Function at " |
| 615 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 616 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 616 &TargetThread::SignalAndWaitUntilSignaled)) | 617 &TargetThread::SignalAndWaitUntilSignaled)) |
| 617 << " was not found in stack:\n" | 618 << " was not found in stack:\n" |
| 618 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 619 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 619 FilePath executable_path; | 620 FilePath executable_path; |
| 620 EXPECT_TRUE(PathService::Get(FILE_EXE, &executable_path)); | 621 EXPECT_TRUE(PathService::Get(FILE_EXE, &executable_path)); |
| 621 EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename); | 622 EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename); |
| 622 } | 623 } |
| 623 | 624 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 649 StackConfiguration(StackConfiguration::WITH_ALLOCA)); | 650 StackConfiguration(StackConfiguration::WITH_ALLOCA)); |
| 650 | 651 |
| 651 // Look up the sample. | 652 // Look up the sample. |
| 652 ASSERT_EQ(1u, profiles.size()); | 653 ASSERT_EQ(1u, profiles.size()); |
| 653 const CallStackProfile& profile = profiles[0]; | 654 const CallStackProfile& profile = profiles[0]; |
| 654 ASSERT_EQ(1u, profile.samples.size()); | 655 ASSERT_EQ(1u, profile.samples.size()); |
| 655 const Sample& sample = profile.samples[0]; | 656 const Sample& sample = profile.samples[0]; |
| 656 | 657 |
| 657 // Check that the stack contains a frame for | 658 // Check that the stack contains a frame for |
| 658 // TargetThread::SignalAndWaitUntilSignaled(). | 659 // TargetThread::SignalAndWaitUntilSignaled(). |
| 659 Sample::const_iterator end_frame = FindFirstFrameWithinFunction( | 660 Frames::const_iterator end_frame = FindFirstFrameWithinFunction( |
| 660 sample, | 661 sample, |
| 661 &TargetThread::SignalAndWaitUntilSignaled); | 662 &TargetThread::SignalAndWaitUntilSignaled); |
| 662 ASSERT_TRUE(end_frame != sample.end()) | 663 ASSERT_TRUE(end_frame != sample.frames.end()) |
| 663 << "Function at " | 664 << "Function at " |
| 664 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 665 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 665 &TargetThread::SignalAndWaitUntilSignaled)) | 666 &TargetThread::SignalAndWaitUntilSignaled)) |
| 666 << " was not found in stack:\n" | 667 << " was not found in stack:\n" |
| 667 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 668 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 668 | 669 |
| 669 // Check that the stack contains a frame for TargetThread::CallWithAlloca(). | 670 // Check that the stack contains a frame for TargetThread::CallWithAlloca(). |
| 670 Sample::const_iterator alloca_frame = FindFirstFrameWithinFunction( | 671 Frames::const_iterator alloca_frame = FindFirstFrameWithinFunction( |
| 671 sample, | 672 sample, |
| 672 &TargetThread::CallWithAlloca); | 673 &TargetThread::CallWithAlloca); |
| 673 ASSERT_TRUE(alloca_frame != sample.end()) | 674 ASSERT_TRUE(alloca_frame != sample.frames.end()) |
| 674 << "Function at " | 675 << "Function at " |
| 675 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 676 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 676 &TargetThread::CallWithAlloca)) | 677 &TargetThread::CallWithAlloca)) |
| 677 << " was not found in stack:\n" | 678 << " was not found in stack:\n" |
| 678 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 679 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 679 | 680 |
| 680 // These frames should be adjacent on the stack. | 681 // These frames should be adjacent on the stack. |
| 681 EXPECT_EQ(1, alloca_frame - end_frame) | 682 EXPECT_EQ(1, alloca_frame - end_frame) |
| 682 << "Stack:\n" | 683 << "Stack:\n" |
| 683 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 684 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 919 } | 920 } |
| 920 | 921 |
| 921 // Look up the sample. | 922 // Look up the sample. |
| 922 ASSERT_EQ(1u, profiles.size()); | 923 ASSERT_EQ(1u, profiles.size()); |
| 923 const CallStackProfile& profile = profiles[0]; | 924 const CallStackProfile& profile = profiles[0]; |
| 924 ASSERT_EQ(1u, profile.samples.size()); | 925 ASSERT_EQ(1u, profile.samples.size()); |
| 925 const Sample& sample = profile.samples[0]; | 926 const Sample& sample = profile.samples[0]; |
| 926 | 927 |
| 927 // Check that the stack contains a frame for | 928 // Check that the stack contains a frame for |
| 928 // TargetThread::CallThroughOtherLibrary(). | 929 // TargetThread::CallThroughOtherLibrary(). |
| 929 Sample::const_iterator other_library_frame = FindFirstFrameWithinFunction( | 930 Frames::const_iterator other_library_frame = FindFirstFrameWithinFunction( |
| 930 sample, | 931 sample, |
| 931 &TargetThread::CallThroughOtherLibrary); | 932 &TargetThread::CallThroughOtherLibrary); |
| 932 ASSERT_TRUE(other_library_frame != sample.end()) | 933 ASSERT_TRUE(other_library_frame != sample.frames.end()) |
| 933 << "Function at " | 934 << "Function at " |
| 934 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 935 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 935 &TargetThread::CallThroughOtherLibrary)) | 936 &TargetThread::CallThroughOtherLibrary)) |
| 936 << " was not found in stack:\n" | 937 << " was not found in stack:\n" |
| 937 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 938 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 938 | 939 |
| 939 // Check that the stack contains a frame for | 940 // Check that the stack contains a frame for |
| 940 // TargetThread::SignalAndWaitUntilSignaled(). | 941 // TargetThread::SignalAndWaitUntilSignaled(). |
| 941 Sample::const_iterator end_frame = FindFirstFrameWithinFunction( | 942 Frames::const_iterator end_frame = FindFirstFrameWithinFunction( |
| 942 sample, | 943 sample, |
| 943 &TargetThread::SignalAndWaitUntilSignaled); | 944 &TargetThread::SignalAndWaitUntilSignaled); |
| 944 ASSERT_TRUE(end_frame != sample.end()) | 945 ASSERT_TRUE(end_frame != sample.frames.end()) |
| 945 << "Function at " | 946 << "Function at " |
| 946 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( | 947 << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>( |
| 947 &TargetThread::SignalAndWaitUntilSignaled)) | 948 &TargetThread::SignalAndWaitUntilSignaled)) |
| 948 << " was not found in stack:\n" | 949 << " was not found in stack:\n" |
| 949 << FormatSampleForDiagnosticOutput(sample, profile.modules); | 950 << FormatSampleForDiagnosticOutput(sample, profile.modules); |
| 950 | 951 |
| 951 // The stack should look like this, resulting in three frames between | 952 // The stack should look like this, resulting in three frames between |
| 952 // SignalAndWaitUntilSignaled and CallThroughOtherLibrary: | 953 // SignalAndWaitUntilSignaled and CallThroughOtherLibrary: |
| 953 // | 954 // |
| 954 // ... WaitableEvent and system frames ... | 955 // ... WaitableEvent and system frames ... |
| (...skipping 21 matching lines...) Expand all Loading... |
| 976 #if defined(STACK_SAMPLING_PROFILER_SUPPORTED) | 977 #if defined(STACK_SAMPLING_PROFILER_SUPPORTED) |
| 977 #define MAYBE_UnloadedLibrary UnloadedLibrary | 978 #define MAYBE_UnloadedLibrary UnloadedLibrary |
| 978 #else | 979 #else |
| 979 #define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary | 980 #define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary |
| 980 #endif | 981 #endif |
| 981 TEST(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) { | 982 TEST(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) { |
| 982 TestLibraryUnload(true); | 983 TestLibraryUnload(true); |
| 983 } | 984 } |
| 984 | 985 |
| 985 } // namespace base | 986 } // namespace base |
| OLD | NEW |