Branch data Line data Source code
1 : : // Copyright (c) 2019-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 : : #include <sync.h>
6 : : #include <test/util/coins.h>
7 : : #include <test/util/random.h>
8 : : #include <test/util/setup_common.h>
9 : : #include <validation.h>
10 : :
11 : : #include <boost/test/unit_test.hpp>
12 : :
13 : : BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
14 : :
15 : : //! Test utilities for detecting when we need to flush the coins cache based
16 : : //! on estimated memory usage.
17 : : //!
18 : : //! @sa Chainstate::GetCoinsCacheSizeState()
19 : : //!
20 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
21 : : {
22 : 1 : Chainstate& chainstate{m_node.chainman->ActiveChainstate()};
23 : :
24 : 1 : constexpr bool is_64_bit = sizeof(void*) == 8;
25 : :
26 : 1 : LOCK(::cs_main);
27 [ + - ]: 1 : auto& view = chainstate.CoinsTip();
28 : :
29 : : // The number of bytes consumed by coin's heap data, i.e. CScript
30 : : // (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
31 : : //
32 : : // See also: Coin::DynamicMemoryUsage().
33 : 1 : constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
34 : :
35 : 1 : auto print_view_mem_usage = [](CCoinsViewCache& view) {
36 [ # # ]: 0 : BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
37 : 0 : };
38 : :
39 : : // PoolResource defaults to 256 KiB that will be allocated, so we'll take that and make it a bit larger.
40 : 1 : constexpr size_t MAX_COINS_CACHE_BYTES = 262144 + 512;
41 : :
42 : : // Without any coins in the cache, we shouldn't need to flush.
43 [ + - + - : 2 : BOOST_TEST(
+ - + - +
- ]
44 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0) != CoinsCacheSizeState::CRITICAL);
45 : :
46 : : // If the initial memory allocations of cacheCoins don't match these common
47 : : // cases, we can't really continue to make assertions about memory usage.
48 : : // End the test early.
49 [ + - + - : 1 : if (view.DynamicMemoryUsage() != 32 && view.DynamicMemoryUsage() != 16) {
+ - - + ]
50 : : // Add a bunch of coins to see that we at least flip over to CRITICAL.
51 : :
52 [ + + ]: 1001 : for (int i{0}; i < 1000; ++i) {
53 [ + - ]: 1000 : const COutPoint res = AddTestCoin(m_rng, view);
54 [ + - + - : 2000 : BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
+ - + - ]
55 : : }
56 : :
57 [ + - + - : 1 : BOOST_CHECK_EQUAL(
+ - ]
58 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
59 : : CoinsCacheSizeState::CRITICAL);
60 : :
61 [ + - + - : 1 : BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
+ - ]
62 [ + - ]: 1 : return;
63 : : }
64 : :
65 [ # # ]: 0 : print_view_mem_usage(view);
66 [ # # # # : 0 : BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32U : 16U);
# # ]
67 : :
68 : : // We should be able to add COINS_UNTIL_CRITICAL coins to the cache before going CRITICAL.
69 : : // This is contingent not only on the dynamic memory usage of the Coins
70 : : // that we're adding (COIN_SIZE bytes per), but also on how much memory the
71 : : // cacheCoins (unordered_map) preallocates.
72 : 0 : constexpr int COINS_UNTIL_CRITICAL{3};
73 : :
74 : : // no coin added, so we have plenty of space left.
75 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
76 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
77 : : CoinsCacheSizeState::OK);
78 : :
79 [ # # ]: 0 : for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
80 [ # # ]: 0 : const COutPoint res = AddTestCoin(m_rng, view);
81 [ # # ]: 0 : print_view_mem_usage(view);
82 [ # # # # : 0 : BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
# # # # ]
83 : :
84 : : // adding first coin causes the MemoryResource to allocate one 256 KiB chunk of memory,
85 : : // pushing us immediately over to LARGE
86 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
87 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0),
88 : : CoinsCacheSizeState::LARGE);
89 : : }
90 : :
91 : : // Adding some additional coins will push us over the edge to CRITICAL.
92 [ # # ]: 0 : for (int i{0}; i < 4; ++i) {
93 [ # # ]: 0 : AddTestCoin(m_rng, view);
94 [ # # ]: 0 : print_view_mem_usage(view);
95 [ # # # # ]: 0 : if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0) ==
96 : : CoinsCacheSizeState::CRITICAL) {
97 : : break;
98 : : }
99 : : }
100 : :
101 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
102 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
103 : : CoinsCacheSizeState::CRITICAL);
104 : :
105 : : // Passing non-zero max mempool usage (512 KiB) should allow us more headroom.
106 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
107 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
108 : : CoinsCacheSizeState::OK);
109 : :
110 [ # # ]: 0 : for (int i{0}; i < 3; ++i) {
111 [ # # ]: 0 : AddTestCoin(m_rng, view);
112 [ # # ]: 0 : print_view_mem_usage(view);
113 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
114 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
115 : : CoinsCacheSizeState::OK);
116 : : }
117 : :
118 : : // Adding another coin with the additional mempool room will put us >90%
119 : : // but not yet critical.
120 [ # # ]: 0 : AddTestCoin(m_rng, view);
121 [ # # ]: 0 : print_view_mem_usage(view);
122 : :
123 : : // Only perform these checks on 64 bit hosts; I haven't done the math for 32.
124 : 0 : if (is_64_bit) {
125 [ # # ]: 0 : float usage_percentage = (float)view.DynamicMemoryUsage() / (MAX_COINS_CACHE_BYTES + (1 << 10));
126 [ # # # # : 0 : BOOST_TEST_MESSAGE("CoinsTip usage percentage: " << usage_percentage);
# # ]
127 [ # # # # : 0 : BOOST_CHECK(usage_percentage >= 0.9);
# # ]
128 [ # # # # : 0 : BOOST_CHECK(usage_percentage < 1);
# # ]
129 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
130 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10), // 1024
131 : : CoinsCacheSizeState::LARGE);
132 : : }
133 : :
134 : : // Using the default max_* values permits way more coins to be added.
135 [ # # ]: 0 : for (int i{0}; i < 1000; ++i) {
136 [ # # ]: 0 : AddTestCoin(m_rng, view);
137 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
138 : : chainstate.GetCoinsCacheSizeState(),
139 : : CoinsCacheSizeState::OK);
140 : : }
141 : :
142 : : // Flushing the view does take us back to OK because ReallocateCache() is called
143 : :
144 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # ]
145 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
146 : : CoinsCacheSizeState::CRITICAL);
147 : :
148 [ # # ]: 0 : view.SetBestBlock(m_rng.rand256());
149 [ # # # # : 0 : BOOST_CHECK(view.Flush());
# # # # ]
150 [ # # ]: 0 : print_view_mem_usage(view);
151 : :
152 [ # # # # : 0 : BOOST_CHECK_EQUAL(
# # # # ]
153 : : chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
154 : : CoinsCacheSizeState::OK);
155 : 1 : }
156 : :
157 : : BOOST_AUTO_TEST_SUITE_END()
|