Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(779)

Side by Side Diff: base/trace_event/malloc_dump_provider.cc

Issue 2273703003: Hook memory tracing into Windows heap shim. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Pull Win memory dump implementation out of shim buildflag. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/trace_event/malloc_dump_provider.h ('k') | base/trace_event/memory_dump_manager.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/trace_event/heap_profiler_allocation_context.h" 12 #include "base/trace_event/heap_profiler_allocation_context.h"
13 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" 13 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
14 #include "base/trace_event/heap_profiler_allocation_register.h" 14 #include "base/trace_event/heap_profiler_allocation_register.h"
15 #include "base/trace_event/heap_profiler_heap_dump_writer.h" 15 #include "base/trace_event/heap_profiler_heap_dump_writer.h"
16 #include "base/trace_event/process_memory_dump.h" 16 #include "base/trace_event/process_memory_dump.h"
17 #include "base/trace_event/trace_event_argument.h" 17 #include "base/trace_event/trace_event_argument.h"
18 #include "build/build_config.h" 18 #include "build/build_config.h"
19 19
20 #if defined(OS_MACOSX) 20 #if defined(OS_MACOSX)
21 #include <malloc/malloc.h> 21 #include <malloc/malloc.h>
22 #else 22 #else
23 #include <malloc.h> 23 #include <malloc.h>
24 #endif 24 #endif
25 #if defined(OS_WIN)
26 #include <windows.h>
27 #endif
25 28
26 namespace base { 29 namespace base {
27 namespace trace_event { 30 namespace trace_event {
28 31
32 namespace {
29 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) 33 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
30 namespace {
31 34
32 using allocator::AllocatorDispatch; 35 using allocator::AllocatorDispatch;
33 36
34 void* HookAlloc(const AllocatorDispatch* self, size_t size) { 37 void* HookAlloc(const AllocatorDispatch* self, size_t size) {
35 const AllocatorDispatch* const next = self->next; 38 const AllocatorDispatch* const next = self->next;
36 void* ptr = next->alloc_function(next, size); 39 void* ptr = next->alloc_function(next, size);
37 if (ptr) 40 if (ptr)
38 MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size); 41 MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
39 return ptr; 42 return ptr;
40 } 43 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 } 77 }
75 78
76 AllocatorDispatch g_allocator_hooks = { 79 AllocatorDispatch g_allocator_hooks = {
77 &HookAlloc, /* alloc_function */ 80 &HookAlloc, /* alloc_function */
78 &HookZeroInitAlloc, /* alloc_zero_initialized_function */ 81 &HookZeroInitAlloc, /* alloc_zero_initialized_function */
79 &HookllocAligned, /* alloc_aligned_function */ 82 &HookllocAligned, /* alloc_aligned_function */
80 &HookRealloc, /* realloc_function */ 83 &HookRealloc, /* realloc_function */
81 &HookFree, /* free_function */ 84 &HookFree, /* free_function */
82 nullptr, /* next */ 85 nullptr, /* next */
83 }; 86 };
87 #endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
84 88
89 #if defined(OS_WIN)
90 // A structure containing some information about a given heap.
91 struct WinHeapInfo {
92 HANDLE heap_id;
93 size_t committed_size;
94 size_t uncommitted_size;
95 size_t allocated_size;
96 size_t block_count;
97 };
98
99 bool GetHeapInformation(WinHeapInfo* heap_info,
100 const std::set<void*>& block_to_skip) {
101 CHECK(::HeapLock(heap_info->heap_id) == TRUE);
102 PROCESS_HEAP_ENTRY heap_entry;
103 heap_entry.lpData = nullptr;
104 // Walk over all the entries in this heap.
105 while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) {
106 if (block_to_skip.count(heap_entry.lpData) == 1)
107 continue;
108 if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
109 heap_info->allocated_size += heap_entry.cbData;
110 heap_info->block_count++;
111 } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
112 heap_info->committed_size += heap_entry.Region.dwCommittedSize;
113 heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
114 }
115 }
116 CHECK(::HeapUnlock(heap_info->heap_id) == TRUE);
117 return true;
118 }
119
120 void WinHeapMemoryDumpImpl(WinHeapInfo* all_heap_info) {
121 // This method might be flaky for 2 reasons:
122 // - GetProcessHeaps is racy by design. It returns a snapshot of the
123 // available heaps, but there's no guarantee that that snapshot remains
124 // valid. If a heap disappears between GetProcessHeaps() and HeapWalk()
125 // then chaos should be assumed. This flakyness is acceptable for tracing.
126 // - The MSDN page for HeapLock says: "If the HeapLock function is called on
127 // a heap created with the HEAP_NO_SERIALIZATION flag, the results are
128 // undefined."
129 #if defined(SYZYASAN)
130 if (base::debug::IsBinaryInstrumented())
131 return;
132 #endif
133
134 // Retrieves the number of heaps in the current process.
135 DWORD number_of_heaps = ::GetProcessHeaps(0, NULL);
136
137 // Try to retrieve a handle to all the heaps owned by this process. Returns
138 // false if the number of heaps has changed.
139 //
140 // This is inherently racy as is, but it's not something that we observe a lot
141 // in Chrome, the heaps tend to be created at startup only.
142 std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]);
143 if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps)
144 return;
145
146 // Skip the pointer to the heap array to avoid accounting the memory used by
147 // this dump provider.
148 std::set<void*> block_to_skip;
149 block_to_skip.insert(all_heaps.get());
150
151 // Retrieves some metrics about each heap.
152 for (size_t i = 0; i < number_of_heaps; ++i) {
Primiano Tucci (use gerrit) 2016/08/25 16:49:07 note for the future: I've no idea what these multi
Sigurður Ásgeirsson 2016/08/25 17:35:52 Added a lengthy comment at the top of the function
153 WinHeapInfo heap_info = {0};
154 heap_info.heap_id = all_heaps[i];
155 GetHeapInformation(&heap_info, block_to_skip);
156
157 all_heap_info->allocated_size += heap_info.allocated_size;
158 all_heap_info->committed_size += heap_info.committed_size;
159 all_heap_info->uncommitted_size += heap_info.uncommitted_size;
160 all_heap_info->block_count += heap_info.block_count;
161 }
162 }
163 #endif // defined(OS_WIN)
85 } // namespace 164 } // namespace
86 #endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
87 165
88 // static 166 // static
89 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; 167 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
90 168
91 // static 169 // static
92 MallocDumpProvider* MallocDumpProvider::GetInstance() { 170 MallocDumpProvider* MallocDumpProvider::GetInstance() {
93 return Singleton<MallocDumpProvider, 171 return Singleton<MallocDumpProvider,
94 LeakySingletonTraits<MallocDumpProvider>>::get(); 172 LeakySingletonTraits<MallocDumpProvider>>::get();
95 } 173 }
96 174
97 MallocDumpProvider::MallocDumpProvider() 175 MallocDumpProvider::MallocDumpProvider()
98 : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {} 176 : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {}
99 177
100 MallocDumpProvider::~MallocDumpProvider() {} 178 MallocDumpProvider::~MallocDumpProvider() {}
101 179
102 // Called at trace dump point time. Creates a snapshot the memory counters for 180 // Called at trace dump point time. Creates a snapshot the memory counters for
103 // the current process. 181 // the current process.
104 bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args, 182 bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
105 ProcessMemoryDump* pmd) { 183 ProcessMemoryDump* pmd) {
106 size_t total_virtual_size = 0; 184 size_t total_virtual_size = 0;
107 size_t resident_size = 0; 185 size_t resident_size = 0;
108 size_t allocated_objects_size = 0; 186 size_t allocated_objects_size = 0;
187 size_t allocated_objects_count = 0;
109 #if defined(USE_TCMALLOC) 188 #if defined(USE_TCMALLOC)
110 bool res = 189 bool res =
111 allocator::GetNumericProperty("generic.heap_size", &total_virtual_size); 190 allocator::GetNumericProperty("generic.heap_size", &total_virtual_size);
112 DCHECK(res); 191 DCHECK(res);
113 res = allocator::GetNumericProperty("generic.total_physical_bytes", 192 res = allocator::GetNumericProperty("generic.total_physical_bytes",
114 &resident_size); 193 &resident_size);
115 DCHECK(res); 194 DCHECK(res);
116 res = allocator::GetNumericProperty("generic.current_allocated_bytes", 195 res = allocator::GetNumericProperty("generic.current_allocated_bytes",
117 &allocated_objects_size); 196 &allocated_objects_size);
118 DCHECK(res); 197 DCHECK(res);
119 #elif defined(OS_MACOSX) || defined(OS_IOS) 198 #elif defined(OS_MACOSX) || defined(OS_IOS)
120 malloc_statistics_t stats = {0}; 199 malloc_statistics_t stats = {0};
121 malloc_zone_statistics(nullptr, &stats); 200 malloc_zone_statistics(nullptr, &stats);
122 total_virtual_size = stats.size_allocated; 201 total_virtual_size = stats.size_allocated;
123 allocated_objects_size = stats.size_in_use; 202 allocated_objects_size = stats.size_in_use;
124 203
125 // The resident size is approximated to the max size in use, which would count 204 // The resident size is approximated to the max size in use, which would count
126 // the total size of all regions other than the free bytes at the end of each 205 // the total size of all regions other than the free bytes at the end of each
127 // region. In each allocation region the allocations are rounded off to a 206 // region. In each allocation region the allocations are rounded off to a
128 // fixed quantum, so the excess region will not be resident. 207 // fixed quantum, so the excess region will not be resident.
129 // See crrev.com/1531463004 for detailed explanation. 208 // See crrev.com/1531463004 for detailed explanation.
130 resident_size = stats.max_size_in_use; 209 resident_size = stats.max_size_in_use;
210 #elif defined(OS_WIN)
211 WinHeapInfo all_heap_info = {};
212 WinHeapMemoryDumpImpl(&all_heap_info);
213 total_virtual_size =
214 all_heap_info.committed_size + all_heap_info.uncommitted_size;
215 // Resident size is approximated with committed heap size. Note that it is
216 // possible to do this with better accuracy on windows by intersecting the
217 // working set with the virtual memory ranges occuipied by the heap. It's not
218 // clear that this is worth it, as it's fairly expensive to do.
219 resident_size = all_heap_info.committed_size;
220 allocated_objects_size = all_heap_info.allocated_size;
221 allocated_objects_count = all_heap_info.block_count;
131 #else 222 #else
132 struct mallinfo info = mallinfo(); 223 struct mallinfo info = mallinfo();
133 DCHECK_GE(info.arena + info.hblkhd, info.uordblks); 224 DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
134 225
135 // In case of Android's jemalloc |arena| is 0 and the outer pages size is 226 // In case of Android's jemalloc |arena| is 0 and the outer pages size is
136 // reported by |hblkhd|. In case of dlmalloc the total is given by 227 // reported by |hblkhd|. In case of dlmalloc the total is given by
137 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF. 228 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
138 total_virtual_size = info.arena + info.hblkhd; 229 total_virtual_size = info.arena + info.hblkhd;
139 resident_size = info.uordblks; 230 resident_size = info.uordblks;
231 // Total allocated space is given by |uordblks|.
Primiano Tucci (use gerrit) 2016/08/25 16:49:07 nit add newline before this line.
Sigurður Ásgeirsson 2016/08/25 17:35:52 Done.
140 allocated_objects_size = info.uordblks; 232 allocated_objects_size = info.uordblks;
141 #endif 233 #endif
142 234
143 MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc"); 235 MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
144 outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes, 236 outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
145 total_virtual_size); 237 total_virtual_size);
146 outer_dump->AddScalar(MemoryAllocatorDump::kNameSize, 238 outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
147 MemoryAllocatorDump::kUnitsBytes, resident_size); 239 MemoryAllocatorDump::kUnitsBytes, resident_size);
148 240
149 // Total allocated space is given by |uordblks|.
150 MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects); 241 MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects);
151 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize, 242 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
152 MemoryAllocatorDump::kUnitsBytes, 243 MemoryAllocatorDump::kUnitsBytes,
153 allocated_objects_size); 244 allocated_objects_size);
245 if (allocated_objects_count != 0) {
246 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
247 MemoryAllocatorDump::kUnitsObjects,
248 allocated_objects_count);
249 }
154 250
155 if (resident_size - allocated_objects_size > 0) { 251 if (resident_size - allocated_objects_size > 0) {
156 // Explicitly specify why is extra memory resident. In tcmalloc it accounts 252 // Explicitly specify why is extra memory resident. In tcmalloc it accounts
157 // for free lists and caches. In mac and ios it accounts for the 253 // for free lists and caches. In mac and ios it accounts for the
158 // fragmentation and metadata. 254 // fragmentation and metadata.
159 MemoryAllocatorDump* other_dump = 255 MemoryAllocatorDump* other_dump =
160 pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches"); 256 pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches");
161 other_dump->AddScalar(MemoryAllocatorDump::kNameSize, 257 other_dump->AddScalar(MemoryAllocatorDump::kNameSize,
162 MemoryAllocatorDump::kUnitsBytes, 258 MemoryAllocatorDump::kUnitsBytes,
163 resident_size - allocated_objects_size); 259 resident_size - allocated_objects_size);
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 tid_dumping_heap_ == PlatformThread::CurrentId()) 344 tid_dumping_heap_ == PlatformThread::CurrentId())
249 return; 345 return;
250 AutoLock lock(allocation_register_lock_); 346 AutoLock lock(allocation_register_lock_);
251 if (!allocation_register_) 347 if (!allocation_register_)
252 return; 348 return;
253 allocation_register_->Remove(address); 349 allocation_register_->Remove(address);
254 } 350 }
255 351
256 } // namespace trace_event 352 } // namespace trace_event
257 } // namespace base 353 } // namespace base
OLDNEW
« no previous file with comments | « base/trace_event/malloc_dump_provider.h ('k') | base/trace_event/memory_dump_manager.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698