Chromium Code Reviews| 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" |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 &HookRealloc, /* realloc_function */ | 89 &HookRealloc, /* realloc_function */ |
| 90 &HookFree, /* free_function */ | 90 &HookFree, /* free_function */ |
| 91 &HookGetSizeEstimate, /* get_size_estimate_function */ | 91 &HookGetSizeEstimate, /* get_size_estimate_function */ |
| 92 nullptr, /* next */ | 92 nullptr, /* next */ |
| 93 }; | 93 }; |
| 94 #endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) | 94 #endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) |
| 95 | 95 |
| 96 #if defined(OS_WIN) | 96 #if defined(OS_WIN) |
| 97 // A structure containing some information about a given heap. | 97 // A structure containing some information about a given heap. |
| 98 struct WinHeapInfo { | 98 struct WinHeapInfo { |
| 99 HANDLE heap_id; | |
| 100 size_t committed_size; | 99 size_t committed_size; |
| 101 size_t uncommitted_size; | 100 size_t uncommitted_size; |
| 102 size_t allocated_size; | 101 size_t allocated_size; |
| 103 size_t block_count; | 102 size_t block_count; |
| 104 }; | 103 }; |
| 105 | 104 |
| 106 bool GetHeapInformation(WinHeapInfo* heap_info, | 105 // NOTE: crbug.com/665516 |
| 107 const std::set<void*>& block_to_skip) { | 106 // Unfortunately, there is no safe way to collect information from secondary |
| 108 // NOTE: crbug.com/464430 | 107 // heaps due to limitations and racy nature of this piece of WinAPI. |
| 109 // As a part of the Client/Server Runtine Subsystem (CSRSS) lockdown in the | 108 void WinHeapMemoryDumpImpl(WinHeapInfo* main_heap_info) { |
| 110 // referenced bug, it will invalidate the heap used by CSRSS. The author has | |
| 111 // not found a way to clean up an invalid heap handle, so it will be left in | |
| 112 // the process's heap list. Therefore we need to support when there is this | |
| 113 // invalid heap handle in the heap list. | |
| 114 // HeapLock implicitly checks certain aspects of the HEAP structure, such as | |
| 115 // the signature. If this passes, we assume that this heap is valid and is | |
| 116 // not the one owned by CSRSS. | |
| 117 if (!::HeapLock(heap_info->heap_id)) { | |
| 118 return false; | |
| 119 } | |
| 120 PROCESS_HEAP_ENTRY heap_entry; | |
| 121 heap_entry.lpData = nullptr; | |
| 122 // Walk over all the entries in this heap. | |
| 123 while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) { | |
| 124 if (block_to_skip.count(heap_entry.lpData) == 1) | |
| 125 continue; | |
| 126 if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { | |
| 127 heap_info->allocated_size += heap_entry.cbData; | |
| 128 heap_info->block_count++; | |
| 129 } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { | |
| 130 heap_info->committed_size += heap_entry.Region.dwCommittedSize; | |
| 131 heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize; | |
| 132 } | |
| 133 } | |
| 134 CHECK(::HeapUnlock(heap_info->heap_id) == TRUE); | |
| 135 return true; | |
| 136 } | |
| 137 | |
| 138 void WinHeapMemoryDumpImpl(WinHeapInfo* all_heap_info) { | |
| 139 // This method might be flaky for 2 reasons: | |
| 140 // - GetProcessHeaps is racy by design. It returns a snapshot of the | |
| 141 // available heaps, but there's no guarantee that that snapshot remains | |
| 142 // valid. If a heap disappears between GetProcessHeaps() and HeapWalk() | |
| 143 // 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 | |
| 148 // 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 | |
| 150 // 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. | |
| 152 #if defined(SYZYASAN) | 109 #if defined(SYZYASAN) |
| 153 if (base::debug::IsBinaryInstrumented()) | 110 if (base::debug::IsBinaryInstrumented()) |
| 154 return; | 111 return; |
| 155 #endif | 112 #endif |
| 113 HANDLE main_heap = ::GetProcessHeap(); | |
| 114 CHECK(main_heap != NULL); | |
|
Primiano Tucci (use gerrit)
2016/11/25 16:00:37
no need for these two CHECKs, it will crash anyway
| |
| 115 CHECK(::HeapLock(main_heap) == TRUE); | |
| 156 | 116 |
| 157 // Retrieves the number of heaps in the current process. | 117 PROCESS_HEAP_ENTRY heap_entry; |
| 158 DWORD number_of_heaps = ::GetProcessHeaps(0, NULL); | 118 heap_entry.lpData = nullptr; |
| 159 | 119 // Walk over all the entries in the main heap. |
| 160 // Try to retrieve a handle to all the heaps owned by this process. Returns | 120 while (::HeapWalk(main_heap, &heap_entry) != FALSE) { |
| 161 // false if the number of heaps has changed. | 121 if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { |
| 162 // | 122 main_heap_info->allocated_size += heap_entry.cbData; |
| 163 // This is inherently racy as is, but it's not something that we observe a lot | 123 main_heap_info->block_count++; |
| 164 // in Chrome, the heaps tend to be created at startup only. | 124 } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { |
| 165 std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]); | 125 main_heap_info->committed_size += heap_entry.Region.dwCommittedSize; |
| 166 if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps) | 126 main_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize; |
| 167 return; | |
| 168 | |
| 169 // Skip the pointer to the heap array to avoid accounting the memory used by | |
| 170 // this dump provider. | |
| 171 std::set<void*> block_to_skip; | |
| 172 block_to_skip.insert(all_heaps.get()); | |
| 173 | |
| 174 // Retrieves some metrics about each heap. | |
| 175 size_t heap_info_errors = 0; | |
| 176 for (size_t i = 0; i < number_of_heaps; ++i) { | |
| 177 WinHeapInfo heap_info = {0}; | |
| 178 heap_info.heap_id = all_heaps[i]; | |
| 179 if (GetHeapInformation(&heap_info, block_to_skip)) { | |
| 180 all_heap_info->allocated_size += heap_info.allocated_size; | |
| 181 all_heap_info->committed_size += heap_info.committed_size; | |
| 182 all_heap_info->uncommitted_size += heap_info.uncommitted_size; | |
| 183 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 } | 127 } |
| 190 } | 128 } |
| 129 CHECK(::HeapUnlock(main_heap) == TRUE); | |
| 191 } | 130 } |
| 192 #endif // defined(OS_WIN) | 131 #endif // defined(OS_WIN) |
| 193 } // namespace | 132 } // namespace |
| 194 | 133 |
| 195 // static | 134 // static |
| 196 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; | 135 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; |
| 197 | 136 |
| 198 // static | 137 // static |
| 199 MallocDumpProvider* MallocDumpProvider::GetInstance() { | 138 MallocDumpProvider* MallocDumpProvider::GetInstance() { |
| 200 return Singleton<MallocDumpProvider, | 139 return Singleton<MallocDumpProvider, |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 230 total_virtual_size = stats.size_allocated; | 169 total_virtual_size = stats.size_allocated; |
| 231 allocated_objects_size = stats.size_in_use; | 170 allocated_objects_size = stats.size_in_use; |
| 232 | 171 |
| 233 // The resident size is approximated to the max size in use, which would count | 172 // The resident size is approximated to the max size in use, which would count |
| 234 // the total size of all regions other than the free bytes at the end of each | 173 // the total size of all regions other than the free bytes at the end of each |
| 235 // region. In each allocation region the allocations are rounded off to a | 174 // region. In each allocation region the allocations are rounded off to a |
| 236 // fixed quantum, so the excess region will not be resident. | 175 // fixed quantum, so the excess region will not be resident. |
| 237 // See crrev.com/1531463004 for detailed explanation. | 176 // See crrev.com/1531463004 for detailed explanation. |
| 238 resident_size = stats.max_size_in_use; | 177 resident_size = stats.max_size_in_use; |
| 239 #elif defined(OS_WIN) | 178 #elif defined(OS_WIN) |
| 240 WinHeapInfo all_heap_info = {}; | 179 WinHeapInfo main_heap_info = {}; |
| 241 WinHeapMemoryDumpImpl(&all_heap_info); | 180 WinHeapMemoryDumpImpl(&main_heap_info); |
| 242 total_virtual_size = | 181 total_virtual_size = |
| 243 all_heap_info.committed_size + all_heap_info.uncommitted_size; | 182 main_heap_info.committed_size + main_heap_info.uncommitted_size; |
| 244 // Resident size is approximated with committed heap size. Note that it is | 183 // Resident size is approximated with committed heap size. Note that it is |
| 245 // possible to do this with better accuracy on windows by intersecting the | 184 // possible to do this with better accuracy on windows by intersecting the |
| 246 // working set with the virtual memory ranges occuipied by the heap. It's not | 185 // working set with the virtual memory ranges occuipied by the heap. It's not |
| 247 // clear that this is worth it, as it's fairly expensive to do. | 186 // clear that this is worth it, as it's fairly expensive to do. |
| 248 resident_size = all_heap_info.committed_size; | 187 resident_size = main_heap_info.committed_size; |
| 249 allocated_objects_size = all_heap_info.allocated_size; | 188 allocated_objects_size = main_heap_info.allocated_size; |
| 250 allocated_objects_count = all_heap_info.block_count; | 189 allocated_objects_count = main_heap_info.block_count; |
| 251 #else | 190 #else |
| 252 struct mallinfo info = mallinfo(); | 191 struct mallinfo info = mallinfo(); |
| 253 DCHECK_GE(info.arena + info.hblkhd, info.uordblks); | 192 DCHECK_GE(info.arena + info.hblkhd, info.uordblks); |
| 254 | 193 |
| 255 // In case of Android's jemalloc |arena| is 0 and the outer pages size is | 194 // In case of Android's jemalloc |arena| is 0 and the outer pages size is |
| 256 // reported by |hblkhd|. In case of dlmalloc the total is given by | 195 // reported by |hblkhd|. In case of dlmalloc the total is given by |
| 257 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF. | 196 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF. |
| 258 total_virtual_size = info.arena + info.hblkhd; | 197 total_virtual_size = info.arena + info.hblkhd; |
| 259 resident_size = info.uordblks; | 198 resident_size = info.uordblks; |
| 260 | 199 |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 374 tid_dumping_heap_ == PlatformThread::CurrentId()) | 313 tid_dumping_heap_ == PlatformThread::CurrentId()) |
| 375 return; | 314 return; |
| 376 AutoLock lock(allocation_register_lock_); | 315 AutoLock lock(allocation_register_lock_); |
| 377 if (!allocation_register_) | 316 if (!allocation_register_) |
| 378 return; | 317 return; |
| 379 allocation_register_->Remove(address); | 318 allocation_register_->Remove(address); |
| 380 } | 319 } |
| 381 | 320 |
| 382 } // namespace trace_event | 321 } // namespace trace_event |
| 383 } // namespace base | 322 } // namespace base |
| OLD | NEW |