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 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 private: | 155 private: |
156 // Note: sample_ is not owned by AllocationInfo, but by the SampleBuffer | 156 // Note: sample_ is not owned by AllocationInfo, but by the SampleBuffer |
157 // created by the profiler. As such, this is only here to track if the sample | 157 // created by the profiler. As such, this is only here to track if the sample |
158 // is still associated with a native allocation, and its fields are never | 158 // is still associated with a native allocation, and its fields are never |
159 // accessed from this class. | 159 // accessed from this class. |
160 Sample* sample_; | 160 Sample* sample_; |
161 uword address_; | 161 uword address_; |
162 intptr_t allocation_size_; | 162 intptr_t allocation_size_; |
163 }; | 163 }; |
164 | 164 |
165 | |
166 // Custom key/value trait specifically for address/size pairs. Unlike | 165 // Custom key/value trait specifically for address/size pairs. Unlike |
167 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. | 166 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
168 class AddressKeyValueTrait : public AllStatic { | 167 class AddressKeyValueTrait : public AllStatic { |
169 public: | 168 public: |
170 typedef const void* Key; | 169 typedef const void* Key; |
171 typedef AllocationInfo* Value; | 170 typedef AllocationInfo* Value; |
172 | 171 |
173 struct Pair { | 172 struct Pair { |
174 Key key; | 173 Key key; |
175 Value value; | 174 Value value; |
176 Pair() : key(NULL), value(NULL) {} | 175 Pair() : key(NULL), value(NULL) {} |
177 Pair(const Key key, const Value& value) : key(key), value(value) {} | 176 Pair(const Key key, const Value& value) : key(key), value(value) {} |
178 Pair(const Pair& other) : key(other.key), value(other.value) {} | 177 Pair(const Pair& other) : key(other.key), value(other.value) {} |
179 }; | 178 }; |
180 | 179 |
181 static Key KeyOf(Pair kv) { return kv.key; } | 180 static Key KeyOf(Pair kv) { return kv.key; } |
182 static Value ValueOf(Pair kv) { return kv.value; } | 181 static Value ValueOf(Pair kv) { return kv.value; } |
183 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } | 182 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } |
184 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } | 183 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } |
185 }; | 184 }; |
186 | 185 |
187 | |
188 // Map class that will be used to store mappings between ptr -> allocation size. | 186 // Map class that will be used to store mappings between ptr -> allocation size. |
189 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { | 187 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { |
190 public: | 188 public: |
191 typedef AddressKeyValueTrait::Key Key; | 189 typedef AddressKeyValueTrait::Key Key; |
192 typedef AddressKeyValueTrait::Value Value; | 190 typedef AddressKeyValueTrait::Value Value; |
193 typedef AddressKeyValueTrait::Pair Pair; | 191 typedef AddressKeyValueTrait::Pair Pair; |
194 | 192 |
195 virtual ~AddressMap() { Clear(); } | 193 virtual ~AddressMap() { Clear(); } |
196 | 194 |
197 void Insert(const Key& key, const Value& value) { | 195 void Insert(const Key& key, const Value& value) { |
(...skipping 17 matching lines...) Expand all Loading... |
215 Pair* result = iter.Next(); | 213 Pair* result = iter.Next(); |
216 while (result != NULL) { | 214 while (result != NULL) { |
217 delete result->value; | 215 delete result->value; |
218 result->value = NULL; | 216 result->value = NULL; |
219 result = iter.Next(); | 217 result = iter.Next(); |
220 } | 218 } |
221 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear(); | 219 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear(); |
222 } | 220 } |
223 }; | 221 }; |
224 | 222 |
225 | |
226 // MallocHooks state / locks. | 223 // MallocHooks state / locks. |
227 bool MallocHooksState::active_ = false; | 224 bool MallocHooksState::active_ = false; |
228 bool MallocHooksState::stack_trace_collection_enabled_ = false; | 225 bool MallocHooksState::stack_trace_collection_enabled_ = false; |
229 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 226 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
230 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 227 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
231 ThreadId MallocHooksState::malloc_hook_mutex_owner_ = | 228 ThreadId MallocHooksState::malloc_hook_mutex_owner_ = |
232 OSThread::kInvalidThreadId; | 229 OSThread::kInvalidThreadId; |
233 | 230 |
234 // Memory allocation state information. | 231 // Memory allocation state information. |
235 intptr_t MallocHooksState::allocation_count_ = 0; | 232 intptr_t MallocHooksState::allocation_count_ = 0; |
236 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 233 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
237 AddressMap* MallocHooksState::address_map_ = NULL; | 234 AddressMap* MallocHooksState::address_map_ = NULL; |
238 | 235 |
239 | |
240 void MallocHooksState::Init() { | 236 void MallocHooksState::Init() { |
241 address_map_ = new AddressMap(); | 237 address_map_ = new AddressMap(); |
242 active_ = true; | 238 active_ = true; |
243 #if defined(DEBUG) | 239 #if defined(DEBUG) |
244 stack_trace_collection_enabled_ = true; | 240 stack_trace_collection_enabled_ = true; |
245 #else | 241 #else |
246 stack_trace_collection_enabled_ = false; | 242 stack_trace_collection_enabled_ = false; |
247 #endif // defined(DEBUG) | 243 #endif // defined(DEBUG) |
248 original_pid_ = OS::ProcessId(); | 244 original_pid_ = OS::ProcessId(); |
249 } | 245 } |
250 | 246 |
251 | |
252 void MallocHooksState::ResetStats() { | 247 void MallocHooksState::ResetStats() { |
253 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); | 248 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
254 allocation_count_ = 0; | 249 allocation_count_ = 0; |
255 heap_allocated_memory_in_bytes_ = 0; | 250 heap_allocated_memory_in_bytes_ = 0; |
256 address_map_->Clear(); | 251 address_map_->Clear(); |
257 } | 252 } |
258 | 253 |
259 | |
260 void MallocHooksState::TearDown() { | 254 void MallocHooksState::TearDown() { |
261 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); | 255 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
262 active_ = false; | 256 active_ = false; |
263 original_pid_ = kInvalidPid; | 257 original_pid_ = kInvalidPid; |
264 ResetStats(); | 258 ResetStats(); |
265 delete address_map_; | 259 delete address_map_; |
266 address_map_ = NULL; | 260 address_map_ = NULL; |
267 } | 261 } |
268 | 262 |
269 | |
270 void MallocHooks::InitOnce() { | 263 void MallocHooks::InitOnce() { |
271 if (!FLAG_profiler_native_memory || MallocHooks::Active()) { | 264 if (!FLAG_profiler_native_memory || MallocHooks::Active()) { |
272 return; | 265 return; |
273 } | 266 } |
274 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 267 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
275 MallocHooksState::malloc_hook_mutex_owner()); | 268 MallocHooksState::malloc_hook_mutex_owner()); |
276 ASSERT(!MallocHooksState::Active()); | 269 ASSERT(!MallocHooksState::Active()); |
277 | 270 |
278 MallocHooksState::Init(); | 271 MallocHooksState::Init(); |
279 | 272 |
280 // Register malloc hooks. | 273 // Register malloc hooks. |
281 bool success = false; | 274 bool success = false; |
282 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); | 275 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
283 ASSERT(success); | 276 ASSERT(success); |
284 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); | 277 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
285 ASSERT(success); | 278 ASSERT(success); |
286 } | 279 } |
287 | 280 |
288 | |
289 void MallocHooks::TearDown() { | 281 void MallocHooks::TearDown() { |
290 if (!FLAG_profiler_native_memory || !MallocHooks::Active()) { | 282 if (!FLAG_profiler_native_memory || !MallocHooks::Active()) { |
291 return; | 283 return; |
292 } | 284 } |
293 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 285 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
294 MallocHooksState::malloc_hook_mutex_owner()); | 286 MallocHooksState::malloc_hook_mutex_owner()); |
295 ASSERT(MallocHooksState::Active()); | 287 ASSERT(MallocHooksState::Active()); |
296 | 288 |
297 // Remove malloc hooks. | 289 // Remove malloc hooks. |
298 bool success = false; | 290 bool success = false; |
299 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 291 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
300 ASSERT(success); | 292 ASSERT(success); |
301 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 293 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
302 ASSERT(success); | 294 ASSERT(success); |
303 | 295 |
304 MallocHooksState::TearDown(); | 296 MallocHooksState::TearDown(); |
305 } | 297 } |
306 | 298 |
307 | |
308 bool MallocHooks::ProfilingEnabled() { | 299 bool MallocHooks::ProfilingEnabled() { |
309 return MallocHooksState::ProfilingEnabled(); | 300 return MallocHooksState::ProfilingEnabled(); |
310 } | 301 } |
311 | 302 |
312 | |
313 bool MallocHooks::stack_trace_collection_enabled() { | 303 bool MallocHooks::stack_trace_collection_enabled() { |
314 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 304 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
315 MallocHooksState::malloc_hook_mutex_owner()); | 305 MallocHooksState::malloc_hook_mutex_owner()); |
316 return MallocHooksState::stack_trace_collection_enabled(); | 306 return MallocHooksState::stack_trace_collection_enabled(); |
317 } | 307 } |
318 | 308 |
319 | |
320 void MallocHooks::set_stack_trace_collection_enabled(bool enabled) { | 309 void MallocHooks::set_stack_trace_collection_enabled(bool enabled) { |
321 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 310 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
322 MallocHooksState::malloc_hook_mutex_owner()); | 311 MallocHooksState::malloc_hook_mutex_owner()); |
323 MallocHooksState::set_stack_trace_collection_enabled(enabled); | 312 MallocHooksState::set_stack_trace_collection_enabled(enabled); |
324 } | 313 } |
325 | 314 |
326 | |
327 void MallocHooks::ResetStats() { | 315 void MallocHooks::ResetStats() { |
328 if (!FLAG_profiler_native_memory) { | 316 if (!FLAG_profiler_native_memory) { |
329 return; | 317 return; |
330 } | 318 } |
331 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 319 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
332 MallocHooksState::malloc_hook_mutex_owner()); | 320 MallocHooksState::malloc_hook_mutex_owner()); |
333 if (MallocHooksState::Active()) { | 321 if (MallocHooksState::Active()) { |
334 MallocHooksState::ResetStats(); | 322 MallocHooksState::ResetStats(); |
335 } | 323 } |
336 } | 324 } |
337 | 325 |
338 | |
339 bool MallocHooks::Active() { | 326 bool MallocHooks::Active() { |
340 if (!FLAG_profiler_native_memory) { | 327 if (!FLAG_profiler_native_memory) { |
341 return false; | 328 return false; |
342 } | 329 } |
343 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 330 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
344 MallocHooksState::malloc_hook_mutex_owner()); | 331 MallocHooksState::malloc_hook_mutex_owner()); |
345 | 332 |
346 return MallocHooksState::Active(); | 333 return MallocHooksState::Active(); |
347 } | 334 } |
348 | 335 |
349 | |
350 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { | 336 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
351 if (!FLAG_profiler_native_memory) { | 337 if (!FLAG_profiler_native_memory) { |
352 return; | 338 return; |
353 } | 339 } |
354 intptr_t allocated_memory = 0; | 340 intptr_t allocated_memory = 0; |
355 intptr_t allocation_count = 0; | 341 intptr_t allocation_count = 0; |
356 bool add_usage = false; | 342 bool add_usage = false; |
357 // AddProperty may call malloc which would result in an attempt | 343 // AddProperty may call malloc which would result in an attempt |
358 // to acquire the lock recursively so we extract the values first | 344 // to acquire the lock recursively so we extract the values first |
359 // and then add the JSON properties. | 345 // and then add the JSON properties. |
360 { | 346 { |
361 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 347 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
362 MallocHooksState::malloc_hook_mutex_owner()); | 348 MallocHooksState::malloc_hook_mutex_owner()); |
363 if (MallocHooksState::Active()) { | 349 if (MallocHooksState::Active()) { |
364 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); | 350 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
365 allocation_count = MallocHooksState::allocation_count(); | 351 allocation_count = MallocHooksState::allocation_count(); |
366 add_usage = true; | 352 add_usage = true; |
367 } | 353 } |
368 } | 354 } |
369 if (add_usage) { | 355 if (add_usage) { |
370 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); | 356 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
371 jsobj->AddProperty("_heapAllocationCount", allocation_count); | 357 jsobj->AddProperty("_heapAllocationCount", allocation_count); |
372 } | 358 } |
373 } | 359 } |
374 | 360 |
375 | |
376 intptr_t MallocHooks::allocation_count() { | 361 intptr_t MallocHooks::allocation_count() { |
377 if (!FLAG_profiler_native_memory) { | 362 if (!FLAG_profiler_native_memory) { |
378 return 0; | 363 return 0; |
379 } | 364 } |
380 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 365 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
381 MallocHooksState::malloc_hook_mutex_owner()); | 366 MallocHooksState::malloc_hook_mutex_owner()); |
382 return MallocHooksState::allocation_count(); | 367 return MallocHooksState::allocation_count(); |
383 } | 368 } |
384 | 369 |
385 | |
386 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { | 370 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
387 if (!FLAG_profiler_native_memory) { | 371 if (!FLAG_profiler_native_memory) { |
388 return 0; | 372 return 0; |
389 } | 373 } |
390 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 374 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
391 MallocHooksState::malloc_hook_mutex_owner()); | 375 MallocHooksState::malloc_hook_mutex_owner()); |
392 return MallocHooksState::heap_allocated_memory_in_bytes(); | 376 return MallocHooksState::heap_allocated_memory_in_bytes(); |
393 } | 377 } |
394 | 378 |
395 | |
396 Sample* MallocHooks::GetSample(const void* ptr) { | 379 Sample* MallocHooks::GetSample(const void* ptr) { |
397 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 380 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
398 MallocHooksState::malloc_hook_mutex_owner()); | 381 MallocHooksState::malloc_hook_mutex_owner()); |
399 | 382 |
400 ASSERT(MallocHooksState::Active()); | 383 ASSERT(MallocHooksState::Active()); |
401 | 384 |
402 if (ptr != NULL) { | 385 if (ptr != NULL) { |
403 AllocationInfo* allocation_info = NULL; | 386 AllocationInfo* allocation_info = NULL; |
404 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { | 387 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { |
405 ASSERT(allocation_info != NULL); | 388 ASSERT(allocation_info != NULL); |
406 return allocation_info->sample(); | 389 return allocation_info->sample(); |
407 } | 390 } |
408 } | 391 } |
409 return NULL; | 392 return NULL; |
410 } | 393 } |
411 | 394 |
412 | |
413 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 395 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
414 if (MallocHooksState::IsLockHeldByCurrentThread() || | 396 if (MallocHooksState::IsLockHeldByCurrentThread() || |
415 !MallocHooksState::IsOriginalProcess()) { | 397 !MallocHooksState::IsOriginalProcess()) { |
416 return; | 398 return; |
417 } | 399 } |
418 | 400 |
419 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 401 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
420 MallocHooksState::malloc_hook_mutex_owner()); | 402 MallocHooksState::malloc_hook_mutex_owner()); |
421 // Now that we hold the lock, check to make sure everything is still active. | 403 // Now that we hold the lock, check to make sure everything is still active. |
422 if ((ptr != NULL) && MallocHooksState::Active()) { | 404 if ((ptr != NULL) && MallocHooksState::Active()) { |
423 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); | 405 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
424 MallocHooksState::address_map()->Insert( | 406 MallocHooksState::address_map()->Insert( |
425 ptr, new AllocationInfo(reinterpret_cast<uword>(ptr), size)); | 407 ptr, new AllocationInfo(reinterpret_cast<uword>(ptr), size)); |
426 } | 408 } |
427 } | 409 } |
428 | 410 |
429 | |
430 void MallocHooksState::RecordFreeHook(const void* ptr) { | 411 void MallocHooksState::RecordFreeHook(const void* ptr) { |
431 if (MallocHooksState::IsLockHeldByCurrentThread() || | 412 if (MallocHooksState::IsLockHeldByCurrentThread() || |
432 !MallocHooksState::IsOriginalProcess()) { | 413 !MallocHooksState::IsOriginalProcess()) { |
433 return; | 414 return; |
434 } | 415 } |
435 | 416 |
436 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), | 417 MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
437 MallocHooksState::malloc_hook_mutex_owner()); | 418 MallocHooksState::malloc_hook_mutex_owner()); |
438 // Now that we hold the lock, check to make sure everything is still active. | 419 // Now that we hold the lock, check to make sure everything is still active. |
439 if ((ptr != NULL) && MallocHooksState::Active()) { | 420 if ((ptr != NULL) && MallocHooksState::Active()) { |
440 AllocationInfo* allocation_info = NULL; | 421 AllocationInfo* allocation_info = NULL; |
441 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { | 422 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { |
442 MallocHooksState::DecrementHeapAllocatedMemoryInBytes( | 423 MallocHooksState::DecrementHeapAllocatedMemoryInBytes( |
443 allocation_info->allocation_size()); | 424 allocation_info->allocation_size()); |
444 const bool result = MallocHooksState::address_map()->Remove(ptr); | 425 const bool result = MallocHooksState::address_map()->Remove(ptr); |
445 ASSERT(result); | 426 ASSERT(result); |
446 delete allocation_info; | 427 delete allocation_info; |
447 } | 428 } |
448 } | 429 } |
449 } | 430 } |
450 | 431 |
451 } // namespace dart | 432 } // namespace dart |
452 | 433 |
453 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 434 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
OLD | NEW |