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

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

Issue 2680213002: Updated MallocHooks to collect stack traces when memory is allocated. (Closed)
Patch Set: Updated MallocHooks to collect stack traces when memory is allocated. 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. If this number is incorrect, some tests in
149 // malloc_hook_tests.cc might fail, particularily
150 // StackTraceMallocHookLengthTest. If this value is updated, please make sure
151 // that the MallocHooks test cases pass on all platforms.
152 static const intptr_t kSkipCount = 5;
153 };
154
58 155
59 // Custom key/value trait specifically for address/size pairs. Unlike 156 // Custom key/value trait specifically for address/size pairs. Unlike
60 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. 157 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
61 class AddressKeyValueTrait { 158 class AddressKeyValueTrait : public AllStatic {
62 public: 159 public:
63 typedef const void* Key; 160 typedef const void* Key;
64 typedef intptr_t Value; 161 typedef AllocationInfo* Value;
65 162
66 struct Pair { 163 struct Pair {
67 Key key; 164 Key key;
68 Value value; 165 Value value;
69 Pair() : key(NULL), value(-1) {} 166 Pair() : key(NULL), value(NULL) {}
70 Pair(const Key key, const Value& value) : key(key), value(value) {} 167 Pair(const Key key, const Value& value) : key(key), value(value) {}
71 Pair(const Pair& other) : key(other.key), value(other.value) {} 168 Pair(const Pair& other) : key(other.key), value(other.value) {}
72 }; 169 };
73 170
74 static Key KeyOf(Pair kv) { return kv.key; } 171 static Key KeyOf(Pair kv) { return kv.key; }
75 static Value ValueOf(Pair kv) { return kv.value; } 172 static Value ValueOf(Pair kv) { return kv.value; }
76 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } 173 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
77 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } 174 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
78 }; 175 };
79 176
80 177
81 // Map class that will be used to store mappings between ptr -> allocation size. 178 // Map class that will be used to store mappings between ptr -> allocation size.
82 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { 179 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
83 public: 180 public:
84 typedef AddressKeyValueTrait::Key Key; 181 typedef AddressKeyValueTrait::Key Key;
85 typedef AddressKeyValueTrait::Value Value; 182 typedef AddressKeyValueTrait::Value Value;
86 typedef AddressKeyValueTrait::Pair Pair; 183 typedef AddressKeyValueTrait::Pair Pair;
87 184
88 inline void Insert(const Key& key, const Value& value) { 185 virtual ~AddressMap() { Clear(); }
186
187 void Insert(const Key& key, const Value& value) {
89 Pair pair(key, value); 188 Pair pair(key, value);
90 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair); 189 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
91 } 190 }
92 191
93 inline bool Lookup(const Key& key, Value* value) { 192 bool Lookup(const Key& key, Value* value) {
94 ASSERT(value != NULL); 193 ASSERT(value != NULL);
95 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key); 194 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
96 if (pair == NULL) { 195 if (pair == NULL) {
97 return false; 196 return false;
98 } else { 197 } else {
99 *value = pair->value; 198 *value = pair->value;
100 return true; 199 return true;
101 } 200 }
102 } 201 }
202
203 void Clear() {
Cutch 2017/02/21 18:53:35 this should be marked virtual in the base class?
bkonyi 2017/02/22 19:12:51 Done.
204 Iterator iter = GetIterator();
205 Pair* result = iter.Next();
206 while (result != NULL) {
207 delete result->value;
208 result->value = NULL;
209 result = iter.Next();
210 }
211 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
212 }
103 }; 213 };
104 214
105 215
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. 216 // MallocHooks state / locks.
177 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; 217 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey;
178 bool MallocHooksState::active_ = false; 218 bool MallocHooksState::active_ = false;
179 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; 219 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
180 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); 220 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
181 221
182 // Memory allocation state information. 222 // Memory allocation state information.
183 intptr_t MallocHooksState::allocation_count_ = 0; 223 intptr_t MallocHooksState::allocation_count_ = 0;
184 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; 224 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
185 AddressMap* MallocHooksState::address_map_ = NULL; 225 AddressMap* MallocHooksState::address_map_ = NULL;
186 226
187 227
228 void MallocHooksState::Init() {
229 address_map_ = new AddressMap();
230 active_ = true;
231 original_pid_ = OS::ProcessId();
232 }
233
234
235 void MallocHooksState::ResetStats() {
236 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
237 allocation_count_ = 0;
238 heap_allocated_memory_in_bytes_ = 0;
239 address_map_->Clear();
240 }
241
242
243 void MallocHooksState::TearDown() {
244 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
245 active_ = false;
246 original_pid_ = kInvalidPid;
247 ResetStats();
248 delete address_map_;
249 address_map_ = NULL;
250 }
251
252
188 void MallocHooks::InitOnce() { 253 void MallocHooks::InitOnce() {
189 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 254 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
190 ASSERT(!MallocHooksState::Active()); 255 ASSERT(!MallocHooksState::Active());
191 256
192 MallocHookScope::InitMallocHookFlag(); 257 MallocHookScope::InitMallocHookFlag();
193 MallocHooksState::Init(); 258 MallocHooksState::Init();
194 259
195 // Register malloc hooks. 260 // Register malloc hooks.
196 bool success = false; 261 bool success = false;
197 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); 262 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook);
(...skipping 12 matching lines...) Expand all
210 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); 275 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
211 ASSERT(success); 276 ASSERT(success);
212 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); 277 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
213 ASSERT(success); 278 ASSERT(success);
214 279
215 MallocHooksState::TearDown(); 280 MallocHooksState::TearDown();
216 MallocHookScope::DestroyMallocHookFlag(); 281 MallocHookScope::DestroyMallocHookFlag();
217 } 282 }
218 283
219 284
285 bool MallocHooks::ProfilingEnabled() {
286 return MallocHooksState::ProfilingEnabled();
287 }
288
289
220 void MallocHooks::ResetStats() { 290 void MallocHooks::ResetStats() {
291 // Set the malloc hook flag before completing the reset since ResetStats()
292 // frees memory.
293 MallocHookScope mhs;
221 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 294 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
222 if (MallocHooksState::Active()) { 295 if (MallocHooksState::Active()) {
223 MallocHooksState::ResetStats(); 296 MallocHooksState::ResetStats();
224 } 297 }
225 } 298 }
226 299
227 300
228 bool MallocHooks::Active() { 301 bool MallocHooks::Active() {
229 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); 302 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
230 return MallocHooksState::Active(); 303 return MallocHooksState::Active();
231 } 304 }
232 305
233 306
234 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { 307 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
235 intptr_t allocated_memory = 0; 308 intptr_t allocated_memory = 0;
236 intptr_t allocation_count = 0; 309 intptr_t allocation_count = 0;
237 bool add_usage = false; 310 bool add_usage = false;
238 // AddProperty may call malloc which would result in an attempt 311 // AddProperty may call malloc which would result in an attempt
239 // to acquire the lock recursively so we extract the values first 312 // to acquire the lock recursively so we extract the values first
240 // and then add the JSON properties. 313 // and then add the JSON properties.
241 { 314 {
242 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 315 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
243 if (Active()) { 316 if (MallocHooksState::Active()) {
244 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); 317 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes();
245 allocation_count = MallocHooksState::allocation_count(); 318 allocation_count = MallocHooksState::allocation_count();
246 add_usage = true; 319 add_usage = true;
247 } 320 }
248 } 321 }
249 if (add_usage) { 322 if (add_usage) {
250 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); 323 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory);
251 jsobj->AddProperty("_heapAllocationCount", allocation_count); 324 jsobj->AddProperty("_heapAllocationCount", allocation_count);
252 } 325 }
253 } 326 }
254 327
255 328
256 intptr_t MallocHooks::allocation_count() { 329 intptr_t MallocHooks::allocation_count() {
257 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 330 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
258 return MallocHooksState::allocation_count(); 331 return MallocHooksState::allocation_count();
259 } 332 }
260 333
261 334
262 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { 335 intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
263 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 336 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
264 return MallocHooksState::heap_allocated_memory_in_bytes(); 337 return MallocHooksState::heap_allocated_memory_in_bytes();
265 } 338 }
266 339
267 340
341 Sample* MallocHooks::GetSample(const void* ptr) {
342 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
343 ASSERT(MallocHooksState::Active());
344
345 if (ptr != NULL) {
346 AllocationInfo* allocation_info = NULL;
347 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
348 ASSERT(allocation_info != NULL);
349 return allocation_info->sample();
350 }
351 }
352 return NULL;
353 }
354
355
268 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { 356 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
269 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 357 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
270 return; 358 return;
271 } 359 }
272 360
273 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 361 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
274 // again. 362 // again.
275 MallocHookScope mhs; 363 MallocHookScope mhs;
276 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 364 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
277 if ((ptr != NULL) && MallocHooksState::Active()) { 365 if ((ptr != NULL) && MallocHooksState::Active()) {
278 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); 366 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
279 MallocHooksState::address_map()->Insert(ptr, size); 367 MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
280 } 368 }
281 } 369 }
282 370
283 371
284 void MallocHooksState::RecordFreeHook(const void* ptr) { 372 void MallocHooksState::RecordFreeHook(const void* ptr) {
285 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 373 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
286 return; 374 return;
287 } 375 }
288 376
289 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 377 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
290 // again. 378 // again.
291 MallocHookScope mhs; 379 MallocHookScope mhs;
292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 380 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
293 if ((ptr != NULL) && MallocHooksState::Active()) { 381 if ((ptr != NULL) && MallocHooksState::Active()) {
294 intptr_t size = 0; 382 AllocationInfo* allocation_info = NULL;
295 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { 383 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
296 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); 384 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
385 allocation_info->allocation_size());
297 MallocHooksState::address_map()->Remove(ptr); 386 MallocHooksState::address_map()->Remove(ptr);
387 delete allocation_info;
298 } 388 }
299 } 389 }
300 } 390 }
301 391
302 } // namespace dart 392 } // namespace dart
303 393
304 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) 394 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698