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

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: Address Primiano's nits. 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 // - Note that multiple heaps occur on Windows primarily because system and
130 // 3rd party DLLs will each create their own private heap. It's possible to
131 // retrieve the heap the CRT allocates from and report specifically on that
132 // heap. It's interesting to report all heaps, as e.g. loading or invoking
133 // on a Windows API may consume memory from a private heap.
134 #if defined(SYZYASAN)
135 if (base::debug::IsBinaryInstrumented())
136 return;
137 #endif
138
139 // Retrieves the number of heaps in the current process.
140 DWORD number_of_heaps = ::GetProcessHeaps(0, NULL);
141
142 // Try to retrieve a handle to all the heaps owned by this process. Returns
143 // false if the number of heaps has changed.
144 //
145 // This is inherently racy as is, but it's not something that we observe a lot
146 // in Chrome, the heaps tend to be created at startup only.
147 std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]);
148 if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps)
149 return;
150
151 // Skip the pointer to the heap array to avoid accounting the memory used by
152 // this dump provider.
153 std::set<void*> block_to_skip;
154 block_to_skip.insert(all_heaps.get());
155
156 // Retrieves some metrics about each heap.
157 for (size_t i = 0; i < number_of_heaps; ++i) {
158 WinHeapInfo heap_info = {0};
159 heap_info.heap_id = all_heaps[i];
160 GetHeapInformation(&heap_info, block_to_skip);
161
162 all_heap_info->allocated_size += heap_info.allocated_size;
163 all_heap_info->committed_size += heap_info.committed_size;
164 all_heap_info->uncommitted_size += heap_info.uncommitted_size;
165 all_heap_info->block_count += heap_info.block_count;
166 }
167 }
168 #endif // defined(OS_WIN)
85 } // namespace 169 } // namespace
86 #endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
87 170
88 // static 171 // static
89 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; 172 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
90 173
91 // static 174 // static
92 MallocDumpProvider* MallocDumpProvider::GetInstance() { 175 MallocDumpProvider* MallocDumpProvider::GetInstance() {
93 return Singleton<MallocDumpProvider, 176 return Singleton<MallocDumpProvider,
94 LeakySingletonTraits<MallocDumpProvider>>::get(); 177 LeakySingletonTraits<MallocDumpProvider>>::get();
95 } 178 }
96 179
97 MallocDumpProvider::MallocDumpProvider() 180 MallocDumpProvider::MallocDumpProvider()
98 : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {} 181 : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {}
99 182
100 MallocDumpProvider::~MallocDumpProvider() {} 183 MallocDumpProvider::~MallocDumpProvider() {}
101 184
102 // Called at trace dump point time. Creates a snapshot the memory counters for 185 // Called at trace dump point time. Creates a snapshot the memory counters for
103 // the current process. 186 // the current process.
104 bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args, 187 bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
105 ProcessMemoryDump* pmd) { 188 ProcessMemoryDump* pmd) {
106 size_t total_virtual_size = 0; 189 size_t total_virtual_size = 0;
107 size_t resident_size = 0; 190 size_t resident_size = 0;
108 size_t allocated_objects_size = 0; 191 size_t allocated_objects_size = 0;
192 size_t allocated_objects_count = 0;
109 #if defined(USE_TCMALLOC) 193 #if defined(USE_TCMALLOC)
110 bool res = 194 bool res =
111 allocator::GetNumericProperty("generic.heap_size", &total_virtual_size); 195 allocator::GetNumericProperty("generic.heap_size", &total_virtual_size);
112 DCHECK(res); 196 DCHECK(res);
113 res = allocator::GetNumericProperty("generic.total_physical_bytes", 197 res = allocator::GetNumericProperty("generic.total_physical_bytes",
114 &resident_size); 198 &resident_size);
115 DCHECK(res); 199 DCHECK(res);
116 res = allocator::GetNumericProperty("generic.current_allocated_bytes", 200 res = allocator::GetNumericProperty("generic.current_allocated_bytes",
117 &allocated_objects_size); 201 &allocated_objects_size);
118 DCHECK(res); 202 DCHECK(res);
119 #elif defined(OS_MACOSX) || defined(OS_IOS) 203 #elif defined(OS_MACOSX) || defined(OS_IOS)
120 malloc_statistics_t stats = {0}; 204 malloc_statistics_t stats = {0};
121 malloc_zone_statistics(nullptr, &stats); 205 malloc_zone_statistics(nullptr, &stats);
122 total_virtual_size = stats.size_allocated; 206 total_virtual_size = stats.size_allocated;
123 allocated_objects_size = stats.size_in_use; 207 allocated_objects_size = stats.size_in_use;
124 208
125 // The resident size is approximated to the max size in use, which would count 209 // 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 210 // 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 211 // region. In each allocation region the allocations are rounded off to a
128 // fixed quantum, so the excess region will not be resident. 212 // fixed quantum, so the excess region will not be resident.
129 // See crrev.com/1531463004 for detailed explanation. 213 // See crrev.com/1531463004 for detailed explanation.
130 resident_size = stats.max_size_in_use; 214 resident_size = stats.max_size_in_use;
215 #elif defined(OS_WIN)
216 WinHeapInfo all_heap_info = {};
217 WinHeapMemoryDumpImpl(&all_heap_info);
218 total_virtual_size =
219 all_heap_info.committed_size + all_heap_info.uncommitted_size;
220 // Resident size is approximated with committed heap size. Note that it is
221 // possible to do this with better accuracy on windows by intersecting the
222 // working set with the virtual memory ranges occuipied by the heap. It's not
223 // clear that this is worth it, as it's fairly expensive to do.
224 resident_size = all_heap_info.committed_size;
225 allocated_objects_size = all_heap_info.allocated_size;
226 allocated_objects_count = all_heap_info.block_count;
131 #else 227 #else
132 struct mallinfo info = mallinfo(); 228 struct mallinfo info = mallinfo();
133 DCHECK_GE(info.arena + info.hblkhd, info.uordblks); 229 DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
134 230
135 // In case of Android's jemalloc |arena| is 0 and the outer pages size is 231 // 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 232 // reported by |hblkhd|. In case of dlmalloc the total is given by
137 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF. 233 // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
138 total_virtual_size = info.arena + info.hblkhd; 234 total_virtual_size = info.arena + info.hblkhd;
139 resident_size = info.uordblks; 235 resident_size = info.uordblks;
236
237 // Total allocated space is given by |uordblks|.
140 allocated_objects_size = info.uordblks; 238 allocated_objects_size = info.uordblks;
141 #endif 239 #endif
142 240
143 MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc"); 241 MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
144 outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes, 242 outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
145 total_virtual_size); 243 total_virtual_size);
146 outer_dump->AddScalar(MemoryAllocatorDump::kNameSize, 244 outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
147 MemoryAllocatorDump::kUnitsBytes, resident_size); 245 MemoryAllocatorDump::kUnitsBytes, resident_size);
148 246
149 // Total allocated space is given by |uordblks|.
150 MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects); 247 MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects);
151 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize, 248 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
152 MemoryAllocatorDump::kUnitsBytes, 249 MemoryAllocatorDump::kUnitsBytes,
153 allocated_objects_size); 250 allocated_objects_size);
251 if (allocated_objects_count != 0) {
252 inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
253 MemoryAllocatorDump::kUnitsObjects,
254 allocated_objects_count);
255 }
154 256
155 if (resident_size - allocated_objects_size > 0) { 257 if (resident_size - allocated_objects_size > 0) {
156 // Explicitly specify why is extra memory resident. In tcmalloc it accounts 258 // 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 259 // for free lists and caches. In mac and ios it accounts for the
158 // fragmentation and metadata. 260 // fragmentation and metadata.
159 MemoryAllocatorDump* other_dump = 261 MemoryAllocatorDump* other_dump =
160 pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches"); 262 pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches");
161 other_dump->AddScalar(MemoryAllocatorDump::kNameSize, 263 other_dump->AddScalar(MemoryAllocatorDump::kNameSize,
162 MemoryAllocatorDump::kUnitsBytes, 264 MemoryAllocatorDump::kUnitsBytes,
163 resident_size - allocated_objects_size); 265 resident_size - allocated_objects_size);
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 tid_dumping_heap_ == PlatformThread::CurrentId()) 350 tid_dumping_heap_ == PlatformThread::CurrentId())
249 return; 351 return;
250 AutoLock lock(allocation_register_lock_); 352 AutoLock lock(allocation_register_lock_);
251 if (!allocation_register_) 353 if (!allocation_register_)
252 return; 354 return;
253 allocation_register_->Remove(address); 355 allocation_register_->Remove(address);
254 } 356 }
255 357
256 } // namespace trace_event 358 } // namespace trace_event
257 } // namespace base 359 } // 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