• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/GrMemoryPool.h"
9 
10 #include "include/private/SkTPin.h"
11 #include "src/core/SkASAN.h"
12 
13 #ifdef SK_DEBUG
14     #include <atomic>
15 #endif
16 
17 #include <tuple>
18 
19 ///////////////////////////////////////////////////////////////////////////////////////////////////
20 
Make(size_t preallocSize,size_t minAllocSize)21 std::unique_ptr<GrMemoryPool> GrMemoryPool::Make(size_t preallocSize, size_t minAllocSize) {
22     static_assert(sizeof(GrMemoryPool) < GrMemoryPool::kMinAllocationSize);
23 
24     preallocSize = SkTPin(preallocSize, kMinAllocationSize,
25                           (size_t) SkBlockAllocator::kMaxAllocationSize);
26     minAllocSize = SkTPin(minAllocSize, kMinAllocationSize,
27                           (size_t) SkBlockAllocator::kMaxAllocationSize);
28     void* mem = operator new(preallocSize);
29     return std::unique_ptr<GrMemoryPool>(new (mem) GrMemoryPool(preallocSize, minAllocSize));
30 }
31 
GrMemoryPool(size_t preallocSize,size_t minAllocSize)32 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize)
33         : fAllocator(SkBlockAllocator::GrowthPolicy::kFixed, minAllocSize,
34                      preallocSize - offsetof(GrMemoryPool, fAllocator) - sizeof(SkBlockAllocator)) {
35     SkDEBUGCODE(
36         fDebug = new Debug;
37         fDebug->fAllocationCount = 0;
38     )
39 }
40 
~GrMemoryPool()41 GrMemoryPool::~GrMemoryPool() {
42     this->reportLeaks();
43     SkASSERT(0 == fDebug->fAllocationCount);
44     SkASSERT(this->isEmpty());
45     SkDEBUGCODE(delete fDebug;)
46 }
47 
reportLeaks() const48 void GrMemoryPool::reportLeaks() const {
49 #ifdef SK_DEBUG
50     int i = 0;
51     int n = fDebug->fAllocatedIDs.count();
52     for (int id : fDebug->fAllocatedIDs) {
53         if (++i == 1) {
54             SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
55         } else if (i < 11) {
56             SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
57         } else if (i == 11) {
58             SkDebugf(", ...\n");
59             break;
60         }
61     }
62 #endif
63 }
64 
allocate(size_t size)65 void* GrMemoryPool::allocate(size_t size) {
66     static_assert(alignof(Header) <= kAlignment);
67     SkDEBUGCODE(this->validate();)
68 
69     SkBlockAllocator::ByteRange alloc = fAllocator.allocate<kAlignment, sizeof(Header)>(size);
70 
71     // Initialize GrMemoryPool's custom header at the start of the allocation
72     Header* header = static_cast<Header*>(alloc.fBlock->ptr(alloc.fAlignedOffset - sizeof(Header)));
73     header->fStart = alloc.fStart;
74     header->fEnd = alloc.fEnd;
75 
76     // Update live count within the block
77     alloc.fBlock->setMetadata(alloc.fBlock->metadata() + 1);
78 
79 #if defined(SK_SANITIZE_ADDRESS)
80     sk_asan_poison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
81 #elif defined(SK_DEBUG)
82     header->fSentinel = SkBlockAllocator::kAssignedMarker;
83 #endif
84 
85 #if defined(SK_DEBUG)
86     header->fID = []{
87         static std::atomic<int> nextID{1};
88         return nextID.fetch_add(1, std::memory_order_relaxed);
89     }();
90 
91     // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
92     fDebug->fAllocatedIDs.add(header->fID);
93     fDebug->fAllocationCount++;
94 #endif
95 
96     // User-facing pointer is after the header padding
97     return alloc.fBlock->ptr(alloc.fAlignedOffset);
98 }
99 
release(void * p)100 void GrMemoryPool::release(void* p) {
101     Header* header = reinterpret_cast<Header*>(reinterpret_cast<intptr_t>(p) - sizeof(Header));
102 
103 #if defined(SK_SANITIZE_ADDRESS)
104     sk_asan_unpoison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
105 #elif defined(SK_DEBUG)
106     SkASSERT(SkBlockAllocator::kAssignedMarker == header->fSentinel);
107     header->fSentinel = SkBlockAllocator::kFreedMarker;
108 #endif
109 
110 #if defined(SK_DEBUG)
111     fDebug->fAllocatedIDs.remove(header->fID);
112     fDebug->fAllocationCount--;
113 #endif
114 
115     SkBlockAllocator::Block* block = fAllocator.owningBlock<kAlignment>(header, header->fStart);
116 
117 #if defined(SK_DEBUG)
118     // (p - block) matches the original alignedOffset value from SkBlockAllocator::allocate().
119     intptr_t alignedOffset = (intptr_t)p - (intptr_t)block;
120     SkASSERT(p == block->ptr(alignedOffset));
121 
122     // Scrub the block contents to prevent use-after-free errors.
123     memset(p, 0xDD, header->fEnd - alignedOffset);
124 #endif
125 
126     int alive = block->metadata();
127     if (alive == 1) {
128         // This was last allocation in the block, so remove it
129         fAllocator.releaseBlock(block);
130     } else {
131         // Update count and release storage of the allocation itself
132         block->setMetadata(alive - 1);
133         block->release(header->fStart, header->fEnd);
134     }
135 }
136 
137 #ifdef SK_DEBUG
validate() const138 void GrMemoryPool::validate() const {
139     fAllocator.validate();
140 
141     int allocCount = 0;
142     for (const auto* b : fAllocator.blocks()) {
143         allocCount += b->metadata();
144     }
145     SkASSERT(allocCount == fDebug->fAllocationCount);
146     SkASSERT(fDebug->fAllocationCount == fDebug->fAllocatedIDs.count());
147     SkASSERT(allocCount > 0 || this->isEmpty());
148 }
149 #endif
150