| 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/lockers.h" | 16 #include "vm/lockers.h" |
| 16 | 17 |
| 17 namespace dart { | 18 namespace dart { |
| 18 | 19 |
| 19 // A locker-type class to automatically grab and release the | 20 // A locker-type class to automatically grab and release the |
| 20 // in_malloc_hook_flag_. | 21 // in_malloc_hook_flag_. |
| 21 class MallocHookScope { | 22 class MallocHookScope { |
| 22 public: | 23 public: |
| 23 static void InitMallocHookFlag() { | 24 static void InitMallocHookFlag() { |
| 24 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); | 25 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 if (pair == NULL) { | 96 if (pair == NULL) { |
| 96 return false; | 97 return false; |
| 97 } else { | 98 } else { |
| 98 *value = pair->value; | 99 *value = pair->value; |
| 99 return true; | 100 return true; |
| 100 } | 101 } |
| 101 } | 102 } |
| 102 }; | 103 }; |
| 103 | 104 |
| 104 | 105 |
| 105 class MallocHooksState { | 106 class MallocHooksState : public AllStatic { |
| 106 public: | 107 public: |
| 107 static void RecordAllocHook(const void* ptr, size_t size); | 108 static void RecordAllocHook(const void* ptr, size_t size); |
| 108 static void RecordFreeHook(const void* ptr); | 109 static void RecordFreeHook(const void* ptr); |
| 109 | 110 |
| 110 static bool initialized() { return initialized_; } | 111 static bool Active() { return active_; } |
| 111 static void Init() { | 112 static void Init() { |
| 112 address_map_ = new AddressMap(); | 113 address_map_ = new AddressMap(); |
| 113 initialized_ = true; | 114 active_ = true; |
| 114 original_pid_ = OS::ProcessId(); | 115 original_pid_ = OS::ProcessId(); |
| 115 } | 116 } |
| 116 | 117 |
| 117 static bool IsOriginalProcess() { | 118 static bool IsOriginalProcess() { |
| 118 ASSERT(original_pid_ != kInvalidPid); | 119 ASSERT(original_pid_ != kInvalidPid); |
| 119 return original_pid_ == OS::ProcessId(); | 120 return original_pid_ == OS::ProcessId(); |
| 120 } | 121 } |
| 121 | 122 |
| 122 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } | 123 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } |
| 123 | 124 |
| 124 static intptr_t allocation_count() { return allocation_count_; } | 125 static intptr_t allocation_count() { return allocation_count_; } |
| 125 | 126 |
| 126 static intptr_t heap_allocated_memory_in_bytes() { | 127 static intptr_t heap_allocated_memory_in_bytes() { |
| 127 return heap_allocated_memory_in_bytes_; | 128 return heap_allocated_memory_in_bytes_; |
| 128 } | 129 } |
| 129 | 130 |
| 130 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { | 131 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| 132 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| 131 ASSERT(size >= 0); | 133 ASSERT(size >= 0); |
| 132 heap_allocated_memory_in_bytes_ += size; | 134 heap_allocated_memory_in_bytes_ += size; |
| 133 ++allocation_count_; | 135 ++allocation_count_; |
| 134 } | 136 } |
| 135 | 137 |
| 136 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) { | 138 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| 139 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| 137 ASSERT(size >= 0); | 140 ASSERT(size >= 0); |
| 138 ASSERT(heap_allocated_memory_in_bytes_ >= size); | 141 ASSERT(heap_allocated_memory_in_bytes_ >= size); |
| 139 heap_allocated_memory_in_bytes_ -= size; | 142 heap_allocated_memory_in_bytes_ -= size; |
| 140 --allocation_count_; | 143 --allocation_count_; |
| 141 ASSERT(allocation_count_ >= 0); | 144 ASSERT(allocation_count_ >= 0); |
| 142 } | 145 } |
| 143 | 146 |
| 144 static AddressMap* address_map() { return address_map_; } | 147 static AddressMap* address_map() { return address_map_; } |
| 145 | 148 |
| 146 static void ResetStats() { | 149 static void ResetStats() { |
| 150 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| 147 allocation_count_ = 0; | 151 allocation_count_ = 0; |
| 148 heap_allocated_memory_in_bytes_ = 0; | 152 heap_allocated_memory_in_bytes_ = 0; |
| 149 address_map_->Clear(); | 153 address_map_->Clear(); |
| 150 } | 154 } |
| 151 | 155 |
| 152 static void TearDown() { | 156 static void TearDown() { |
| 153 initialized_ = false; | 157 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| 158 active_ = false; |
| 154 original_pid_ = kInvalidPid; | 159 original_pid_ = kInvalidPid; |
| 155 ResetStats(); | 160 ResetStats(); |
| 156 delete address_map_; | 161 delete address_map_; |
| 157 } | 162 } |
| 158 | 163 |
| 159 private: | 164 private: |
| 160 static bool initialized_; | 165 static bool active_; |
| 161 static intptr_t original_pid_; | 166 static intptr_t original_pid_; |
| 162 static Mutex* malloc_hook_mutex_; | 167 static Mutex* malloc_hook_mutex_; |
| 163 static intptr_t allocation_count_; | 168 static intptr_t allocation_count_; |
| 164 static intptr_t heap_allocated_memory_in_bytes_; | 169 static intptr_t heap_allocated_memory_in_bytes_; |
| 165 static AddressMap* address_map_; | 170 static AddressMap* address_map_; |
| 166 | 171 |
| 167 static const intptr_t kInvalidPid = -1; | 172 static const intptr_t kInvalidPid = -1; |
| 168 | |
| 169 DISALLOW_ALLOCATION(); | |
| 170 DISALLOW_COPY_AND_ASSIGN(MallocHooksState); | |
| 171 }; | 173 }; |
| 172 | 174 |
| 173 | 175 |
| 174 // MallocHooks state / locks. | 176 // MallocHooks state / locks. |
| 175 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; | 177 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; |
| 176 bool MallocHooksState::initialized_ = false; | 178 bool MallocHooksState::active_ = false; |
| 177 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 179 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
| 178 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 180 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
| 179 | 181 |
| 180 // Memory allocation state information. | 182 // Memory allocation state information. |
| 181 intptr_t MallocHooksState::allocation_count_ = 0; | 183 intptr_t MallocHooksState::allocation_count_ = 0; |
| 182 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 184 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
| 183 AddressMap* MallocHooksState::address_map_ = NULL; | 185 AddressMap* MallocHooksState::address_map_ = NULL; |
| 184 | 186 |
| 185 | 187 |
| 186 void MallocHooks::InitOnce() { | 188 void MallocHooks::InitOnce() { |
| 187 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 189 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 188 ASSERT(!MallocHooksState::initialized()); | 190 ASSERT(!MallocHooksState::Active()); |
| 189 | 191 |
| 190 MallocHookScope::InitMallocHookFlag(); | 192 MallocHookScope::InitMallocHookFlag(); |
| 191 MallocHooksState::Init(); | 193 MallocHooksState::Init(); |
| 192 | 194 |
| 193 // Register malloc hooks. | 195 // Register malloc hooks. |
| 194 bool success = false; | 196 bool success = false; |
| 195 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); | 197 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
| 196 ASSERT(success); | 198 ASSERT(success); |
| 197 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); | 199 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
| 198 ASSERT(success); | 200 ASSERT(success); |
| 199 } | 201 } |
| 200 | 202 |
| 201 | 203 |
| 202 void MallocHooks::TearDown() { | 204 void MallocHooks::TearDown() { |
| 203 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 205 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 204 ASSERT(MallocHooksState::initialized()); | 206 ASSERT(MallocHooksState::Active()); |
| 205 | 207 |
| 206 // Remove malloc hooks. | 208 // Remove malloc hooks. |
| 207 bool success = false; | 209 bool success = false; |
| 208 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 210 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
| 209 ASSERT(success); | 211 ASSERT(success); |
| 210 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 212 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
| 211 ASSERT(success); | 213 ASSERT(success); |
| 212 | 214 |
| 213 MallocHooksState::TearDown(); | 215 MallocHooksState::TearDown(); |
| 214 MallocHookScope::DestroyMallocHookFlag(); | 216 MallocHookScope::DestroyMallocHookFlag(); |
| 215 } | 217 } |
| 216 | 218 |
| 217 | 219 |
| 218 void MallocHooks::ResetStats() { | 220 void MallocHooks::ResetStats() { |
| 219 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 221 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 220 ASSERT(MallocHooksState::initialized()); | 222 if (MallocHooksState::Active()) { |
| 221 | 223 MallocHooksState::ResetStats(); |
| 222 MallocHooksState::ResetStats(); | 224 } |
| 223 } | 225 } |
| 224 | 226 |
| 225 | 227 |
| 226 bool MallocHooks::Initialized() { | 228 bool MallocHooks::Active() { |
| 227 return MallocHooksState::initialized(); | 229 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| 230 return MallocHooksState::Active(); |
| 228 } | 231 } |
| 229 | 232 |
| 230 | 233 |
| 234 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
| 235 intptr_t allocated_memory = 0; |
| 236 intptr_t allocation_count = 0; |
| 237 bool add_usage = false; |
| 238 // AddProperty may call malloc which would result in an attempt |
| 239 // to acquire the lock recursively so we extract the values first |
| 240 // and then add the JSON properties. |
| 241 { |
| 242 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 243 if (Active()) { |
| 244 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
| 245 allocation_count = MallocHooksState::allocation_count(); |
| 246 add_usage = true; |
| 247 } |
| 248 } |
| 249 if (add_usage) { |
| 250 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
| 251 jsobj->AddProperty("_heapAllocationCount", allocation_count); |
| 252 } |
| 253 } |
| 254 |
| 255 |
| 231 intptr_t MallocHooks::allocation_count() { | 256 intptr_t MallocHooks::allocation_count() { |
| 232 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 257 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 233 return MallocHooksState::allocation_count(); | 258 return MallocHooksState::allocation_count(); |
| 234 } | 259 } |
| 235 | 260 |
| 236 | 261 |
| 237 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { | 262 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
| 238 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 263 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 239 return MallocHooksState::heap_allocated_memory_in_bytes(); | 264 return MallocHooksState::heap_allocated_memory_in_bytes(); |
| 240 } | 265 } |
| 241 | 266 |
| 242 | 267 |
| 243 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 268 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
| 244 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 269 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { |
| 245 return; | 270 return; |
| 246 } | 271 } |
| 247 | 272 |
| 248 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks | 273 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks |
| 249 // again. | 274 // again. |
| 250 MallocHookScope mhs; | 275 MallocHookScope mhs; |
| 251 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 276 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 252 ASSERT(MallocHooksState::initialized()); | 277 if ((ptr != NULL) && MallocHooksState::Active()) { |
| 253 | |
| 254 if (ptr != NULL) { | |
| 255 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); | 278 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
| 256 MallocHooksState::address_map()->Insert(ptr, size); | 279 MallocHooksState::address_map()->Insert(ptr, size); |
| 257 } | 280 } |
| 258 } | 281 } |
| 259 | 282 |
| 260 | 283 |
| 261 void MallocHooksState::RecordFreeHook(const void* ptr) { | 284 void MallocHooksState::RecordFreeHook(const void* ptr) { |
| 262 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 285 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { |
| 263 return; | 286 return; |
| 264 } | 287 } |
| 265 | 288 |
| 266 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks | 289 // Set the malloc hook flag before grabbing the mutex to avoid calling hooks |
| 267 // again. | 290 // again. |
| 268 MallocHookScope mhs; | 291 MallocHookScope mhs; |
| 269 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| 270 ASSERT(MallocHooksState::initialized()); | 293 if ((ptr != NULL) && MallocHooksState::Active()) { |
| 271 | |
| 272 if (ptr != NULL) { | |
| 273 intptr_t size = 0; | 294 intptr_t size = 0; |
| 274 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { | 295 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
| 275 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); | 296 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
| 276 MallocHooksState::address_map()->Remove(ptr); | 297 MallocHooksState::address_map()->Remove(ptr); |
| 277 } | 298 } |
| 278 } | 299 } |
| 279 } | 300 } |
| 280 | 301 |
| 281 } // namespace dart | 302 } // namespace dart |
| 282 | 303 |
| 283 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 304 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
| OLD | NEW |