Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/debug/scoped_heap_usage.h" | |
| 6 | |
| 7 #include <malloc.h> | |
| 8 #include <stdint.h> | |
| 9 // TODO(siggi): DO NOT SUBMIT | |
| 10 #include <windows.h> | |
| 11 | |
| 12 #include "base/allocator/allocator_shim.h" | |
| 13 #include "base/lazy_instance.h" | |
| 14 #include "base/threading/thread_local.h" | |
| 15 | |
| 16 namespace base { | |
| 17 namespace debug { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 using base::allocator::AllocatorDispatch; | |
| 22 | |
| 23 base::LazyInstance<base::ThreadLocalPointer<ScopedHeapUsage::AllocatorUsage>>:: | |
| 24 Leaky g_thread_allocator_usage = LAZY_INSTANCE_INITIALIZER; | |
| 25 | |
| 26 // Forward declared as it needs to delegate memory allocation to the next | |
| 27 // lower shim. | |
| 28 ScopedHeapUsage::AllocatorUsage* GetOrCreateThreadUsage(); | |
| 29 | |
| 30 // DO NOT SUBMIT. | |
| 31 // This functionality needs to be exposed on the heap shims. Note that under the | |
| 32 // linux libc, it appears the heap doesn't keep track of the user size, but | |
| 33 // instead allows querying the actual user size of the allocation with | |
|
chrisha
2016/07/21 14:23:30
Did you mean: s/actual user size/actual size/
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
| |
| 34 // malloc_usable_size. I'm not sure how best to deal with this. | |
|
chrisha
2016/07/21 14:23:31
Maybe by making allocated and freed track actual b
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
So the problem here is that on other platforms mal
Sigurður Ásgeirsson
2016/08/19 18:21:49
Yeah - I think this'll be platform dependent enoug
Sigurður Ásgeirsson
2016/08/19 18:21:49
Side-tracking of individual allocs is a no-go for
| |
| 35 size_t GetAllocSize(void* ptr) { | |
| 36 if (ptr == nullptr) | |
| 37 return 0U; | |
| 38 | |
| 39 HANDLE heap_handle = reinterpret_cast<HANDLE>(_get_heap_handle()); | |
| 40 return ::HeapSize(heap_handle, 0, ptr); | |
| 41 } | |
| 42 | |
| 43 size_t EstimateOverhead(size_t size) { | |
|
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
this is going to be very tricky on tcmalloc (Linux
Sigurður Ásgeirsson
2016/08/19 18:21:49
Yeah, it doesn't have to be very accurate though.
| |
| 44 // TODO(siggi): Less Windows specific! | |
| 45 const size_t kHeaderSize = 8; | |
| 46 #if defined(ARCH_CPU_64_BITS) | |
| 47 const size_t kAllocationGranularity = 16; | |
| 48 #else | |
| 49 const size_t kAllocationGranularity = 8; | |
| 50 #endif | |
| 51 size_t overhead = 0; | |
| 52 if (size % kAllocationGranularity != 0) | |
| 53 overhead = kAllocationGranularity - (size % kAllocationGranularity); | |
| 54 | |
| 55 // This is a lower-bound estimate on the Windows heap overhead. | |
| 56 return kHeaderSize + overhead; | |
| 57 } | |
| 58 | |
| 59 void RecordAlloc(size_t size) { | |
| 60 ScopedHeapUsage::AllocatorUsage* usage = GetOrCreateThreadUsage(); | |
| 61 if (usage == nullptr) | |
| 62 return; | |
| 63 | |
| 64 usage->alloc_ops++; | |
| 65 usage->alloc_bytes += size; | |
| 66 usage->alloc_overhead_bytes += EstimateOverhead(size); | |
| 67 | |
| 68 uint64_t allocated_bytes = usage->alloc_bytes - usage->free_bytes; | |
| 69 if (allocated_bytes > usage->max_allocated_bytes) | |
| 70 usage->max_allocated_bytes = allocated_bytes; | |
| 71 } | |
| 72 | |
| 73 void RecordFree(size_t size) { | |
| 74 ScopedHeapUsage::AllocatorUsage* usage = GetOrCreateThreadUsage(); | |
| 75 if (usage == nullptr) | |
| 76 return; | |
| 77 | |
| 78 usage->free_ops++; | |
| 79 usage->free_bytes += size; | |
| 80 } | |
| 81 | |
| 82 void* AllocFn(const AllocatorDispatch* self, size_t size) { | |
| 83 void* ret = self->next->alloc_function(self, size); | |
| 84 if (ret != nullptr) | |
| 85 RecordAlloc(size); | |
| 86 | |
| 87 return ret; | |
| 88 } | |
| 89 | |
| 90 void* AllocZeroInitializedFn(const AllocatorDispatch* self, | |
| 91 size_t n, | |
| 92 size_t size) { | |
| 93 void* ret = self->next->alloc_zero_initialized_function(self, n, size); | |
| 94 if (ret != nullptr) | |
| 95 RecordAlloc(n * size); | |
| 96 | |
| 97 return ret; | |
| 98 } | |
| 99 | |
| 100 void* AllocAlignedFn(const AllocatorDispatch* self, | |
| 101 size_t alignment, | |
| 102 size_t size) { | |
| 103 void* ret = self->next->alloc_aligned_function(self, alignment, size); | |
| 104 if (ret != nullptr) | |
| 105 RecordAlloc(size); | |
| 106 | |
| 107 return ret; | |
| 108 } | |
| 109 | |
| 110 void* ReallocFn(const AllocatorDispatch* self, void* address, size_t size) { | |
| 111 if (address != nullptr) | |
| 112 RecordFree(GetAllocSize(address)); | |
| 113 | |
| 114 void* ret = self->next->realloc_function(self, address, size); | |
| 115 if (ret != nullptr) | |
| 116 RecordAlloc(size); | |
| 117 | |
| 118 return ret; | |
| 119 } | |
| 120 | |
| 121 void FreeFn(const AllocatorDispatch* self, void* address) { | |
| 122 size_t alloc_size = GetAllocSize(address); | |
| 123 self->next->free_function(self, address); | |
| 124 RecordFree(alloc_size); | |
| 125 } | |
| 126 | |
| 127 // The dispatch for the heap interept used. | |
| 128 AllocatorDispatch allocator_dispatch = { | |
| 129 &AllocFn, &AllocZeroInitializedFn, &AllocAlignedFn, &ReallocFn, &FreeFn, | |
| 130 nullptr}; | |
| 131 | |
| 132 ScopedHeapUsage::AllocatorUsage* GetOrCreateThreadUsage() { | |
| 133 using AllocatorUsage = ScopedHeapUsage::AllocatorUsage; | |
| 134 | |
| 135 base::ThreadLocalPointer<AllocatorUsage>& usage_tls = | |
| 136 g_thread_allocator_usage.Get(); | |
|
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
Isn't this going to re-enter (either the lazy inst
Sigurður Ásgeirsson
2016/08/19 18:21:49
I haven't seen it reenter, but obviously I haven't
| |
| 137 | |
| 138 AllocatorUsage* usage = usage_tls.Get(); | |
| 139 if (usage == nullptr) { | |
| 140 CHECK(allocator_dispatch.next != nullptr); | |
|
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
no need for this check, if this is null will crash
Sigurður Ásgeirsson
2016/08/19 18:21:49
Acknowledged.
| |
| 141 | |
| 142 // Delegate the memory allocation to the next lower heap shim to avoid | |
| 143 // infinite recursion. | |
| 144 const AllocatorDispatch* next = allocator_dispatch.next; | |
| 145 usage = reinterpret_cast<AllocatorUsage*>( | |
| 146 next->alloc_function(next, sizeof(AllocatorUsage))); | |
|
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
you could just use alloc_zero_initialized_function
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
| |
| 147 memset(usage, 0, sizeof(AllocatorUsage)); | |
| 148 usage_tls.Set(usage); | |
|
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
so the problem here is that you are leaking one "u
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
| |
| 149 } | |
| 150 | |
| 151 return usage; | |
| 152 } | |
| 153 | |
| 154 } // namespace | |
| 155 | |
| 156 ScopedHeapUsage::ScopedHeapUsage() : thread_usage_(GetOrCreateThreadUsage()) { | |
| 157 if (thread_usage_ != nullptr) { | |
| 158 usage_at_creation_ = *thread_usage_; | |
| 159 // Reset the max allocation tally for this scope. | |
| 160 thread_usage_->max_allocated_bytes = 0U; | |
| 161 } else { | |
|
chrisha
2016/07/21 14:23:31
Shouldn't thread_usage_ always be non-null at this
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
+1
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
| |
| 162 // TODO(siggi): Is this even a good idea? | |
| 163 usage_at_creation_ = AllocatorUsage(); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 ScopedHeapUsage::~ScopedHeapUsage() { | |
| 168 if (thread_usage_ != nullptr && | |
| 169 usage_at_creation_.max_allocated_bytes > | |
| 170 thread_usage_->max_allocated_bytes) { | |
| 171 // Restore the outer scope's max allocation tally, as it's larger than | |
| 172 // our scope's max. | |
|
chrisha
2016/07/21 14:23:30
This mechanism requires that scopes be properly ne
Primiano Tucci (use gerrit)
2016/07/21 15:59:20
maybe you can have a global Atomic32 g_scoped_heap
Sigurður Ásgeirsson
2016/08/19 18:21:49
I'd prefer to just document this problem away, but
Sigurður Ásgeirsson
2016/08/19 18:21:49
Acknowledged.
| |
| 173 thread_usage_->max_allocated_bytes = usage_at_creation_.max_allocated_bytes; | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 ScopedHeapUsage::AllocatorUsage ScopedHeapUsage::Now() { | |
| 178 AllocatorUsage* usage = GetOrCreateThreadUsage(); | |
| 179 if (usage == nullptr) | |
|
chrisha
2016/07/21 14:23:30
Can this even be null?
Sigurður Ásgeirsson
2016/08/19 18:21:49
Done.
| |
| 180 return AllocatorUsage(); | |
| 181 | |
| 182 return *usage; | |
| 183 } | |
| 184 | |
| 185 void ScopedHeapUsage::Initialize() { | |
| 186 InsertAllocatorDispatch(&allocator_dispatch); | |
| 187 } | |
| 188 | |
| 189 void ScopedHeapUsage::TearDownForTesting() { | |
| 190 RemoveAllocatorDispatchForTesting(&allocator_dispatch); | |
| 191 } | |
| 192 | |
| 193 } // namespace debug | |
| 194 } // namespace base | |
| OLD | NEW |