| /* | |
| * Copyright 2016 Google Inc. All Rights Reserved. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef NB_MEMORY_TRACKER_HELPERS_H_ | |
| #define NB_MEMORY_TRACKER_HELPERS_H_ | |
| #include <map> | |
| #include <vector> | |
| #include "nb/analytics/memory_tracker.h" | |
| #include "nb/atomic.h" | |
| #include "starboard/mutex.h" | |
| #include "starboard/thread.h" | |
| #include "starboard/types.h" | |
| #include "starboard/log.h" | |
| namespace nb { | |
| namespace analytics { | |
| class AllocationGroup; | |
| struct AllocationRecord; | |
| template <typename Type> | |
| class ThreadLocalPointer { | |
| public: | |
| ThreadLocalPointer() { | |
| slot_ = SbThreadCreateLocalKey(NULL); // No destructor for pointer. | |
| SB_DCHECK(kSbThreadLocalKeyInvalid != slot_); | |
| } | |
| ~ThreadLocalPointer() { SbThreadDestroyLocalKey(slot_); } | |
| Type* Get() const { | |
| void* ptr = SbThreadGetLocalValue(slot_); | |
| Type* type_ptr = static_cast<Type*>(ptr); | |
| return type_ptr; | |
| } | |
| void Set(Type* ptr) { | |
| void* void_ptr = static_cast<void*>(ptr); | |
| SbThreadSetLocalValue(slot_, void_ptr); | |
| } | |
| private: | |
| SbThreadLocalKey slot_; | |
| SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>); | |
| }; | |
| class ThreadLocalBoolean { | |
| public: | |
| ThreadLocalBoolean() : default_value_(false) {} | |
| explicit ThreadLocalBoolean(bool default_value) | |
| : default_value_(default_value) {} | |
| ~ThreadLocalBoolean() {} | |
| bool Get() const { | |
| bool val = tlp_.Get() != NULL; | |
| return val ^ default_value_; | |
| } | |
| void Set(bool val) { | |
| val = val ^ default_value_; | |
| tlp_.Set(val ? TruePointer() : FalsePointer()); | |
| } | |
| private: | |
| static void* TruePointer() { return reinterpret_cast<void*>(0x1); } | |
| static void* FalsePointer() { return NULL; } | |
| ThreadLocalPointer<void> tlp_; | |
| const bool default_value_; | |
| SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); | |
| }; | |
| // An AllocationGroup is a collection of allocations that are logically lumped | |
| // together, such as "Javascript" or "Graphics". | |
| class AllocationGroup { | |
| public: | |
| AllocationGroup(const std::string& name); | |
| ~AllocationGroup(); | |
| const std::string& name() const { return name_; } | |
| void AddAllocation(int64_t num_bytes); | |
| void GetAggregateStats(int32_t* num_allocs, int64_t* allocation_bytes) const; | |
| int64_t allocation_bytes() const; | |
| int32_t num_allocations() const; | |
| private: | |
| const std::string name_; | |
| nb::atomic_int64_t allocation_bytes_; | |
| nb::atomic_int32_t num_allocations_; | |
| }; | |
| // A self locking data structure that maps strings -> AllocationGroups. This is | |
| // used to resolve MemoryGroup names (e.g. "Javascript") to an AllocationGroup | |
| // which can be used to group allocations together. | |
| class AtomicStringAllocationGroupMap { | |
| public: | |
| AtomicStringAllocationGroupMap(); | |
| ~AtomicStringAllocationGroupMap(); | |
| AllocationGroup* Ensure(const std::string& name); | |
| AllocationGroup* GetDefaultUnaccounted(); | |
| bool Erase(const std::string& key); | |
| void GetAll(std::vector<const AllocationGroup*>* output) const; | |
| private: | |
| typedef std::map<std::string, AllocationGroup*> Map; | |
| Map group_map_; | |
| AllocationGroup* unaccounted_group_; | |
| mutable starboard::Mutex mutex_; | |
| }; | |
| class AllocationGroupStack { | |
| public: | |
| AllocationGroupStack() { Push_DebugBreak(NULL); } | |
| ~AllocationGroupStack() {} | |
| void Push(AllocationGroup* group); | |
| void Pop(); | |
| AllocationGroup* Peek(); | |
| void Push_DebugBreak(AllocationGroup* ag) { debug_stack_.push_back(ag); } | |
| void Pop_DebugBreak() { debug_stack_.pop_back(); } | |
| AllocationGroup* Peek_DebugBreak() { | |
| if (debug_stack_.empty()) { | |
| return NULL; | |
| } | |
| return debug_stack_.back(); | |
| } | |
| private: | |
| SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroupStack); | |
| typedef std::vector<AllocationGroup*> AllocationGroupPtrVec; | |
| AllocationGroupPtrVec alloc_group_stack_, debug_stack_; | |
| }; | |
| // A per-pointer map of allocations to AllocRecords. This map is thread safe. | |
| class AtomicAllocationMap { | |
| public: | |
| AtomicAllocationMap(); | |
| ~AtomicAllocationMap(); | |
| // Returns true if Added. Otherwise false means that the pointer | |
| // already existed. | |
| bool Add(const void* memory, const AllocationRecord& alloc_record); | |
| // Returns true if the memory exists in this set. | |
| bool Get(const void* memory, AllocationRecord* alloc_record) const; | |
| // Return true if the memory existed in this set. If true | |
| // then output alloc_record is written with record that was found. | |
| // otherwise the record is written as 0 bytes and null key. | |
| bool Remove(const void* memory, AllocationRecord* alloc_record); | |
| bool Accept(AllocationVisitor* visitor) const; | |
| size_t Size() const; | |
| bool Empty() const; | |
| void Clear(); | |
| private: | |
| SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap); | |
| typedef std::map<const void*, AllocationRecord> PointerMap; | |
| PointerMap pointer_map_; | |
| mutable starboard::Mutex mutex_; | |
| }; | |
| // A per-pointer map of allocations to AllocRecords. This is a hybrid data | |
| // structure consisting of a hashtable of maps. Each pointer that is | |
| // stored or retrieved is hashed to a random bucket. Each bucket has it's own | |
| // lock. This distributed pattern increases performance significantly by | |
| // reducing contention. The top-level hashtable is of constant size and does | |
| // not resize. Each bucket is implemented as it's own map of elements. | |
| class ConcurrentAllocationMap { | |
| public: | |
| static const int kNumElements = 511; | |
| ConcurrentAllocationMap(); | |
| ~ConcurrentAllocationMap(); | |
| // Returns true if Added. Otherwise false means that the pointer | |
| // already existed. | |
| bool Add(const void* memory, const AllocationRecord& alloc_record); | |
| // Returns true if the memory exists in this set. | |
| bool Get(const void* memory, AllocationRecord* alloc_record) const; | |
| // Return true if the memory existed in this set. If true | |
| // then output alloc_record is written with record that was found. | |
| // otherwise the record is written as 0 bytes and null key. | |
| bool Remove(const void* memory, AllocationRecord* alloc_record); | |
| size_t Size() const; | |
| bool Empty() const; | |
| void Clear(); | |
| // Provides access to all the allocations within in a thread safe manner. | |
| bool Accept(AllocationVisitor* visitor) const; | |
| AtomicAllocationMap& GetMapForPointer(const void* ptr); | |
| const AtomicAllocationMap& GetMapForPointer(const void* ptr) const; | |
| private: | |
| SB_DISALLOW_COPY_AND_ASSIGN(ConcurrentAllocationMap); | |
| // Takes a pointer and generates a hash. | |
| static size_t hash_ptr(const void* ptr); | |
| int ToIndex(const void* ptr) const; | |
| AtomicAllocationMap pointer_map_array_[kNumElements]; | |
| }; | |
| class SimpleThread { | |
| public: | |
| explicit SimpleThread(const std::string& name); | |
| virtual ~SimpleThread() = 0; | |
| // Subclasses should override the Run method. | |
| virtual void Run() = 0; | |
| void Join() { | |
| Cancel(); | |
| DoJoin(); | |
| } | |
| // If Join() is intended to interrupt the Run() function then override | |
| // Cancel() to send a signal. | |
| // Example: | |
| // virtual void Cancel() { finished_ = true; } | |
| // virtual void Run() { | |
| // while (!finished_) { /* do work */ } | |
| // } | |
| virtual void Cancel() {} | |
| // Calls SbThreadCreate() and starts running code. | |
| void Start(); | |
| private: | |
| static void* ThreadEntryPoint(void* context); | |
| void DoJoin(); | |
| void DoStart(); | |
| const std::string name_; | |
| SbThread thread_; | |
| SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread); | |
| }; | |
| } // namespace analytics | |
| } // namespace nb | |
| #endif // NB_MEMORY_TRACKER_HELPERS_H_ |