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

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

Issue 2680213002: Updated MallocHooks to collect stack traces when memory is allocated. (Closed)
Patch Set: Rough patch: addressed initial comments + converted sample filters from using Isolates to Dart_Port… 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 stack_trace_collection_enabled() {
33 return OSThread::DoesCurrentThreadExist();
34 }
35
36 static bool IsOriginalProcess() {
37 ASSERT(original_pid_ != kInvalidPid);
38 return original_pid_ == OS::ProcessId();
39 }
40
41 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
42
43 static intptr_t allocation_count() { return allocation_count_; }
44
45 static intptr_t heap_allocated_memory_in_bytes() {
46 return heap_allocated_memory_in_bytes_;
47 }
48
49 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
50 ASSERT(size >= 0);
51 heap_allocated_memory_in_bytes_ += size;
52 ++allocation_count_;
53 }
54
55 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
56 ASSERT(size >= 0);
57 ASSERT(heap_allocated_memory_in_bytes_ >= size);
58 heap_allocated_memory_in_bytes_ -= size;
59 --allocation_count_;
60 ASSERT(allocation_count_ >= 0);
61 }
62
63 static AddressMap* address_map() { return address_map_; }
64
65 static void ResetStats();
66 static void TearDown();
67
68 private:
69 static bool initialized_;
70 static intptr_t original_pid_;
71 static Mutex* malloc_hook_mutex_;
zra 2017/02/08 18:14:14 It can be nice have the Mutex first with a comment
bkonyi 2017/02/09 21:22:46 Done.
72 static intptr_t allocation_count_;
73 static intptr_t heap_allocated_memory_in_bytes_;
74 static AddressMap* address_map_;
75
76 static const intptr_t kInvalidPid = -1;
77 };
78
19 // A locker-type class to automatically grab and release the 79 // A locker-type class to automatically grab and release the
20 // in_malloc_hook_flag_. 80 // in_malloc_hook_flag_.
21 class MallocHookScope { 81 class MallocHookScope {
22 public: 82 public:
23 static void InitMallocHookFlag() { 83 static void InitMallocHookFlag() {
24 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); 84 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey);
25 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); 85 in_malloc_hook_flag_ = OSThread::CreateThreadLocal();
26 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); 86 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0);
27 } 87 }
28 88
29 static void DestroyMallocHookFlag() { 89 static void DestroyMallocHookFlag() {
30 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
zra 2017/02/08 18:14:14 Maybe change ASSERT() -> if() instead of removing.
bkonyi 2017/02/09 21:22:46 I can't remember exactly why I removed this... I'm
31 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); 90 OSThread::DeleteThreadLocal(in_malloc_hook_flag_);
32 in_malloc_hook_flag_ = kUnsetThreadLocalKey; 91 in_malloc_hook_flag_ = kUnsetThreadLocalKey;
33 } 92 }
34 93
35 MallocHookScope() { 94 MallocHookScope() {
36 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); 95 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
37 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1); 96 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1);
38 } 97 }
39 98
40 ~MallocHookScope() { 99 ~MallocHookScope() {
41 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); 100 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
42 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); 101 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0);
43 } 102 }
44 103
45 static bool IsInHook() { 104 static bool IsInHook() {
46 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); 105 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
47 return OSThread::GetThreadLocal(in_malloc_hook_flag_); 106 return OSThread::GetThreadLocal(in_malloc_hook_flag_);
48 } 107 }
49 108
50 private: 109 private:
51 static ThreadLocalKey in_malloc_hook_flag_; 110 static ThreadLocalKey in_malloc_hook_flag_;
52 111
53 DISALLOW_ALLOCATION(); 112 DISALLOW_ALLOCATION();
54 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); 113 DISALLOW_COPY_AND_ASSIGN(MallocHookScope);
55 }; 114 };
56 115
116 // AllocationInfo contains all information related to a given allocation
117 // including:
118 // -Allocation size in bytes
119 // -Stack trace corresponding to the location of allocation, if applicable
120 class AllocationInfo {
121 public:
122 explicit AllocationInfo(intptr_t allocation_size)
123 : sample_(NULL), allocation_size_(allocation_size) {
124 // Stack trace collection is disabled when we are in the process of creating
125 // the first OSThread in order to prevent deadlocks.
126 if (OSThread::DoesCurrentThreadExist()) {
127 sample_ = Profiler::SampleNativeAllocation();
zra 2017/02/08 18:14:14 Let's add a TODO here for the following issue. Thi
bkonyi 2017/02/09 21:22:46 Done.
128 }
129 }
130
131 Sample* sample() const { return sample_; }
132 intptr_t allocation_size() const { return allocation_size_; }
133
134 private:
135 Sample* sample_;
136 intptr_t allocation_size_;
137 };
138
57 139
58 // Custom key/value trait specifically for address/size pairs. Unlike 140 // Custom key/value trait specifically for address/size pairs. Unlike
59 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. 141 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
60 class AddressKeyValueTrait { 142 class AddressKeyValueTrait : public AllStatic {
61 public: 143 public:
62 typedef const void* Key; 144 typedef const void* Key;
63 typedef intptr_t Value; 145 typedef AllocationInfo* Value;
64 146
65 struct Pair { 147 struct Pair {
66 Key key; 148 Key key;
67 Value value; 149 Value value;
68 Pair() : key(NULL), value(-1) {} 150 Pair() : key(NULL), value(NULL) {}
69 Pair(const Key key, const Value& value) : key(key), value(value) {} 151 Pair(const Key key, const Value& value) : key(key), value(value) {}
70 Pair(const Pair& other) : key(other.key), value(other.value) {} 152 Pair(const Pair& other) : key(other.key), value(other.value) {}
71 }; 153 };
72 154
73 static Key KeyOf(Pair kv) { return kv.key; } 155 static Key KeyOf(Pair kv) { return kv.key; }
74 static Value ValueOf(Pair kv) { return kv.value; } 156 static Value ValueOf(Pair kv) { return kv.value; }
75 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } 157 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
76 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } 158 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
77 }; 159 };
78 160
79 161
80 // Map class that will be used to store mappings between ptr -> allocation size. 162 // Map class that will be used to store mappings between ptr -> allocation size.
81 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { 163 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
82 public: 164 public:
83 typedef AddressKeyValueTrait::Key Key; 165 typedef AddressKeyValueTrait::Key Key;
84 typedef AddressKeyValueTrait::Value Value; 166 typedef AddressKeyValueTrait::Value Value;
85 typedef AddressKeyValueTrait::Pair Pair; 167 typedef AddressKeyValueTrait::Pair Pair;
86 168
169 ~AddressMap() { Clear(); }
zra 2017/02/08 18:14:14 I think we need to mark the destructor of BaseDire
bkonyi 2017/02/09 21:22:46 Done.
170
87 inline void Insert(const Key& key, const Value& value) { 171 inline void Insert(const Key& key, const Value& value) {
zra 2017/02/08 18:14:14 Is inline needed here and below?
bkonyi 2017/02/09 21:22:46 No, I've removed them. Forgot to upload them in th
88 Pair pair(key, value); 172 Pair pair(key, value);
89 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair); 173 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
90 } 174 }
91 175
92 inline bool Lookup(const Key& key, Value* value) { 176 inline bool Lookup(const Key& key, Value* value) {
93 ASSERT(value != NULL); 177 ASSERT(value != NULL);
94 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key); 178 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
95 if (pair == NULL) { 179 if (pair == NULL) {
96 return false; 180 return false;
97 } else { 181 } else {
98 *value = pair->value; 182 *value = pair->value;
99 return true; 183 return true;
100 } 184 }
101 } 185 }
186
187 inline void Clear() {
188 Iterator iter = GetIterator();
189 Pair* result = iter.Next();
190 while (result != NULL) {
191 delete result->value;
192 result = iter.Next();
193 }
194 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
195 }
102 }; 196 };
103 197
104 198
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. 199 // MallocHooks state / locks.
175 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; 200 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey;
176 bool MallocHooksState::initialized_ = false; 201 bool MallocHooksState::initialized_ = false;
177 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; 202 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
178 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); 203 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
179 204
180 // Memory allocation state information. 205 // Memory allocation state information.
181 intptr_t MallocHooksState::allocation_count_ = 0; 206 intptr_t MallocHooksState::allocation_count_ = 0;
182 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; 207 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
183 AddressMap* MallocHooksState::address_map_ = NULL; 208 AddressMap* MallocHooksState::address_map_ = NULL;
(...skipping 24 matching lines...) Expand all
208 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); 233 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
209 ASSERT(success); 234 ASSERT(success);
210 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); 235 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
211 ASSERT(success); 236 ASSERT(success);
212 237
213 MallocHooksState::TearDown(); 238 MallocHooksState::TearDown();
214 MallocHookScope::DestroyMallocHookFlag(); 239 MallocHookScope::DestroyMallocHookFlag();
215 } 240 }
216 241
217 242
243 bool MallocHooks::stack_trace_collection_enabled() {
244 return MallocHooksState::stack_trace_collection_enabled();
245 }
246
247
218 void MallocHooks::ResetStats() { 248 void MallocHooks::ResetStats() {
249 // Set the malloc hook flag before completing the reset since ResetStats()
250 // frees memory.
251 MallocHookScope mhs;
219 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 252 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
220 ASSERT(MallocHooksState::initialized()); 253 ASSERT(MallocHooksState::initialized());
221 254
222 MallocHooksState::ResetStats(); 255 MallocHooksState::ResetStats();
223 } 256 }
224 257
225 258
226 bool MallocHooks::Initialized() { 259 bool MallocHooks::Initialized() {
227 return MallocHooksState::initialized(); 260 return MallocHooksState::initialized();
228 } 261 }
229 262
230 263
231 intptr_t MallocHooks::allocation_count() { 264 intptr_t MallocHooks::allocation_count() {
232 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 265 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
233 return MallocHooksState::allocation_count(); 266 return MallocHooksState::allocation_count();
234 } 267 }
235 268
236 269
237 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { 270 intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
238 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 271 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
239 return MallocHooksState::heap_allocated_memory_in_bytes(); 272 return MallocHooksState::heap_allocated_memory_in_bytes();
240 } 273 }
241 274
242 275
276 Sample* MallocHooks::GetSample(const void* ptr) {
277 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
278 ASSERT(MallocHooksState::initialized());
279
280 if (ptr != NULL) {
281 AllocationInfo* allocation_info = NULL;
282 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
283 ASSERT(allocation_info != NULL);
284 return allocation_info->sample();
285 }
286 }
287 return NULL;
288 }
289
290
291 void MallocHooksState::Init() {
292 address_map_ = new AddressMap();
293 initialized_ = true;
294 original_pid_ = OS::ProcessId();
295 }
296
297
298 void MallocHooksState::ResetStats() {
299 allocation_count_ = 0;
300 heap_allocated_memory_in_bytes_ = 0;
301 address_map_->Clear();
302 }
303
304
305 void MallocHooksState::TearDown() {
306 initialized_ = false;
307 original_pid_ = kInvalidPid;
308 ResetStats();
309 delete address_map_;
310 }
311
312
243 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { 313 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
244 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 314 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
245 return; 315 return;
246 } 316 }
247 317
248 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 318 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
249 // again. 319 // again.
250 MallocHookScope mhs; 320 MallocHookScope mhs;
251 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 321 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
252 ASSERT(MallocHooksState::initialized()); 322 ASSERT(MallocHooksState::initialized());
253 323
254 if (ptr != NULL) { 324 if (ptr != NULL) {
255 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); 325 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
256 MallocHooksState::address_map()->Insert(ptr, size); 326 MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
257 } 327 }
258 } 328 }
259 329
260 330
261 void MallocHooksState::RecordFreeHook(const void* ptr) { 331 void MallocHooksState::RecordFreeHook(const void* ptr) {
262 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { 332 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) {
263 return; 333 return;
264 } 334 }
265 335
266 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks 336 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks
267 // again. 337 // again.
268 MallocHookScope mhs; 338 MallocHookScope mhs;
269 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); 339 MutexLocker ml(MallocHooksState::malloc_hook_mutex());
270 ASSERT(MallocHooksState::initialized()); 340 ASSERT(MallocHooksState::initialized());
271 341
272 if (ptr != NULL) { 342 if (ptr != NULL) {
273 intptr_t size = 0; 343 AllocationInfo* allocation_info = NULL;
274 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { 344 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
275 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); 345 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
346 allocation_info->allocation_size());
276 MallocHooksState::address_map()->Remove(ptr); 347 MallocHooksState::address_map()->Remove(ptr);
348 delete allocation_info;
277 } 349 }
278 } 350 }
279 } 351 }
280 352
281 } // namespace dart 353 } // namespace dart
282 354
283 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) 355 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698