Index: runtime/vm/malloc_hooks.cc |
diff --git a/runtime/vm/malloc_hooks.cc b/runtime/vm/malloc_hooks.cc |
deleted file mode 100644 |
index 6502bbb3a06ecfcad03a192cd283c6983a794643..0000000000000000000000000000000000000000 |
--- a/runtime/vm/malloc_hooks.cc |
+++ /dev/null |
@@ -1,448 +0,0 @@ |
-// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-#include "platform/globals.h" |
- |
-#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) && \ |
- !defined(TARGET_ARCH_DBC) && !defined(HOST_OS_FUCHSIA) |
- |
-#include "vm/malloc_hooks.h" |
- |
-#include "gperftools/malloc_hook.h" |
- |
-#include "platform/assert.h" |
-#include "vm/hash_map.h" |
-#include "vm/json_stream.h" |
-#include "vm/os_thread.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 ThreadId* malloc_hook_mutex_owner() { |
- return &malloc_hook_mutex_owner_; |
- } |
- static bool IsLockHeldByCurrentThread() { |
- return (malloc_hook_mutex_owner_ == OSThread::GetCurrentThreadId()); |
- } |
- |
- 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_; |
- static ThreadId malloc_hook_mutex_owner_; |
- |
- // 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 similar to MutexLocker which tracks which thread |
-// currently holds the lock. We use this instead of MutexLocker and |
-// mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only |
-// enabled for debug mode. |
-class MallocLocker : public ValueObject { |
- public: |
- explicit MallocLocker(Mutex* mutex, ThreadId* owner) |
- : mutex_(mutex), owner_(owner) { |
- ASSERT(owner != NULL); |
- mutex_->Lock(); |
- ASSERT(*owner_ == OSThread::kInvalidThreadId); |
- *owner_ = OSThread::GetCurrentThreadId(); |
- } |
- |
- virtual ~MallocLocker() { |
- ASSERT(*owner_ == OSThread::GetCurrentThreadId()); |
- *owner_ = OSThread::kInvalidThreadId; |
- mutex_->Unlock(); |
- } |
- |
- private: |
- Mutex* mutex_; |
- ThreadId* owner_; |
-}; |
- |
-// 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: |
- AllocationInfo(uword address, intptr_t allocation_size) |
- : sample_(NULL), address_(address), 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, address, |
- allocation_size); |
- ASSERT(sample_->native_allocation_address() == address_); |
- } |
- } |
- |
- Sample* sample() const { return sample_; } |
- intptr_t allocation_size() const { return allocation_size_; } |
- |
- private: |
- // Note: sample_ is not owned by AllocationInfo, but by the SampleBuffer |
- // created by the profiler. As such, this is only here to track if the sample |
- // is still associated with a native allocation, and its fields are never |
- // accessed from this class. |
- Sample* sample_; |
- uword address_; |
- intptr_t allocation_size_; |
-}; |
- |
- |
-// 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 : public AllStatic { |
- public: |
- typedef const void* Key; |
- typedef AllocationInfo* Value; |
- |
- struct Pair { |
- Key key; |
- Value value; |
- 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) {} |
- }; |
- |
- static Key KeyOf(Pair kv) { return kv.key; } |
- static Value ValueOf(Pair kv) { return kv.value; } |
- static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); } |
- static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; } |
-}; |
- |
- |
-// Map class that will be used to store mappings between ptr -> allocation size. |
-class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> { |
- public: |
- typedef AddressKeyValueTrait::Key Key; |
- typedef AddressKeyValueTrait::Value Value; |
- typedef AddressKeyValueTrait::Pair Pair; |
- |
- virtual ~AddressMap() { Clear(); } |
- |
- void Insert(const Key& key, const Value& value) { |
- Pair pair(key, value); |
- MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair); |
- } |
- |
- bool Lookup(const Key& key, Value* value) { |
- ASSERT(value != NULL); |
- Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key); |
- if (pair == NULL) { |
- return false; |
- } else { |
- *value = pair->value; |
- return true; |
- } |
- } |
- |
- void Clear() { |
- Iterator iter = GetIterator(); |
- Pair* result = iter.Next(); |
- while (result != NULL) { |
- delete result->value; |
- result->value = NULL; |
- result = iter.Next(); |
- } |
- MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear(); |
- } |
-}; |
- |
- |
-// 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(); |
-ThreadId MallocHooksState::malloc_hook_mutex_owner_ = |
- OSThread::kInvalidThreadId; |
- |
-// Memory allocation state information. |
-intptr_t MallocHooksState::allocation_count_ = 0; |
-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 || MallocHooks::Active()) { |
- return; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- ASSERT(!MallocHooksState::Active()); |
- |
- MallocHooksState::Init(); |
- |
- // Register malloc hooks. |
- bool success = false; |
- success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
- ASSERT(success); |
- success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
- ASSERT(success); |
-} |
- |
- |
-void MallocHooks::TearDown() { |
- if (!FLAG_enable_malloc_hooks || !MallocHooks::Active()) { |
- return; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- ASSERT(MallocHooksState::Active()); |
- |
- // Remove malloc hooks. |
- bool success = false; |
- success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
- ASSERT(success); |
- success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
- ASSERT(success); |
- |
- MallocHooksState::TearDown(); |
-} |
- |
- |
-bool MallocHooks::ProfilingEnabled() { |
- return MallocHooksState::ProfilingEnabled(); |
-} |
- |
- |
-bool MallocHooks::stack_trace_collection_enabled() { |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- return MallocHooksState::stack_trace_collection_enabled(); |
-} |
- |
- |
-void MallocHooks::set_stack_trace_collection_enabled(bool enabled) { |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- MallocHooksState::set_stack_trace_collection_enabled(enabled); |
-} |
- |
- |
-void MallocHooks::ResetStats() { |
- if (!FLAG_enable_malloc_hooks) { |
- return; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- if (MallocHooksState::Active()) { |
- MallocHooksState::ResetStats(); |
- } |
-} |
- |
- |
-bool MallocHooks::Active() { |
- if (!FLAG_enable_malloc_hooks) { |
- return false; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- |
- return MallocHooksState::Active(); |
-} |
- |
- |
-void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
- if (!FLAG_enable_malloc_hooks) { |
- return; |
- } |
- intptr_t allocated_memory = 0; |
- intptr_t allocation_count = 0; |
- bool add_usage = false; |
- // AddProperty may call malloc which would result in an attempt |
- // to acquire the lock recursively so we extract the values first |
- // and then add the JSON properties. |
- { |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- if (MallocHooksState::Active()) { |
- allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
- allocation_count = MallocHooksState::allocation_count(); |
- add_usage = true; |
- } |
- } |
- if (add_usage) { |
- jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
- jsobj->AddProperty("_heapAllocationCount", allocation_count); |
- } |
-} |
- |
- |
-intptr_t MallocHooks::allocation_count() { |
- if (!FLAG_enable_malloc_hooks) { |
- return 0; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- return MallocHooksState::allocation_count(); |
-} |
- |
- |
-intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
- if (!FLAG_enable_malloc_hooks) { |
- return 0; |
- } |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- return MallocHooksState::heap_allocated_memory_in_bytes(); |
-} |
- |
- |
-Sample* MallocHooks::GetSample(const void* ptr) { |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- |
- 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 (MallocHooksState::IsLockHeldByCurrentThread() || |
- !MallocHooksState::IsOriginalProcess()) { |
- return; |
- } |
- |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- // Now that we hold the lock, check to make sure everything is still active. |
- if ((ptr != NULL) && MallocHooksState::Active()) { |
- MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
- MallocHooksState::address_map()->Insert( |
- ptr, new AllocationInfo(reinterpret_cast<uword>(ptr), size)); |
- } |
-} |
- |
- |
-void MallocHooksState::RecordFreeHook(const void* ptr) { |
- if (MallocHooksState::IsLockHeldByCurrentThread() || |
- !MallocHooksState::IsOriginalProcess()) { |
- return; |
- } |
- |
- MallocLocker ml(MallocHooksState::malloc_hook_mutex(), |
- MallocHooksState::malloc_hook_mutex_owner()); |
- // Now that we hold the lock, check to make sure everything is still active. |
- if ((ptr != NULL) && MallocHooksState::Active()) { |
- AllocationInfo* allocation_info = NULL; |
- if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) { |
- MallocHooksState::DecrementHeapAllocatedMemoryInBytes( |
- allocation_info->allocation_size()); |
- const bool result = MallocHooksState::address_map()->Remove(ptr); |
- ASSERT(result); |
- delete allocation_info; |
- } |
- } |
-} |
- |
-} // namespace dart |
- |
-#endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) && |
- // !defined(TARGET_ARCH_DBC) && !defined(HOST_OS_FUCHSIA) |