Chromium Code Reviews| 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..54c74a5e312d2b1f32b2e3b3a19db627c79e3f26 |
| --- /dev/null |
| +++ b/base/trace_event/memory_peak_detector_unittest.cc |
| @@ -0,0 +1,335 @@ |
| +// 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/gtest/include/gtest/gtest.h" |
| + |
| +namespace base { |
| +namespace trace_event { |
| + |
| +namespace { |
| +class MockMemoryDumpProvider : public MemoryDumpProvider { |
| + public: |
| + MockMemoryDumpProvider(WaitableEvent* evt) : evt_(evt) {} |
| + bool OnMemoryDump(const MemoryDumpArgs&, ProcessMemoryDump*) override { |
| + NOTREACHED(); |
| + return true; |
| + } |
| + |
| + void PollFastMemoryTotal(uint64_t* memory_total) override { evt_->Signal(); } |
| + |
| + WaitableEvent* const evt_; |
| +}; |
| +} // namespace |
| + |
| +class MemoryPeakDetectorTest : public testing::Test { |
| + public: |
| + MemoryPeakDetectorTest() : testing::Test() {} |
| + |
| + void RestartThreadAndReinitializePeakDetector() { |
| + if (bg_thread_) |
| + bg_thread_->Stop(); |
| + bg_thread_.reset(new Thread("Peak Detector Test Thread")); |
| + bg_thread_->Start(); |
| + peak_detector_->Initialize( |
| + Bind(&MemoryPeakDetectorTest::MockGetDumpProviders, Unretained(this)), |
| + bg_thread_->task_runner(), |
| + Bind(&MemoryPeakDetectorTest::OnPeakDetectedCallback, |
| + Unretained(this))); |
| + } |
| + |
| + void SetUp() override { |
| + peak_detector_ = MemoryPeakDetector::GetInstance(); |
|
hjd
2017/04/03 13:25:30
Maybe it would be better if each test constructed
Primiano Tucci (use gerrit)
2017/04/03 15:51:17
Hmm It's a singleton, so in order to construct it
Primiano Tucci (use gerrit)
2017/04/03 20:28:16
Okay you made very good arguments. Using an actual
|
| + get_mdp_call_count = 0; |
| + if (initialize_peak_detector_during_setup()) |
| + RestartThreadAndReinitializePeakDetector(); |
| + } |
| + |
| + void TearDown() override { |
| + dump_providers.clear(); |
| + peak_detector_->TearDownForTesting(); |
| + bg_thread_->Stop(); |
| + bg_thread_.reset(); |
| + } |
| + |
| + // Calls MemoryPeakDetector::state_for_testing() on the bg thread and returns |
| + // the result on the current thread. |
| + MemoryPeakDetector::State GetPeakDetectorState() { |
| + 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(); |
| + }; |
| + |
| + 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; |
| + } |
| + |
| + // 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; |
| + } |
| + |
| + void MockGetDumpProviders(MemoryPeakDetector::DumpProvidersList* mdps) { |
| + get_mdp_call_count++; |
| + *mdps = dump_providers; |
| + } |
| + |
| + void OnPeakDetectedCallback() { |
| + // TODO(primiano): use in upcoming CLs. |
| + } |
| + |
| + scoped_refptr<MemoryDumpProviderInfo> CreateMockDumpProvider( |
| + WaitableEvent* evt) { |
| + std::unique_ptr<MemoryDumpProvider> mdp(new MockMemoryDumpProvider(evt)); |
| + 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; |
| + } |
| + |
| + protected: |
| + virtual bool initialize_peak_detector_during_setup() const { return true; } |
| + |
| + MemoryPeakDetector::DumpProvidersList dump_providers; |
| + uint32_t get_mdp_call_count; |
| + MemoryPeakDetector* peak_detector_; |
| + std::unique_ptr<Thread> bg_thread_; |
| +}; |
| + |
| +class MemoryPeakDetectorTestNoAutoInitialize : public MemoryPeakDetectorTest { |
| + bool initialize_peak_detector_during_setup() const override { return false; } |
| +}; |
| + |
| +TEST_F(MemoryPeakDetectorTest, GetDumpProvidersFunctionCalled) { |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(1u, get_mdp_call_count); |
| + EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
| + |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(0u, GetNumPollingTasksRan()); |
| +} |
| + |
| +TEST_F(MemoryPeakDetectorTestNoAutoInitialize, NotifyBeforeInitialize) { |
| + WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED); |
| + scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(&evt); |
| + dump_providers.push_back(mdp); |
| + peak_detector_->NotifyMemoryDumpProvidersChanged(); |
|
ssid
2017/04/03 17:55:28
Can you add a
EXPECT_EQ(MemoryPeakDetector::NOT_I
Primiano Tucci (use gerrit)
2017/04/03 20:28:16
Good point, done
|
| + RestartThreadAndReinitializePeakDetector(); |
| + |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
| + evt.Wait(); // Wait for a PollFastMemoryTotal() call. |
| + |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(1u, get_mdp_call_count); |
| + EXPECT_GE(GetNumPollingTasksRan(), 1u); |
| +} |
| + |
| +TEST_F(MemoryPeakDetectorTest, DoubleStop) { |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + |
| + EXPECT_EQ(1u, get_mdp_call_count); |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(0u, GetNumPollingTasksRan()); |
| +} |
| + |
| +TEST_F(MemoryPeakDetectorTest, OneDumpProviderRegisteredBeforeStart) { |
| + WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED); |
| + scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(&evt); |
| + dump_providers.push_back(mdp); |
| + |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + evt.Wait(); // Signaled when PollFastMemoryTotal() is called on the MockMDP. |
| + EXPECT_EQ(MemoryPeakDetector::RUNNING, GetPeakDetectorState()); |
| + |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + |
| + EXPECT_EQ(1u, get_mdp_call_count); |
| + EXPECT_GT(GetNumPollingTasksRan(), 0u); |
| +} |
| + |
| +TEST_F(MemoryPeakDetectorTest, ReInitializeAndRebindToNewThread) { |
| + WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED); |
| + scoped_refptr<MemoryDumpProviderInfo> mdp = CreateMockDumpProvider(&evt); |
| + dump_providers.push_back(mdp); |
| + |
| + for (int i = 0; i < 5; ++i) { |
| + evt.Reset(); |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + evt.Wait(); // Wait for a PollFastMemoryTotal() call. |
| + peak_detector_->Stop(); |
| + |
| + // Reinitialize and re-bind to a new task runner. |
| + RestartThreadAndReinitializePeakDetector(); |
| + } |
| +} |
| + |
| +TEST_F(MemoryPeakDetectorTest, OneDumpProviderRegisteredOutOfBand) { |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(1u, get_mdp_call_count); |
| + |
| + // 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(&evt); |
| + 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, get_mdp_call_count); |
| + |
| + // Now simulate the unregisration and expect that the PeakDetector transitions |
| + // back to ENABLED. |
| + dump_providers.clear(); |
| + peak_detector_->NotifyMemoryDumpProvidersChanged(); |
| + bg_thread_->FlushForTesting(); |
|
ssid
2017/04/03 17:55:28
There is FlushForTesting calls in all the tests. I
Primiano Tucci (use gerrit)
2017/04/03 20:28:16
I removed all of them but two. Most of them where
|
| + EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(3u, get_mdp_call_count); |
| + 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(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(num_poll_tasks, GetNumPollingTasksRan()); |
| +} |
| + |
| +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(&evt1); |
| + scoped_refptr<MemoryDumpProviderInfo> mdp2 = CreateMockDumpProvider(&evt2); |
| + |
| + // Register only one MDP and start the detector. |
| + dump_providers.push_back(mdp1); |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + 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(); |
| + bg_thread_->FlushForTesting(); |
| + 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(); |
| + bg_thread_->FlushForTesting(); |
| + 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(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + dump_providers.clear(); |
| + mdp1 = nullptr; |
| + mdp2 = nullptr; |
| + |
| + num_poll_tasks = GetNumPollingTasksRan(); |
| + peak_detector_->Start(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::ENABLED, GetPeakDetectorState()); |
| + PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
| + peak_detector_->Stop(); |
| + bg_thread_->FlushForTesting(); |
| + EXPECT_EQ(MemoryPeakDetector::DISABLED, GetPeakDetectorState()); |
| + EXPECT_EQ(num_poll_tasks, GetNumPollingTasksRan()); |
| + |
| + EXPECT_EQ(6u, get_mdp_call_count); |
| +} |
| + |
| +} // namespace trace_event |
| +} // namespace base |