From ea28413eb20461fec76c31915bc828097ae94307 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 10 May 2023 11:31:20 -0500 Subject: [PATCH] Added a bit of fuzz testing over mtree splits This isn't the greatest coverage as we don't have a verifiable simulation. Simulating the splitting-bucket-tree that is the mtree is tricky. So right now this mostly just checks there's no internal assert failures and if we have the expected number of entries afterwards. --- lfs.c | 7 +--- tests/test_mtree.toml | 87 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/lfs.c b/lfs.c index b849946f..0620c9e1 100644 --- a/lfs.c +++ b/lfs.c @@ -4523,12 +4523,7 @@ static inline int lfsr_mtree_isinlined(lfs_t *lfs) { } static inline lfs_ssize_t lfsr_mtree_weight(lfs_t *lfs) { - // inlined mdir? - if (lfsr_mtree_isinlined(lfs)) { - return lfs->mroot.rbyd.weight; - } else { - return lfsr_btree_weight(&lfs->mtree); - } + return lfsr_btree_weight(&lfs->mtree); } static int lfsr_mtree_lookup(lfs_t *lfs, lfs_ssize_t mid, lfsr_mdir_t *mdir_) { diff --git a/tests/test_mtree.toml b/tests/test_mtree.toml index e119f15c..696fef86 100644 --- a/tests/test_mtree.toml +++ b/tests/test_mtree.toml @@ -63,7 +63,7 @@ code = ''' # TODO test many mroots # try creating a range of entries that may or may not split our mtree -[cases.test_mtree_splitting] +[cases.test_mtree_split] defines.N = [5, 10, 20, 40, 80, 160, 320] in = 'lfs.c' code = ''' @@ -71,6 +71,7 @@ code = ''' lfs_t lfs; lfsr_format(&lfs, cfg) => 0; + // create entries lfsr_mount(&lfs, cfg) => 0; lfsr_mdir_t mdir; lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0; @@ -88,6 +89,7 @@ code = ''' } lfsr_unmount(&lfs) => 0; + // try looking up each entry lfsr_mount(&lfs, cfg) => 0; lfs_ssize_t mid = -1; rid = 0; @@ -112,7 +114,7 @@ code = ''' # create a range of entries, and commit to the mroot several times, # this makes it more likely to force uninlining without necessarily # splitting -[cases.test_mtree_uninlining] +[cases.test_mtree_uninline] defines.N = [5, 10, 20, 40, 80, 160, 320] defines.M = [5, 5000] in = 'lfs.c' @@ -121,6 +123,7 @@ code = ''' lfs_t lfs; lfsr_format(&lfs, cfg) => 0; + // create entries lfsr_mount(&lfs, cfg) => 0; lfsr_mdir_t mdir; lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0; @@ -137,6 +140,8 @@ code = ''' rid += 1; } + // also append a bunch of fs-level attributes, if the mdir should be + // uninlined this will force it to happen for (lfs_size_t i = 0; i < M; i++) { lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, &alphas[i % 26], 1))) => 0; @@ -148,6 +153,7 @@ code = ''' } lfsr_unmount(&lfs) => 0; + // try looking up each entry lfsr_mount(&lfs, cfg) => 0; lfs_ssize_t mid = -1; rid = 0; @@ -174,3 +180,80 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# create random entries +[cases.test_mtree_split_fuzz] +defines.N = [5, 10, 20, 40, 80, 160, 320] +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 +in = 'lfs.c' +code = ''' + const char *alphas = "abcdefghijklmnopqrstuvwxyz"; + + // iterate through severals seeds that we can reproduce easily + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); + // create lfs here since we need to reset each iteration, we're + // space constrained and we can't expect gc to work at this point + lfs_t lfs; + lfsr_format(&lfs, cfg) => 0; + + lfsr_mount(&lfs, cfg) => 0; + // at least keep track of the number of entries we expect + lfs_size_t count = 0; + + uint32_t prng = seed; + for (lfs_size_t i = 0; i < N; i++) { + // choose a pseudo-random mid + lfs_ssize_t mid = lfsr_mtree_weight(&lfs) == 0 + ? -1 + : (lfs_ssize_t)(TEST_PRNG(&prng) % lfsr_mtree_weight(&lfs)); + // fetch mdir + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; + // choose a pseudo-random rid + lfs_ssize_t rid = TEST_PRNG(&prng) % (mdir.rbyd.weight+1); + + // add to rbyd, potentially splitting the mdir + lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS( + LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0; + + // make sure we can look up the new entry + uint8_t buffer[4]; + lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED, buffer, 4) => 1; + assert(memcmp(buffer, &alphas[i % 26], 1) == 0); + + count += 1; + } + lfsr_unmount(&lfs) => 0; + + lfsr_mount(&lfs, cfg) => 0; + // try looking up each entry + lfs_size_t count_ = 0; + + for (lfs_ssize_t mid = (lfsr_mtree_isinlined(&lfs) ? -1 : 0); + mid < lfsr_mtree_weight(&lfs); + mid++) { + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; + for (lfs_ssize_t rid = 0; + rid < (lfs_ssize_t)mdir.rbyd.weight; + rid++) { + uint8_t buffer[4]; + lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED, + buffer, 4) => 1; + + count_ += 1; + } + } + + // the mtree is a bit difficult to simulate, but we can at least test + // we ended up with the right number of entries + assert(count_ == count); + + lfsr_unmount(&lfs) => 0; + } +'''