/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/ganesh/GrMemoryPool.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkTPin.h" #include #include #ifdef SK_DEBUG #include #endif /////////////////////////////////////////////////////////////////////////////////////////////////// std::unique_ptr GrMemoryPool::Make(size_t preallocSize, size_t minAllocSize) { static_assert(sizeof(GrMemoryPool) < GrMemoryPool::kMinAllocationSize); preallocSize = SkTPin(preallocSize, kMinAllocationSize, (size_t) SkBlockAllocator::kMaxAllocationSize); minAllocSize = SkTPin(minAllocSize, kMinAllocationSize, (size_t) SkBlockAllocator::kMaxAllocationSize); void* mem = operator new(preallocSize); return std::unique_ptr(new (mem) GrMemoryPool(preallocSize, minAllocSize)); } GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) : fAllocator(SkBlockAllocator::GrowthPolicy::kFixed, minAllocSize, preallocSize - offsetof(GrMemoryPool, fAllocator) - sizeof(SkBlockAllocator)) { SkDEBUGCODE( fDebug = new Debug; fDebug->fAllocationCount = 0; ) } GrMemoryPool::~GrMemoryPool() { this->reportLeaks(); SkASSERT(0 == fDebug->fAllocationCount); SkASSERT(this->isEmpty()); SkDEBUGCODE(delete fDebug;) } void GrMemoryPool::reportLeaks() const { #ifdef SK_DEBUG int i = 0; int n = fDebug->fAllocatedIDs.count(); for (int id : fDebug->fAllocatedIDs) { if (++i == 1) { SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : ""); } else if (i < 11) { SkDebugf(", %d%s", id, (n == i ? "\n" : "")); } else if (i == 11) { SkDebugf(", ...\n"); break; } } #endif } void* GrMemoryPool::allocate(size_t size) { static_assert(alignof(Header) <= kAlignment); SkDEBUGCODE(this->validate();) SkBlockAllocator::ByteRange alloc = fAllocator.allocate(size); // Initialize GrMemoryPool's custom header at the start of the allocation Header* header = static_cast(alloc.fBlock->ptr(alloc.fAlignedOffset - sizeof(Header))); header->fStart = alloc.fStart; header->fEnd = alloc.fEnd; // Update live count within the block alloc.fBlock->setMetadata(alloc.fBlock->metadata() + 1); #if defined(SK_SANITIZE_ADDRESS) sk_asan_poison_memory_region(&header->fSentinel, sizeof(header->fSentinel)); #elif defined(SK_DEBUG) header->fSentinel = SkBlockAllocator::kAssignedMarker; #endif #if defined(SK_DEBUG) header->fID = []{ static std::atomic nextID{1}; return nextID.fetch_add(1, std::memory_order_relaxed); }(); // You can set a breakpoint here when a leaked ID is allocated to see the stack frame. fDebug->fAllocatedIDs.add(header->fID); fDebug->fAllocationCount++; #endif // User-facing pointer is after the header padding return alloc.fBlock->ptr(alloc.fAlignedOffset); } void GrMemoryPool::release(void* p) { Header* header = reinterpret_cast(reinterpret_cast(p) - sizeof(Header)); #if defined(SK_SANITIZE_ADDRESS) sk_asan_unpoison_memory_region(&header->fSentinel, sizeof(header->fSentinel)); #elif defined(SK_DEBUG) SkASSERT(SkBlockAllocator::kAssignedMarker == header->fSentinel); header->fSentinel = SkBlockAllocator::kFreedMarker; #endif #if defined(SK_DEBUG) fDebug->fAllocatedIDs.remove(header->fID); fDebug->fAllocationCount--; #endif SkBlockAllocator::Block* block = fAllocator.owningBlock(header, header->fStart); #if defined(SK_DEBUG) // (p - block) matches the original alignedOffset value from SkBlockAllocator::allocate(). intptr_t alignedOffset = (intptr_t)p - (intptr_t)block; SkASSERT(p == block->ptr(alignedOffset)); // Scrub the block contents to prevent use-after-free errors. memset(p, 0xDD, header->fEnd - alignedOffset); #endif int alive = block->metadata(); if (alive == 1) { // This was last allocation in the block, so remove it fAllocator.releaseBlock(block); } else { // Update count and release storage of the allocation itself block->setMetadata(alive - 1); block->release(header->fStart, header->fEnd); } } #ifdef SK_DEBUG void GrMemoryPool::validate() const { fAllocator.validate(); int allocCount = 0; for (const auto* b : fAllocator.blocks()) { allocCount += b->metadata(); } SkASSERT(allocCount == fDebug->fAllocationCount); SkASSERT(fDebug->fAllocationCount == fDebug->fAllocatedIDs.count()); SkASSERT(allocCount > 0 || this->isEmpty()); } #endif