| Index: runtime/vm/malloc_hooks.cc
|
| diff --git a/runtime/vm/malloc_hooks.cc b/runtime/vm/malloc_hooks.cc
|
| index 9e5c6587c3065498c8a757de121770e315e1b03d..30fdaaa79e3dc914d9fb638d77bdb7a457ca2f50 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 {
|
| @@ -55,18 +131,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) {}
|
| };
|
| @@ -85,12 +193,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) {
|
| @@ -100,82 +210,24 @@ 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;
|
| };
|
|
|
|
|
| // MallocHooks state / locks.
|
| ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey;
|
| 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();
|
|
|
| @@ -185,6 +237,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() {
|
| MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
| ASSERT(!MallocHooksState::Active());
|
| @@ -217,7 +299,27 @@ 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() {
|
| + // 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();
|
| @@ -226,7 +328,7 @@ void MallocHooks::ResetStats() {
|
|
|
|
|
| bool MallocHooks::Active() {
|
| - ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread());
|
| + MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
| return MallocHooksState::Active();
|
| }
|
|
|
| @@ -240,7 +342,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;
|
| @@ -265,6 +367,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;
|
| @@ -276,7 +393,7 @@ void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
|
| MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
| if ((ptr != NULL) && MallocHooksState::Active()) {
|
| MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
|
| - MallocHooksState::address_map()->Insert(ptr, size);
|
| + MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
|
| }
|
| }
|
|
|
| @@ -291,14 +408,17 @@ void MallocHooksState::RecordFreeHook(const void* ptr) {
|
| MallocHookScope mhs;
|
| MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
| if ((ptr != NULL) && MallocHooksState::Active()) {
|
| - 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)) {
|
| + 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)
|
|
|