LCOV - code coverage report
Current view: top level - src/test/fuzz - dbwrapper.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 71.1 % 228 162
Test Date: 2026-06-09 06:53:21 Functions: 93.6 % 47 44
Branches: 43.4 % 426 185

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 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 <dbwrapper.h>
       6                 :             : #include <compat/byteswap.h>
       7                 :             : #include <random.h>
       8                 :             : #include <sync.h>
       9                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      10                 :             : #include <test/fuzz/fuzz.h>
      11                 :             : #include <test/fuzz/util.h>
      12                 :             : #include <test/util/random.h>
      13                 :             : #include <test/util/setup_common.h>
      14                 :             : #include <util/byte_units.h>
      15                 :             : #include <util/check.h>
      16                 :             : #include <util/threadpool.h>
      17                 :             : 
      18                 :             : #include <leveldb/env.h>
      19                 :             : #include <leveldb/helpers/memenv/memenv.h>
      20                 :             : 
      21                 :             : #include <algorithm>
      22                 :             : #include <cassert>
      23                 :             : #include <cstdint>
      24                 :             : #include <deque>
      25                 :             : #include <functional>
      26                 :             : #include <future>
      27                 :             : #include <latch>
      28                 :             : #include <map>
      29                 :             : #include <memory>
      30                 :             : #include <numeric>
      31                 :             : #include <optional>
      32                 :             : #include <set>
      33                 :             : #include <string>
      34                 :             : #include <tuple>
      35                 :             : #include <vector>
      36                 :             : 
      37                 :             : namespace {
      38                 :             : 
      39                 :             : /**
      40                 :             :  * A leveldb::Env that wraps a memenv and captures scheduled background
      41                 :             :  * work (compaction) instead of dispatching to a real thread. The fuzz
      42                 :             :  * harness calls RunOne() or DrainWork() at fuzzer-chosen points to
      43                 :             :  * execute it, giving deterministic control over when compaction
      44                 :             :  * interleaves with foreground operations.
      45                 :             :  *
      46                 :             :  * Deadlock prevention: LevelDB's MakeRoomForWrite blocks on a condition
      47                 :             :  * variable when the previous immutable memtable is still awaiting compaction,
      48                 :             :  * or when the L0 file count hits kL0_StopWritesTrigger. Since both conditions
      49                 :             :  * can only be resolved by the (deferred) background work, the harness drains
      50                 :             :  * all pending work before every write to avoid a single-threaded deadlock.
      51                 :             :  * Callers must also DrainWork() before destroying the CDBWrapper, since the
      52                 :             :  * leveldb destructor waits for any pending background work to complete.
      53                 :             :  *
      54                 :             :  * The same reasoning rules out exercising DBOptions::force_compact under
      55                 :             :  * this env, because CompactRange(nullptr, nullptr) blocks waiting for
      56                 :             :  * background work that is queued on the (blocked) foreground thread. The
      57                 :             :  * sibling dbwrapper_threaded target covers that path.
      58                 :             :  */
      59                 :             : class DeterministicEnv final : public leveldb::EnvWrapper
      60                 :             : {
      61                 :             :     using WorkFunction = void (*)(void*);
      62                 :             : 
      63                 :             :     struct Work {
      64                 :             :         WorkFunction function;
      65                 :             :         void* arg;
      66                 :             :     };
      67                 :             : 
      68                 :             :     Mutex m_mutex;
      69                 :             :     std::deque<Work> m_queue GUARDED_BY(m_mutex);
      70                 :             : 
      71                 :             : public:
      72         [ +  - ]:         539 :     explicit DeterministicEnv(leveldb::Env* base) : EnvWrapper(base) {}
      73                 :             : 
      74                 :        9605 :     void Schedule(WorkFunction function, void* arg) override EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      75                 :             :     {
      76                 :        9605 :         LOCK(m_mutex);
      77   [ +  -  +  - ]:        9605 :         m_queue.push_back({function, arg});
      78                 :        9605 :     }
      79                 :             : 
      80                 :             :     /** Execute one pending background task. The task may schedule a
      81                 :             :      *  successor which is left pending for a later call. */
      82                 :       76807 :     bool RunOne() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      83                 :             :     {
      84                 :       76807 :         Work work;
      85                 :       76807 :         {
      86                 :       76807 :             LOCK(m_mutex);
      87   [ +  +  +  - ]:       76807 :             if (m_queue.empty()) return false;
      88                 :        9605 :             work = m_queue.front();
      89         [ +  - ]:        9605 :             m_queue.pop_front();
      90                 :       67202 :         }
      91                 :        9605 :         work.function(work.arg);
      92                 :        9605 :         return true;
      93                 :             :     }
      94                 :             : 
      95                 :             :     /** Execute pending background tasks until none remain. */
      96   [ -  -  -  -  :       73482 :     void DrainWork() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { while (RunOne()) {} }
          -  -  -  -  -  
             -  -  -  +  
                      + ]
      97                 :             : };
      98                 :             : 
      99                 :             : constexpr size_t MAX_VALUE_LEN{4096};
     100                 :             : constexpr uint8_t MAX_VALUE_MULTIPLIER{8};
     101                 :             : constexpr size_t WRITE_BATCH_HEADER{12}; // See kHeader in db/write_batch.cc
     102                 :             : 
     103                 :             : /** Mirror of CDBWrapper::OBFUSCATION_KEY, the fixed key under which leveldb
     104                 :             :  *  stores the obfuscation metadata entry when obfuscation is enabled. */
     105                 :             : const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14};
     106                 :             : 
     107                 :             : /** Generate a deterministic value from key and size. The fuzz input picks
     108                 :             :  *  a 16-bit length (up to MAX_VALUE_LEN) and an 8-bit multiplier so that a
     109                 :             :  *  small amount of fuzz input can produce a wide range of value sizes. */
     110                 :      434635 : std::vector<uint8_t> MakeValue(uint16_t key, uint32_t size)
     111                 :             : {
     112                 :      434635 :     std::vector<uint8_t> v(size);
     113                 :      434635 :     std::iota(v.begin(), v.end(), static_cast<uint8_t>(key ^ (key >> 8)));
     114                 :      434635 :     return v;
     115                 :             : }
     116                 :             : 
     117                 :             : /** Equivalent to leveldb::BytewiseComparator() on 2-byte little-endian
     118                 :             :  *  serialized uint16_t keys, while keeping the oracle keyed by uint16_t. */
     119                 :             : struct LevelDBBytewiseU16Cmp {
     120   [ +  -  -  +  :      472603 :     bool operator()(uint16_t a, uint16_t b) const { return internal_bswap_16(a) < internal_bswap_16(b); }
          -  -  -  -  +  
          +  +  +  +  -  
          +  -  +  -  -  
          -  -  -  +  +  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  +  
          +  +  +  +  +  
                   +  + ]
     121                 :             : };
     122                 :             : 
     123                 :             : /** key → value-size map ordered by LevelDB's bytewise comparator. */
     124                 :             : using Oracle = std::map<uint16_t, uint32_t, LevelDBBytewiseU16Cmp>;
     125                 :             : 
     126                 :             : struct FailUnserialize {
     127                 :             :     template <typename Stream>
     128         [ +  - ]:        2640 :     void Unserialize(Stream&) { throw std::ios_base::failure{"always fail"}; }
     129                 :             : };
     130                 :             : 
     131                 :      223198 : uint16_t ConsumeKey(FuzzedDataProvider& provider) { return provider.ConsumeIntegral<uint16_t>(); }
     132                 :       63616 : uint32_t ConsumeValueSize(FuzzedDataProvider& provider)
     133                 :             : {
     134                 :       63616 :     const uint16_t len{provider.ConsumeIntegralInRange<uint16_t>(0, MAX_VALUE_LEN)};
     135                 :       63616 :     const uint8_t multiplier{provider.ConsumeIntegralInRange<uint8_t>(1, MAX_VALUE_MULTIPLIER)};
     136                 :       63616 :     return static_cast<uint32_t>(len) * multiplier;
     137                 :             : }
     138                 :             : 
     139                 :             : /** Verify that the DB iterator matches the oracle, handling the obfuscation
     140                 :             :  *  metadata entry (stored under a non-uint16_t key) when obfuscation is on. */
     141                 :       62089 : void VerifyIterator(CDBWrapper& dbw, const Oracle& oracle,
     142                 :             :                     bool obfuscate, std::optional<uint16_t> seek_key = std::nullopt)
     143                 :             : {
     144         [ +  + ]:       62089 :     const std::unique_ptr<CDBIterator> it{dbw.NewIterator()};
     145         [ +  + ]:       62089 :     auto oracle_it{seek_key ? oracle.lower_bound(*seek_key) : oracle.begin()};
     146         [ +  + ]:       62089 :     if (seek_key) {
     147         [ +  - ]:       13180 :         it->Seek(*seek_key);
     148                 :             :     } else {
     149         [ +  - ]:       48909 :         it->SeekToFirst();
     150                 :             :     }
     151   [ +  -  +  -  :      471724 :     for (; it->Valid(); it->Next()) {
                   +  + ]
     152                 :      409635 :         uint16_t db_key;
     153   [ +  -  -  + ]:      409635 :         assert(it->GetKey(db_key));
     154   [ +  +  +  + ]:      409635 :         if (oracle_it != oracle.end() && db_key == oracle_it->first) {
     155                 :      368609 :             std::vector<uint8_t> db_value;
     156   [ +  -  -  + ]:      368609 :             assert(it->GetValue(db_value));
     157   [ +  -  -  + ]:      368609 :             assert(db_value == MakeValue(db_key, oracle_it->second));
     158                 :      368609 :             ++oracle_it;
     159                 :      368609 :         } else {
     160         [ -  + ]:       41026 :             assert(obfuscate);
     161         [ +  - ]:       41026 :             std::string key_str;
     162   [ +  -  -  + ]:       41026 :             assert(it->GetKey(key_str));
     163         [ -  + ]:       41026 :             assert(key_str == OBFUSCATION_KEY);
     164                 :       41026 :         }
     165                 :             :     }
     166         [ -  + ]:       62089 :     assert(oracle_it == oracle.end());
     167                 :       62089 : }
     168                 :             : 
     169                 :             : /** Maximum number of concurrent reader threads in dbwrapper_concurrent_reads. */
     170                 :             : constexpr size_t MAX_READ_WORKERS{16};
     171                 :             : 
     172                 :             : /** Build randomized DBParams from the fuzz input, shared by all targets. */
     173                 :       47875 : DBParams ConsumeDBParams(FuzzedDataProvider& provider, leveldb::Env* testing_env,
     174                 :             :                          bool obfuscate, DBOptions options = {})
     175                 :             : {
     176                 :       95750 :     return DBParams{
     177                 :             :         .path = "dbwrapper_fuzz",
     178                 :       47875 :         .cache_bytes = provider.ConsumeIntegralInRange<size_t>(64 << 10, 1_MiB),
     179                 :             :         .obfuscate = obfuscate,
     180                 :             :         .options = options,
     181                 :             :         .testing_env = testing_env,
     182                 :       47875 :         .max_file_size = provider.ConsumeBool()
     183         [ +  + ]:       47875 :             ? DBWRAPPER_MAX_FILE_SIZE
     184                 :        5705 :             : provider.ConsumeIntegralInRange<size_t>(1_MiB, 4_MiB),
     185                 :       47875 :     };
     186                 :             : }
     187                 :             : 
     188                 :             : /** A single read-only operation run concurrently in dbwrapper_concurrent_reads. */
     189                 :             : enum class ReadOp { Read, Exists, IteratorSeek };
     190                 :             : using ReadQuery = std::tuple<ReadOp, uint16_t>;
     191                 :             : using Results = std::vector<std::optional<std::string>>;
     192                 :             : 
     193                 :           0 : Results RunReadQueries(CDBWrapper& db, const std::vector<ReadQuery>& queries, FastRandomContext& rng)
     194                 :             : {
     195         [ #  # ]:           0 :     std::vector<size_t> order(queries.size());
     196                 :           0 :     std::iota(order.begin(), order.end(), size_t{0});
     197                 :           0 :     std::shuffle(order.begin(), order.end(), rng);
     198                 :             : 
     199   [ #  #  #  # ]:           0 :     Results results(queries.size());
     200         [ #  # ]:           0 :     for (const auto i : order) {
     201   [ #  #  #  # ]:           0 :         const auto& [op, key] = queries[i];
     202   [ #  #  #  # ]:           0 :         std::string v;
     203   [ #  #  #  # ]:           0 :         switch (op) {
     204                 :           0 :         case ReadOp::Read:
     205   [ #  #  #  # ]:           0 :             if (db.Read(key, v)) results[i] = std::move(v);
     206                 :             :             break;
     207                 :           0 :         case ReadOp::Exists:
     208   [ #  #  #  # ]:           0 :             if (db.Exists(key)) results[i] = std::move(v);
     209                 :             :             break;
     210                 :           0 :         case ReadOp::IteratorSeek: {
     211   [ #  #  #  # ]:           0 :             const std::unique_ptr<CDBIterator> it{db.NewIterator()};
     212         [ #  # ]:           0 :             it->Seek(key);
     213   [ #  #  #  #  :           0 :             if (it->Valid() && it->GetValue(v)) results[i] = std::move(v);
             #  #  #  # ]
     214                 :           0 :             break;
     215                 :           0 :         }
     216                 :             :         }
     217                 :           0 :     }
     218                 :           0 :     return results;
     219                 :           0 : }
     220                 :             : 
     221                 :             : template <typename DrainWorkFn, typename RunOneFn>
     222                 :        1158 : void TestDbWrapper(FuzzedDataProvider& provider,
     223                 :             :                       leveldb::Env* testing_env,
     224                 :             :                       DrainWorkFn drain_work,
     225                 :             :                       RunOneFn run_one,
     226                 :             :                       bool allow_force_compact)
     227                 :             : {
     228                 :        1158 :     SeedRandomStateForTest(SeedRand::ZEROS);
     229                 :             : 
     230                 :        1158 :     const bool obfuscate{provider.ConsumeBool()};
     231                 :             : 
     232                 :       49033 :     const auto make_db{[&](DBOptions options = {}) {
     233   [ +  -  +  - ]:       95750 :         return std::make_unique<CDBWrapper>(ConsumeDBParams(provider, testing_env, obfuscate, options));
     234                 :             :     }};
     235                 :        1158 :     std::unique_ptr<CDBWrapper> dbw{make_db()};
     236                 :             : 
     237                 :             :     // Oracle: key → value size. Content is reconstructed via MakeValue().
     238                 :        1158 :     Oracle oracle;
     239                 :             : 
     240   [ +  +  +  + ]:      164610 :     LIMITED_WHILE(provider.ConsumeBool(), 1'000)
     241                 :             :     {
     242         [ +  - ]:      163452 :         CallOneOf(
     243                 :             :             provider,
     244                 :             :             // --- Mutations ---
     245                 :       24180 :             [&] {
     246                 :       12090 :                 const auto key{ConsumeKey(provider)};
     247                 :       12090 :                 const auto size{ConsumeValueSize(provider)};
     248                 :        6086 :                 drain_work();
     249   [ +  -  +  - ]:       12090 :                 dbw->Write(key, MakeValue(key, size), /*fSync=*/provider.ConsumeBool());
     250                 :       12090 :                 oracle[key] = size;
     251                 :             :             },
     252                 :       89886 :             [&] {
     253                 :       44943 :                 const auto key{ConsumeKey(provider)};
     254                 :       23943 :                 drain_work();
     255                 :       44943 :                 dbw->Erase(key, /*fSync=*/provider.ConsumeBool());
     256                 :       44943 :                 oracle.erase(key);
     257                 :             :             },
     258                 :        7093 :             [&] {
     259   [ +  -  +  - ]:        7093 :                 CDBBatch batch{*dbw};
     260   [ +  -  +  - ]:        7093 :                 std::map<uint16_t, uint32_t> batch_writes;
     261                 :        7093 :                 std::set<uint16_t> batch_erases;
     262                 :       19337 :                 const auto fill{[&] {
     263   [ +  +  +  +  :      132493 :                     LIMITED_WHILE(provider.ConsumeBool(), 20)
             +  +  +  + ]
     264                 :             :                     {
     265                 :      120249 :                         const auto key{ConsumeKey(provider)};
     266   [ +  +  +  + ]:      120249 :                         if (provider.ConsumeBool()) {
     267                 :       51526 :                             const auto size{ConsumeValueSize(provider)};
     268   [ +  -  +  - ]:       51526 :                             batch.Write(key, MakeValue(key, size));
     269                 :       51526 :                             batch_writes[key] = size;
     270                 :       51526 :                             batch_erases.erase(key);
     271                 :             :                         } else {
     272                 :       68723 :                             batch.Erase(key);
     273                 :       68723 :                             batch_erases.insert(key);
     274                 :       68723 :                             batch_writes.erase(key);
     275                 :             :                         }
     276                 :             :                     }
     277                 :             :                 }};
     278   [ +  -  +  - ]:        7093 :                 fill();
     279   [ +  +  +  + ]:        7093 :                 if (provider.ConsumeBool()) {
     280   [ +  -  -  +  :        5151 :                     assert(batch.ApproximateSize() >= WRITE_BATCH_HEADER);
             +  -  -  + ]
     281   [ +  -  +  - ]:        5151 :                     batch.Clear();
     282   [ +  -  -  +  :        5151 :                     assert(batch.ApproximateSize() == WRITE_BATCH_HEADER);
             +  -  -  + ]
     283                 :        5151 :                     batch_writes.clear();
     284                 :        5151 :                     batch_erases.clear();
     285   [ +  -  +  - ]:        5151 :                     fill();
     286                 :             :                 }
     287         [ +  - ]:        4521 :                 drain_work();
     288   [ +  -  +  - ]:        7093 :                 dbw->WriteBatch(batch, /*fSync=*/provider.ConsumeBool());
     289   [ +  -  +  +  :       31187 :                 for (const auto& [k, v] : batch_writes) oracle[k] = v;
             +  -  +  + ]
     290   [ +  +  +  + ]:       23389 :                 for (const auto& k : batch_erases) oracle.erase(k);
     291                 :        7093 :             },
     292                 :       93434 :             [&] {
     293                 :       29006 :                 drain_work();
     294   [ +  -  +  - ]:       46717 :                 dbw.reset();
     295                 :       46717 :                 DBOptions options{};
     296   [ +  -  +  +  :       46717 :                 if (allow_force_compact && provider.ConsumeBool()) {
             -  +  -  - ]
     297                 :       14234 :                     options.force_compact = true;
     298                 :             :                 }
     299                 :       46717 :                 dbw = make_db(options);
     300                 :       46717 :                 VerifyIterator(*dbw, oracle, obfuscate);
     301                 :             :             },
     302                 :             :             // --- Reads ---
     303                 :        9794 :             [&] {
     304                 :        4897 :                 const auto key{ConsumeKey(provider)};
     305                 :        4897 :                 std::vector<uint8_t> value;
     306   [ +  -  +  - ]:        4897 :                 const bool found{dbw->Read(key, value)};
     307   [ +  +  +  + ]:        4897 :                 if (const auto it{oracle.find(key)}; it != oracle.end()) {
     308   [ +  -  +  -  :        4820 :                     assert(found && value == MakeValue(key, it->second));
          -  +  +  -  +  
                -  -  + ]
     309                 :             :                 } else {
     310   [ -  +  -  + ]:        2487 :                     assert(!found);
     311                 :             :                 }
     312                 :        4897 :             },
     313                 :       37544 :             [&] {
     314                 :       18772 :                 const auto key{ConsumeKey(provider)};
     315   [ -  +  -  + ]:       18772 :                 assert(dbw->Exists(key) == oracle.contains(key));
     316                 :             :             },
     317                 :        3990 :             [&] {
     318                 :        1995 :                 uint16_t key{};
     319   [ +  +  +  +  :        1995 :                 if (!oracle.empty() && provider.ConsumeBool()) {
             +  +  +  + ]
     320                 :        1304 :                     auto it{oracle.begin()};
     321                 :        1304 :                     std::advance(it, provider.ConsumeIntegralInRange<size_t>(0, oracle.size() - 1));
     322                 :        1304 :                     key = it->first;
     323                 :             :                 } else {
     324                 :         691 :                     key = ConsumeKey(provider);
     325                 :             :                 }
     326                 :             :                 FailUnserialize wrong_type;
     327   [ -  +  -  + ]:        1995 :                 assert(!dbw->Read(key, wrong_type));
     328                 :             :             },
     329                 :       28428 :             [&] {
     330                 :       28428 :                 const auto seek_key{provider.ConsumeBool()
     331   [ +  +  +  + ]:       14214 :                                         ? std::optional<uint16_t>{ConsumeKey(provider)}
     332                 :             :                                         : std::nullopt};
     333                 :       14214 :                 VerifyIterator(*dbw, oracle, obfuscate, seek_key);
     334                 :             :             },
     335                 :             :             // --- Stats ---
     336                 :        3930 :             [&] {
     337   [ +  +  +  +  :        3218 :                 assert(dbw->IsEmpty() == (oracle.empty() && !obfuscate));
          -  +  +  +  +  
                +  -  + ]
     338                 :             :             },
     339                 :        8376 :             [&] {
     340                 :        4188 :                 const auto [k1, k2]{std::minmax({ConsumeKey(provider), ConsumeKey(provider)}, LevelDBBytewiseU16Cmp{})};
     341                 :        4188 :                 const size_t estimate_size{dbw->EstimateSize(k1, k2)};
     342   [ +  +  -  +  :        4188 :                 if (k1 == k2) assert(estimate_size == 0);
             +  +  -  + ]
     343                 :             :             },
     344                 :        1512 :             [&] {
     345                 :        1512 :                 (void)dbw->DynamicMemoryUsage();
     346                 :             :             },
     347                 :             :             // --- Compaction control (no-op when run_one is no-op) ---
     348                 :        3325 :             [&] {
     349                 :        3325 :                 run_one();
     350                 :             :             });
     351                 :             :     }
     352                 :             : 
     353         [ +  - ]:        1158 :     VerifyIterator(*dbw, oracle, obfuscate);
     354         [ +  - ]:        1158 :     drain_work();
     355                 :        1158 : }
     356                 :             : 
     357                 :             : } // namespace
     358                 :             : 
     359   [ +  -  +  -  :        1008 : FUZZ_TARGET(dbwrapper, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })
                   +  - ]
     360                 :             : {
     361                 :         539 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     362                 :             : 
     363         [ +  - ]:         539 :     const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};
     364         [ +  - ]:         539 :     DeterministicEnv det_env{memenv.get()};
     365         [ +  - ]:         539 :     TestDbWrapper(
     366                 :             :         provider, &det_env,
     367                 :       64095 :         [&] { det_env.DrainWork(); },
     368                 :        3325 :         [&] { return det_env.RunOne(); },
     369                 :             :         /*allow_force_compact=*/false);
     370                 :         539 : }
     371                 :             : 
     372   [ +  -  +  -  :        1087 : FUZZ_TARGET(dbwrapper_threaded, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })
                   +  - ]
     373                 :             : {
     374                 :         619 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     375                 :             : 
     376         [ +  - ]:         619 :     const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};
     377         [ +  - ]:         619 :     TestDbWrapper(
     378                 :             :         provider, memenv.get(),
     379                 :             :         /*drain_work=*/[] {},
     380                 :             :         /*run_one=*/[] { return false; },
     381                 :             :         /*allow_force_compact=*/true);
     382                 :         619 : }
     383                 :             : 
     384   [ +  -  +  -  :         468 : FUZZ_TARGET(dbwrapper_concurrent_reads, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })
                   +  - ]
     385                 :             : {
     386                 :           0 :     SeedRandomStateForTest(SeedRand::ZEROS);
     387                 :             : 
     388                 :           0 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     389                 :             : 
     390         [ #  # ]:           0 :     const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};
     391         [ #  # ]:           0 :     DeterministicEnv det_env{memenv.get()};
     392                 :             : 
     393   [ #  #  #  # ]:           0 :     CDBWrapper db{ConsumeDBParams(provider, &det_env, /*obfuscate=*/provider.ConsumeBool())};
     394                 :             : 
     395                 :             :     // Seed the DB. Drain work after small batches so we don't deadlock on a
     396                 :             :     // scheduled compaction.
     397                 :           0 :     const size_t num_entries{provider.ConsumeIntegralInRange<size_t>(100, 5'000)};
     398                 :           0 :     std::vector<uint16_t> keys;
     399         [ #  # ]:           0 :     keys.reserve(num_entries);
     400                 :             :     constexpr size_t SEED_BATCH_SIZE{400};
     401         [ #  # ]:           0 :     for (size_t start{0}; start < num_entries; start += SEED_BATCH_SIZE) {
     402         [ #  # ]:           0 :         CDBBatch batch{db};
     403         [ #  # ]:           0 :         const size_t end{std::min(start + SEED_BATCH_SIZE, num_entries)};
     404         [ #  # ]:           0 :         for (size_t i{start}; i < end; ++i) {
     405                 :           0 :             const auto k{ConsumeKey(provider)};
     406   [ #  #  #  # ]:           0 :             batch.Write(k, MakeValue(k, ConsumeValueSize(provider)));
     407         [ #  # ]:           0 :             keys.push_back(k);
     408                 :             :         }
     409                 :             :         det_env.DrainWork();
     410         [ #  # ]:           0 :         db.WriteBatch(batch, /*fSync=*/true);
     411                 :           0 :     }
     412                 :             : 
     413   [ #  #  #  #  :           0 :     while (provider.ConsumeBool() && det_env.RunOne()) {}
             #  #  #  # ]
     414                 :             : 
     415                 :             :     // Build query list from seeded and random keys.
     416                 :           0 :     const size_t num_queries{provider.ConsumeIntegralInRange<size_t>(1, 2'000)};
     417                 :           0 :     std::vector<ReadQuery> queries;
     418         [ #  # ]:           0 :     queries.reserve(num_queries);
     419         [ #  # ]:           0 :     for (size_t i{0}; i < num_queries; ++i) {
     420                 :           0 :         const auto op{static_cast<ReadOp>(provider.ConsumeIntegralInRange<int>(0, 2))};
     421                 :           0 :         const uint16_t key{provider.ConsumeBool()
     422         [ #  # ]:           0 :                                ? keys[provider.ConsumeIntegralInRange<size_t>(0, keys.size() - 1)]
     423                 :           0 :                                : ConsumeKey(provider)};
     424         [ #  # ]:           0 :         queries.emplace_back(op, key);
     425                 :             :     }
     426                 :             : 
     427                 :             :     // Baseline read on a single thread
     428                 :           0 :     FastRandomContext rng{ConsumeUInt256(provider)};
     429         [ #  # ]:           0 :     const Results baseline{RunReadQueries(db, queries, rng)};
     430                 :             : 
     431   [ #  #  #  # ]:           0 :     ThreadPool pool{"dbfuzz"};
     432         [ #  # ]:           0 :     pool.Start(MAX_READ_WORKERS);
     433                 :             : 
     434                 :             :     // Workers + main thread synchronize on the latch so all reads start together.
     435                 :           0 :     std::latch start_latch{static_cast<ptrdiff_t>(MAX_READ_WORKERS + 1)};
     436         [ #  # ]:           0 :     std::vector<std::function<Results()>> tasks(MAX_READ_WORKERS);
     437         [ #  # ]:           0 :     std::generate(tasks.begin(), tasks.end(), [&] {
     438                 :           0 :         return [&, seed = rng.rand256()]() -> Results {
     439                 :           0 :             FastRandomContext thread_rng{seed};
     440         [ #  # ]:           0 :             start_latch.arrive_and_wait();
     441         [ #  # ]:           0 :             return RunReadQueries(db, queries, thread_rng);
     442                 :           0 :         };
     443                 :             :     });
     444         [ #  # ]:           0 :     auto futures{*Assert(pool.Submit(std::move(tasks)))};
     445                 :             : 
     446                 :             :     // Release the workers and immediately run the queued compaction on this
     447                 :             :     // thread, so compaction races against the concurrent reads.
     448         [ #  # ]:           0 :     start_latch.arrive_and_wait();
     449                 :           0 :     det_env.DrainWork();
     450                 :             : 
     451   [ #  #  #  #  :           0 :     for (auto& fut : futures) assert(fut.get() == baseline);
                   #  # ]
     452                 :             :     det_env.DrainWork();
     453                 :           0 : }
        

Generated by: LCOV version 2.0-1