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