LCOV - code coverage report
Current view: top level - src/test/util - poolresourcetester.h (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 98.1 % 54 53
Test Date: 2025-08-13 04:26:42 Functions: 100.0 % 5 5
Branches: 68.9 % 90 62

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2022 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                 :             : #ifndef BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
       6                 :             : #define BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
       7                 :             : 
       8                 :             : #include <support/allocators/pool.h>
       9                 :             : #include <util/check.h>
      10                 :             : 
      11                 :             : #include <algorithm>
      12                 :             : #include <cassert>
      13                 :             : #include <cstddef>
      14                 :             : #include <cstdint>
      15                 :             : #include <vector>
      16                 :             : 
      17                 :             : /**
      18                 :             :  * Helper to get access to private parts of PoolResource. Used in unit tests and in the fuzzer
      19                 :             :  */
      20                 :             : class PoolResourceTester
      21                 :             : {
      22                 :             :     struct PtrAndBytes {
      23                 :             :         uintptr_t ptr;
      24                 :             :         std::size_t size;
      25                 :             : 
      26                 :       11807 :         PtrAndBytes(const void* p, std::size_t s)
      27                 :       11807 :             : ptr(reinterpret_cast<uintptr_t>(p)), size(s)
      28                 :             :         {
      29                 :             :         }
      30                 :             : 
      31                 :             :         /**
      32                 :             :          * defines a sort ordering by the pointer value
      33                 :             :          */
      34                 :      431984 :         friend bool operator<(PtrAndBytes const& a, PtrAndBytes const& b)
      35                 :             :         {
      36   [ +  +  +  +  :      431691 :             return a.ptr < b.ptr;
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             +  -  -  +  
                      + ]
      37                 :             :         }
      38                 :             :     };
      39                 :             : 
      40                 :             : public:
      41                 :             :     /**
      42                 :             :      * Extracts the number of elements per freelist
      43                 :             :      */
      44                 :             :     template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
      45                 :          10 :     static std::vector<std::size_t> FreeListSizes(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
      46                 :             :     {
      47                 :          10 :         auto sizes = std::vector<std::size_t>();
      48         [ +  + ]:          30 :         for (const auto* ptr : resource.m_free_lists) {
      49                 :          20 :             size_t size = 0;
      50         [ +  + ]:          27 :             while (ptr != nullptr) {
      51                 :           7 :                 ++size;
      52                 :           7 :                 const auto* ptr_ = ptr;
      53                 :             :                 ASAN_UNPOISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
      54                 :           7 :                 ptr = ptr->m_next;
      55                 :             :                 ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
      56                 :             :             }
      57         [ +  - ]:          20 :             sizes.push_back(size);
      58                 :             :         }
      59                 :          10 :         return sizes;
      60                 :           0 :     }
      61                 :             : 
      62                 :             :     /**
      63                 :             :      * How many bytes are still available from the last allocated chunk
      64                 :             :      */
      65                 :             :     template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
      66                 :          10 :     static std::size_t AvailableMemoryFromChunk(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
      67                 :             :     {
      68   [ +  -  +  -  :          10 :         return resource.m_available_memory_end - resource.m_available_memory_it;
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
      69                 :             :     }
      70                 :             : 
      71                 :             :     /**
      72                 :             :      * Once all blocks are given back to the resource, tests that the freelists are consistent:
      73                 :             :      *
      74                 :             :      * * All data in the freelists must come from the chunks
      75                 :             :      * * Memory doesn't overlap
      76                 :             :      * * Each byte in the chunks can be accounted for in either the freelist or as available bytes.
      77                 :             :      */
      78                 :             :     template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
      79                 :          14 :     static void CheckAllDataAccountedFor(const PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>& resource)
      80                 :             :     {
      81                 :             :         // collect all free blocks by iterating all freelists
      82                 :          14 :         std::vector<PtrAndBytes> free_blocks;
      83         [ +  + ]:         118 :         for (std::size_t freelist_idx = 0; freelist_idx < resource.m_free_lists.size(); ++freelist_idx) {
      84                 :         104 :             std::size_t bytes = freelist_idx * resource.ELEM_ALIGN_BYTES;
      85                 :         104 :             auto* ptr = resource.m_free_lists[freelist_idx];
      86         [ +  + ]:       11637 :             while (ptr != nullptr) {
      87         [ +  - ]:       11533 :                 free_blocks.emplace_back(ptr, bytes);
      88                 :       11533 :                 const auto* ptr_ = ptr;
      89                 :             :                 ASAN_UNPOISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
      90                 :       11533 :                 ptr = ptr->m_next;
      91                 :             :                 ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
      92                 :             :             }
      93                 :             :         }
      94                 :             :         // also add whatever has not yet been used for blocks
      95                 :          14 :         auto num_available_bytes = resource.m_available_memory_end - resource.m_available_memory_it;
      96         [ +  - ]:          14 :         if (num_available_bytes > 0) {
      97         [ +  - ]:          14 :             free_blocks.emplace_back(resource.m_available_memory_it, num_available_bytes);
      98                 :             :         }
      99                 :             : 
     100                 :             :         // collect all chunks
     101                 :          14 :         std::vector<PtrAndBytes> chunks;
     102   [ +  -  +  + ]:         274 :         for (const std::byte* ptr : resource.m_allocated_chunks) {
     103         [ +  - ]:         260 :             chunks.emplace_back(ptr, resource.ChunkSizeBytes());
     104                 :             :         }
     105                 :             : 
     106                 :             :         // now we have all the data from all freelists on the one hand side, and all chunks on the other hand side.
     107                 :             :         // To check if all of them match, sort by address and iterate.
     108                 :          14 :         std::sort(free_blocks.begin(), free_blocks.end());
     109                 :          14 :         std::sort(chunks.begin(), chunks.end());
     110                 :             : 
     111                 :          14 :         auto chunk_it = chunks.begin();
     112                 :          14 :         auto chunk_ptr_remaining = chunk_it->ptr;
     113                 :          14 :         auto chunk_size_remaining = chunk_it->size;
     114         [ +  + ]:       11561 :         for (const auto& free_block : free_blocks) {
     115         [ +  + ]:       11547 :             if (chunk_size_remaining == 0) {
     116         [ -  + ]:         246 :                 assert(chunk_it != chunks.end());
     117         [ -  + ]:         246 :                 ++chunk_it;
     118         [ -  + ]:         246 :                 assert(chunk_it != chunks.end());
     119                 :         246 :                 chunk_ptr_remaining = chunk_it->ptr;
     120                 :         246 :                 chunk_size_remaining = chunk_it->size;
     121                 :             :             }
     122         [ -  + ]:       11547 :             assert(free_block.ptr == chunk_ptr_remaining);                   // ensure addresses match
     123         [ -  + ]:       11547 :             assert(free_block.size <= chunk_size_remaining);                 // ensure no overflow
     124         [ -  + ]:       11547 :             assert((free_block.ptr & (resource.ELEM_ALIGN_BYTES - 1)) == 0); // ensure correct alignment
     125                 :       11547 :             chunk_ptr_remaining += free_block.size;
     126                 :       11547 :             chunk_size_remaining -= free_block.size;
     127                 :             :         }
     128                 :             :         // ensure we are at the end of the chunks
     129         [ -  + ]:          14 :         assert(chunk_ptr_remaining == chunk_it->ptr + chunk_it->size);
     130         [ -  + ]:          14 :         ++chunk_it;
     131         [ -  + ]:          14 :         assert(chunk_it == chunks.end());
     132         [ -  + ]:          14 :         assert(chunk_size_remaining == 0);
     133                 :          14 :     }
     134                 :             : };
     135                 :             : 
     136                 :             : #endif // BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
        

Generated by: LCOV version 2.0-1