| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/trace_event/malloc_dump_provider.h" | 5 #include "base/trace_event/malloc_dump_provider.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include "base/allocator/allocator_extension.h" | 9 #include "base/allocator/allocator_extension.h" |
| 10 #include "base/allocator/allocator_shim.h" | 10 #include "base/allocator/allocator_shim.h" |
| 11 #include "base/allocator/features.h" | 11 #include "base/allocator/features.h" |
| 12 #include "base/debug/profiler.h" | 12 #include "base/debug/profiler.h" |
| 13 #include "base/trace_event/heap_profiler_allocation_context.h" | 13 #include "base/trace_event/heap_profiler_allocation_context.h" |
| 14 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" | 14 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" |
| 15 #include "base/trace_event/heap_profiler_allocation_register.h" | 15 #include "base/trace_event/heap_profiler_allocation_register.h" |
| 16 #include "base/trace_event/heap_profiler_heap_dump_writer.h" | 16 #include "base/trace_event/heap_profiler_heap_dump_writer.h" |
| 17 #include "base/trace_event/process_memory_dump.h" | 17 #include "base/trace_event/process_memory_dump.h" |
| 18 #include "base/trace_event/trace_event_argument.h" | 18 #include "base/trace_event/trace_event_argument.h" |
| 19 #include "build/build_config.h" | 19 #include "build/build_config.h" |
| 20 | 20 |
| 21 #if defined(OS_MACOSX) | 21 #if defined(OS_MACOSX) |
| 22 #include <malloc/malloc.h> | 22 #include <malloc/malloc.h> |
| 23 #else | 23 #else |
| 24 #include <malloc.h> | 24 #include <malloc.h> |
| 25 #endif | 25 #endif |
| 26 #if defined(OS_WIN) | 26 #if defined(OS_WIN) |
| 27 #include <windows.h> | 27 #include <windows.h> |
| 28 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa366703 . |
| 29 #define HEAP_LFH 2 |
| 28 #endif | 30 #endif |
| 29 | 31 |
| 30 namespace base { | 32 namespace base { |
| 31 namespace trace_event { | 33 namespace trace_event { |
| 32 | 34 |
| 33 namespace { | 35 namespace { |
| 34 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) | 36 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) |
| 35 | 37 |
| 36 using allocator::AllocatorDispatch; | 38 using allocator::AllocatorDispatch; |
| 37 | 39 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 #if defined(OS_WIN) | 98 #if defined(OS_WIN) |
| 97 // A structure containing some information about a given heap. | 99 // A structure containing some information about a given heap. |
| 98 struct WinHeapInfo { | 100 struct WinHeapInfo { |
| 99 HANDLE heap_id; | 101 HANDLE heap_id; |
| 100 size_t committed_size; | 102 size_t committed_size; |
| 101 size_t uncommitted_size; | 103 size_t uncommitted_size; |
| 102 size_t allocated_size; | 104 size_t allocated_size; |
| 103 size_t block_count; | 105 size_t block_count; |
| 104 }; | 106 }; |
| 105 | 107 |
| 106 bool GetHeapInformation(WinHeapInfo* heap_info, | 108 bool TryHeapLock(HANDLE heap) { |
| 107 const std::set<void*>& block_to_skip) { | 109 HANDLE main_heap = ::GetProcessHeap(); |
| 110 |
| 111 // NOTE: crbug.com/665516 |
| 112 // We should never try to lock a heap created with HEAP_NO_SERIALIZE flag, |
| 113 // since behaviour of HeapLock is undefined and confirmed to be crashy. |
| 114 // Unfortunately, WinAPI lacks a function to tell the flags heap has been |
| 115 // created with, so we don't account any potentially unsafe heap. |
| 116 ULONG heap_info; |
| 117 if (!::HeapQueryInformation(heap, HeapCompatibilityInformation, &heap_info, |
| 118 sizeof(heap_info), nullptr)) { |
| 119 return false; |
| 120 } |
| 121 // Low-fragmentation heaps are used by default since Windows Vista and |
| 122 // incompatible with HEAP_NO_SERIALIZE flag, hence there is an indicator. |
| 123 // However, main heap is always accountable even if not LFH because |
| 124 // Windows Runtime should be able to spawn some utility threads implicitly. |
| 125 if (heap_info != HEAP_LFH && heap != main_heap) { |
| 126 return false; |
| 127 } |
| 128 |
| 108 // NOTE: crbug.com/464430 | 129 // NOTE: crbug.com/464430 |
| 109 // As a part of the Client/Server Runtine Subsystem (CSRSS) lockdown in the | 130 // As a part of the Client/Server Runtine Subsystem (CSRSS) lockdown in the |
| 110 // referenced bug, it will invalidate the heap used by CSRSS. The author has | 131 // referenced bug, it will invalidate the heap used by CSRSS. |
| 111 // not found a way to clean up an invalid heap handle, so it will be left in | 132 // HeapLock implicitly checks certain aspects of heap structure. |
| 112 // the process's heap list. Therefore we need to support when there is this | 133 // If this passes, we assume that this heap is valid and is not a heap |
| 113 // invalid heap handle in the heap list. | 134 // owned by CSRSS nor some another corrupted heap. |
| 114 // HeapLock implicitly checks certain aspects of the HEAP structure, such as | 135 if (!::HeapLock(heap)) { |
| 115 // the signature. If this passes, we assume that this heap is valid and is | 136 CHECK(heap != main_heap) << "Main WinHeap is not accessible."; |
| 116 // not the one owned by CSRSS. | 137 return false; |
| 117 if (!::HeapLock(heap_info->heap_id)) { | 138 } else { |
| 139 return true; |
| 140 } |
| 141 } |
| 142 |
| 143 bool GetWinHeapInformation(WinHeapInfo* heap_info, |
| 144 const std::set<void*>& block_to_skip) { |
| 145 // Some heaps aren't accountable, see TryHeapLock for details. |
| 146 if (!TryHeapLock(heap_info->heap_id)) { |
| 118 return false; | 147 return false; |
| 119 } | 148 } |
| 120 PROCESS_HEAP_ENTRY heap_entry; | 149 PROCESS_HEAP_ENTRY heap_entry; |
| 121 heap_entry.lpData = nullptr; | 150 heap_entry.lpData = nullptr; |
| 122 // Walk over all the entries in this heap. | 151 // Walk over all the entries in this heap. |
| 123 while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) { | 152 while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) { |
| 124 if (block_to_skip.count(heap_entry.lpData) == 1) | 153 if (block_to_skip.count(heap_entry.lpData) == 1) |
| 125 continue; | 154 continue; |
| 126 if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { | 155 if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { |
| 127 heap_info->allocated_size += heap_entry.cbData; | 156 heap_info->allocated_size += heap_entry.cbData; |
| 128 heap_info->block_count++; | 157 heap_info->block_count++; |
| 129 } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { | 158 } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { |
| 130 heap_info->committed_size += heap_entry.Region.dwCommittedSize; | 159 heap_info->committed_size += heap_entry.Region.dwCommittedSize; |
| 131 heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize; | 160 heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize; |
| 132 } | 161 } |
| 133 } | 162 } |
| 134 CHECK(::HeapUnlock(heap_info->heap_id) == TRUE); | 163 CHECK(::HeapUnlock(heap_info->heap_id) == TRUE); |
| 135 return true; | 164 return true; |
| 136 } | 165 } |
| 137 | 166 |
| 138 void WinHeapMemoryDumpImpl(WinHeapInfo* all_heap_info) { | 167 void WinHeapMemoryDumpImpl(WinHeapInfo* all_heap_info) { |
| 139 // This method might be flaky for 2 reasons: | 168 // This method might be flaky for 2 reasons: |
| 140 // - GetProcessHeaps is racy by design. It returns a snapshot of the | 169 // - GetProcessHeaps is racy by design. It returns a snapshot of the |
| 141 // available heaps, but there's no guarantee that that snapshot remains | 170 // available heaps, but there's no guarantee that that snapshot remains |
| 142 // valid. If a heap disappears between GetProcessHeaps() and HeapWalk() | 171 // valid. If a heap disappears between GetProcessHeaps() and HeapWalk() |
| 143 // then chaos should be assumed. This flakyness is acceptable for tracing. | 172 // then chaos should be assumed. This flakyness is acceptable for tracing. |
| 144 // - The MSDN page for HeapLock says: "If the HeapLock function is called on | |
| 145 // a heap created with the HEAP_NO_SERIALIZATION flag, the results are | |
| 146 // undefined." | |
| 147 // - Note that multiple heaps occur on Windows primarily because system and | 173 // - Note that multiple heaps occur on Windows primarily because system and |
| 148 // 3rd party DLLs will each create their own private heap. It's possible to | 174 // 3rd party DLLs will each create their own private heap. It's possible to |
| 149 // retrieve the heap the CRT allocates from and report specifically on that | 175 // retrieve the heap the CRT allocates from and report specifically on that |
| 150 // heap. It's interesting to report all heaps, as e.g. loading or invoking | 176 // heap. It's interesting to report all heaps, as e.g. loading or invoking |
| 151 // on a Windows API may consume memory from a private heap. | 177 // on a Windows API may consume memory from a private heap. |
| 152 #if defined(SYZYASAN) | 178 #if defined(SYZYASAN) |
| 153 if (base::debug::IsBinaryInstrumented()) | 179 if (base::debug::IsBinaryInstrumented()) |
| 154 return; | 180 return; |
| 155 #endif | 181 #endif |
| 156 | 182 |
| 157 // Retrieves the number of heaps in the current process. | 183 // Retrieves the number of heaps in the current process. |
| 158 DWORD number_of_heaps = ::GetProcessHeaps(0, NULL); | 184 DWORD number_of_heaps = ::GetProcessHeaps(0, NULL); |
| 159 | 185 |
| 160 // Try to retrieve a handle to all the heaps owned by this process. Returns | 186 // Try to retrieve a handle to all the heaps owned by this process. Returns |
| 161 // false if the number of heaps has changed. | 187 // false if the number of heaps has changed. |
| 162 // | 188 // |
| 163 // This is inherently racy as is, but it's not something that we observe a lot | 189 // This is inherently racy as is, but it's not something that we observe a lot |
| 164 // in Chrome, the heaps tend to be created at startup only. | 190 // in Chrome, the heaps tend to be created at startup only. |
| 165 std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]); | 191 std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]); |
| 166 if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps) | 192 if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps) |
| 167 return; | 193 return; |
| 168 | 194 |
| 169 // Skip the pointer to the heap array to avoid accounting the memory used by | 195 // Skip the pointer to the heap array to avoid accounting the memory used by |
| 170 // this dump provider. | 196 // this dump provider. |
| 171 std::set<void*> block_to_skip; | 197 std::set<void*> block_to_skip; |
| 172 block_to_skip.insert(all_heaps.get()); | 198 block_to_skip.insert(all_heaps.get()); |
| 173 | 199 |
| 174 // Retrieves some metrics about each heap. | 200 // Retrieves some metrics about each heap. |
| 175 size_t heap_info_errors = 0; | |
| 176 for (size_t i = 0; i < number_of_heaps; ++i) { | 201 for (size_t i = 0; i < number_of_heaps; ++i) { |
| 177 WinHeapInfo heap_info = {0}; | 202 WinHeapInfo heap_info = {0}; |
| 178 heap_info.heap_id = all_heaps[i]; | 203 heap_info.heap_id = all_heaps[i]; |
| 179 if (GetHeapInformation(&heap_info, block_to_skip)) { | 204 if (GetWinHeapInformation(&heap_info, block_to_skip)) { |
| 180 all_heap_info->allocated_size += heap_info.allocated_size; | 205 all_heap_info->allocated_size += heap_info.allocated_size; |
| 181 all_heap_info->committed_size += heap_info.committed_size; | 206 all_heap_info->committed_size += heap_info.committed_size; |
| 182 all_heap_info->uncommitted_size += heap_info.uncommitted_size; | 207 all_heap_info->uncommitted_size += heap_info.uncommitted_size; |
| 183 all_heap_info->block_count += heap_info.block_count; | 208 all_heap_info->block_count += heap_info.block_count; |
| 184 } else { | |
| 185 ++heap_info_errors; | |
| 186 // See notes in GetHeapInformation() but we only expect 1 heap to not be | |
| 187 // able to be read. | |
| 188 CHECK_EQ(1u, heap_info_errors); | |
| 189 } | 209 } |
| 190 } | 210 } |
| 191 } | 211 } |
| 192 #endif // defined(OS_WIN) | 212 #endif // defined(OS_WIN) |
| 193 } // namespace | 213 } // namespace |
| 194 | 214 |
| 195 // static | 215 // static |
| 196 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; | 216 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; |
| 197 | 217 |
| 198 // static | 218 // static |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 tid_dumping_heap_ == PlatformThread::CurrentId()) | 394 tid_dumping_heap_ == PlatformThread::CurrentId()) |
| 375 return; | 395 return; |
| 376 AutoLock lock(allocation_register_lock_); | 396 AutoLock lock(allocation_register_lock_); |
| 377 if (!allocation_register_) | 397 if (!allocation_register_) |
| 378 return; | 398 return; |
| 379 allocation_register_->Remove(address); | 399 allocation_register_->Remove(address); |
| 380 } | 400 } |
| 381 | 401 |
| 382 } // namespace trace_event | 402 } // namespace trace_event |
| 383 } // namespace base | 403 } // namespace base |
| OLD | NEW |