Index: base/trace_event/memory_dump_manager_unittest.cc |
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc |
index 898f003807eb92ddb5f9f6a4f1865a335a5b1296..03b3afa32e5201f3239a2c41e3d0fca8414fd1ed 100644 |
--- a/base/trace_event/memory_dump_manager_unittest.cc |
+++ b/base/trace_event/memory_dump_manager_unittest.cc |
@@ -17,6 +17,7 @@ |
#include "base/test/test_io_thread.h" |
#include "base/test/trace_event_analyzer.h" |
#include "base/thread_task_runner_handle.h" |
+#include "base/threading/platform_thread.h" |
#include "base/threading/thread.h" |
#include "base/trace_event/memory_dump_provider.h" |
#include "base/trace_event/process_memory_dump.h" |
@@ -93,8 +94,17 @@ class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate { |
class MockMemoryDumpProvider : public MemoryDumpProvider { |
public: |
+ MOCK_METHOD0(Destructor, void()); |
MOCK_METHOD2(OnMemoryDump, |
bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd)); |
+ |
+ MockMemoryDumpProvider() : enable_mock_destructor(false) {} |
+ ~MockMemoryDumpProvider() override { |
+ if (enable_mock_destructor) |
+ Destructor(); |
+ } |
+ |
+ bool enable_mock_destructor; |
}; |
class MemoryDumpManagerTest : public testing::Test { |
@@ -785,7 +795,7 @@ TEST_F(MemoryDumpManagerTest, DisableTracingWhileDumping) { |
// Register also an unbound dump provider. Unbound dump providers are always |
// invoked after bound ones. |
MockMemoryDumpProvider unbound_mdp; |
- RegisterDumpProvider(&unbound_mdp); |
+ RegisterDumpProvider(&unbound_mdp, nullptr, kDefaultOptions); |
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); |
EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); |
@@ -877,5 +887,72 @@ TEST_F(MemoryDumpManagerTest, DumpOnBehalfOfOtherProcess) { |
ASSERT_EQ(events[0]->id, events[2]->id); |
} |
+// Tests the basics of the UnregisterAndDeleteDumpProviderSoon(): the |
+// unregistration should actually delete the providers and not leak them. |
+TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoon) { |
+ InitializeMemoryDumpManager(false /* is_coordinator */); |
+ static const int kNumProviders = 3; |
+ int dtor_count = 0; |
+ std::vector<scoped_ptr<MemoryDumpProvider>> mdps; |
+ for (int i = 0; i < kNumProviders; ++i) { |
+ scoped_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider); |
+ mdp->enable_mock_destructor = true; |
+ EXPECT_CALL(*mdp, Destructor()) |
+ .WillOnce(Invoke([&dtor_count]() { dtor_count++; })); |
+ RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions); |
+ mdps.push_back(std::move(mdp)); |
+ } |
+ |
+ while (!mdps.empty()) { |
+ mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdps.back())); |
+ mdps.pop_back(); |
+ } |
+ |
+ ASSERT_EQ(kNumProviders, dtor_count); |
+} |
+ |
+// This test checks against races when unregistering an unbound dump provider |
+// from another thread while dumping. It registers one MDP and, when |
+// OnMemoryDump() is called, it invokes UnregisterAndDeleteDumpProviderSoon() |
+// from another thread. The OnMemoryDump() and the dtor call are expected to |
+// happen on the same thread (the MemoryDumpManager utility thread). |
+TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) { |
+ InitializeMemoryDumpManager(false /* is_coordinator */); |
+ scoped_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider); |
+ mdp->enable_mock_destructor = true; |
+ RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions); |
+ |
+ base::PlatformThreadRef thread_ref; |
+ auto self_unregister_from_another_thread = [&mdp, &thread_ref]( |
+ const MemoryDumpArgs&, ProcessMemoryDump*) -> bool { |
+ thread_ref = PlatformThread::CurrentRef(); |
+ TestIOThread thread_for_unregistration(TestIOThread::kAutoStart); |
+ thread_for_unregistration.PostTaskAndWait( |
+ FROM_HERE, |
+ base::Bind( |
+ &MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon, |
+ base::Unretained(MemoryDumpManager::GetInstance()), |
+ base::Passed(scoped_ptr<MemoryDumpProvider>(std::move(mdp))))); |
+ thread_for_unregistration.Stop(); |
+ return true; |
+ }; |
+ EXPECT_CALL(*mdp, OnMemoryDump(_, _)) |
+ .Times(1) |
+ .WillOnce(Invoke(self_unregister_from_another_thread)); |
+ EXPECT_CALL(*mdp, Destructor()) |
+ .Times(1) |
+ .WillOnce(Invoke([&thread_ref]() { |
+ EXPECT_EQ(thread_ref, PlatformThread::CurrentRef()); |
+ })); |
+ |
+ EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); |
+ EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2); |
+ for (int i = 0; i < 2; ++i) { |
+ RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, |
+ MemoryDumpLevelOfDetail::DETAILED); |
+ } |
+ DisableTracing(); |
+} |
+ |
} // namespace trace_event |
} // namespace base |