Chromium Code Reviews| 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/lockers.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 to automatically grab and release the |
| 21 // in_malloc_hook_flag_. | 21 // in_malloc_hook_flag_. |
| 22 class MallocHookScope { | 22 class MallocHookScope { |
| 23 public: | 23 public: |
| 24 static void InitMallocHookFlag() { | 24 static void InitMallocHookFlag() { |
| 25 MutexLocker ml(malloc_hook_scope_mutex_); | |
| 25 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); | 26 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); |
| 26 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); | 27 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); |
| 27 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | 28 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); |
| 28 } | 29 } |
| 29 | 30 |
| 30 static void DestroyMallocHookFlag() { | 31 static void DestroyMallocHookFlag() { |
| 32 MutexLocker ml(malloc_hook_scope_mutex_); | |
| 31 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 33 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); |
| 32 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); | 34 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); |
| 33 in_malloc_hook_flag_ = kUnsetThreadLocalKey; | 35 in_malloc_hook_flag_ = kUnsetThreadLocalKey; |
| 34 } | 36 } |
| 35 | 37 |
| 36 MallocHookScope() { | 38 MallocHookScope() { |
| 39 MutexLocker ml(malloc_hook_scope_mutex_); | |
| 37 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 40 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); |
| 38 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1); | 41 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1); |
| 39 } | 42 } |
| 40 | 43 |
| 41 ~MallocHookScope() { | 44 ~MallocHookScope() { |
| 45 MutexLocker ml(malloc_hook_scope_mutex_); | |
| 42 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 46 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); |
| 43 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | 47 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); |
| 44 } | 48 } |
| 45 | 49 |
| 46 static bool IsInHook() { | 50 static bool IsInHook() { |
| 47 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 51 MutexLocker ml(malloc_hook_scope_mutex_); |
|
bkonyi
2017/02/17 21:15:11
I added a mutex to the MallocHookScope class so th
| |
| 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 // MallocHookScope::Active() is checked after the lock is taken before | |
| 58 // proceeding to act on the allocation/free. | |
| 59 return false; | |
| 60 } | |
| 48 return OSThread::GetThreadLocal(in_malloc_hook_flag_); | 61 return OSThread::GetThreadLocal(in_malloc_hook_flag_); |
| 49 } | 62 } |
| 50 | 63 |
| 64 static bool Active() { | |
|
zra
2017/02/17 21:42:26
rm
| |
| 65 MutexLocker ml(malloc_hook_scope_mutex_); | |
| 66 return in_malloc_hook_flag_ != kUnsetThreadLocalKey; | |
|
bkonyi
2017/02/17 21:15:11
I'm guessing this needs parens around the boolean
| |
| 67 } | |
| 68 | |
| 51 private: | 69 private: |
| 70 static Mutex* malloc_hook_scope_mutex_; | |
| 52 static ThreadLocalKey in_malloc_hook_flag_; | 71 static ThreadLocalKey in_malloc_hook_flag_; |
| 53 | 72 |
| 54 DISALLOW_ALLOCATION(); | 73 DISALLOW_ALLOCATION(); |
| 55 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); | 74 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); |
| 56 }; | 75 }; |
| 57 | 76 |
| 58 | 77 |
| 59 // Custom key/value trait specifically for address/size pairs. Unlike | 78 // Custom key/value trait specifically for address/size pairs. Unlike |
| 60 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. | 79 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
| 61 class AddressKeyValueTrait { | 80 class AddressKeyValueTrait { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 166 static intptr_t original_pid_; | 185 static intptr_t original_pid_; |
| 167 static Mutex* malloc_hook_mutex_; | 186 static Mutex* malloc_hook_mutex_; |
| 168 static intptr_t allocation_count_; | 187 static intptr_t allocation_count_; |
| 169 static intptr_t heap_allocated_memory_in_bytes_; | 188 static intptr_t heap_allocated_memory_in_bytes_; |
| 170 static AddressMap* address_map_; | 189 static AddressMap* address_map_; |
| 171 | 190 |
| 172 static const intptr_t kInvalidPid = -1; | 191 static const intptr_t kInvalidPid = -1; |
| 173 }; | 192 }; |
| 174 | 193 |
| 175 | 194 |
| 195 // MallocHookScope state. | |
| 196 Mutex* MallocHookScope::malloc_hook_scope_mutex_ = new Mutex(); | |
| 197 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
| 198 | |
| 176 // MallocHooks state / locks. | 199 // MallocHooks state / locks. |
| 177 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
| 178 bool MallocHooksState::active_ = false; | 200 bool MallocHooksState::active_ = false; |
| 179 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 201 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
| 180 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 202 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
| 181 | 203 |
| 182 // Memory allocation state information. | 204 // Memory allocation state information. |
| 183 intptr_t MallocHooksState::allocation_count_ = 0; | 205 intptr_t MallocHooksState::allocation_count_ = 0; |
| 184 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 206 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
| 185 AddressMap* MallocHooksState::address_map_ = NULL; | 207 AddressMap* MallocHooksState::address_map_ = NULL; |
| 186 | 208 |
| 187 | 209 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 205 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 227 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 206 ASSERT(MallocHooksState::Active()); | 228 ASSERT(MallocHooksState::Active()); |
| 207 | 229 |
| 208 // Remove malloc hooks. | 230 // Remove malloc hooks. |
| 209 bool success = false; | 231 bool success = false; |
| 210 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 232 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
| 211 ASSERT(success); | 233 ASSERT(success); |
| 212 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 234 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
| 213 ASSERT(success); | 235 ASSERT(success); |
| 214 | 236 |
| 215 MallocHooksState::TearDown(); | 237 MallocHooksState::TearDown(); |
|
zra
2017/02/17 21:29:53
!MallocHooksState::Active() implies !MallocHookSco
| |
| 216 MallocHookScope::DestroyMallocHookFlag(); | 238 MallocHookScope::DestroyMallocHookFlag(); |
| 217 } | 239 } |
| 218 | 240 |
| 219 | 241 |
| 220 void MallocHooks::ResetStats() { | 242 void MallocHooks::ResetStats() { |
| 221 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 243 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 222 if (MallocHooksState::Active()) { | 244 if (MallocHooksState::Active()) { |
| 223 MallocHooksState::ResetStats(); | 245 MallocHooksState::ResetStats(); |
| 224 } | 246 } |
| 225 } | 247 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 285 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 264 return MallocHooksState::heap_allocated_memory_in_bytes(); | 286 return MallocHooksState::heap_allocated_memory_in_bytes(); |
| 265 } | 287 } |
| 266 | 288 |
| 267 | 289 |
| 268 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 290 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
| 269 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 291 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { |
| 270 return; | 292 return; |
| 271 } | 293 } |
| 272 | 294 |
| 273 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks | |
| 274 // again. | |
| 275 MallocHookScope mhs; | |
| 276 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 295 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 277 if ((ptr != NULL) && MallocHooksState::Active()) { | 296 // Now that we hold the lock, check to make sure everything is still active. |
| 297 if ((ptr != NULL) && MallocHooksState::Active() && | |
| 298 MallocHookScope::Active()) { | |
|
bkonyi
2017/02/17 21:15:11
This extra condition, MallocHookScope::Active(), i
zra
2017/02/17 21:29:53
I don't think it's possible that we could have Mal
| |
| 299 // Set the malloc hook flag to avoid calling hooks again if memory is | |
| 300 // allocated/freed below. | |
| 301 MallocHookScope mhs; | |
| 278 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); | 302 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
| 279 MallocHooksState::address_map()->Insert(ptr, size); | 303 MallocHooksState::address_map()->Insert(ptr, size); |
| 280 } | 304 } |
| 281 } | 305 } |
| 282 | 306 |
| 283 | 307 |
| 284 void MallocHooksState::RecordFreeHook(const void* ptr) { | 308 void MallocHooksState::RecordFreeHook(const void* ptr) { |
| 285 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 309 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { |
| 286 return; | 310 return; |
| 287 } | 311 } |
| 288 | 312 |
| 289 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks | |
| 290 // again. | |
| 291 MallocHookScope mhs; | |
| 292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 313 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 293 if ((ptr != NULL) && MallocHooksState::Active()) { | 314 // Now that we hold the lock, check to make sure everything is still active. |
| 315 if ((ptr != NULL) && MallocHooksState::Active() && | |
| 316 MallocHookScope::Active()) { | |
|
zra
2017/02/17 21:29:53
ditto
| |
| 317 // Set the malloc hook flag to avoid calling hooks again if memory is | |
| 318 // allocated/freed below. | |
| 319 MallocHookScope mhs; | |
| 294 intptr_t size = 0; | 320 intptr_t size = 0; |
| 295 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { | 321 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
| 296 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); | 322 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
| 297 MallocHooksState::address_map()->Remove(ptr); | 323 MallocHooksState::address_map()->Remove(ptr); |
| 298 } | 324 } |
| 299 } | 325 } |
| 300 } | 326 } |
| 301 | 327 |
| 302 } // namespace dart | 328 } // namespace dart |
| 303 | 329 |
| 304 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 330 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
| OLD | NEW |