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

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

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

Powered by Google App Engine
This is Rietveld 408576698