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(fAllocationCount = 0;)
36 }
37
~GrMemoryPool()38 GrMemoryPool::~GrMemoryPool() {
39 this->reportLeaks();
40 SkASSERT(0 == fAllocationCount);
41 SkASSERT(this->isEmpty());
42 }
43
reportLeaks() const44 void GrMemoryPool::reportLeaks() const {
45 #ifdef SK_DEBUG
46 int i = 0;
47 int n = fAllocatedIDs.count();
48 for (int id : fAllocatedIDs) {
49 if (++i == 1) {
50 SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
51 } else if (i < 11) {
52 SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
53 } else if (i == 11) {
54 SkDebugf(", ...\n");
55 break;
56 }
57 }
58 #endif
59 }
60
allocate(size_t size)61 void* GrMemoryPool::allocate(size_t size) {
62 static_assert(alignof(Header) <= kAlignment);
63 SkDEBUGCODE(this->validate();)
64
65 SkBlockAllocator::ByteRange alloc = fAllocator.allocate<kAlignment, sizeof(Header)>(size);
66
67 // Initialize GrMemoryPool's custom header at the start of the allocation
68 Header* header = static_cast<Header*>(alloc.fBlock->ptr(alloc.fAlignedOffset - sizeof(Header)));
69 header->fStart = alloc.fStart;
70 header->fEnd = alloc.fEnd;
71
72 // Update live count within the block
73 alloc.fBlock->setMetadata(alloc.fBlock->metadata() + 1);
74
75 #if defined(SK_SANITIZE_ADDRESS)
76 sk_asan_poison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
77 #elif defined(SK_DEBUG)
78 header->fSentinel = SkBlockAllocator::kAssignedMarker;
79 #endif
80
81 #if defined(SK_DEBUG)
82 header->fID = []{
83 static std::atomic<int> nextID{1};
84 return nextID.fetch_add(1, std::memory_order_relaxed);
85 }();
86
87 // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
88 fAllocatedIDs.add(header->fID);
89 fAllocationCount++;
90 #endif
91
92 // User-facing pointer is after the header padding
93 return alloc.fBlock->ptr(alloc.fAlignedOffset);
94 }
95
release(void * p)96 void GrMemoryPool::release(void* p) {
97 Header* header = reinterpret_cast<Header*>(reinterpret_cast<intptr_t>(p) - sizeof(Header));
98
99 #if defined(SK_SANITIZE_ADDRESS)
100 sk_asan_unpoison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
101 #elif defined(SK_DEBUG)
102 SkASSERT(SkBlockAllocator::kAssignedMarker == header->fSentinel);
103 header->fSentinel = SkBlockAllocator::kFreedMarker;
104 #endif
105
106 #if defined(SK_DEBUG)
107 fAllocatedIDs.remove(header->fID);
108 fAllocationCount--;
109 #endif
110
111 SkBlockAllocator::Block* block = fAllocator.owningBlock<kAlignment>(header, header->fStart);
112
113 #if defined(SK_DEBUG)
114 // (p - block) matches the original alignedOffset value from SkBlockAllocator::allocate().
115 intptr_t alignedOffset = (intptr_t)p - (intptr_t)block;
116 SkASSERT(p == block->ptr(alignedOffset));
117
118 // Scrub the block contents to prevent use-after-free errors.
119 memset(p, 0xDD, header->fEnd - alignedOffset);
120 #endif
121
122 int alive = block->metadata();
123 if (alive == 1) {
124 // This was last allocation in the block, so remove it
125 fAllocator.releaseBlock(block);
126 } else {
127 // Update count and release storage of the allocation itself
128 block->setMetadata(alive - 1);
129 block->release(header->fStart, header->fEnd);
130 }
131 }
132
133 #ifdef SK_DEBUG
validate() const134 void GrMemoryPool::validate() const {
135 fAllocator.validate();
136
137 int allocCount = 0;
138 for (const auto* b : fAllocator.blocks()) {
139 allocCount += b->metadata();
140 }
141 SkASSERT(allocCount == fAllocationCount);
142 SkASSERT(fAllocationCount == fAllocatedIDs.count());
143 SkASSERT(allocCount > 0 || this->isEmpty());
144 }
145 #endif
146