Bitcoin Core 22.99.0
P2P Digital Currency
allocator_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2012-2019 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6#include <util/system.h>
7
8#include <limits>
9#include <memory>
10#include <stdexcept>
11#include <utility>
12#include <vector>
13
14#include <boost/test/unit_test.hpp>
15
16BOOST_AUTO_TEST_SUITE(allocator_tests)
17
19{
20 // Fake memory base address for testing
21 // without actually using memory.
22 void *synth_base = reinterpret_cast<void*>(0x08000000);
23 const size_t synth_size = 1024*1024;
24 Arena b(synth_base, synth_size, 16);
25 void *chunk = b.alloc(1000);
26#ifdef ARENA_DEBUG
27 b.walk();
28#endif
29 BOOST_CHECK(chunk != nullptr);
30 BOOST_CHECK(b.stats().used == 1008); // Aligned to 16
31 BOOST_CHECK(b.stats().total == synth_size); // Nothing has disappeared?
32 b.free(chunk);
33#ifdef ARENA_DEBUG
34 b.walk();
35#endif
36 BOOST_CHECK(b.stats().used == 0);
37 BOOST_CHECK(b.stats().free == synth_size);
38 try { // Test exception on double-free
39 b.free(chunk);
40 BOOST_CHECK(0);
41 } catch(std::runtime_error &)
42 {
43 }
44
45 void *a0 = b.alloc(128);
46 void *a1 = b.alloc(256);
47 void *a2 = b.alloc(512);
48 BOOST_CHECK(b.stats().used == 896);
49 BOOST_CHECK(b.stats().total == synth_size);
50#ifdef ARENA_DEBUG
51 b.walk();
52#endif
53 b.free(a0);
54#ifdef ARENA_DEBUG
55 b.walk();
56#endif
57 BOOST_CHECK(b.stats().used == 768);
58 b.free(a1);
59 BOOST_CHECK(b.stats().used == 512);
60 void *a3 = b.alloc(128);
61#ifdef ARENA_DEBUG
62 b.walk();
63#endif
64 BOOST_CHECK(b.stats().used == 640);
65 b.free(a2);
66 BOOST_CHECK(b.stats().used == 128);
67 b.free(a3);
68 BOOST_CHECK(b.stats().used == 0);
70 BOOST_CHECK(b.stats().total == synth_size);
71 BOOST_CHECK(b.stats().free == synth_size);
73
74 std::vector<void*> addr;
75 BOOST_CHECK(b.alloc(0) == nullptr); // allocating 0 always returns nullptr
76#ifdef ARENA_DEBUG
77 b.walk();
78#endif
79 // Sweeping allocate all memory
80 for (int x=0; x<1024; ++x)
81 addr.push_back(b.alloc(1024));
82 BOOST_CHECK(b.stats().free == 0);
83 BOOST_CHECK(b.alloc(1024) == nullptr); // memory is full, this must return nullptr
84 BOOST_CHECK(b.alloc(0) == nullptr);
85 for (int x=0; x<1024; ++x)
86 b.free(addr[x]);
87 addr.clear();
88 BOOST_CHECK(b.stats().total == synth_size);
89 BOOST_CHECK(b.stats().free == synth_size);
90
91 // Now in the other direction...
92 for (int x=0; x<1024; ++x)
93 addr.push_back(b.alloc(1024));
94 for (int x=0; x<1024; ++x)
95 b.free(addr[1023-x]);
96 addr.clear();
97
98 // Now allocate in smaller unequal chunks, then deallocate haphazardly
99 // Not all the chunks will succeed allocating, but freeing nullptr is
100 // allowed so that is no problem.
101 for (int x=0; x<2048; ++x)
102 addr.push_back(b.alloc(x+1));
103 for (int x=0; x<2048; ++x)
104 b.free(addr[((x*23)%2048)^242]);
105 addr.clear();
106
107 // Go entirely wild: free and alloc interleaved,
108 // generate targets and sizes using pseudo-randomness.
109 for (int x=0; x<2048; ++x)
110 addr.push_back(nullptr);
111 uint32_t s = 0x12345678;
112 for (int x=0; x<5000; ++x) {
113 int idx = s & (addr.size()-1);
114 if (s & 0x80000000) {
115 b.free(addr[idx]);
116 addr[idx] = nullptr;
117 } else if(!addr[idx]) {
118 addr[idx] = b.alloc((s >> 16) & 2047);
119 }
120 bool lsb = s & 1;
121 s >>= 1;
122 if (lsb)
123 s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
124 }
125 for (void *ptr: addr)
126 b.free(ptr);
127 addr.clear();
128
129 BOOST_CHECK(b.stats().total == synth_size);
130 BOOST_CHECK(b.stats().free == synth_size);
131}
132
135{
136public:
137 TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {}
138 void* AllocateLocked(size_t len, bool *lockingSuccess) override
139 {
140 *lockingSuccess = false;
141 if (count > 0) {
142 --count;
143
144 if (lockedcount > 0) {
145 --lockedcount;
146 *lockingSuccess = true;
147 }
148
149 return reinterpret_cast<void*>(uint64_t{static_cast<uint64_t>(0x08000000) + (count << 24)}); // Fake address, do not actually use this memory
150 }
151 return nullptr;
152 }
153 void FreeLocked(void* addr, size_t len) override
154 {
155 }
156 size_t GetLimit() override
157 {
158 return std::numeric_limits<size_t>::max();
159 }
160private:
161 int count;
163};
164
165BOOST_AUTO_TEST_CASE(lockedpool_tests_mock)
166{
167 // Test over three virtual arenas, of which one will succeed being locked
168 std::unique_ptr<LockedPageAllocator> x = std::make_unique<TestLockedPageAllocator>(3, 1);
169 LockedPool pool(std::move(x));
170 BOOST_CHECK(pool.stats().total == 0);
171 BOOST_CHECK(pool.stats().locked == 0);
172
173 // Ensure unreasonable requests are refused without allocating anything
174 void *invalid_toosmall = pool.alloc(0);
175 BOOST_CHECK(invalid_toosmall == nullptr);
176 BOOST_CHECK(pool.stats().used == 0);
177 BOOST_CHECK(pool.stats().free == 0);
178 void *invalid_toobig = pool.alloc(LockedPool::ARENA_SIZE+1);
179 BOOST_CHECK(invalid_toobig == nullptr);
180 BOOST_CHECK(pool.stats().used == 0);
181 BOOST_CHECK(pool.stats().free == 0);
182
183 void *a0 = pool.alloc(LockedPool::ARENA_SIZE / 2);
184 BOOST_CHECK(a0);
186 void *a1 = pool.alloc(LockedPool::ARENA_SIZE / 2);
187 BOOST_CHECK(a1);
188 void *a2 = pool.alloc(LockedPool::ARENA_SIZE / 2);
189 BOOST_CHECK(a2);
190 void *a3 = pool.alloc(LockedPool::ARENA_SIZE / 2);
191 BOOST_CHECK(a3);
192 void *a4 = pool.alloc(LockedPool::ARENA_SIZE / 2);
193 BOOST_CHECK(a4);
194 void *a5 = pool.alloc(LockedPool::ARENA_SIZE / 2);
195 BOOST_CHECK(a5);
196 // We've passed a count of three arenas, so this allocation should fail
197 void *a6 = pool.alloc(16);
198 BOOST_CHECK(!a6);
199
200 pool.free(a0);
201 pool.free(a2);
202 pool.free(a4);
203 pool.free(a1);
204 pool.free(a3);
205 pool.free(a5);
208 BOOST_CHECK(pool.stats().used == 0);
209}
210
211// These tests used the live LockedPoolManager object, this is also used
212// by other tests so the conditions are somewhat less controllable and thus the
213// tests are somewhat more error-prone.
214BOOST_AUTO_TEST_CASE(lockedpool_tests_live)
215{
217 LockedPool::Stats initial = pool.stats();
218
219 void *a0 = pool.alloc(16);
220 BOOST_CHECK(a0);
221 // Test reading and writing the allocated memory
222 *((uint32_t*)a0) = 0x1234;
223 BOOST_CHECK(*((uint32_t*)a0) == 0x1234);
224
225 pool.free(a0);
226 try { // Test exception on double-free
227 pool.free(a0);
228 BOOST_CHECK(0);
229 } catch(std::runtime_error &)
230 {
231 }
232 // If more than one new arena was allocated for the above tests, something is wrong
233 BOOST_CHECK(pool.stats().total <= (initial.total + LockedPool::ARENA_SIZE));
234 // Usage must be back to where it started
235 BOOST_CHECK(pool.stats().used == initial.used);
236}
237
BOOST_AUTO_TEST_CASE(arena_tests)
void * alloc(size_t size)
Allocate size bytes from this arena.
Definition: lockedpool.cpp:57
Stats stats() const
Get arena usage statistics.
Definition: lockedpool.cpp:130
void free(void *ptr)
Free a previously allocated chunk of memory.
Definition: lockedpool.cpp:92
OS-dependent allocation and deallocation of locked/pinned memory pages.
Definition: lockedpool.h:20
Pool for locked memory chunks.
Definition: lockedpool.h:127
void free(void *ptr)
Free a previously allocated chunk of memory.
Definition: lockedpool.cpp:316
Stats stats() const
Get pool usage statistics.
Definition: lockedpool.cpp:330
void * alloc(size_t size)
Allocate size bytes from this arena.
Definition: lockedpool.cpp:294
static const size_t ARENA_SIZE
Size of one arena of locked memory.
Definition: lockedpool.h:134
Singleton class to keep track of locked (ie, non-swappable) memory, for use in std::allocator templat...
Definition: lockedpool.h:219
static LockedPoolManager & Instance()
Return the current instance, or create it once.
Definition: lockedpool.h:222
Mock LockedPageAllocator for testing.
void FreeLocked(void *addr, size_t len) override
Unlock and free memory pages.
size_t GetLimit() override
Get the total limit on the amount of memory that may be locked by this process, in bytes.
TestLockedPageAllocator(int count_in, int lockedcount_in)
void * AllocateLocked(size_t len, bool *lockingSuccess) override
Allocate and lock memory pages.
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
size_t used
Definition: lockedpool.h:60
size_t chunks_used
Definition: lockedpool.h:63
size_t total
Definition: lockedpool.h:62
size_t free
Definition: lockedpool.h:61
size_t chunks_free
Definition: lockedpool.h:64
Memory statistics.
Definition: lockedpool.h:146