diff options
-rw-r--r-- | base/trace_event/BUILD.gn | 3 | ||||
-rw-r--r-- | base/trace_event/memory_dump_manager.cc | 4 | ||||
-rw-r--r-- | base/trace_event/trace_event.gypi | 3 | ||||
-rw-r--r-- | base/trace_event/winheap_dump_provider_win.cc | 105 | ||||
-rw-r--r-- | base/trace_event/winheap_dump_provider_win.h | 54 | ||||
-rw-r--r-- | base/trace_event/winheap_dump_provider_win_unittest.cc | 79 |
6 files changed, 248 insertions, 0 deletions
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn index db5509e..b981c87 100644 --- a/base/trace_event/BUILD.gn +++ b/base/trace_event/BUILD.gn @@ -42,6 +42,8 @@ source_set("trace_event") { "trace_event_system_stats_monitor.h", "trace_event_win.cc", "trace_event_win.h", + "winheap_dump_provider_win.cc", + "winheap_dump_provider_win.h", ] if (is_nacl) { @@ -95,6 +97,7 @@ source_set("trace_event_unittests") { "trace_event_system_stats_monitor_unittest.cc", "trace_event_unittest.cc", "trace_event_win_unittest.cc", + "winheap_dump_provider_win_unittest.cc", ] deps = [ diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc index 63d91bb4..859e8e0 100644 --- a/base/trace_event/memory_dump_manager.cc +++ b/base/trace_event/memory_dump_manager.cc @@ -17,6 +17,8 @@ #include "base/trace_event/malloc_dump_provider.h" #include "base/trace_event/process_memory_maps_dump_provider.h" #include "base/trace_event/process_memory_totals_dump_provider.h" +#elif defined(OS_WIN) +#include "base/trace_event/winheap_dump_provider_win.h" #endif namespace base { @@ -165,6 +167,8 @@ void MemoryDumpManager::Initialize() { RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); RegisterDumpProvider(MallocDumpProvider::GetInstance()); +#elif defined(OS_WIN) + RegisterDumpProvider(WinHeapDumpProvider::GetInstance()); #endif } diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi index 5cd8ca4..af06876 100644 --- a/base/trace_event/trace_event.gypi +++ b/base/trace_event/trace_event.gypi @@ -42,6 +42,8 @@ 'trace_event/trace_event_system_stats_monitor.h', 'trace_event/trace_event_win.cc', 'trace_event/trace_event_win.h', + 'trace_event/winheap_dump_provider_win.cc', + 'trace_event/winheap_dump_provider_win.h', ], 'conditions': [ ['OS == "linux" or OS == "android"', { @@ -63,6 +65,7 @@ 'trace_event/trace_event_system_stats_monitor_unittest.cc', 'trace_event/trace_event_unittest.cc', 'trace_event/trace_event_win_unittest.cc', + 'trace_event/winheap_dump_provider_win_unittest.cc', ], }, } diff --git a/base/trace_event/winheap_dump_provider_win.cc b/base/trace_event/winheap_dump_provider_win.cc new file mode 100644 index 0000000..9ce58c2 --- /dev/null +++ b/base/trace_event/winheap_dump_provider_win.cc @@ -0,0 +1,105 @@ +// Copyright 2015 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/winheap_dump_provider_win.h" + +#include <windows.h> + +#include "base/trace_event/process_memory_dump.h" + +namespace base { +namespace trace_event { + +namespace { + +const char kDumperFriendlyName[] = "winheap"; + +// Report a heap dump to a process memory dump. The |heap_info| structure +// contains the information about this heap, and |heap_name| will be used to +// represent it in the report. +bool ReportHeapDump(ProcessMemoryDump* pmd, + const WinHeapInfo& heap_info, + const std::string& heap_name) { + MemoryAllocatorDump* dump = + pmd->CreateAllocatorDump(kDumperFriendlyName, heap_name); + if (!dump) + return false; + dump->set_physical_size_in_bytes(heap_info.committed_size); + dump->set_allocated_objects_count(heap_info.block_count); + dump->set_allocated_objects_size_in_bytes(heap_info.allocated_size); + return true; +} + +} // namespace + +WinHeapDumpProvider* WinHeapDumpProvider::GetInstance() { + return Singleton<WinHeapDumpProvider, + LeakySingletonTraits<WinHeapDumpProvider>>::get(); +} + +bool WinHeapDumpProvider::DumpInto(ProcessMemoryDump* pmd) { + DCHECK_NE(reinterpret_cast<ProcessMemoryDump*>(nullptr), pmd); + + // Retrieves the number of heaps in the current process. + DWORD number_of_heaps = ::GetProcessHeaps(0, NULL); + WinHeapInfo all_heap_info = {0}; + + // Try to retrieve a handle to all the heaps owned by this process. Returns + // false if the number of heaps has changed. + // + // This is inherently racy as is, but it's not something that we observe a lot + // in Chrome, the heaps tend to be created at startup only. + scoped_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]); + if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps) + return false; + + // Skip the pointer to the heap array to avoid accounting the memory used by + // this dump provider. + std::set<void*> block_to_skip; + block_to_skip.insert(all_heaps.get()); + + // Retrieves some metrics about each heap. + for (size_t i = 0; i < number_of_heaps; ++i) { + WinHeapInfo heap_info = {0}; + heap_info.heap_id = all_heaps[i]; + GetHeapInformation(&heap_info, block_to_skip); + + all_heap_info.allocated_size += heap_info.allocated_size; + all_heap_info.committed_size += heap_info.committed_size; + all_heap_info.block_count += heap_info.block_count; + } + // Report the heap dump. + if (!ReportHeapDump(pmd, all_heap_info, MemoryAllocatorDump::kRootHeap)) + return false; + + return true; +} + +const char* WinHeapDumpProvider::GetFriendlyName() const { + return kDumperFriendlyName; +} + +bool WinHeapDumpProvider::GetHeapInformation( + WinHeapInfo* heap_info, + const std::set<void*>& block_to_skip) { + CHECK(::HeapLock(heap_info->heap_id) == TRUE); + PROCESS_HEAP_ENTRY heap_entry; + heap_entry.lpData = nullptr; + // Walk over all the entries in this heap. + while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) { + if (block_to_skip.count(heap_entry.lpData) == 1) + continue; + if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { + heap_info->allocated_size += heap_entry.cbData; + heap_info->block_count++; + } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { + heap_info->committed_size += heap_entry.Region.dwCommittedSize; + } + } + CHECK(::HeapUnlock(heap_info->heap_id) == TRUE); + return true; +} + +} // namespace trace_event +} // namespace base diff --git a/base/trace_event/winheap_dump_provider_win.h b/base/trace_event/winheap_dump_provider_win.h new file mode 100644 index 0000000..621ade7 --- /dev/null +++ b/base/trace_event/winheap_dump_provider_win.h @@ -0,0 +1,54 @@ +// Copyright 2015 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. + +#ifndef BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_ +#define BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_ + +#include <set> + +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { +namespace trace_event { + +// A structure containing some information about a given heap. +struct WinHeapInfo { + HANDLE heap_id; + size_t committed_size; + size_t allocated_size; + size_t block_count; +}; + +// Dump provider which collects process-wide heap memory stats. This provider +// iterates over all the heaps of the current process to gather some metrics +// about them. +class BASE_EXPORT WinHeapDumpProvider : public MemoryDumpProvider { + public: + static WinHeapDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool DumpInto(ProcessMemoryDump* pmd) override; + const char* GetFriendlyName() const override; + + private: + friend class WinHeapDumpProviderTest; + friend struct DefaultSingletonTraits<WinHeapDumpProvider>; + + // Retrieves the information about given heap. The |heap_info| should contain + // a valid handle to an existing heap. The blocks contained in the + // |block_to_skip| set will be ignored. + bool GetHeapInformation(WinHeapInfo* heap_info, + const std::set<void*>& block_to_skip); + + WinHeapDumpProvider() {} + ~WinHeapDumpProvider() override {} + + DISALLOW_COPY_AND_ASSIGN(WinHeapDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_ diff --git a/base/trace_event/winheap_dump_provider_win_unittest.cc b/base/trace_event/winheap_dump_provider_win_unittest.cc new file mode 100644 index 0000000..44d9c1b --- /dev/null +++ b/base/trace_event/winheap_dump_provider_win_unittest.cc @@ -0,0 +1,79 @@ +// Copyright 2015 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/winheap_dump_provider_win.h" + +#include <windows.h> + +#include "base/trace_event/memory_dump_session_state.h" +#include "base/trace_event/process_memory_dump.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +class WinHeapDumpProviderTest : public testing::Test { + public: + bool GetHeapInformation(WinHeapDumpProvider* provider, + WinHeapInfo* heap_info, + const std::set<void*>& block_to_skip) { + return provider->GetHeapInformation(heap_info, block_to_skip); + } +}; + +TEST_F(WinHeapDumpProviderTest, DumpInto) { + ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState())); + + WinHeapDumpProvider* winheap_dump_provider = + WinHeapDumpProvider::GetInstance(); + ASSERT_NE(reinterpret_cast<WinHeapDumpProvider*>(nullptr), + winheap_dump_provider); + + ASSERT_TRUE(winheap_dump_provider->DumpInto(&pmd)); +} + +TEST_F(WinHeapDumpProviderTest, GetHeapInformation) { + ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState())); + + WinHeapDumpProvider* winheap_dump_provider = + WinHeapDumpProvider::GetInstance(); + ASSERT_NE(reinterpret_cast<WinHeapDumpProvider*>(nullptr), + winheap_dump_provider); + + HANDLE heap = ::HeapCreate(NULL, 0, 0); + ASSERT_NE(nullptr, heap); + + const size_t kAllocSize = 42; + void* alloc = ::HeapAlloc(heap, 0, kAllocSize); + ASSERT_NE(nullptr, alloc); + + WinHeapInfo heap_info = {0}; + heap_info.heap_id = heap; + std::set<void*> block_to_skip; + + // Put the allocation into the skip list and make sure that the provider + // ignores it. + block_to_skip.insert(alloc); + ASSERT_TRUE( + GetHeapInformation(winheap_dump_provider, &heap_info, block_to_skip)); + EXPECT_EQ(0U, heap_info.block_count); + EXPECT_EQ(0U, heap_info.allocated_size); + // We can't check the committed size here, as it can depend on the version of + // kernel32.dll. + + // Remove the allocation from the skip list and check if it's analysed + // properlyly. + block_to_skip.erase(alloc); + ASSERT_TRUE( + GetHeapInformation(winheap_dump_provider, &heap_info, block_to_skip)); + EXPECT_EQ(1, heap_info.block_count); + EXPECT_EQ(kAllocSize, heap_info.allocated_size); + EXPECT_LT(kAllocSize, heap_info.committed_size); + + EXPECT_TRUE(::HeapFree(heap, 0, alloc)); + EXPECT_EQ(TRUE, ::HeapDestroy(heap)); +} + +} // namespace trace_event +} // namespace base |