LCOV - code coverage report
Current view: top level - src/test - allocator_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 98.6 % 144 142
Test Date: 2026-04-27 06:44:50 Functions: 100.0 % 9 9
Branches: 51.6 % 638 329

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

Generated by: LCOV version 2.0-1