OLD | NEW |
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/os_thread.h" |
17 | 17 |
18 namespace dart { | 18 namespace dart { |
19 | 19 |
20 // A locker-type class to automatically grab and release the | 20 // A locker-type class similar to MutexLocker which tracks which thread |
21 // in_malloc_hook_flag_. | 21 // currently holds the lock. We use this instead of MutexLocker and |
22 class MallocHookScope { | 22 // mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only |
| 23 // enabled for debug mode. |
| 24 class MallocLocker : public ValueObject { |
23 public: | 25 public: |
24 static void InitMallocHookFlag() { | 26 explicit MallocLocker(Mutex* mutex, ThreadId* owner) |
25 MutexLocker ml(malloc_hook_scope_mutex_); | 27 : mutex_(mutex), owner_(owner) { |
26 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); | 28 ASSERT(owner != NULL); |
27 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); | 29 mutex_->Lock(); |
28 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | 30 ASSERT(*owner_ == OSThread::kInvalidThreadId); |
| 31 *owner_ = OSThread::GetCurrentThreadId(); |
29 } | 32 } |
30 | 33 |
31 static void DestroyMallocHookFlag() { | 34 virtual ~MallocLocker() { |
32 MutexLocker ml(malloc_hook_scope_mutex_); | 35 ASSERT(*owner_ == OSThread::GetCurrentThreadId()); |
33 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 36 *owner_ = OSThread::kInvalidThreadId; |
34 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); | 37 mutex_->Unlock(); |
35 in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
36 } | |
37 | |
38 MallocHookScope() { | |
39 MutexLocker ml(malloc_hook_scope_mutex_); | |
40 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | |
41 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1); | |
42 } | |
43 | |
44 ~MallocHookScope() { | |
45 MutexLocker ml(malloc_hook_scope_mutex_); | |
46 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | |
47 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | |
48 } | |
49 | |
50 static bool IsInHook() { | |
51 MutexLocker ml(malloc_hook_scope_mutex_); | |
52 if (in_malloc_hook_flag_ == kUnsetThreadLocalKey) { | |
53 // Bail out if the malloc hook flag is invalid. This means that | |
54 // MallocHookState::TearDown() has been called and MallocHookScope is no | |
55 // longer intitialized. Don't worry if MallocHookState::TearDown() is | |
56 // called before the hooks grab the mutex, since | |
57 // MallocHooksState::Active() is checked after the lock is taken before | |
58 // proceeding to act on the allocation/free. | |
59 return false; | |
60 } | |
61 return OSThread::GetThreadLocal(in_malloc_hook_flag_); | |
62 } | 38 } |
63 | 39 |
64 private: | 40 private: |
65 static Mutex* malloc_hook_scope_mutex_; | 41 Mutex* mutex_; |
66 static ThreadLocalKey in_malloc_hook_flag_; | 42 ThreadId* owner_; |
67 | |
68 DISALLOW_ALLOCATION(); | |
69 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); | |
70 }; | 43 }; |
71 | 44 |
72 | 45 |
73 // Custom key/value trait specifically for address/size pairs. Unlike | 46 // Custom key/value trait specifically for address/size pairs. Unlike |
74 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. | 47 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
75 class AddressKeyValueTrait { | 48 class AddressKeyValueTrait { |
76 public: | 49 public: |
77 typedef const void* Key; | 50 typedef const void* Key; |
78 typedef intptr_t Value; | 51 typedef intptr_t Value; |
79 | 52 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 active_ = true; | 101 active_ = true; |
129 original_pid_ = OS::ProcessId(); | 102 original_pid_ = OS::ProcessId(); |
130 } | 103 } |
131 | 104 |
132 static bool IsOriginalProcess() { | 105 static bool IsOriginalProcess() { |
133 ASSERT(original_pid_ != kInvalidPid); | 106 ASSERT(original_pid_ != kInvalidPid); |
134 return original_pid_ == OS::ProcessId(); | 107 return original_pid_ == OS::ProcessId(); |
135 } | 108 } |
136 | 109 |
137 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } | 110 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } |
| 111 static ThreadId* malloc_hook_mutex_owner() { |
| 112 return &malloc_hook_mutex_owner_; |
| 113 } |
| 114 static bool IsLockHeldByCurrentThread() { |
| 115 return (malloc_hook_mutex_owner_ == OSThread::GetCurrentThreadId()); |
| 116 } |
138 | 117 |
139 static intptr_t allocation_count() { return allocation_count_; } | 118 static intptr_t allocation_count() { return allocation_count_; } |
140 | 119 |
141 static intptr_t heap_allocated_memory_in_bytes() { | 120 static intptr_t heap_allocated_memory_in_bytes() { |
142 return heap_allocated_memory_in_bytes_; | 121 return heap_allocated_memory_in_bytes_; |
143 } | 122 } |
144 | 123 |
145 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { | 124 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { |
146 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); | 125 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
147 ASSERT(size >= 0); | 126 ASSERT(size >= 0); |
(...skipping 24 matching lines...) Expand all Loading... |
172 active_ = false; | 151 active_ = false; |
173 original_pid_ = kInvalidPid; | 152 original_pid_ = kInvalidPid; |
174 ResetStats(); | 153 ResetStats(); |
175 delete address_map_; | 154 delete address_map_; |
176 } | 155 } |
177 | 156 |
178 private: | 157 private: |
179 static bool active_; | 158 static bool active_; |
180 static intptr_t original_pid_; | 159 static intptr_t original_pid_; |
181 static Mutex* malloc_hook_mutex_; | 160 static Mutex* malloc_hook_mutex_; |
| 161 static ThreadId malloc_hook_mutex_owner_; |
182 static intptr_t allocation_count_; | 162 static intptr_t allocation_count_; |
183 static intptr_t heap_allocated_memory_in_bytes_; | 163 static intptr_t heap_allocated_memory_in_bytes_; |
184 static AddressMap* address_map_; | 164 static AddressMap* address_map_; |
185 | 165 |
186 static const intptr_t kInvalidPid = -1; | 166 static const intptr_t kInvalidPid = -1; |
187 }; | 167 }; |
188 | 168 |
189 | 169 |
190 // MallocHookScope state. | |
191 Mutex* MallocHookScope::malloc_hook_scope_mutex_ = new Mutex(); | |
192 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
193 | |
194 // MallocHooks state / locks. | 170 // MallocHooks state / locks. |
195 bool MallocHooksState::active_ = false; | 171 bool MallocHooksState::active_ = false; |
196 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 172 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
197 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 173 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
| 174 ThreadId MallocHooksState::malloc_hook_mutex_owner_ = |
| 175 OSThread::kInvalidThreadId; |
198 | 176 |
199 // Memory allocation state information. | 177 // Memory allocation state information. |
200 intptr_t MallocHooksState::allocation_count_ = 0; | 178 intptr_t MallocHooksState::allocation_count_ = 0; |
201 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 179 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
202 AddressMap* MallocHooksState::address_map_ = NULL; | 180 AddressMap* MallocHooksState::address_map_ = NULL; |
203 | 181 |
204 | 182 |
205 void MallocHooks::InitOnce() { | 183 void MallocHooks::InitOnce() { |
206 if (!FLAG_enable_malloc_hooks) { | 184 if (!FLAG_enable_malloc_hooks) { |
207 return; | 185 return; |
208 } | 186 } |
209 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 187 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 188 MallocHooksState::malloc_hook_mutex_owner()); |
210 ASSERT(!MallocHooksState::Active()); | 189 ASSERT(!MallocHooksState::Active()); |
211 | 190 |
212 MallocHookScope::InitMallocHookFlag(); | |
213 MallocHooksState::Init(); | 191 MallocHooksState::Init(); |
214 | 192 |
215 // Register malloc hooks. | 193 // Register malloc hooks. |
216 bool success = false; | 194 bool success = false; |
217 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); | 195 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
218 ASSERT(success); | 196 ASSERT(success); |
219 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); | 197 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
220 ASSERT(success); | 198 ASSERT(success); |
221 } | 199 } |
222 | 200 |
223 | 201 |
224 void MallocHooks::TearDown() { | 202 void MallocHooks::TearDown() { |
225 if (!FLAG_enable_malloc_hooks) { | 203 if (!FLAG_enable_malloc_hooks) { |
226 return; | 204 return; |
227 } | 205 } |
228 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 206 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 207 MallocHooksState::malloc_hook_mutex_owner()); |
229 ASSERT(MallocHooksState::Active()); | 208 ASSERT(MallocHooksState::Active()); |
230 | 209 |
231 // Remove malloc hooks. | 210 // Remove malloc hooks. |
232 bool success = false; | 211 bool success = false; |
233 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 212 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
234 ASSERT(success); | 213 ASSERT(success); |
235 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 214 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
236 ASSERT(success); | 215 ASSERT(success); |
237 | 216 |
238 MallocHooksState::TearDown(); | 217 MallocHooksState::TearDown(); |
239 MallocHookScope::DestroyMallocHookFlag(); | |
240 } | 218 } |
241 | 219 |
242 | 220 |
243 void MallocHooks::ResetStats() { | 221 void MallocHooks::ResetStats() { |
244 if (!FLAG_enable_malloc_hooks) { | 222 if (!FLAG_enable_malloc_hooks) { |
245 return; | 223 return; |
246 } | 224 } |
247 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 225 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 226 MallocHooksState::malloc_hook_mutex_owner()); |
248 if (MallocHooksState::Active()) { | 227 if (MallocHooksState::Active()) { |
249 MallocHooksState::ResetStats(); | 228 MallocHooksState::ResetStats(); |
250 } | 229 } |
251 } | 230 } |
252 | 231 |
253 | 232 |
254 bool MallocHooks::Active() { | 233 bool MallocHooks::Active() { |
255 if (!FLAG_enable_malloc_hooks) { | 234 if (!FLAG_enable_malloc_hooks) { |
256 return false; | 235 return false; |
257 } | 236 } |
258 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); | 237 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); |
259 return MallocHooksState::Active(); | 238 return MallocHooksState::Active(); |
260 } | 239 } |
261 | 240 |
262 | 241 |
263 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { | 242 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
264 if (!FLAG_enable_malloc_hooks) { | 243 if (!FLAG_enable_malloc_hooks) { |
265 return; | 244 return; |
266 } | 245 } |
267 intptr_t allocated_memory = 0; | 246 intptr_t allocated_memory = 0; |
268 intptr_t allocation_count = 0; | 247 intptr_t allocation_count = 0; |
269 bool add_usage = false; | 248 bool add_usage = false; |
270 // AddProperty may call malloc which would result in an attempt | 249 // AddProperty may call malloc which would result in an attempt |
271 // to acquire the lock recursively so we extract the values first | 250 // to acquire the lock recursively so we extract the values first |
272 // and then add the JSON properties. | 251 // and then add the JSON properties. |
273 { | 252 { |
274 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 253 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 254 MallocHooksState::malloc_hook_mutex_owner()); |
275 if (Active()) { | 255 if (Active()) { |
276 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); | 256 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
277 allocation_count = MallocHooksState::allocation_count(); | 257 allocation_count = MallocHooksState::allocation_count(); |
278 add_usage = true; | 258 add_usage = true; |
279 } | 259 } |
280 } | 260 } |
281 if (add_usage) { | 261 if (add_usage) { |
282 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); | 262 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
283 jsobj->AddProperty("_heapAllocationCount", allocation_count); | 263 jsobj->AddProperty("_heapAllocationCount", allocation_count); |
284 } | 264 } |
285 } | 265 } |
286 | 266 |
287 | 267 |
288 intptr_t MallocHooks::allocation_count() { | 268 intptr_t MallocHooks::allocation_count() { |
289 if (!FLAG_enable_malloc_hooks) { | 269 if (!FLAG_enable_malloc_hooks) { |
290 return 0; | 270 return 0; |
291 } | 271 } |
292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 272 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 273 MallocHooksState::malloc_hook_mutex_owner()); |
293 return MallocHooksState::allocation_count(); | 274 return MallocHooksState::allocation_count(); |
294 } | 275 } |
295 | 276 |
296 | 277 |
297 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { | 278 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
298 if (!FLAG_enable_malloc_hooks) { | 279 if (!FLAG_enable_malloc_hooks) { |
299 return 0; | 280 return 0; |
300 } | 281 } |
301 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 282 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 283 MallocHooksState::malloc_hook_mutex_owner()); |
302 return MallocHooksState::heap_allocated_memory_in_bytes(); | 284 return MallocHooksState::heap_allocated_memory_in_bytes(); |
303 } | 285 } |
304 | 286 |
305 | 287 |
306 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 288 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
307 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 289 if (MallocHooksState::IsLockHeldByCurrentThread() || |
| 290 !MallocHooksState::IsOriginalProcess()) { |
308 return; | 291 return; |
309 } | 292 } |
310 | 293 |
311 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 294 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 295 MallocHooksState::malloc_hook_mutex_owner()); |
312 // Now that we hold the lock, check to make sure everything is still active. | 296 // Now that we hold the lock, check to make sure everything is still active. |
313 if ((ptr != NULL) && MallocHooksState::Active()) { | 297 if ((ptr != NULL) && MallocHooksState::Active()) { |
314 // Set the malloc hook flag to avoid calling hooks again if memory is | |
315 // allocated/freed below. | |
316 MallocHookScope mhs; | |
317 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); | 298 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
318 MallocHooksState::address_map()->Insert(ptr, size); | 299 MallocHooksState::address_map()->Insert(ptr, size); |
319 } | 300 } |
320 } | 301 } |
321 | 302 |
322 | 303 |
323 void MallocHooksState::RecordFreeHook(const void* ptr) { | 304 void MallocHooksState::RecordFreeHook(const void* ptr) { |
324 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 305 if (MallocHooksState::IsLockHeldByCurrentThread() || |
| 306 !MallocHooksState::IsOriginalProcess()) { |
325 return; | 307 return; |
326 } | 308 } |
327 | 309 |
328 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 310 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
| 311 MallocHooksState::malloc_hook_mutex_owner()); |
329 // Now that we hold the lock, check to make sure everything is still active. | 312 // Now that we hold the lock, check to make sure everything is still active. |
330 if ((ptr != NULL) && MallocHooksState::Active()) { | 313 if ((ptr != NULL) && MallocHooksState::Active()) { |
331 // Set the malloc hook flag to avoid calling hooks again if memory is | |
332 // allocated/freed below. | |
333 MallocHookScope mhs; | |
334 intptr_t size = 0; | 314 intptr_t size = 0; |
335 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { | 315 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
336 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); | 316 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
337 MallocHooksState::address_map()->Remove(ptr); | 317 MallocHooksState::address_map()->Remove(ptr); |
338 } | 318 } |
339 } | 319 } |
340 } | 320 } |
341 | 321 |
342 } // namespace dart | 322 } // namespace dart |
343 | 323 |
344 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 324 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
OLD | NEW |