Chromium Code Reviews| Index: runtime/vm/malloc_hooks.cc |
| diff --git a/runtime/vm/malloc_hooks.cc b/runtime/vm/malloc_hooks.cc |
| index 4f7de9e20d70b8b4c0326e5d47d996f0496ea5dc..35d09fdd10b35f12fab6998fce2a5963ad74ce6a 100644 |
| --- a/runtime/vm/malloc_hooks.cc |
| +++ b/runtime/vm/malloc_hooks.cc |
| @@ -4,7 +4,8 @@ |
| #include "platform/globals.h" |
| -#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
| +#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) && \ |
| + !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA) |
| #include "vm/malloc_hooks.h" |
| @@ -14,9 +15,84 @@ |
| #include "vm/hash_map.h" |
| #include "vm/json_stream.h" |
| #include "vm/lockers.h" |
| +#include "vm/profiler.h" |
| namespace dart { |
| +class AddressMap; |
| + |
| +// MallocHooksState contains all of the state related to the configuration of |
| +// the malloc hooks, allocation information, and locks. |
| +class MallocHooksState : public AllStatic { |
| + public: |
| + static void RecordAllocHook(const void* ptr, size_t size); |
| + static void RecordFreeHook(const void* ptr); |
| + |
| + static bool Active() { |
| + ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + return active_; |
| + } |
| + static void Init(); |
| + |
| + static bool ProfilingEnabled() { return (OSThread::TryCurrent() != NULL); } |
| + |
| + static bool stack_trace_collection_enabled() { |
| + return stack_trace_collection_enabled_; |
| + } |
| + |
| + static void set_stack_trace_collection_enabled(bool enabled) { |
| + stack_trace_collection_enabled_ = enabled; |
| + } |
| + |
| + static bool IsOriginalProcess() { |
| + ASSERT(original_pid_ != kInvalidPid); |
| + return original_pid_ == OS::ProcessId(); |
| + } |
| + |
| + static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } |
| + |
| + static intptr_t allocation_count() { return allocation_count_; } |
| + |
| + static intptr_t heap_allocated_memory_in_bytes() { |
| + return heap_allocated_memory_in_bytes_; |
| + } |
| + |
| + static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| + ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + ASSERT(size >= 0); |
| + heap_allocated_memory_in_bytes_ += size; |
| + ++allocation_count_; |
| + } |
| + |
| + static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| + ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + ASSERT(size >= 0); |
| + ASSERT(heap_allocated_memory_in_bytes_ >= size); |
| + heap_allocated_memory_in_bytes_ -= size; |
| + --allocation_count_; |
| + ASSERT(allocation_count_ >= 0); |
| + } |
| + |
| + static AddressMap* address_map() { return address_map_; } |
| + |
| + static void ResetStats(); |
| + static void TearDown(); |
| + |
| + private: |
| + static Mutex* malloc_hook_mutex_; |
| + |
| + // Variables protected by malloc_hook_mutex_. |
| + static bool active_; |
| + static bool stack_trace_collection_enabled_; |
| + static intptr_t allocation_count_; |
| + static intptr_t heap_allocated_memory_in_bytes_; |
| + static AddressMap* address_map_; |
| + // End protected variables. |
| + |
| + static intptr_t original_pid_; |
| + static const intptr_t kInvalidPid = -1; |
| +}; |
| + |
| // A locker-type class to automatically grab and release the |
| // in_malloc_hook_flag_. |
| class MallocHookScope { |
| @@ -69,18 +145,50 @@ class MallocHookScope { |
| DISALLOW_COPY_AND_ASSIGN(MallocHookScope); |
| }; |
| +// AllocationInfo contains all information related to a given allocation |
| +// including: |
| +// -Allocation size in bytes |
| +// -Stack trace corresponding to the location of allocation, if applicable |
| +class AllocationInfo { |
| + public: |
| + explicit AllocationInfo(intptr_t allocation_size) |
| + : sample_(NULL), allocation_size_(allocation_size) { |
| + // Stack trace collection is disabled when we are in the process of creating |
| + // the first OSThread in order to prevent deadlocks. |
| + if (MallocHooksState::ProfilingEnabled() && |
| + MallocHooksState::stack_trace_collection_enabled()) { |
| + sample_ = Profiler::SampleNativeAllocation(kSkipCount); |
| + } |
| + } |
| + |
| + Sample* sample() const { return sample_; } |
| + intptr_t allocation_size() const { return allocation_size_; } |
| + |
| + private: |
| + Sample* sample_; |
| + intptr_t allocation_size_; |
| + |
| + // The number of frames that are generated by the malloc hooks and collection |
| + // of the stack trace. These frames are ignored when collecting the stack |
| + // trace for a memory allocation. If this number is incorrect, some tests in |
| + // malloc_hook_tests.cc might fail, particularily |
| + // StackTraceMallocHookLengthTest. If this value is updated, please make sure |
| + // that the MallocHooks test cases pass on all platforms. |
| + static const intptr_t kSkipCount = 5; |
| +}; |
| + |
| // Custom key/value trait specifically for address/size pairs. Unlike |
| // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
| -class AddressKeyValueTrait { |
| +class AddressKeyValueTrait : public AllStatic { |
| public: |
| typedef const void* Key; |
| - typedef intptr_t Value; |
| + typedef AllocationInfo* Value; |
| struct Pair { |
| Key key; |
| Value value; |
| - Pair() : key(NULL), value(-1) {} |
| + Pair() : key(NULL), value(NULL) {} |
| Pair(const Key key, const Value& value) : key(key), value(value) {} |
| Pair(const Pair& other) : key(other.key), value(other.value) {} |
| }; |
| @@ -99,12 +207,14 @@ class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { |
| typedef AddressKeyValueTrait::Value Value; |
| typedef AddressKeyValueTrait::Pair Pair; |
| - inline void Insert(const Key& key, const Value& value) { |
| + virtual ~AddressMap() { Clear(); } |
| + |
| + void Insert(const Key& key, const Value& value) { |
| Pair pair(key, value); |
| MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair); |
| } |
| - inline bool Lookup(const Key& key, Value* value) { |
| + bool Lookup(const Key& key, Value* value) { |
| ASSERT(value != NULL); |
| Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key); |
| if (pair == NULL) { |
| @@ -114,76 +224,17 @@ class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { |
| return true; |
| } |
| } |
| -}; |
| - |
| - |
| -class MallocHooksState : public AllStatic { |
| - public: |
| - static void RecordAllocHook(const void* ptr, size_t size); |
| - static void RecordFreeHook(const void* ptr); |
| - |
| - static bool Active() { return active_; } |
| - static void Init() { |
| - address_map_ = new AddressMap(); |
| - active_ = true; |
| - original_pid_ = OS::ProcessId(); |
| - } |
| - |
| - static bool IsOriginalProcess() { |
| - ASSERT(original_pid_ != kInvalidPid); |
| - return original_pid_ == OS::ProcessId(); |
| - } |
| - |
| - static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; } |
| - |
| - static intptr_t allocation_count() { return allocation_count_; } |
| - |
| - static intptr_t heap_allocated_memory_in_bytes() { |
| - return heap_allocated_memory_in_bytes_; |
| - } |
| - |
| - static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| - ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| - ASSERT(size >= 0); |
| - heap_allocated_memory_in_bytes_ += size; |
| - ++allocation_count_; |
| - } |
| - static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) { |
| - ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| - ASSERT(size >= 0); |
| - ASSERT(heap_allocated_memory_in_bytes_ >= size); |
| - heap_allocated_memory_in_bytes_ -= size; |
| - --allocation_count_; |
| - ASSERT(allocation_count_ >= 0); |
| - } |
| - |
| - static AddressMap* address_map() { return address_map_; } |
| - |
| - static void ResetStats() { |
| - ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| - allocation_count_ = 0; |
| - heap_allocated_memory_in_bytes_ = 0; |
| - address_map_->Clear(); |
| - } |
| - |
| - static void TearDown() { |
| - ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| - active_ = false; |
| - original_pid_ = kInvalidPid; |
| - ResetStats(); |
| - delete address_map_; |
| + void Clear() { |
| + Iterator iter = GetIterator(); |
| + Pair* result = iter.Next(); |
| + while (result != NULL) { |
| + delete result->value; |
| + result->value = NULL; |
| + result = iter.Next(); |
| + } |
| + MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear(); |
| } |
| - |
| - private: |
| - static bool active_; |
| - static intptr_t original_pid_; |
| - static Mutex* malloc_hook_mutex_; |
| - static intptr_t allocation_count_; |
| - static intptr_t heap_allocated_memory_in_bytes_; |
| - static AddressMap* address_map_; |
| - |
| - static const intptr_t kInvalidPid = -1; |
| }; |
| @@ -193,6 +244,7 @@ ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; |
| // MallocHooks state / locks. |
| bool MallocHooksState::active_ = false; |
| +bool MallocHooksState::stack_trace_collection_enabled_ = false; |
| intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
| Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
| @@ -202,6 +254,36 @@ intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
| AddressMap* MallocHooksState::address_map_ = NULL; |
| +void MallocHooksState::Init() { |
| + address_map_ = new AddressMap(); |
| + active_ = true; |
| +#if defined(DEBUG) |
| + stack_trace_collection_enabled_ = true; |
| +#else |
| + stack_trace_collection_enabled_ = false; |
| +#endif // defined(DEBUG) |
| + original_pid_ = OS::ProcessId(); |
| +} |
| + |
| + |
| +void MallocHooksState::ResetStats() { |
| + ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + allocation_count_ = 0; |
| + heap_allocated_memory_in_bytes_ = 0; |
| + address_map_->Clear(); |
| +} |
| + |
| + |
| +void MallocHooksState::TearDown() { |
| + ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + active_ = false; |
| + original_pid_ = kInvalidPid; |
| + ResetStats(); |
| + delete address_map_; |
| + address_map_ = NULL; |
| +} |
| + |
| + |
| void MallocHooks::InitOnce() { |
| if (!FLAG_enable_malloc_hooks) { |
| return; |
| @@ -240,10 +322,30 @@ void MallocHooks::TearDown() { |
| } |
| +bool MallocHooks::ProfilingEnabled() { |
| + return MallocHooksState::ProfilingEnabled(); |
| +} |
| + |
| + |
| +bool MallocHooks::stack_trace_collection_enabled() { |
| + MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| + return MallocHooksState::stack_trace_collection_enabled(); |
| +} |
| + |
| + |
| +void MallocHooks::set_stack_trace_collection_enabled(bool enabled) { |
| + MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| + MallocHooksState::set_stack_trace_collection_enabled(enabled); |
| +} |
| + |
| + |
| void MallocHooks::ResetStats() { |
| if (!FLAG_enable_malloc_hooks) { |
| return; |
| } |
| + // Set the malloc hook flag before completing the reset since ResetStats() |
| + // frees memory. |
| + MallocHookScope mhs; |
| MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| if (MallocHooksState::Active()) { |
| MallocHooksState::ResetStats(); |
| @@ -255,7 +357,7 @@ bool MallocHooks::Active() { |
| if (!FLAG_enable_malloc_hooks) { |
| return false; |
| } |
| - ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); |
| + MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| return MallocHooksState::Active(); |
| } |
| @@ -272,7 +374,7 @@ void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
| // and then add the JSON properties. |
| { |
| MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| - if (Active()) { |
| + if (MallocHooksState::Active()) { |
| allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
| allocation_count = MallocHooksState::allocation_count(); |
| add_usage = true; |
| @@ -303,6 +405,21 @@ intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
| } |
| +Sample* MallocHooks::GetSample(const void* ptr) { |
| + MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| + ASSERT(MallocHooksState::Active()); |
| + |
| + if (ptr != NULL) { |
| + AllocationInfo* allocation_info = NULL; |
| + if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { |
| + ASSERT(allocation_info != NULL); |
| + return allocation_info->sample(); |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
| if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { |
| return; |
| @@ -315,7 +432,7 @@ void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
| // allocated/freed below. |
| MallocHookScope mhs; |
| MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
| - MallocHooksState::address_map()->Insert(ptr, size); |
| + MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size)); |
| } |
| } |
| @@ -328,17 +445,18 @@ void MallocHooksState::RecordFreeHook(const void* ptr) { |
| MutexLocker ml(MallocHooksState::malloc_hook_mutex()); |
| // Now that we hold the lock, check to make sure everything is still active. |
| if ((ptr != NULL) && MallocHooksState::Active()) { |
| - // Set the malloc hook flag to avoid calling hooks again if memory is |
| - // allocated/freed below. |
| - MallocHookScope mhs; |
| - intptr_t size = 0; |
| - if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
| - MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
| + AllocationInfo* allocation_info = NULL; |
| + if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { |
| + MallocHookScope mhs; |
|
bkonyi
2017/02/23 21:37:04
Note: This MallocHookScope was accidentally remove
|
| + MallocHooksState::DecrementHeapAllocatedMemoryInBytes( |
| + allocation_info->allocation_size()); |
| MallocHooksState::address_map()->Remove(ptr); |
| + delete allocation_info; |
| } |
| } |
| } |
| } // namespace dart |
| -#endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
| +#endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) && |
| + // !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA) |