Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1007)

Unified Diff: base/trace_event/memory_peak_detector_unittest.cc

Issue 2786373002: memory-infra: Add peak-detector skeleton. (Closed)
Patch Set: minor typos Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/trace_event/memory_peak_detector.cc ('k') | tools/gn/bootstrap/bootstrap.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « base/trace_event/memory_peak_detector.cc ('k') | tools/gn/bootstrap/bootstrap.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698