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