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/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 |