blob: d00462092e06734d0385d36ade9036ae39917421 [file] [log] [blame]
/*
* 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 "nb/simple_thread.h"
#include "starboard/mutex.h"
#include "starboard/thread.h"
#include "starboard/types.h"
#include "starboard/log.h"
namespace nb {
namespace analytics {
class AllocationGroup;
class 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:
explicit 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_;
SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroup);
};
// 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_;
SB_DISALLOW_COPY_AND_ASSIGN(AtomicStringAllocationGroupMap);
};
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];
};
} // namespace analytics
} // namespace nb
#endif // NB_MEMORY_TRACKER_HELPERS_H_