Index: base/trace_event/memory_peak_detector_unittest.cc |
diff --git a/base/trace_event/memory_peak_detector_unittest.cc b/base/trace_event/memory_peak_detector_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9a9b92217c123958be39ee07a6a5092a63c149b9 |
--- /dev/null |
+++ b/base/trace_event/memory_peak_detector_unittest.cc |
@@ -0,0 +1,381 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/trace_event/memory_peak_detector.h" |
+ |
+#include <memory> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/run_loop.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/test/test_timeouts.h" |
+#include "base/threading/platform_thread.h" |
+#include "base/threading/thread.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "base/trace_event/memory_dump_provider.h" |
+#include "base/trace_event/memory_dump_provider_info.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::_; |
+using ::testing::Invoke; |
+using ::testing::Return; |
+ |
+namespace base { |
+namespace trace_event { |
+ |
+namespace { |
+ |
+class MockMemoryDumpProvider : public MemoryDumpProvider { |
+ public: |
+ bool OnMemoryDump(const MemoryDumpArgs&, ProcessMemoryDump*) override { |
+ NOTREACHED(); |
+ return true; |
+ } |
+ |
+ MOCK_METHOD1(PollFastMemoryTotal, void(uint64_t*)); |
+}; |
+ |
+// Wrapper to use gmock on a callback. |
+struct OnPeakDetectedWrapper { |
+ MOCK_METHOD0(OnPeak, void()); |
+}; |
+ |
+} // namespace |
+ |
+class MemoryPeakDetectorTest : public testing::Test { |
+ public: |
+ struct FriendDeleter { |
+ void operator()(MemoryPeakDetector* inst) { delete inst; } |
+ }; |
+ |
+ MemoryPeakDetectorTest() : testing::Test() {} |
+ |
+ std::unique_ptr<MemoryPeakDetector, FriendDeleter> NewInstance() { |
+ return std::unique_ptr<MemoryPeakDetector, FriendDeleter>( |
+ new MemoryPeakDetector()); |
+ } |
+ |
+ void RestartThreadAndReinitializePeakDetector() { |
+ bg_thread_.reset(new Thread("Peak Detector Test Thread")); |
+ bg_thread_->Start(); |
+ peak_detector_ = NewInstance(); |
+ peak_detector_->Setup( |
+ Bind(&MemoryPeakDetectorTest::MockGetDumpProviders, Unretained(this)), |
+ bg_thread_->task_runner(), |
+ Bind(&OnPeakDetectedWrapper::OnPeak, Unretained(&on_peak_callback_))); |
+ } |
+ |
+ void SetUp() override { |
+ get_mdp_call_count_ = 0; |
+ RestartThreadAndReinitializePeakDetector(); |
+ } |
+ |
+ void TearDown() override { |
+ peak_detector_->TearDown(); |
+ bg_thread_->FlushForTesting(); |
+ EXPECT_EQ(MemoryPeakDetector::NOT_INITIALIZED, GetPeakDetectorState()); |
+ dump_providers_.clear(); |
+ } |
+ |
+ // Calls MemoryPeakDetector::state_for_testing() on the bg thread and returns |
+ // the result on the current thread. |
+ MemoryPeakDetector::State GetPeakDetectorState() { |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ MemoryPeakDetector::State res = MemoryPeakDetector::NOT_INITIALIZED; |
+ auto get_fn = [](MemoryPeakDetector* peak_detector, WaitableEvent* evt, |
+ MemoryPeakDetector::State* res) { |
+ *res = peak_detector->state_for_testing(); |
+ evt->Signal(); |
+ }; |
+ bg_thread_->task_runner()->PostTask( |
+ FROM_HERE, Bind(get_fn, Unretained(&*peak_detector_), Unretained(&evt), |
+ Unretained(&res))); |
+ evt.Wait(); |
+ return res; |
+ } |
+ |
+ // Calls MemoryPeakDetector::poll_tasks_count_for_testing() on the bg thread |
+ // and returns the result on the current thread. |
+ uint32_t GetNumPollingTasksRan() { |
+ uint32_t res = 0; |
+ auto get_fn = [](MemoryPeakDetector* peak_detector, WaitableEvent* evt, |
+ uint32_t* res) { |
+ *res = peak_detector->poll_tasks_count_for_testing(); |
+ evt->Signal(); |
+ }; |
+ |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ bg_thread_->task_runner()->PostTask( |
+ FROM_HERE, Bind(get_fn, Unretained(&*peak_detector_), Unretained(&evt), |
+ Unretained(&res))); |
+ evt.Wait(); |
+ return res; |
+ } |
+ |
+ // Called on the |bg_thread_|. |
+ void MockGetDumpProviders(MemoryPeakDetector::DumpProvidersList* mdps) { |
+ get_mdp_call_count_++; |
+ *mdps = dump_providers_; |
+ } |
+ |
+ uint32_t GetNumGetDumpProvidersCalls() { |
+ bg_thread_->FlushForTesting(); |
+ return get_mdp_call_count_; |
+ } |
+ |
+ scoped_refptr<MemoryDumpProviderInfo> CreateMockDumpProvider() { |
+ std::unique_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider()); |
+ MemoryDumpProvider::Options opt; |
+ opt.is_fast_polling_supported = true; |
+ scoped_refptr<MemoryDumpProviderInfo> mdp_info( |
+ new MemoryDumpProviderInfo(mdp.get(), "Mock MDP", nullptr, opt, false)); |
+ |
+ // The |mdp| instance will be destroyed together with the |mdp_info|. |
+ mdp_info->owned_dump_provider = std::move(mdp); |
+ return mdp_info; |
+ } |
+ |
+ static MockMemoryDumpProvider& GetMockMDP( |
+ const scoped_refptr<MemoryDumpProviderInfo>& mdp_info) { |
+ return *static_cast<MockMemoryDumpProvider*>(mdp_info->dump_provider); |
+ } |
+ |
+ protected: |
+ MemoryPeakDetector::DumpProvidersList dump_providers_; |
+ uint32_t get_mdp_call_count_; |
+ std::unique_ptr<MemoryPeakDetector, FriendDeleter> peak_detector_; |
+ std::unique_ptr<Thread> bg_thread_; |
+ OnPeakDetectedWrapper on_peak_callback_; |
+}; |
+ |
+TEST_F(MemoryPeakDetectorTest, GetDumpProvidersFunctionCalled) { |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ peak_detector_->Start(); |
+ EXPECT_EQ(1u, GetNumGetDumpProvidersCalls()); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(0u, GetNumPollingTasksRan()); |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, NotifyBeforeInitialize) { |
+ peak_detector_->TearDown(); |
+ |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(); |
+ EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); })); |
+ dump_providers_.push_back(mdp); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ EXPECT_EQ(MemoryPeakDetector::NOT_INITIALIZED, GetPeakDetectorState()); |
+ RestartThreadAndReinitializePeakDetector(); |
+ |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ evt.Wait(); // Wait for a PollFastMemoryTotal() call. |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(1u, GetNumGetDumpProvidersCalls()); |
+ EXPECT_GE(GetNumPollingTasksRan(), 1u); |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, DoubleStop) { |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ |
+ EXPECT_EQ(1u, GetNumGetDumpProvidersCalls()); |
+ EXPECT_EQ(0u, GetNumPollingTasksRan()); |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, OneDumpProviderRegisteredBeforeStart) { |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(); |
+ EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); })); |
+ dump_providers_.push_back(mdp); |
+ |
+ peak_detector_->Start(); |
+ evt.Wait(); // Signaled when PollFastMemoryTotal() is called on the MockMDP. |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(1u, GetNumGetDumpProvidersCalls()); |
+ EXPECT_GT(GetNumPollingTasksRan(), 0u); |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, ReInitializeAndRebindToNewThread) { |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(); |
+ EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); })); |
+ dump_providers_.push_back(mdp); |
+ |
+ for (int i = 0; i < 5; ++i) { |
+ evt.Reset(); |
+ peak_detector_->Start(); |
+ evt.Wait(); // Wait for a PollFastMemoryTotal() call. |
+ // Check that calling TearDown implicitly does a Stop(). |
+ peak_detector_->TearDown(); |
+ |
+ // Reinitialize and re-bind to a new task runner. |
+ RestartThreadAndReinitializePeakDetector(); |
+ } |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, OneDumpProviderRegisteredOutOfBand) { |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(1u, GetNumGetDumpProvidersCalls()); |
+ |
+ // Check that no poll tasks are posted before any dump provider is registered. |
+ PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ EXPECT_EQ(0u, GetNumPollingTasksRan()); |
+ |
+ // Registed the MDP After Start() has been issued and expect that the |
+ // PeakDetector transitions ENABLED -> RUNNING on the next |
+ // NotifyMemoryDumpProvidersChanged() call. |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(); |
+ EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt](uint64_t*) { evt.Signal(); })); |
+ dump_providers_.push_back(mdp); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ |
+ evt.Wait(); // Signaled when PollFastMemoryTotal() is called on the MockMDP. |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ EXPECT_EQ(2u, GetNumGetDumpProvidersCalls()); |
+ |
+ // Now simulate the unregisration and expect that the PeakDetector transitions |
+ // back to ENABLED. |
+ dump_providers_.clear(); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(3u, GetNumGetDumpProvidersCalls()); |
+ uint32_t num_poll_tasks = GetNumPollingTasksRan(); |
+ EXPECT_GT(num_poll_tasks, 0u); |
+ |
+ // At this point, no more polling tasks should be posted. |
+ PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(num_poll_tasks, GetNumPollingTasksRan()); |
+} |
+ |
+// Test that a sequence of Start()/Stop() back-to-back doesn't end up creating |
+// several outstanding timer tasks and instead respects the polling_interval_ms. |
+TEST_F(MemoryPeakDetectorTest, StartStopQuickly) { |
+ WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(); |
+ dump_providers_.push_back(mdp); |
+ const uint32_t kNumPolls = 20; |
+ uint32_t polls_done = 0; |
+ EXPECT_CALL(GetMockMDP(mdp), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&polls_done, &evt, kNumPolls](uint64_t*) { |
+ if (++polls_done == kNumPolls) |
+ evt.Signal(); |
+ })); |
+ |
+ const TimeTicks tstart = TimeTicks::Now(); |
+ for (int i = 0; i < 5; i++) { |
+ peak_detector_->Start(); |
+ peak_detector_->Stop(); |
+ } |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ evt.Wait(); // Wait for kNumPolls. |
+ const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF(); |
+ |
+ // TODO(primiano): this will become config.polling_interval_ms in the next CL. |
+ const uint32_t polling_interval_ms = 1; |
+ EXPECT_GE(time_ms, kNumPolls * polling_interval_ms); |
+ peak_detector_->Stop(); |
+} |
+ |
+TEST_F(MemoryPeakDetectorTest, RegisterAndUnregisterTwoDumpProviders) { |
+ WaitableEvent evt1(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ WaitableEvent evt2(WaitableEvent::ResetPolicy::MANUAL, |
+ WaitableEvent::InitialState::NOT_SIGNALED); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp1 = CreateMockDumpProvider(); |
+ scoped_refptr<MemoryDumpProviderInfo> mdp2 = CreateMockDumpProvider(); |
+ EXPECT_CALL(GetMockMDP(mdp1), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt1](uint64_t*) { evt1.Signal(); })); |
+ EXPECT_CALL(GetMockMDP(mdp2), PollFastMemoryTotal(_)) |
+ .WillRepeatedly(Invoke([&evt2](uint64_t*) { evt2.Signal(); })); |
+ |
+ // Register only one MDP and start the detector. |
+ dump_providers_.push_back(mdp1); |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ |
+ // Wait for one poll task and then register also the other one. |
+ evt1.Wait(); |
+ dump_providers_.push_back(mdp2); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ evt2.Wait(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ |
+ // Now unregister the first MDP and check that everything is still running. |
+ dump_providers_.erase(dump_providers_.begin()); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ |
+ // Now unregister both and check that the detector goes to idle. |
+ dump_providers_.clear(); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ |
+ // Now re-register both and check that the detector re-activates posting |
+ // new polling tasks. |
+ uint32_t num_poll_tasks = GetNumPollingTasksRan(); |
+ evt1.Reset(); |
+ evt2.Reset(); |
+ dump_providers_.push_back(mdp1); |
+ dump_providers_.push_back(mdp2); |
+ peak_detector_->NotifyMemoryDumpProvidersChanged(); |
+ evt1.Wait(); |
+ evt2.Wait(); |
+ EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
+ EXPECT_GT(GetNumPollingTasksRan(), num_poll_tasks); |
+ |
+ // Stop everything, tear down the MDPs, restart the detector and check that |
+ // it detector doesn't accidentally try to re-access them. |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ dump_providers_.clear(); |
+ mdp1 = nullptr; |
+ mdp2 = nullptr; |
+ |
+ num_poll_tasks = GetNumPollingTasksRan(); |
+ peak_detector_->Start(); |
+ EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
+ PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ |
+ peak_detector_->Stop(); |
+ EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
+ EXPECT_EQ(num_poll_tasks, GetNumPollingTasksRan()); |
+ |
+ EXPECT_EQ(6u, GetNumGetDumpProvidersCalls()); |
+} |
+ |
+} // namespace trace_event |
+} // namespace base |