diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc index 63d91bb44363eb..859e8e02dc75df 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 5cd8ca4373f190..af068762ad2498 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 00000000000000..9ce58c21da9488 --- /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 + +#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>::get(); +} + +bool WinHeapDumpProvider::DumpInto(ProcessMemoryDump* pmd) { + DCHECK_NE(reinterpret_cast(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 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 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& 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 00000000000000..621ade77373252 --- /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 + +#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; + + // 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& 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 00000000000000..44d9c1b2be8724 --- /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 + +#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& 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(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(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 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