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 
5 #include <support/lockedpool.h>
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 
16 BOOST_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 {
136 public:
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  }
160 private:
161  int count;
163 };
164 
165 BOOST_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.
214 BOOST_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 
LockedPoolManager
Singleton class to keep track of locked (ie, non-swappable) memory, for use in std::allocator templat...
Definition: lockedpool.h:218
LockedPool::Stats::free
size_t free
Definition: lockedpool.h:148
BOOST_AUTO_TEST_SUITE
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
TestLockedPageAllocator::GetLimit
size_t GetLimit() override
Get the total limit on the amount of memory that may be locked by this process, in bytes.
Definition: allocator_tests.cpp:156
Arena::Stats::chunks_free
size_t chunks_free
Definition: lockedpool.h:64
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(arena_tests)
Definition: allocator_tests.cpp:18
Arena::Stats::chunks_used
size_t chunks_used
Definition: lockedpool.h:63
TestLockedPageAllocator::count
int count
Definition: allocator_tests.cpp:161
LockedPoolManager::Instance
static LockedPoolManager & Instance()
Return the current instance, or create it once.
Definition: lockedpool.h:222
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
Arena::Stats::total
size_t total
Definition: lockedpool.h:62
LockedPool::Stats::total
size_t total
Definition: lockedpool.h:149
LockedPool::Stats::locked
size_t locked
Definition: lockedpool.h:150
TestLockedPageAllocator::lockedcount
int lockedcount
Definition: allocator_tests.cpp:162
LockedPool::alloc
void * alloc(size_t size)
Allocate size bytes from this arena.
Definition: lockedpool.cpp:294
Arena::stats
Stats stats() const
Get arena usage statistics.
Definition: lockedpool.cpp:130
Arena::Stats::free
size_t free
Definition: lockedpool.h:61
LockedPool::Stats::used
size_t used
Definition: lockedpool.h:147
TestLockedPageAllocator::TestLockedPageAllocator
TestLockedPageAllocator(int count_in, int lockedcount_in)
Definition: allocator_tests.cpp:137
LockedPool::Stats
Memory statistics.
Definition: lockedpool.h:145
system.h
Arena
Definition: lockedpool.h:48
Arena::free
void free(void *ptr)
Free a previously allocated chunk of memory.
Definition: lockedpool.cpp:92
LockedPool::free
void free(void *ptr)
Free a previously allocated chunk of memory.
Definition: lockedpool.cpp:316
TestLockedPageAllocator::FreeLocked
void FreeLocked(void *addr, size_t len) override
Unlock and free memory pages.
Definition: allocator_tests.cpp:153
lockedpool.h
LockedPool::ARENA_SIZE
static const size_t ARENA_SIZE
Size of one arena of locked memory.
Definition: lockedpool.h:134
LockedPool
Pool for locked memory chunks.
Definition: lockedpool.h:126
TestLockedPageAllocator
Mock LockedPageAllocator for testing.
Definition: allocator_tests.cpp:134
Arena::alloc
void * alloc(size_t size)
Allocate size bytes from this arena.
Definition: lockedpool.cpp:57
TestLockedPageAllocator::AllocateLocked
void * AllocateLocked(size_t len, bool *lockingSuccess) override
Allocate and lock memory pages.
Definition: allocator_tests.cpp:138
LockedPool::stats
Stats stats() const
Get pool usage statistics.
Definition: lockedpool.cpp:330
Arena::Stats::used
size_t used
Definition: lockedpool.h:60
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
LockedPageAllocator
OS-dependent allocation and deallocation of locked/pinned memory pages.
Definition: lockedpool.h:19