Loading libmemunreachable/Allocator.cpp +60 −74 Original line number Diff line number Diff line Loading @@ -33,9 +33,9 @@ #include "android-base/macros.h" #include "anon_vma_naming.h" #include "Allocator.h" #include "LinkedList.h" #include "anon_vma_naming.h" // runtime interfaces used: // abort Loading @@ -57,10 +57,9 @@ static constexpr size_t kChunkSize = 256 * 1024; static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize; static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4; static constexpr size_t kMinBucketAllocationSize = 8; static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1; static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize; static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1; static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize; std::atomic<int> heap_count; Loading Loading @@ -107,8 +106,7 @@ static inline unsigned int log2(size_t n) { } static inline unsigned int size_to_bucket(size_t size) { if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize; if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize; return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize); } Loading Loading @@ -140,8 +138,7 @@ static void* MapAligned(size_t size, size_t align) { // Trim beginning if (aligned_ptr != ptr) { ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr); ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr); munmap(ptr, extra); map_size -= extra; ptr = aligned_ptr; Loading @@ -151,14 +148,13 @@ static void* MapAligned(size_t size, size_t align) { if (map_size != size) { assert(map_size > size); assert(ptr != NULL); munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size); munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size); } #define PR_SET_VMA 0x53564d41 #define PR_SET_VMA_ANON_NAME 0 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc"); return ptr; } Loading @@ -176,19 +172,14 @@ class Chunk { bool Empty(); static Chunk* ptr_to_chunk(void* ptr) { return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1)); return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1)); } static bool is_chunk(void* ptr) { return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0; } unsigned int free_count() { return free_count_; } HeapImpl* heap() { return heap_; } unsigned int free_count() { return free_count_; } HeapImpl* heap() { return heap_; } LinkedList<Chunk*> node_; // linked list sorted by minimum free count private: Loading @@ -210,13 +201,10 @@ class Chunk { char data_[0]; unsigned int ptr_to_n(void* ptr) { ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_); ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_); return offset / allocation_size_; } void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; } void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; } }; static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page"); Loading @@ -237,11 +225,15 @@ void Chunk::operator delete(void *ptr) { munmap(ptr, kChunkSize); } Chunk::Chunk(HeapImpl* heap, int bucket) : node_(this), heap_(heap), bucket_(bucket), allocation_size_( bucket_to_size(bucket)), max_allocations_( kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_( max_allocations_), frees_since_purge_(0) { Chunk::Chunk(HeapImpl* heap, int bucket) : node_(this), heap_(heap), bucket_(bucket), allocation_size_(bucket_to_size(bucket)), max_allocations_(kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(max_allocations_), frees_since_purge_(0) { memset(dirty_pages_, 0, sizeof(dirty_pages_)); memset(free_bitmap_, 0xff, sizeof(free_bitmap_)); } Loading @@ -254,8 +246,7 @@ void* Chunk::Alloc() { assert(free_count_ > 0); unsigned int i = first_free_bitmap_; while (free_bitmap_[i] == 0) i++; while (free_bitmap_[i] == 0) i++; assert(i < arraysize(free_bitmap_)); unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1; assert(free_bitmap_[i] & (1U << bit)); Loading Loading @@ -310,8 +301,7 @@ void Chunk::Purge() { } // Override new operator on HeapImpl to use mmap to allocate a page void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept { void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept { assert(count == sizeof(HeapImpl)); void* mem = MapAligned(kPageSize, kPageSize); if (!mem) { Loading @@ -326,9 +316,7 @@ void HeapImpl::operator delete(void *ptr) { munmap(ptr, kPageSize); } HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) { } HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {} bool HeapImpl::Empty() { for (unsigned int i = 0; i < kNumBuckets; i++) { Loading Loading @@ -397,8 +385,7 @@ void HeapImpl::FreeLocked(void *ptr) { void* HeapImpl::MapAlloc(size_t size) { size = (size + kPageSize - 1) & ~(kPageSize - 1); MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked( sizeof(MapAllocation))); MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation))); void* ptr = MapAligned(size, kChunkSize); if (!ptr) { FreeLocked(allocation); Loading @@ -414,8 +401,7 @@ void* HeapImpl::MapAlloc(size_t size) { void HeapImpl::MapFree(void* ptr) { MapAllocation** allocation = &map_allocation_list_; while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next; while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next; assert(*allocation != nullptr); Loading @@ -439,8 +425,8 @@ void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) { LinkedList<Chunk*>* node = head; // Insert into new list, sorted by lowest free count while (node->next() != head && node->data() != nullptr && node->data()->free_count() < chunk->free_count()) while (node->next() != head && node->data() != nullptr && node->data()->free_count() < chunk->free_count()) node = node->next(); node->insert(chunk->node_); Loading libmemunreachable/Allocator.h +56 −71 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ class HeapImpl; template <typename T> class Allocator; // Non-templated class that implements wraps HeapImpl to keep // implementation out of the header file class Heap { Loading Loading @@ -65,12 +64,8 @@ public: } // Comparators, copied objects will be equal bool operator ==(const Heap& other) const { return impl_ == other.impl_; } bool operator !=(const Heap& other) const { return !(*this == other); } bool operator==(const Heap& other) const { return impl_ == other.impl_; } bool operator!=(const Heap& other) const { return !(*this == other); } // std::unique_ptr wrapper that allocates using allocate and deletes using // deallocate Loading @@ -80,8 +75,7 @@ public: template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args) { HeapImpl* impl = impl_; return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) { return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) { reinterpret_cast<T*>(ptr)->~T(); deallocate(impl, ptr); }); Loading @@ -105,30 +99,25 @@ template<typename T> class STLAllocator { public: using value_type = T; ~STLAllocator() { } ~STLAllocator() {} // Construct an STLAllocator on top of a Heap STLAllocator(const Heap& heap) : // NOLINT, implicit heap_(heap) { } STLAllocator(const Heap& heap) : // NOLINT, implicit heap_(heap) {} // Rebind an STLAllocator from an another STLAllocator template <typename U> STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit heap_(other.heap_) { } STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit heap_(other.heap_) {} STLAllocator(const STLAllocator&) = default; STLAllocator<T>& operator=(const STLAllocator<T>&) = default; T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); } T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); } void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); } void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); } template <typename U> bool operator==(const STLAllocator<U>& other) const { Loading @@ -146,7 +135,6 @@ protected: Heap heap_; }; // Allocator extends STLAllocator with some convenience methods for allocating // a single object and for constructing unique_ptr and shared_ptr objects with // appropriate deleters. Loading @@ -155,14 +143,14 @@ class Allocator : public STLAllocator<T> { public: ~Allocator() {} Allocator(const Heap& other) : // NOLINT, implicit STLAllocator<T>(other) { } Allocator(const Heap& other) : // NOLINT, implicit STLAllocator<T>(other) {} template <typename U> Allocator(const STLAllocator<U>& other) : // NOLINT, implicit STLAllocator<T>(other) { } Allocator(const STLAllocator<U>& other) : // NOLINT, implicit STLAllocator<T>(other) {} Allocator(const Allocator&) = default; Allocator<T>& operator=(const Allocator<T>&) = default; Loading @@ -171,12 +159,8 @@ class Allocator : public STLAllocator<T> { using STLAllocator<T>::deallocate; using STLAllocator<T>::heap_; T* allocate() { return STLAllocator<T>::allocate(1); } void deallocate(void* ptr) { heap_.deallocate(ptr); } T* allocate() { return STLAllocator<T>::allocate(1); } void deallocate(void* ptr) { heap_.deallocate(ptr); } using shared_ptr = Heap::shared_ptr<T>; Loading Loading @@ -214,7 +198,8 @@ template<class Key, class T, class Compare = std::less<Key>> using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>; template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>; Loading libmemunreachable/HeapWalker.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -114,8 +114,8 @@ bool HeapWalker::DetectLeaks() { return true; } bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out, size_t* leak_bytes_out) { bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out, size_t* leak_bytes_out) { leaked.clear(); size_t num_leaks = 0; Loading Loading @@ -159,7 +159,8 @@ static bool MapOverPage(void* addr) { return true; } void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) { void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) { uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr); if (addr != walking_ptr_) { handler.reset(); Loading libmemunreachable/HeapWalker.h +18 −20 Original line number Diff line number Diff line Loading @@ -34,29 +34,29 @@ struct Range { bool operator==(const Range& other) const { return this->begin == other.begin && this->end == other.end; } bool operator!=(const Range& other) const { return !(*this == other); } bool operator!=(const Range& other) const { return !(*this == other); } }; // Comparator for Ranges that returns equivalence for overlapping ranges struct compare_range { bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; } bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; } }; class HeapWalker { public: explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator), allocations_(allocator), allocation_bytes_(0), roots_(allocator), root_vals_(allocator), segv_handler_(allocator), walking_ptr_(0) { explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator), allocations_(allocator), allocation_bytes_(0), roots_(allocator), root_vals_(allocator), segv_handler_(allocator), walking_ptr_(0) { valid_allocations_range_.end = 0; valid_allocations_range_.begin = ~valid_allocations_range_.end; segv_handler_.install(SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { segv_handler_.install( SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { this->HandleSegFault(handler, signal, siginfo, uctx); }); } Loading @@ -68,8 +68,7 @@ class HeapWalker { bool DetectLeaks(); bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes); bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes); size_t Allocations(); size_t AllocationBytes(); Loading @@ -84,7 +83,6 @@ class HeapWalker { }; private: void RecurseRoot(const Range& root); bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info); void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*); Loading libmemunreachable/LeakFolding.cpp +29 −35 Original line number Diff line number Diff line Loading @@ -55,15 +55,12 @@ void LeakFolding::ComputeDAG() { } void LeakFolding::AccumulateLeaks(SCCInfo* dominator) { std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) { std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) { if (scc->accumulator != dominator) { scc->accumulator = dominator; dominator->cuumulative_size += scc->size; dominator->cuumulative_count += scc->count; scc->node.Foreach([&](SCCInfo* ref) { walk(ref); }); scc->node.Foreach([&](SCCInfo* ref) { walk(ref); }); } }); walk(dominator); Loading @@ -73,11 +70,9 @@ bool LeakFolding::FoldLeaks() { Allocator<LeakInfo> leak_allocator = allocator_; // Find all leaked allocations insert them into leak_map_ and leak_graph_ heap_walker_.ForEachAllocation( [&](const Range& range, HeapWalker::AllocationInfo& allocation) { heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) { if (!allocation.referenced_from_root) { auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range), auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range), std::forward_as_tuple(range, allocator_)); LeakInfo& leak = it.first->second; leak_graph_.push_back(&leak.node); Loading Loading @@ -110,8 +105,8 @@ bool LeakFolding::FoldLeaks() { return true; } bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out) { bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out) { size_t num_leaks = 0; size_t leak_bytes = 0; for (auto& it : leak_map_) { Loading @@ -123,8 +118,7 @@ bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, for (auto& it : leak_map_) { const LeakInfo& leak = it.second; if (leak.scc->dominator) { leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1, leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1, leak.scc->cuumulative_size - leak.range.size()}); } } Loading Loading
libmemunreachable/Allocator.cpp +60 −74 Original line number Diff line number Diff line Loading @@ -33,9 +33,9 @@ #include "android-base/macros.h" #include "anon_vma_naming.h" #include "Allocator.h" #include "LinkedList.h" #include "anon_vma_naming.h" // runtime interfaces used: // abort Loading @@ -57,10 +57,9 @@ static constexpr size_t kChunkSize = 256 * 1024; static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize; static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4; static constexpr size_t kMinBucketAllocationSize = 8; static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1; static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize; static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1; static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize; std::atomic<int> heap_count; Loading Loading @@ -107,8 +106,7 @@ static inline unsigned int log2(size_t n) { } static inline unsigned int size_to_bucket(size_t size) { if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize; if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize; return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize); } Loading Loading @@ -140,8 +138,7 @@ static void* MapAligned(size_t size, size_t align) { // Trim beginning if (aligned_ptr != ptr) { ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr); ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr); munmap(ptr, extra); map_size -= extra; ptr = aligned_ptr; Loading @@ -151,14 +148,13 @@ static void* MapAligned(size_t size, size_t align) { if (map_size != size) { assert(map_size > size); assert(ptr != NULL); munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size); munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size); } #define PR_SET_VMA 0x53564d41 #define PR_SET_VMA_ANON_NAME 0 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc"); return ptr; } Loading @@ -176,19 +172,14 @@ class Chunk { bool Empty(); static Chunk* ptr_to_chunk(void* ptr) { return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1)); return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1)); } static bool is_chunk(void* ptr) { return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0; } unsigned int free_count() { return free_count_; } HeapImpl* heap() { return heap_; } unsigned int free_count() { return free_count_; } HeapImpl* heap() { return heap_; } LinkedList<Chunk*> node_; // linked list sorted by minimum free count private: Loading @@ -210,13 +201,10 @@ class Chunk { char data_[0]; unsigned int ptr_to_n(void* ptr) { ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_); ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_); return offset / allocation_size_; } void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; } void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; } }; static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page"); Loading @@ -237,11 +225,15 @@ void Chunk::operator delete(void *ptr) { munmap(ptr, kChunkSize); } Chunk::Chunk(HeapImpl* heap, int bucket) : node_(this), heap_(heap), bucket_(bucket), allocation_size_( bucket_to_size(bucket)), max_allocations_( kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_( max_allocations_), frees_since_purge_(0) { Chunk::Chunk(HeapImpl* heap, int bucket) : node_(this), heap_(heap), bucket_(bucket), allocation_size_(bucket_to_size(bucket)), max_allocations_(kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(max_allocations_), frees_since_purge_(0) { memset(dirty_pages_, 0, sizeof(dirty_pages_)); memset(free_bitmap_, 0xff, sizeof(free_bitmap_)); } Loading @@ -254,8 +246,7 @@ void* Chunk::Alloc() { assert(free_count_ > 0); unsigned int i = first_free_bitmap_; while (free_bitmap_[i] == 0) i++; while (free_bitmap_[i] == 0) i++; assert(i < arraysize(free_bitmap_)); unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1; assert(free_bitmap_[i] & (1U << bit)); Loading Loading @@ -310,8 +301,7 @@ void Chunk::Purge() { } // Override new operator on HeapImpl to use mmap to allocate a page void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept { void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept { assert(count == sizeof(HeapImpl)); void* mem = MapAligned(kPageSize, kPageSize); if (!mem) { Loading @@ -326,9 +316,7 @@ void HeapImpl::operator delete(void *ptr) { munmap(ptr, kPageSize); } HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) { } HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {} bool HeapImpl::Empty() { for (unsigned int i = 0; i < kNumBuckets; i++) { Loading Loading @@ -397,8 +385,7 @@ void HeapImpl::FreeLocked(void *ptr) { void* HeapImpl::MapAlloc(size_t size) { size = (size + kPageSize - 1) & ~(kPageSize - 1); MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked( sizeof(MapAllocation))); MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation))); void* ptr = MapAligned(size, kChunkSize); if (!ptr) { FreeLocked(allocation); Loading @@ -414,8 +401,7 @@ void* HeapImpl::MapAlloc(size_t size) { void HeapImpl::MapFree(void* ptr) { MapAllocation** allocation = &map_allocation_list_; while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next; while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next; assert(*allocation != nullptr); Loading @@ -439,8 +425,8 @@ void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) { LinkedList<Chunk*>* node = head; // Insert into new list, sorted by lowest free count while (node->next() != head && node->data() != nullptr && node->data()->free_count() < chunk->free_count()) while (node->next() != head && node->data() != nullptr && node->data()->free_count() < chunk->free_count()) node = node->next(); node->insert(chunk->node_); Loading
libmemunreachable/Allocator.h +56 −71 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ class HeapImpl; template <typename T> class Allocator; // Non-templated class that implements wraps HeapImpl to keep // implementation out of the header file class Heap { Loading Loading @@ -65,12 +64,8 @@ public: } // Comparators, copied objects will be equal bool operator ==(const Heap& other) const { return impl_ == other.impl_; } bool operator !=(const Heap& other) const { return !(*this == other); } bool operator==(const Heap& other) const { return impl_ == other.impl_; } bool operator!=(const Heap& other) const { return !(*this == other); } // std::unique_ptr wrapper that allocates using allocate and deletes using // deallocate Loading @@ -80,8 +75,7 @@ public: template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args) { HeapImpl* impl = impl_; return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) { return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) { reinterpret_cast<T*>(ptr)->~T(); deallocate(impl, ptr); }); Loading @@ -105,30 +99,25 @@ template<typename T> class STLAllocator { public: using value_type = T; ~STLAllocator() { } ~STLAllocator() {} // Construct an STLAllocator on top of a Heap STLAllocator(const Heap& heap) : // NOLINT, implicit heap_(heap) { } STLAllocator(const Heap& heap) : // NOLINT, implicit heap_(heap) {} // Rebind an STLAllocator from an another STLAllocator template <typename U> STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit heap_(other.heap_) { } STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit heap_(other.heap_) {} STLAllocator(const STLAllocator&) = default; STLAllocator<T>& operator=(const STLAllocator<T>&) = default; T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); } T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); } void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); } void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); } template <typename U> bool operator==(const STLAllocator<U>& other) const { Loading @@ -146,7 +135,6 @@ protected: Heap heap_; }; // Allocator extends STLAllocator with some convenience methods for allocating // a single object and for constructing unique_ptr and shared_ptr objects with // appropriate deleters. Loading @@ -155,14 +143,14 @@ class Allocator : public STLAllocator<T> { public: ~Allocator() {} Allocator(const Heap& other) : // NOLINT, implicit STLAllocator<T>(other) { } Allocator(const Heap& other) : // NOLINT, implicit STLAllocator<T>(other) {} template <typename U> Allocator(const STLAllocator<U>& other) : // NOLINT, implicit STLAllocator<T>(other) { } Allocator(const STLAllocator<U>& other) : // NOLINT, implicit STLAllocator<T>(other) {} Allocator(const Allocator&) = default; Allocator<T>& operator=(const Allocator<T>&) = default; Loading @@ -171,12 +159,8 @@ class Allocator : public STLAllocator<T> { using STLAllocator<T>::deallocate; using STLAllocator<T>::heap_; T* allocate() { return STLAllocator<T>::allocate(1); } void deallocate(void* ptr) { heap_.deallocate(ptr); } T* allocate() { return STLAllocator<T>::allocate(1); } void deallocate(void* ptr) { heap_.deallocate(ptr); } using shared_ptr = Heap::shared_ptr<T>; Loading Loading @@ -214,7 +198,8 @@ template<class Key, class T, class Compare = std::less<Key>> using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>; template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>; Loading
libmemunreachable/HeapWalker.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -114,8 +114,8 @@ bool HeapWalker::DetectLeaks() { return true; } bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out, size_t* leak_bytes_out) { bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out, size_t* leak_bytes_out) { leaked.clear(); size_t num_leaks = 0; Loading Loading @@ -159,7 +159,8 @@ static bool MapOverPage(void* addr) { return true; } void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) { void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) { uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr); if (addr != walking_ptr_) { handler.reset(); Loading
libmemunreachable/HeapWalker.h +18 −20 Original line number Diff line number Diff line Loading @@ -34,29 +34,29 @@ struct Range { bool operator==(const Range& other) const { return this->begin == other.begin && this->end == other.end; } bool operator!=(const Range& other) const { return !(*this == other); } bool operator!=(const Range& other) const { return !(*this == other); } }; // Comparator for Ranges that returns equivalence for overlapping ranges struct compare_range { bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; } bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; } }; class HeapWalker { public: explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator), allocations_(allocator), allocation_bytes_(0), roots_(allocator), root_vals_(allocator), segv_handler_(allocator), walking_ptr_(0) { explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator), allocations_(allocator), allocation_bytes_(0), roots_(allocator), root_vals_(allocator), segv_handler_(allocator), walking_ptr_(0) { valid_allocations_range_.end = 0; valid_allocations_range_.begin = ~valid_allocations_range_.end; segv_handler_.install(SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { segv_handler_.install( SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { this->HandleSegFault(handler, signal, siginfo, uctx); }); } Loading @@ -68,8 +68,7 @@ class HeapWalker { bool DetectLeaks(); bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes); bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes); size_t Allocations(); size_t AllocationBytes(); Loading @@ -84,7 +83,6 @@ class HeapWalker { }; private: void RecurseRoot(const Range& root); bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info); void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*); Loading
libmemunreachable/LeakFolding.cpp +29 −35 Original line number Diff line number Diff line Loading @@ -55,15 +55,12 @@ void LeakFolding::ComputeDAG() { } void LeakFolding::AccumulateLeaks(SCCInfo* dominator) { std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) { std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) { if (scc->accumulator != dominator) { scc->accumulator = dominator; dominator->cuumulative_size += scc->size; dominator->cuumulative_count += scc->count; scc->node.Foreach([&](SCCInfo* ref) { walk(ref); }); scc->node.Foreach([&](SCCInfo* ref) { walk(ref); }); } }); walk(dominator); Loading @@ -73,11 +70,9 @@ bool LeakFolding::FoldLeaks() { Allocator<LeakInfo> leak_allocator = allocator_; // Find all leaked allocations insert them into leak_map_ and leak_graph_ heap_walker_.ForEachAllocation( [&](const Range& range, HeapWalker::AllocationInfo& allocation) { heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) { if (!allocation.referenced_from_root) { auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range), auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range), std::forward_as_tuple(range, allocator_)); LeakInfo& leak = it.first->second; leak_graph_.push_back(&leak.node); Loading Loading @@ -110,8 +105,8 @@ bool LeakFolding::FoldLeaks() { return true; } bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out) { bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out) { size_t num_leaks = 0; size_t leak_bytes = 0; for (auto& it : leak_map_) { Loading @@ -123,8 +118,7 @@ bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, for (auto& it : leak_map_) { const LeakInfo& leak = it.second; if (leak.scc->dominator) { leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1, leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1, leak.scc->cuumulative_size - leak.range.size()}); } } Loading