2018-07-27 00:36:45 +02:00
|
|
|
// Copyright (c) 2012-2018 The Bitcoin Core developers
|
2014-12-13 05:09:33 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2014-03-18 10:11:00 +01:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2017-11-10 01:57:53 +01:00
|
|
|
#include <util.h>
|
2012-08-22 11:34:32 +02:00
|
|
|
|
2017-11-10 01:57:53 +01:00
|
|
|
#include <support/allocators/secure.h>
|
|
|
|
#include <test/test_bitcoin.h>
|
Split up util.cpp/h
Split up util.cpp/h into:
- string utilities (hex, base32, base64): no internal dependencies, no dependency on boost (apart from foreach)
- money utilities (parsesmoney, formatmoney)
- time utilities (gettime*, sleep, format date):
- and the rest (logging, argument parsing, config file parsing)
The latter is basically the environment and OS handling,
and is stripped of all utility functions, so we may want to
rename it to something else than util.cpp/h for clarity (Matt suggested
osinterface).
Breaks dependency of sha256.cpp on all the things pulled in by util.
2014-08-21 16:11:09 +02:00
|
|
|
|
2018-04-02 20:31:40 +02:00
|
|
|
#include <memory>
|
|
|
|
|
2013-04-13 07:13:08 +02:00
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
|
2015-03-12 09:34:42 +01:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(allocator_tests, BasicTestingSetup)
|
2012-08-22 11:34:32 +02:00
|
|
|
|
2016-09-18 09:55:14 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(arena_tests)
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
2016-09-18 09:55:14 +02:00
|
|
|
// Fake memory base address for testing
|
|
|
|
// without actually using memory.
|
|
|
|
void *synth_base = reinterpret_cast<void*>(0x08000000);
|
|
|
|
const size_t synth_size = 1024*1024;
|
|
|
|
Arena b(synth_base, synth_size, 16);
|
|
|
|
void *chunk = b.alloc(1000);
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
BOOST_CHECK(chunk != nullptr);
|
|
|
|
BOOST_CHECK(b.stats().used == 1008); // Aligned to 16
|
|
|
|
BOOST_CHECK(b.stats().total == synth_size); // Nothing has disappeared?
|
|
|
|
b.free(chunk);
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
BOOST_CHECK(b.stats().used == 0);
|
|
|
|
BOOST_CHECK(b.stats().free == synth_size);
|
|
|
|
try { // Test exception on double-free
|
|
|
|
b.free(chunk);
|
|
|
|
BOOST_CHECK(0);
|
|
|
|
} catch(std::runtime_error &)
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
|
|
|
}
|
2016-09-18 09:55:14 +02:00
|
|
|
|
|
|
|
void *a0 = b.alloc(128);
|
|
|
|
void *a1 = b.alloc(256);
|
|
|
|
void *a2 = b.alloc(512);
|
|
|
|
BOOST_CHECK(b.stats().used == 896);
|
|
|
|
BOOST_CHECK(b.stats().total == synth_size);
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
b.free(a0);
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
BOOST_CHECK(b.stats().used == 768);
|
|
|
|
b.free(a1);
|
|
|
|
BOOST_CHECK(b.stats().used == 512);
|
|
|
|
void *a3 = b.alloc(128);
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
BOOST_CHECK(b.stats().used == 640);
|
|
|
|
b.free(a2);
|
|
|
|
BOOST_CHECK(b.stats().used == 128);
|
|
|
|
b.free(a3);
|
|
|
|
BOOST_CHECK(b.stats().used == 0);
|
2018-04-09 09:50:19 +02:00
|
|
|
BOOST_CHECK_EQUAL(b.stats().chunks_used, 0U);
|
2016-09-18 09:55:14 +02:00
|
|
|
BOOST_CHECK(b.stats().total == synth_size);
|
|
|
|
BOOST_CHECK(b.stats().free == synth_size);
|
2018-04-09 09:50:19 +02:00
|
|
|
BOOST_CHECK_EQUAL(b.stats().chunks_free, 1U);
|
2016-09-18 09:55:14 +02:00
|
|
|
|
|
|
|
std::vector<void*> addr;
|
|
|
|
BOOST_CHECK(b.alloc(0) == nullptr); // allocating 0 always returns nullptr
|
|
|
|
#ifdef ARENA_DEBUG
|
|
|
|
b.walk();
|
|
|
|
#endif
|
|
|
|
// Sweeping allocate all memory
|
|
|
|
for (int x=0; x<1024; ++x)
|
|
|
|
addr.push_back(b.alloc(1024));
|
|
|
|
BOOST_CHECK(b.stats().free == 0);
|
|
|
|
BOOST_CHECK(b.alloc(1024) == nullptr); // memory is full, this must return nullptr
|
|
|
|
BOOST_CHECK(b.alloc(0) == nullptr);
|
|
|
|
for (int x=0; x<1024; ++x)
|
|
|
|
b.free(addr[x]);
|
|
|
|
addr.clear();
|
|
|
|
BOOST_CHECK(b.stats().total == synth_size);
|
|
|
|
BOOST_CHECK(b.stats().free == synth_size);
|
|
|
|
|
|
|
|
// Now in the other direction...
|
|
|
|
for (int x=0; x<1024; ++x)
|
|
|
|
addr.push_back(b.alloc(1024));
|
|
|
|
for (int x=0; x<1024; ++x)
|
|
|
|
b.free(addr[1023-x]);
|
|
|
|
addr.clear();
|
|
|
|
|
|
|
|
// Now allocate in smaller unequal chunks, then deallocate haphazardly
|
|
|
|
// Not all the chunks will succeed allocating, but freeing nullptr is
|
|
|
|
// allowed so that is no problem.
|
|
|
|
for (int x=0; x<2048; ++x)
|
|
|
|
addr.push_back(b.alloc(x+1));
|
|
|
|
for (int x=0; x<2048; ++x)
|
|
|
|
b.free(addr[((x*23)%2048)^242]);
|
|
|
|
addr.clear();
|
|
|
|
|
|
|
|
// Go entirely wild: free and alloc interleaved,
|
|
|
|
// generate targets and sizes using pseudo-randomness.
|
|
|
|
for (int x=0; x<2048; ++x)
|
|
|
|
addr.push_back(0);
|
|
|
|
uint32_t s = 0x12345678;
|
|
|
|
for (int x=0; x<5000; ++x) {
|
|
|
|
int idx = s & (addr.size()-1);
|
|
|
|
if (s & 0x80000000) {
|
|
|
|
b.free(addr[idx]);
|
|
|
|
addr[idx] = 0;
|
|
|
|
} else if(!addr[idx]) {
|
|
|
|
addr[idx] = b.alloc((s >> 16) & 2047);
|
|
|
|
}
|
|
|
|
bool lsb = s & 1;
|
|
|
|
s >>= 1;
|
|
|
|
if (lsb)
|
|
|
|
s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
|
2012-08-22 11:34:32 +02:00
|
|
|
}
|
2016-09-18 09:55:14 +02:00
|
|
|
for (void *ptr: addr)
|
|
|
|
b.free(ptr);
|
|
|
|
addr.clear();
|
2012-08-22 11:34:32 +02:00
|
|
|
|
2016-09-18 09:55:14 +02:00
|
|
|
BOOST_CHECK(b.stats().total == synth_size);
|
|
|
|
BOOST_CHECK(b.stats().free == synth_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Mock LockedPageAllocator for testing */
|
|
|
|
class TestLockedPageAllocator: public LockedPageAllocator
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
2016-09-18 09:55:14 +02:00
|
|
|
public:
|
|
|
|
TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {}
|
2017-06-20 21:58:56 +02:00
|
|
|
void* AllocateLocked(size_t len, bool *lockingSuccess) override
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
2016-09-18 09:55:14 +02:00
|
|
|
*lockingSuccess = false;
|
|
|
|
if (count > 0) {
|
|
|
|
--count;
|
|
|
|
|
|
|
|
if (lockedcount > 0) {
|
|
|
|
--lockedcount;
|
|
|
|
*lockingSuccess = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return reinterpret_cast<void*>(0x08000000 + (count<<24)); // Fake address, do not actually use this memory
|
|
|
|
}
|
|
|
|
return 0;
|
2012-08-22 11:34:32 +02:00
|
|
|
}
|
2017-06-20 21:58:56 +02:00
|
|
|
void FreeLocked(void* addr, size_t len) override
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
|
|
|
}
|
2017-06-20 21:58:56 +02:00
|
|
|
size_t GetLimit() override
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
2016-09-18 09:55:14 +02:00
|
|
|
return std::numeric_limits<size_t>::max();
|
2012-08-22 11:34:32 +02:00
|
|
|
}
|
2016-09-18 09:55:14 +02:00
|
|
|
private:
|
|
|
|
int count;
|
|
|
|
int lockedcount;
|
|
|
|
};
|
2012-08-22 11:34:32 +02:00
|
|
|
|
2016-09-18 09:55:14 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(lockedpool_tests_mock)
|
|
|
|
{
|
|
|
|
// Test over three virtual arenas, of which one will succeed being locked
|
|
|
|
std::unique_ptr<LockedPageAllocator> x(new TestLockedPageAllocator(3, 1));
|
|
|
|
LockedPool pool(std::move(x));
|
|
|
|
BOOST_CHECK(pool.stats().total == 0);
|
|
|
|
BOOST_CHECK(pool.stats().locked == 0);
|
2012-08-22 11:34:32 +02:00
|
|
|
|
2016-11-02 22:03:50 +01:00
|
|
|
// Ensure unreasonable requests are refused without allocating anything
|
|
|
|
void *invalid_toosmall = pool.alloc(0);
|
|
|
|
BOOST_CHECK(invalid_toosmall == nullptr);
|
|
|
|
BOOST_CHECK(pool.stats().used == 0);
|
|
|
|
BOOST_CHECK(pool.stats().free == 0);
|
|
|
|
void *invalid_toobig = pool.alloc(LockedPool::ARENA_SIZE+1);
|
|
|
|
BOOST_CHECK(invalid_toobig == nullptr);
|
|
|
|
BOOST_CHECK(pool.stats().used == 0);
|
|
|
|
BOOST_CHECK(pool.stats().free == 0);
|
|
|
|
|
2016-09-18 09:55:14 +02:00
|
|
|
void *a0 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a0);
|
|
|
|
BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE);
|
|
|
|
void *a1 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a1);
|
|
|
|
void *a2 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a2);
|
|
|
|
void *a3 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a3);
|
|
|
|
void *a4 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a4);
|
|
|
|
void *a5 = pool.alloc(LockedPool::ARENA_SIZE / 2);
|
|
|
|
BOOST_CHECK(a5);
|
|
|
|
// We've passed a count of three arenas, so this allocation should fail
|
|
|
|
void *a6 = pool.alloc(16);
|
|
|
|
BOOST_CHECK(!a6);
|
|
|
|
|
|
|
|
pool.free(a0);
|
|
|
|
pool.free(a2);
|
|
|
|
pool.free(a4);
|
|
|
|
pool.free(a1);
|
|
|
|
pool.free(a3);
|
|
|
|
pool.free(a5);
|
|
|
|
BOOST_CHECK(pool.stats().total == 3*LockedPool::ARENA_SIZE);
|
|
|
|
BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE);
|
|
|
|
BOOST_CHECK(pool.stats().used == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// These tests used the live LockedPoolManager object, this is also used
|
|
|
|
// by other tests so the conditions are somewhat less controllable and thus the
|
|
|
|
// tests are somewhat more error-prone.
|
|
|
|
BOOST_AUTO_TEST_CASE(lockedpool_tests_live)
|
|
|
|
{
|
|
|
|
LockedPoolManager &pool = LockedPoolManager::Instance();
|
|
|
|
LockedPool::Stats initial = pool.stats();
|
|
|
|
|
|
|
|
void *a0 = pool.alloc(16);
|
|
|
|
BOOST_CHECK(a0);
|
|
|
|
// Test reading and writing the allocated memory
|
|
|
|
*((uint32_t*)a0) = 0x1234;
|
|
|
|
BOOST_CHECK(*((uint32_t*)a0) == 0x1234);
|
|
|
|
|
|
|
|
pool.free(a0);
|
|
|
|
try { // Test exception on double-free
|
|
|
|
pool.free(a0);
|
|
|
|
BOOST_CHECK(0);
|
|
|
|
} catch(std::runtime_error &)
|
2012-08-22 11:34:32 +02:00
|
|
|
{
|
|
|
|
}
|
2016-09-18 09:55:14 +02:00
|
|
|
// If more than one new arena was allocated for the above tests, something is wrong
|
|
|
|
BOOST_CHECK(pool.stats().total <= (initial.total + LockedPool::ARENA_SIZE));
|
|
|
|
// Usage must be back to where it started
|
|
|
|
BOOST_CHECK(pool.stats().used == initial.used);
|
2012-08-22 11:34:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|