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) : mutex_(mutex) { |
25 MutexLocker ml(malloc_hook_scope_mutex_); | 27 mutex_->Lock(); |
26 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); | 28 ASSERT(owner_ == OSThread::kInvalidThreadId); |
27 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); | 29 owner_ = OSThread::GetCurrentThreadId(); |
28 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | |
29 } | 30 } |
30 | 31 |
31 static void DestroyMallocHookFlag() { | 32 virtual ~MallocLocker() { |
32 MutexLocker ml(malloc_hook_scope_mutex_); | 33 ASSERT(IsOwnedByCurrentThread()); |
33 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 34 owner_ = OSThread::kInvalidThreadId; |
34 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); | 35 mutex_->Unlock(); |
35 in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
36 } | 36 } |
37 | 37 |
38 MallocHookScope() { | 38 static bool IsOwnedByCurrentThread() { |
39 MutexLocker ml(malloc_hook_scope_mutex_); | 39 return (owner_ == OSThread::GetCurrentThreadId()); |
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 } | 40 } |
63 | 41 |
64 private: | 42 private: |
65 static Mutex* malloc_hook_scope_mutex_; | 43 Mutex* mutex_; |
66 static ThreadLocalKey in_malloc_hook_flag_; | 44 static ThreadId owner_; |
siva
2017/02/23 01:46:36
It might be cleaner to not declare this as static
bkonyi
2017/02/23 02:03:27
Done.
| |
67 | |
68 DISALLOW_ALLOCATION(); | |
69 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); | |
70 }; | 45 }; |
71 | 46 |
72 | 47 |
73 // Custom key/value trait specifically for address/size pairs. Unlike | 48 // Custom key/value trait specifically for address/size pairs. Unlike |
74 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. | 49 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
75 class AddressKeyValueTrait { | 50 class AddressKeyValueTrait { |
76 public: | 51 public: |
77 typedef const void* Key; | 52 typedef const void* Key; |
78 typedef intptr_t Value; | 53 typedef intptr_t Value; |
79 | 54 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
180 static intptr_t original_pid_; | 155 static intptr_t original_pid_; |
181 static Mutex* malloc_hook_mutex_; | 156 static Mutex* malloc_hook_mutex_; |
182 static intptr_t allocation_count_; | 157 static intptr_t allocation_count_; |
183 static intptr_t heap_allocated_memory_in_bytes_; | 158 static intptr_t heap_allocated_memory_in_bytes_; |
184 static AddressMap* address_map_; | 159 static AddressMap* address_map_; |
185 | 160 |
186 static const intptr_t kInvalidPid = -1; | 161 static const intptr_t kInvalidPid = -1; |
187 }; | 162 }; |
188 | 163 |
189 | 164 |
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. | 165 // MallocHooks state / locks. |
195 bool MallocHooksState::active_ = false; | 166 bool MallocHooksState::active_ = false; |
196 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 167 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
197 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 168 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
169 ThreadId MallocLocker::owner_ = OSThread::kInvalidThreadId; | |
198 | 170 |
199 // Memory allocation state information. | 171 // Memory allocation state information. |
200 intptr_t MallocHooksState::allocation_count_ = 0; | 172 intptr_t MallocHooksState::allocation_count_ = 0; |
201 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 173 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
202 AddressMap* MallocHooksState::address_map_ = NULL; | 174 AddressMap* MallocHooksState::address_map_ = NULL; |
203 | 175 |
204 | 176 |
205 void MallocHooks::InitOnce() { | 177 void MallocHooks::InitOnce() { |
206 if (!FLAG_enable_malloc_hooks) { | 178 if (!FLAG_enable_malloc_hooks) { |
207 return; | 179 return; |
208 } | 180 } |
209 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 181 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
210 ASSERT(!MallocHooksState::Active()); | 182 ASSERT(!MallocHooksState::Active()); |
211 | 183 |
212 MallocHookScope::InitMallocHookFlag(); | |
213 MallocHooksState::Init(); | 184 MallocHooksState::Init(); |
214 | 185 |
215 // Register malloc hooks. | 186 // Register malloc hooks. |
216 bool success = false; | 187 bool success = false; |
217 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); | 188 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
218 ASSERT(success); | 189 ASSERT(success); |
219 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); | 190 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
220 ASSERT(success); | 191 ASSERT(success); |
221 } | 192 } |
222 | 193 |
223 | 194 |
224 void MallocHooks::TearDown() { | 195 void MallocHooks::TearDown() { |
225 if (!FLAG_enable_malloc_hooks) { | 196 if (!FLAG_enable_malloc_hooks) { |
226 return; | 197 return; |
227 } | 198 } |
228 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 199 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
229 ASSERT(MallocHooksState::Active()); | 200 ASSERT(MallocHooksState::Active()); |
230 | 201 |
231 // Remove malloc hooks. | 202 // Remove malloc hooks. |
232 bool success = false; | 203 bool success = false; |
233 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 204 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
234 ASSERT(success); | 205 ASSERT(success); |
235 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 206 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
236 ASSERT(success); | 207 ASSERT(success); |
237 | 208 |
238 MallocHooksState::TearDown(); | 209 MallocHooksState::TearDown(); |
239 MallocHookScope::DestroyMallocHookFlag(); | |
240 } | 210 } |
241 | 211 |
242 | 212 |
243 void MallocHooks::ResetStats() { | 213 void MallocHooks::ResetStats() { |
244 if (!FLAG_enable_malloc_hooks) { | 214 if (!FLAG_enable_malloc_hooks) { |
245 return; | 215 return; |
246 } | 216 } |
247 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 217 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
248 if (MallocHooksState::Active()) { | 218 if (MallocHooksState::Active()) { |
249 MallocHooksState::ResetStats(); | 219 MallocHooksState::ResetStats(); |
250 } | 220 } |
251 } | 221 } |
252 | 222 |
253 | 223 |
254 bool MallocHooks::Active() { | 224 bool MallocHooks::Active() { |
255 if (!FLAG_enable_malloc_hooks) { | 225 if (!FLAG_enable_malloc_hooks) { |
256 return false; | 226 return false; |
257 } | 227 } |
258 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); | 228 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); |
259 return MallocHooksState::Active(); | 229 return MallocHooksState::Active(); |
260 } | 230 } |
261 | 231 |
262 | 232 |
263 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { | 233 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
264 if (!FLAG_enable_malloc_hooks) { | 234 if (!FLAG_enable_malloc_hooks) { |
265 return; | 235 return; |
266 } | 236 } |
267 intptr_t allocated_memory = 0; | 237 intptr_t allocated_memory = 0; |
268 intptr_t allocation_count = 0; | 238 intptr_t allocation_count = 0; |
269 bool add_usage = false; | 239 bool add_usage = false; |
270 // AddProperty may call malloc which would result in an attempt | 240 // AddProperty may call malloc which would result in an attempt |
271 // to acquire the lock recursively so we extract the values first | 241 // to acquire the lock recursively so we extract the values first |
272 // and then add the JSON properties. | 242 // and then add the JSON properties. |
273 { | 243 { |
274 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 244 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
275 if (Active()) { | 245 if (Active()) { |
276 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); | 246 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
277 allocation_count = MallocHooksState::allocation_count(); | 247 allocation_count = MallocHooksState::allocation_count(); |
278 add_usage = true; | 248 add_usage = true; |
279 } | 249 } |
280 } | 250 } |
281 if (add_usage) { | 251 if (add_usage) { |
282 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); | 252 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
283 jsobj->AddProperty("_heapAllocationCount", allocation_count); | 253 jsobj->AddProperty("_heapAllocationCount", allocation_count); |
284 } | 254 } |
285 } | 255 } |
286 | 256 |
287 | 257 |
288 intptr_t MallocHooks::allocation_count() { | 258 intptr_t MallocHooks::allocation_count() { |
289 if (!FLAG_enable_malloc_hooks) { | 259 if (!FLAG_enable_malloc_hooks) { |
290 return 0; | 260 return 0; |
291 } | 261 } |
292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 262 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
293 return MallocHooksState::allocation_count(); | 263 return MallocHooksState::allocation_count(); |
294 } | 264 } |
295 | 265 |
296 | 266 |
297 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { | 267 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
298 if (!FLAG_enable_malloc_hooks) { | 268 if (!FLAG_enable_malloc_hooks) { |
299 return 0; | 269 return 0; |
300 } | 270 } |
301 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 271 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
302 return MallocHooksState::heap_allocated_memory_in_bytes(); | 272 return MallocHooksState::heap_allocated_memory_in_bytes(); |
303 } | 273 } |
304 | 274 |
305 | 275 |
306 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 276 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
307 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 277 if (MallocLocker::IsOwnedByCurrentThread() || |
278 !MallocHooksState::IsOriginalProcess()) { | |
308 return; | 279 return; |
309 } | 280 } |
310 | 281 |
311 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 282 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
312 // Now that we hold the lock, check to make sure everything is still active. | 283 // Now that we hold the lock, check to make sure everything is still active. |
313 if ((ptr != NULL) && MallocHooksState::Active()) { | 284 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); | 285 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
318 MallocHooksState::address_map()->Insert(ptr, size); | 286 MallocHooksState::address_map()->Insert(ptr, size); |
319 } | 287 } |
320 } | 288 } |
321 | 289 |
322 | 290 |
323 void MallocHooksState::RecordFreeHook(const void* ptr) { | 291 void MallocHooksState::RecordFreeHook(const void* ptr) { |
324 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 292 if (MallocLocker::IsOwnedByCurrentThread() || |
293 !MallocHooksState::IsOriginalProcess()) { | |
325 return; | 294 return; |
326 } | 295 } |
327 | 296 |
328 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 297 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
329 // Now that we hold the lock, check to make sure everything is still active. | 298 // Now that we hold the lock, check to make sure everything is still active. |
330 if ((ptr != NULL) && MallocHooksState::Active()) { | 299 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; | 300 intptr_t size = 0; |
335 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { | 301 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
336 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); | 302 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
337 MallocHooksState::address_map()->Remove(ptr); | 303 MallocHooksState::address_map()->Remove(ptr); |
338 } | 304 } |
339 } | 305 } |
340 } | 306 } |
341 | 307 |
342 } // namespace dart | 308 } // namespace dart |
343 | 309 |
344 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 310 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
OLD | NEW |