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

Side by Side Diff: runtime/vm/malloc_hooks.cc

Issue 2680213002: Updated MallocHooks to collect stack traces when memory is allocated. (Closed)
Patch Set: Added tests and modified stack walker to allow for skipping an arbitrary number of frames before co… Created 3 years, 10 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
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "platform/globals.h" 5 #include "platform/globals.h"
6 6
7 #if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) 7 #if defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
8 8
9 #include "vm/malloc_hooks.h" 9 #include "vm/malloc_hooks.h"
10 10
11 #include "gperftools/malloc_hook.h" 11 #include "gperftools/malloc_hook.h"
12 12
13 #include "platform/assert.h" 13 #include "platform/assert.h"
14 #include "vm/hash_map.h" 14 #include "vm/hash_map.h"
15 #include "vm/lockers.h" 15 #include "vm/lockers.h"
16 #include "vm/profiler.h"
16 17
17 namespace dart { 18 namespace dart {
18 19
20 class AddressMap;
21
22 // MallocHooksState contains all of the state related to the configuration of
23 // the malloc hooks, allocation information, and locks.
24 class MallocHooksState : public AllStatic {
25 public:
26 static void RecordAllocHook(const void* ptr, size_t size);
27 static void RecordFreeHook(const void* ptr);
28
29 static bool initialized() { return initialized_; }
30 static void Init();
31
32 static bool ProfilingEnabled() { return (OSThread::TryCurrent() != NULL); }
33
34 static bool IsOriginalProcess() {
35 ASSERT(original_pid_ != kInvalidPid);
36 return original_pid_ == OS::ProcessId();
37 }
38
39 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
40
41 static intptr_t allocation_count() { return allocation_count_; }
42
43 static intptr_t heap_allocated_memory_in_bytes() {
44 return heap_allocated_memory_in_bytes_;
45 }
46
47 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
48 ASSERT(size >= 0);
49 heap_allocated_memory_in_bytes_ += size;
50 ++allocation_count_;
51 }
52
53 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
54 ASSERT(size >= 0);
55 ASSERT(heap_allocated_memory_in_bytes_ >= size);
56 heap_allocated_memory_in_bytes_ -= size;
57 --allocation_count_;
58 ASSERT(allocation_count_ >= 0);
59 }
60
61 static AddressMap* address_map() { return address_map_; }
62
63 static void ResetStats();
64 static void TearDown();
65
66 private:
67 static Mutex* malloc_hook_mutex_;
68
69 // Variables protected by malloc_hook_mutex_.
70 static bool initialized_;
71 static intptr_t allocation_count_;
72 static intptr_t heap_allocated_memory_in_bytes_;
73 static AddressMap* address_map_;
74 // End protected variables.
75
76 static intptr_t original_pid_;
77 static const intptr_t kInvalidPid = -1;
78 };
79
19 // A locker-type class to automatically grab and release the 80 // A locker-type class to automatically grab and release the
20 // in_malloc_hook_flag_. 81 // in_malloc_hook_flag_.
21 class MallocHookScope { 82 class MallocHookScope {
22 public: 83 public:
23 static void InitMallocHookFlag() { 84 static void InitMallocHookFlag() {
24 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); 85 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey);
25 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); 86 in_malloc_hook_flag_ = OSThread::CreateThreadLocal();
26 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); 87 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0);
27 } 88 }
28 89
(...skipping 18 matching lines...) Expand all
47 return OSThread::GetThreadLocal(in_malloc_hook_flag_); 108 return OSThread::GetThreadLocal(in_malloc_hook_flag_);
48 } 109 }
49 110
50 private: 111 private:
51 static ThreadLocalKey in_malloc_hook_flag_; 112 static ThreadLocalKey in_malloc_hook_flag_;
52 113
53 DISALLOW_ALLOCATION(); 114 DISALLOW_ALLOCATION();
54 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); 115 DISALLOW_COPY_AND_ASSIGN(MallocHookScope);
55 }; 116 };
56 117
118 // AllocationInfo contains all information related to a given allocation
119 // including:
120 // -Allocation size in bytes
121 // -Stack trace corresponding to the location of allocation, if applicable
122 class AllocationInfo {
123 public:
124 explicit AllocationInfo(intptr_t allocation_size)
125 : sample_(NULL), allocation_size_(allocation_size) {
126 // Stack trace collection is disabled when we are in the process of creating
127 // the first OSThread in order to prevent deadlocks.
128 if (MallocHooksState::ProfilingEnabled()) {
129 sample_ = Profiler::SampleNativeAllocation(kSkipCount);
130 }
131 }
132
133 Sample* sample() const { return sample_; }
134 intptr_t allocation_size() const { return allocation_size_; }
135
136 private:
137 Sample* sample_;
138 intptr_t allocation_size_;
139
140 // The number of frames that are generated by the malloc hooks and collection
141 // of the stack trace. These frames are ignored when collecting the stack
142 // trace for a memory allocation.
143 static const intptr_t kSkipCount = 5;
zra 2017/02/15 05:50:46 Is this the right value for Debug and Release? ia3
bkonyi 2017/02/16 00:28:28 I've just tested and this is the right value for d
zra 2017/02/16 21:45:27 ia32 is built with gcc on Linux, so we do use the
144 };
145
57 146
58 // Custom key/value trait specifically for address/size pairs. Unlike 147 // Custom key/value trait specifically for address/size pairs. Unlike
59 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. 148 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
60 class AddressKeyValueTrait { 149 class AddressKeyValueTrait : public AllStatic {
61 public: 150 public:
62 typedef const void* Key; 151 typedef const void* Key;
63 typedef intptr_t Value; 152 typedef AllocationInfo* Value;
64 153
65 struct Pair { 154 struct Pair {
66 Key key; 155 Key key;
67 Value value; 156 Value value;
68 Pair() : key(NULL), value(-1) {} 157 Pair() : key(NULL), value(NULL) {}
69 Pair(const Key key, const Value& value) : key(key), value(value) {} 158 Pair(const Key key, const Value& value) : key(key), value(value) {}
70 Pair(const Pair& other) : key(other.key), value(other.value) {} 159 Pair(const Pair& other) : key(other.key), value(other.value) {}
71 }; 160 };
72 161
73 static Key KeyOf(Pair kv) { return kv.key; } 162 static Key KeyOf(Pair kv) { return kv.key; }
74 static Value ValueOf(Pair kv) { return kv.value; } 163 static Value ValueOf(Pair kv) { return kv.value; }
75 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } 164 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
76 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } 165 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
77 }; 166 };
78 167
79 168
80 // Map class that will be used to store mappings between ptr -> allocation size. 169 // Map class that will be used to store mappings between ptr -> allocation size.
81 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { 170 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
82 public: 171 public:
83 typedef AddressKeyValueTrait::Key Key; 172 typedef AddressKeyValueTrait::Key Key;
84 typedef AddressKeyValueTrait::Value Value; 173 typedef AddressKeyValueTrait::Value Value;
85 typedef AddressKeyValueTrait::Pair Pair; 174 typedef AddressKeyValueTrait::Pair Pair;
86 175
87 inline void Insert(const Key& key, const Value& value) { 176 virtual ~AddressMap() { Clear(); }
177
178 void Insert(const Key& key, const Value& value) {
88 Pair pair(key, value); 179 Pair pair(key, value);
89 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair); 180 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
90 } 181 }
91 182
92 inline bool Lookup(const Key& key, Value* value) { 183 bool Lookup(const Key& key, Value* value) {
93 ASSERT(value != NULL); 184 ASSERT(value != NULL);
94 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key); 185 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
95 if (pair == NULL) { 186 if (pair == NULL) {
96 return false; 187 return false;
97 } else { 188 } else {
98 *value = pair->value; 189 *value = pair->value;
99 return true; 190 return true;
100 } 191 }
101 } 192 }
193
194 void Clear() {
195 Iterator iter = GetIterator();
196 Pair* result = iter.Next();
197 while (result != NULL) {
198 delete result->value;
zra 2017/02/15 05:50:46 result->value = NULL;
bkonyi 2017/02/16 00:28:28 Done.
199 result = iter.Next();
200 }
201 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
202 }
102 }; 203 };
103 204
104 205
105 class MallocHooksState {
106 public:
107 static void RecordAllocHook(const void* ptr, size_t size);
108 static void RecordFreeHook(const void* ptr);
109
110 static bool initialized() { return initialized_; }
111 static void Init() {
112 address_map_ = new AddressMap();
113 initialized_ = true;
114 original_pid_ = OS::ProcessId();
115 }
116
117 static bool IsOriginalProcess() {
118 ASSERT(original_pid_ != kInvalidPid);
119 return original_pid_ == OS::ProcessId();
120 }
121
122 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
123
124 static intptr_t allocation_count() { return allocation_count_; }
125
126 static intptr_t heap_allocated_memory_in_bytes() {
127 return heap_allocated_memory_in_bytes_;
128 }
129
130 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
131 ASSERT(size >= 0);
132 heap_allocated_memory_in_bytes_ += size;
133 ++allocation_count_;
134 }
135
136 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
137 ASSERT(size >= 0);
138 ASSERT(heap_allocated_memory_in_bytes_ >= size);
139 heap_allocated_memory_in_bytes_ -= size;
140 --allocation_count_;
141 ASSERT(allocation_count_ >= 0);
142 }
143
144 static AddressMap* address_map() { return address_map_; }
145
146 static void ResetStats() {
147 allocation_count_ = 0;
148 heap_allocated_memory_in_bytes_ = 0;
149 address_map_->Clear();
150 }
151
152 static void TearDown() {
153 initialized_ = false;
154 original_pid_ = kInvalidPid;
155 ResetStats();
156 delete address_map_;
157 }
158
159 private:
160 static bool initialized_;
161 static intptr_t original_pid_;
162 static Mutex* malloc_hook_mutex_;
163 static intptr_t allocation_count_;
164 static intptr_t heap_allocated_memory_in_bytes_;
165 static AddressMap* address_map_;
166
167 static const intptr_t kInvalidPid = -1;
168
169 DISALLOW_ALLOCATION();
170 DISALLOW_COPY_AND_ASSIGN(MallocHooksState);
171 };
172
173
174 // MallocHooks state / locks. 206 // MallocHooks state / locks.
175 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; 207 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey;
176 bool MallocHooksState::initialized_ = false; 208 bool MallocHooksState::initialized_ = false;
177 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; 209 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
178 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); 210 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
179 211
180 // Memory allocation state information. 212 // Memory allocation state information.
181 intptr_t MallocHooksState::allocation_count_ = 0; 213 intptr_t MallocHooksState::allocation_count_ = 0;
182 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; 214 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
183 AddressMap* MallocHooksState::address_map_ = NULL; 215 AddressMap* MallocHooksState::address_map_ = NULL;
(...skipping 24 matching lines...) Expand all
208 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); 240 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
209 ASSERT(success); 241 ASSERT(success);
210 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); 242 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
211 ASSERT(success); 243 ASSERT(success);
212 244
213 MallocHooksState::TearDown(); 245 MallocHooksState::TearDown();
214 MallocHookScope::DestroyMallocHookFlag(); 246 MallocHookScope::DestroyMallocHookFlag();
215 } 247 }
216 248
217 249
250 bool MallocHooks::ProfilingEnabled() {
251 return MallocHooksState::ProfilingEnabled();
252 }
253
254
218 void MallocHooks::ResetStats() { 255 void MallocHooks::ResetStats() {
256 // Set the malloc hook flag before completing the reset since ResetStats()
257 // frees memory.
258 MallocHookScope mhs;
219 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 259 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
220 ASSERT(MallocHooksState::initialized()); 260 ASSERT(MallocHooksState::initialized());
221 261
222 MallocHooksState::ResetStats(); 262 MallocHooksState::ResetStats();
223 } 263 }
224 264
225 265
226 bool MallocHooks::Initialized() { 266 bool MallocHooks::Initialized() {
227 return MallocHooksState::initialized(); 267 return MallocHooksState::initialized();
228 } 268 }
229 269
230 270
231 intptr_t MallocHooks::allocation_count() { 271 intptr_t MallocHooks::allocation_count() {
232 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 272 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
233 return MallocHooksState::allocation_count(); 273 return MallocHooksState::allocation_count();
234 } 274 }
235 275
236 276
237 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { 277 intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
238 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 278 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
239 return MallocHooksState::heap_allocated_memory_in_bytes(); 279 return MallocHooksState::heap_allocated_memory_in_bytes();
240 } 280 }
241 281
242 282
283 Sample* MallocHooks::GetSample(const void* ptr) {
284 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
285 ASSERT(MallocHooksState::initialized());
286
287 if (ptr != NULL) {
288 AllocationInfo* allocation_info = NULL;
289 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
290 ASSERT(allocation_info != NULL);
291 return allocation_info->sample();
292 }
293 }
294 return NULL;
295 }
296
297
298 void MallocHooksState::Init() {
299 address_map_ = new AddressMap();
300 initialized_ = true;
301 original_pid_ = OS::ProcessId();
302 }
303
304
305 void MallocHooksState::ResetStats() {
306 allocation_count_ = 0;
307 heap_allocated_memory_in_bytes_ = 0;
308 address_map_->Clear();
309 }
310
311
312 void MallocHooksState::TearDown() {
313 initialized_ = false;
314 original_pid_ = kInvalidPid;
315 ResetStats();
316 delete address_map_;
zra 2017/02/15 05:50:46 address_map_ = NULL;
bkonyi 2017/02/16 00:28:28 Done.
317 }
318
319
243 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { 320 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
244 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 321 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
245 return; 322 return;
246 } 323 }
247 324
248 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 325 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
249 // again. 326 // again.
250 MallocHookScope mhs; 327 MallocHookScope mhs;
251 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 328 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
252 ASSERT(MallocHooksState::initialized()); 329 ASSERT(MallocHooksState::initialized());
253 330
254 if (ptr != NULL) { 331 if (ptr != NULL) {
255 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); 332 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
256 MallocHooksState::address_map()->Insert(ptr, size); 333 MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
257 } 334 }
258 } 335 }
259 336
260 337
261 void MallocHooksState::RecordFreeHook(const void* ptr) { 338 void MallocHooksState::RecordFreeHook(const void* ptr) {
262 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 339 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
263 return; 340 return;
264 } 341 }
265 342
266 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 343 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
267 // again. 344 // again.
268 MallocHookScope mhs; 345 MallocHookScope mhs;
269 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 346 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
270 ASSERT(MallocHooksState::initialized()); 347 ASSERT(MallocHooksState::initialized());
271 348
272 if (ptr != NULL) { 349 if (ptr != NULL) {
273 intptr_t size = 0; 350 AllocationInfo* allocation_info = NULL;
274 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { 351 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
275 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); 352 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
353 allocation_info->allocation_size());
276 MallocHooksState::address_map()->Remove(ptr); 354 MallocHooksState::address_map()->Remove(ptr);
355 delete allocation_info;
277 } 356 }
278 } 357 }
279 } 358 }
280 359
281 } // namespace dart 360 } // namespace dart
282 361
283 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) 362 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698