Added bench.py and bench_runner.c for benchmarking

These are really just different flavors of test.py and test_runner.c
without support for power-loss testing, but with support for measuring
the cumulative number of bytes read, programmed, and erased.

Note that the existing define parameterization should work perfectly
fine for running benchmarks across various dimensions:

./scripts/bench.py \
    runners/bench_runner \
    bench_file_read \
    -gnor \
    -DSIZE='range(0,131072,1024)'

Also added a couple basic benchmarks as a starting point.
This commit is contained in:
Christopher Haster 2022-09-20 02:01:59 -05:00
parent 20ec0be875
commit 4fe0738ff4
20 changed files with 4253 additions and 405 deletions

2
.gitignore vendored
View File

@ -5,6 +5,7 @@
*.ci
*.csv
*.t.c
*.b.c
*.a.c
*.gcno
*.gcda
@ -17,3 +18,4 @@ tests/*.toml.*
scripts/__pycache__
.gdb_history
runners/test_runner
runners/bench_runner

View File

@ -7,7 +7,8 @@ $(if $(findstring n,$(MAKEFLAGS)),, $(shell mkdir -p \
$(BUILDDIR) \
$(BUILDDIR)bd \
$(BUILDDIR)runners \
$(BUILDDIR)tests))
$(BUILDDIR)tests \
$(BUILDDIR)benches))
endif
# overridable target/src/tools/flags/etc
@ -45,6 +46,18 @@ TEST_CI := $(TEST_TAC:%.t.a.c=%.t.a.ci)
TEST_GCNO := $(TEST_TAC:%.t.a.c=%.t.a.gcno)
TEST_GCDA := $(TEST_TAC:%.t.a.c=%.t.a.gcda)
BENCHES ?= $(wildcard benches/*.toml)
BENCH_SRC ?= $(SRC) \
$(filter-out $(wildcard bd/*.*.c),$(wildcard bd/*.c)) \
runners/bench_runner.c
BENCH_BC := $(BENCHES:%.toml=$(BUILDDIR)%.b.c) $(BENCH_SRC:%.c=$(BUILDDIR)%.b.c)
BENCH_BAC := $(BENCH_BC:%.b.c=%.b.a.c)
BENCH_OBJ := $(BENCH_BAC:%.b.a.c=%.b.a.o)
BENCH_DEP := $(BENCH_BAC:%.b.a.c=%.b.a.d)
BENCH_CI := $(BENCH_BAC:%.b.a.c=%.b.a.ci)
BENCH_GCNO := $(BENCH_BAC:%.b.a.c=%.b.a.gcno)
BENCH_GCDA := $(BENCH_BAC:%.b.a.c=%.b.a.gcda)
ifdef DEBUG
override CFLAGS += -O0
else
@ -60,27 +73,31 @@ override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
override CFLAGS += -ftrack-macro-expansion=0
override TESTFLAGS += -b
override BENCHFLAGS += -b
# forward -j flag
override TESTFLAGS += $(filter -j%,$(MAKEFLAGS))
override BENCHFLAGS += $(filter -j%,$(MAKEFLAGS))
ifdef VERBOSE
override TESTFLAGS += -v
override CODEFLAGS += -v
override DATAFLAGS += -v
override STACKFLAGS += -v
override STRUCTFLAGS += -v
override COVERAGEFLAGS += -v
override TESTFLAGS += -v
override TESTCFLAGS += -v
override CODEFLAGS += -v
override DATAFLAGS += -v
override STACKFLAGS += -v
override STRUCTFLAGS += -v
override COVERAGEFLAGS += -v
override TESTFLAGS += -v
override TESTCFLAGS += -v
override BENCHFLAGS += -v
override BENCHCFLAGS += -v
endif
ifdef EXEC
override TESTFLAGS += --exec="$(EXEC)"
override TESTFLAGS += --exec="$(EXEC)"
override BENCHFLAGS += --exec="$(EXEC)"
endif
ifdef BUILDDIR
override CODEFLAGS += --build-dir="$(BUILDDIR:/=)"
override DATAFLAGS += --build-dir="$(BUILDDIR:/=)"
override STACKFLAGS += --build-dir="$(BUILDDIR:/=)"
override STRUCTFLAGS += --build-dir="$(BUILDDIR:/=)"
override COVERAGEFLAGS += --build-dir="$(BUILDDIR:/=)"
override CODEFLAGS += --build-dir="$(BUILDDIR:/=)"
override DATAFLAGS += --build-dir="$(BUILDDIR:/=)"
override STACKFLAGS += --build-dir="$(BUILDDIR:/=)"
override STRUCTFLAGS += --build-dir="$(BUILDDIR:/=)"
override COVERAGEFLAGS += --build-dir="$(BUILDDIR:/=)"
endif
ifneq ($(NM),nm)
override CODEFLAGS += --nm-tool="$(NM)"
@ -119,6 +136,17 @@ test: test-runner
test-list: test-runner
./scripts/test.py $(BUILDDIR)runners/test_runner $(TESTFLAGS) -l
.PHONY: bench-runner build-bench
bench-runner build-bench: $(BUILDDIR)runners/bench_runner
.PHONY: bench
bench: bench-runner
./scripts/bench.py $(BUILDDIR)runners/bench_runner $(BENCHFLAGS)
.PHONY: bench-list
bench-list: bench-runner
./scripts/bench.py $(BUILDDIR)runners/bench_runner $(BENCHFLAGS) -l
.PHONY: code
code: $(OBJ)
./scripts/code.py $^ -S $(CODEFLAGS)
@ -186,6 +214,9 @@ $(BUILDDIR)lfs.csv: \
$(BUILDDIR)runners/test_runner: $(TEST_OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
$(BUILDDIR)runners/bench_runner: $(BENCH_OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
# our main build rule generates .o, .d, and .ci files, the latter
# used for stack analysis
$(BUILDDIR)%.o $(BUILDDIR)%.ci: %.c
@ -206,6 +237,12 @@ $(BUILDDIR)%.t.c: %.toml
$(BUILDDIR)%.t.c: %.c $(TESTS)
./scripts/test.py -c $(TESTS) -s $< $(TESTCFLAGS) -o $@
$(BUILDDIR)%.b.c: %.toml
./scripts/bench.py -c $< $(BENCHCFLAGS) -o $@
$(BUILDDIR)%.b.c: %.c $(BENCHES)
./scripts/bench.py -c $(BENCHES) -s $< $(BENCHCFLAGS) -o $@
# clean everything
.PHONY: clean
clean:
@ -219,6 +256,7 @@ clean:
$(BUILDDIR)lfs.struct.csv \
$(BUILDDIR)lfs.coverage.csv)
rm -f $(BUILDDIR)runners/test_runner
rm -f $(BUILDDIR)runners/bench_runner
rm -f $(OBJ)
rm -f $(DEP)
rm -f $(ASM)
@ -230,3 +268,10 @@ clean:
rm -f $(TEST_CI)
rm -f $(TEST_GCNO)
rm -f $(TEST_GCDA)
rm -f $(BENCH_BC)
rm -f $(BENCH_BAC)
rm -f $(BENCH_OBJ)
rm -f $(BENCH_DEP)
rm -f $(BENCH_CI)
rm -f $(BENCH_GCNO)
rm -f $(BENCH_GCDA)

View File

@ -1,5 +1,5 @@
/*
* Testing block device, wraps filebd and rambd while providing a bunch
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2022, The littlefs authors.
@ -11,7 +11,7 @@
#define _POSIX_C_SOURCE 199309L
#endif
#include "bd/lfs_testbd.h"
#include "bd/lfs_emubd.h"
#include <stdlib.h>
#include <fcntl.h>
@ -29,14 +29,14 @@
// Note we can only modify a block if we have exclusive access to it (rc == 1)
//
static lfs_testbd_block_t *lfs_testbd_incblock(lfs_testbd_block_t *block) {
static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
if (block) {
block->rc += 1;
}
return block;
}
static void lfs_testbd_decblock(lfs_testbd_block_t *block) {
static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
if (block) {
block->rc -= 1;
if (block->rc == 0) {
@ -45,34 +45,34 @@ static void lfs_testbd_decblock(lfs_testbd_block_t *block) {
}
}
static lfs_testbd_block_t *lfs_testbd_mutblock(
static lfs_emubd_block_t *lfs_emubd_mutblock(
const struct lfs_config *cfg,
lfs_testbd_block_t **block) {
lfs_testbd_block_t *block_ = *block;
lfs_emubd_block_t **block) {
lfs_emubd_block_t *block_ = *block;
if (block_ && block_->rc == 1) {
// rc == 1? can modify
return block_;
} else if (block_) {
// rc > 1? need to create a copy
lfs_testbd_block_t *nblock = malloc(
sizeof(lfs_testbd_block_t) + cfg->block_size);
lfs_emubd_block_t *nblock = malloc(
sizeof(lfs_emubd_block_t) + cfg->block_size);
if (!nblock) {
return NULL;
}
memcpy(nblock, block_,
sizeof(lfs_testbd_block_t) + cfg->block_size);
sizeof(lfs_emubd_block_t) + cfg->block_size);
nblock->rc = 1;
lfs_testbd_decblock(block_);
lfs_emubd_decblock(block_);
*block = nblock;
return nblock;
} else {
// no block? need to allocate
lfs_testbd_block_t *nblock = malloc(
sizeof(lfs_testbd_block_t) + cfg->block_size);
lfs_emubd_block_t *nblock = malloc(
sizeof(lfs_emubd_block_t) + cfg->block_size);
if (!nblock) {
return NULL;
}
@ -81,7 +81,7 @@ static lfs_testbd_block_t *lfs_testbd_mutblock(
nblock->wear = 0;
// zero for consistency
lfs_testbd_t *bd = cfg->context;
lfs_emubd_t *bd = cfg->context;
memset(nblock->data,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
cfg->block_size);
@ -92,11 +92,11 @@ static lfs_testbd_block_t *lfs_testbd_mutblock(
}
// testbd create/destroy
// emubd create/destroy
int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_testbd_config *bdcfg) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, "
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_emubd_config *bdcfg) {
LFS_EMUBD_TRACE("lfs_emubd_createcfg(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@ -113,25 +113,28 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
bdcfg->powerloss_data, bdcfg->track_branches);
lfs_testbd_t *bd = cfg->context;
lfs_emubd_t *bd = cfg->context;
bd->cfg = bdcfg;
// allocate our block array, all blocks start as uninitialized
bd->blocks = malloc(cfg->block_count * sizeof(lfs_testbd_block_t*));
bd->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
if (!bd->blocks) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_testbd_block_t*));
memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_emubd_block_t*));
// setup testing things
bd->read = 0;
bd->prog = 0;
bd->erased = 0;
bd->power_cycles = bd->cfg->power_cycles;
bd->disk = NULL;
if (bd->cfg->disk_path) {
bd->disk = malloc(sizeof(lfs_testbd_disk_t));
bd->disk = malloc(sizeof(lfs_emubd_disk_t));
if (!bd->disk) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
bd->disk->rc = 1;
@ -146,7 +149,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
#endif
if (bd->disk->fd < 0) {
int err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
return err;
}
@ -155,7 +158,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
if (bd->cfg->erase_value != -1) {
bd->disk->scratch = malloc(cfg->block_size);
if (!bd->disk->scratch) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
memset(bd->disk->scratch,
@ -164,12 +167,12 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
}
}
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", 0);
return 0;
}
int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, "
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
@ -179,19 +182,19 @@ int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path);
static const struct lfs_testbd_config defaults = {.erase_value=-1};
int err = lfs_testbd_createcfg(cfg, path, &defaults);
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
static const struct lfs_emubd_config defaults = {.erase_value=-1};
int err = lfs_emubd_createcfg(cfg, path, &defaults);
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
return err;
}
int lfs_testbd_destroy(const struct lfs_config *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg);
lfs_testbd_t *bd = cfg->context;
int lfs_emubd_destroy(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
// decrement reference counts
for (lfs_block_t i = 0; i < cfg->block_count; i++) {
lfs_testbd_decblock(bd->blocks[i]);
lfs_emubd_decblock(bd->blocks[i]);
}
free(bd->blocks);
@ -205,7 +208,7 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) {
}
}
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
return 0;
}
@ -213,12 +216,12 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) {
// block device API
int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_read(%p, "
LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context;
lfs_emubd_t *bd = cfg->context;
// check if read is valid
LFS_ASSERT(block < cfg->block_count);
@ -227,12 +230,12 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
LFS_ASSERT(off+size <= cfg->block_size);
// get the block
const lfs_testbd_block_t *b = bd->blocks[block];
const lfs_emubd_block_t *b = bd->blocks[block];
if (b) {
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
}
@ -243,8 +246,10 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
memset(buffer,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
size);
}
}
// track reads
bd->read += size;
if (bd->cfg->read_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->read_sleep/1000000000,
@ -252,21 +257,21 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
return err;
}
}
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
return 0;
}
int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_prog(%p, "
LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context;
lfs_emubd_t *bd = cfg->context;
// check if write is valid
LFS_ASSERT(block < cfg->block_count);
@ -275,23 +280,23 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
LFS_ASSERT(off+size <= cfg->block_size);
// get the block
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
// block bad?
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGERROR) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
LFS_EMUBD_BADBLOCK_PROGERROR) {
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGNOOP ||
LFS_EMUBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
LFS_EMUBD_BADBLOCK_ERASENOOP) {
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 0;
}
}
@ -313,18 +318,20 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
ssize_t res2 = write(bd->disk->fd, buffer, size);
if (res2 < 0) {
int err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
}
// track progs
bd->prog += size;
if (bd->cfg->prog_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->prog_sleep/1000000000,
@ -332,7 +339,7 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
return err;
}
}
@ -346,21 +353,21 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
}
}
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 0;
}
int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context;
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
lfs_emubd_t *bd = cfg->context;
// check if erase is valid
LFS_ASSERT(block < cfg->block_count);
// get the block
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
@ -368,12 +375,12 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
if (bd->cfg->erase_cycles) {
if (b->wear >= bd->cfg->erase_cycles) {
if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASEERROR) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
LFS_EMUBD_BADBLOCK_ERASEERROR) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
LFS_EMUBD_BADBLOCK_ERASENOOP) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
return 0;
}
} else {
@ -393,7 +400,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
SEEK_SET);
if (res1 < 0) {
int err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
@ -402,12 +409,14 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
cfg->block_size);
if (res2 < 0) {
int err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
}
}
// track erases
bd->erased += cfg->block_size;
if (bd->cfg->erase_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->erase_sleep/1000000000,
@ -415,7 +424,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
return err;
}
}
@ -429,98 +438,142 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
}
}
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 0;
}
int lfs_testbd_sync(const struct lfs_config *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg);
int lfs_emubd_sync(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
// do nothing
(void)cfg;
LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
return 0;
}
/// Additional extended API for driving test features ///
// simulated wear operations
lfs_emubd_sio_t lfs_emubd_getread(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_getread(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_getread -> %"PRIu64, bd->read);
return bd->read;
}
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
lfs_emubd_sio_t lfs_emubd_getprog(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_getprog(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_getprog -> %"PRIu64, bd->prog);
return bd->prog;
}
lfs_emubd_sio_t lfs_emubd_geterased(const struct lfs_config *cfg) {
LFS_EMUBD_TRACE("lfs_emubd_geterased(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_geterased -> %"PRIu64, bd->erased);
return bd->erased;
}
int lfs_emubd_setread(const struct lfs_config *cfg, lfs_emubd_io_t read) {
LFS_EMUBD_TRACE("lfs_emubd_setread(%p, %"PRIu64")", (void*)cfg, read);
lfs_emubd_t *bd = cfg->context;
bd->read = read;
LFS_EMUBD_TRACE("lfs_emubd_setread -> %d", 0);
return 0;
}
int lfs_emubd_setprog(const struct lfs_config *cfg, lfs_emubd_io_t prog) {
LFS_EMUBD_TRACE("lfs_emubd_setprog(%p, %"PRIu64")", (void*)cfg, prog);
lfs_emubd_t *bd = cfg->context;
bd->prog = prog;
LFS_EMUBD_TRACE("lfs_emubd_setprog -> %d", 0);
return 0;
}
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
lfs_emubd_t *bd = cfg->context;
bd->erased = erased;
LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
return 0;
}
lfs_emubd_swear_t lfs_emubd_getwear(const struct lfs_config *cfg,
lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_getwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_emubd_t *bd = cfg->context;
// check if block is valid
LFS_ASSERT(block < cfg->block_count);
// get the wear
lfs_testbd_wear_t wear;
const lfs_testbd_block_t *b = bd->blocks[block];
lfs_emubd_wear_t wear;
const lfs_emubd_block_t *b = bd->blocks[block];
if (b) {
wear = b->wear;
} else {
wear = 0;
}
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, wear);
LFS_EMUBD_TRACE("lfs_emubd_getwear -> %"PRIu32, wear);
return wear;
}
int lfs_testbd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_testbd_wear_t wear) {
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_testbd_t *bd = cfg->context;
int lfs_emubd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_emubd_wear_t wear) {
LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32")", (void*)cfg, block);
lfs_emubd_t *bd = cfg->context;
// check if block is valid
LFS_ASSERT(block < cfg->block_count);
// set the wear
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
if (!b) {
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %"PRIu32, LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
b->wear = wear;
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %"PRIu32, 0);
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, 0);
return 0;
}
lfs_testbd_spowercycles_t lfs_testbd_getpowercycles(
lfs_emubd_spowercycles_t lfs_emubd_getpowercycles(
const struct lfs_config *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles(%p)", (void*)cfg);
lfs_testbd_t *bd = cfg->context;
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles(%p)", (void*)cfg);
lfs_emubd_t *bd = cfg->context;
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles -> %"PRIi32, bd->power_cycles);
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles -> %"PRIi32, bd->power_cycles);
return bd->power_cycles;
}
int lfs_testbd_setpowercycles(const struct lfs_config *cfg,
lfs_testbd_powercycles_t power_cycles) {
LFS_TESTBD_TRACE("lfs_testbd_setpowercycles(%p, %"PRIi32")",
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
lfs_emubd_powercycles_t power_cycles) {
LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
(void*)cfg, power_cycles);
lfs_testbd_t *bd = cfg->context;
lfs_emubd_t *bd = cfg->context;
bd->power_cycles = power_cycles;
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles -> %d", 0);
return 0;
}
int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy) {
LFS_TESTBD_TRACE("lfs_testbd_copy(%p, %p)", (void*)cfg, (void*)copy);
lfs_testbd_t *bd = cfg->context;
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
lfs_emubd_t *bd = cfg->context;
// lazily copy over our block array
copy->blocks = malloc(cfg->block_count * sizeof(lfs_testbd_block_t*));
copy->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
if (!copy->blocks) {
LFS_TESTBD_TRACE("lfs_testbd_copy -> %d", LFS_ERR_NOMEM);
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM;
}
for (size_t i = 0; i < cfg->block_count; i++) {
copy->blocks[i] = lfs_testbd_incblock(bd->blocks[i]);
copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
}
// other state
@ -531,7 +584,7 @@ int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy) {
}
copy->cfg = bd->cfg;
LFS_TESTBD_TRACE("lfs_testbd_copy -> %d", 0);
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
return 0;
}

View File

@ -1,13 +1,13 @@
/*
* Testing block device, wraps filebd and rambd while providing a bunch
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS_TESTBD_H
#define LFS_TESTBD_H
#ifndef LFS_EMUBD_H
#define LFS_EMUBD_H
#include "lfs.h"
#include "lfs_util.h"
@ -21,11 +21,11 @@ extern "C"
// Block device specific tracing
#ifndef LFS_TESTBD_TRACE
#ifdef LFS_TESTBD_YES_TRACE
#define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#ifndef LFS_EMUBD_TRACE
#ifdef LFS_EMUBD_YES_TRACE
#define LFS_EMUBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
#else
#define LFS_TESTBD_TRACE(...)
#define LFS_EMUBD_TRACE(...)
#endif
#endif
@ -35,34 +35,38 @@ extern "C"
//
// Not that read-noop is not allowed. Read _must_ return a consistent (but
// may be arbitrary) value on every read.
typedef enum lfs_testbd_badblock_behavior {
LFS_TESTBD_BADBLOCK_PROGERROR,
LFS_TESTBD_BADBLOCK_ERASEERROR,
LFS_TESTBD_BADBLOCK_READERROR,
LFS_TESTBD_BADBLOCK_PROGNOOP,
LFS_TESTBD_BADBLOCK_ERASENOOP,
} lfs_testbd_badblock_behavior_t;
typedef enum lfs_emubd_badblock_behavior {
LFS_EMUBD_BADBLOCK_PROGERROR,
LFS_EMUBD_BADBLOCK_ERASEERROR,
LFS_EMUBD_BADBLOCK_READERROR,
LFS_EMUBD_BADBLOCK_PROGNOOP,
LFS_EMUBD_BADBLOCK_ERASENOOP,
} lfs_emubd_badblock_behavior_t;
// Mode determining how power-loss behaves during testing. For now this
// only supports a noop behavior, leaving the data on-disk untouched.
typedef enum lfs_testbd_powerloss_behavior {
LFS_TESTBD_POWERLOSS_NOOP,
} lfs_testbd_powerloss_behavior_t;
typedef enum lfs_emubd_powerloss_behavior {
LFS_EMUBD_POWERLOSS_NOOP,
} lfs_emubd_powerloss_behavior_t;
// Type for measuring read/program/erase operations
typedef uint64_t lfs_emubd_io_t;
typedef int64_t lfs_emubd_sio_t;
// Type for measuring wear
typedef uint32_t lfs_testbd_wear_t;
typedef int32_t lfs_testbd_swear_t;
typedef uint32_t lfs_emubd_wear_t;
typedef int32_t lfs_emubd_swear_t;
// Type for tracking power-cycles
typedef uint32_t lfs_testbd_powercycles_t;
typedef int32_t lfs_testbd_spowercycles_t;
typedef uint32_t lfs_emubd_powercycles_t;
typedef int32_t lfs_emubd_spowercycles_t;
// Type for delays in nanoseconds
typedef uint64_t lfs_testbd_sleep_t;
typedef int64_t lfs_testbd_ssleep_t;
typedef uint64_t lfs_emubd_sleep_t;
typedef int64_t lfs_emubd_ssleep_t;
// testbd config, this is required for testing
struct lfs_testbd_config {
// emubd config, this is required for testing
struct lfs_emubd_config {
// 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding the extra block-device
// operations to store the erase value.
@ -73,15 +77,15 @@ struct lfs_testbd_config {
uint32_t erase_cycles;
// The mode determining how bad-blocks fail
lfs_testbd_badblock_behavior_t badblock_behavior;
lfs_emubd_badblock_behavior_t badblock_behavior;
// Number of write operations (erase/prog) before triggering a power-loss.
// power_cycles=0 disables this. The exact behavior of power-loss is
// controlled by a combination of powerloss_behavior and powerloss_cb.
lfs_testbd_powercycles_t power_cycles;
lfs_emubd_powercycles_t power_cycles;
// The mode determining how power-loss affects disk
lfs_testbd_powerloss_behavior_t powerloss_behavior;
lfs_emubd_powerloss_behavior_t powerloss_behavior;
// Function to call to emulate power-loss. The exact behavior of power-loss
// is up to the runner to provide.
@ -100,98 +104,119 @@ struct lfs_testbd_config {
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_sleep_t read_sleep;
lfs_emubd_sleep_t read_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_sleep_t prog_sleep;
lfs_emubd_sleep_t prog_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_sleep_t erase_sleep;
lfs_emubd_sleep_t erase_sleep;
};
// A reference counted block
typedef struct lfs_testbd_block {
typedef struct lfs_emubd_block {
uint32_t rc;
lfs_testbd_wear_t wear;
lfs_emubd_wear_t wear;
uint8_t data[];
} lfs_testbd_block_t;
} lfs_emubd_block_t;
// Disk mirror
typedef struct lfs_testbd_disk {
typedef struct lfs_emubd_disk {
uint32_t rc;
int fd;
uint8_t *scratch;
} lfs_testbd_disk_t;
} lfs_emubd_disk_t;
// testbd state
typedef struct lfs_testbd {
// emubd state
typedef struct lfs_emubd {
// array of copy-on-write blocks
lfs_testbd_block_t **blocks;
lfs_emubd_block_t **blocks;
// some other test state
uint32_t power_cycles;
lfs_testbd_disk_t *disk;
lfs_emubd_io_t read;
lfs_emubd_io_t prog;
lfs_emubd_io_t erased;
lfs_emubd_powercycles_t power_cycles;
lfs_emubd_disk_t *disk;
const struct lfs_testbd_config *cfg;
} lfs_testbd_t;
const struct lfs_emubd_config *cfg;
} lfs_emubd_t;
/// Block device API ///
// Create a test block device using the geometry in lfs_config
// Create an emulating block device using the geometry in lfs_config
//
// Note that filebd is used if a path is provided, if path is NULL
// testbd will use rambd which can be much faster.
int lfs_testbd_create(const struct lfs_config *cfg, const char *path);
int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_testbd_config *bdcfg);
// emubd will use rambd which can be much faster.
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
const struct lfs_emubd_config *bdcfg);
// Clean up memory associated with block device
int lfs_testbd_destroy(const struct lfs_config *cfg);
int lfs_emubd_destroy(const struct lfs_config *cfg);
// Read a block
int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block);
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
// Sync the block device
int lfs_testbd_sync(const struct lfs_config *cfg);
int lfs_emubd_sync(const struct lfs_config *cfg);
/// Additional extended API for driving test features ///
// Get total amount of bytes read
lfs_emubd_sio_t lfs_emubd_getread(const struct lfs_config *cfg);
// Get total amount of bytes programmed
lfs_emubd_sio_t lfs_emubd_getprog(const struct lfs_config *cfg);
// Get total amount of bytes erased
lfs_emubd_sio_t lfs_emubd_geterased(const struct lfs_config *cfg);
// Manually set amount of bytes read
int lfs_emubd_setread(const struct lfs_config *cfg, lfs_emubd_io_t read);
// Manually set amount of bytes programmed
int lfs_emubd_setprog(const struct lfs_config *cfg, lfs_emubd_io_t prog);
// Manually set amount of bytes erased
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased);
// Get simulated wear on a given block
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
lfs_emubd_swear_t lfs_emubd_getwear(const struct lfs_config *cfg,
lfs_block_t block);
// Manually set simulated wear on a given block
int lfs_testbd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_testbd_wear_t wear);
int lfs_emubd_setwear(const struct lfs_config *cfg,
lfs_block_t block, lfs_emubd_wear_t wear);
// Get the remaining power-cycles
lfs_testbd_spowercycles_t lfs_testbd_getpowercycles(
lfs_emubd_spowercycles_t lfs_emubd_getpowercycles(
const struct lfs_config *cfg);
// Manually set the remaining power-cycles
int lfs_testbd_setpowercycles(const struct lfs_config *cfg,
lfs_testbd_powercycles_t power_cycles);
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
lfs_emubd_powercycles_t power_cycles);
// Create a copy-on-write copy of the state of this block device
int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy);
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy);
#ifdef __cplusplus

284
benches/bench_dir.toml Normal file
View File

@ -0,0 +1,284 @@
# deterministic prng
code = '''
static uint32_t xorshift32(uint32_t *state) {
uint32_t x = *state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
*state = x;
return x;
}
'''
[cases.bench_dir_open]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = xorshift32(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
}
// then read the files
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: xorshift32(&prng) % N;
sprintf(name, "file%08x", i_);
lfs_file_t file;
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
uint32_t file_prng = i_;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
lfs_file_read(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
assert(buffer[k] == xorshift32(&file_prng));
}
}
lfs_file_close(&lfs, &file) => 0;
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_dir_creat]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
BENCH_START();
uint32_t prng = 42;
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: xorshift32(&prng) % N;
sprintf(name, "file%08x", i_);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
uint32_t file_prng = i_;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = xorshift32(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_dir_remove]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = xorshift32(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
}
// then remove the files
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: xorshift32(&prng) % N;
sprintf(name, "file%08x", i_);
int err = lfs_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOENT);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_dir_read]
defines.N = 1024
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
// first create the files
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint32_t file_prng = i;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = xorshift32(&file_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
}
// then read the directory
BENCH_START();
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (int i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_dir_mkdir]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.N = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
BENCH_START();
uint32_t prng = 42;
char name[256];
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: xorshift32(&prng) % N;
printf("hm %d\n", i);
sprintf(name, "dir%08x", i_);
int err = lfs_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_dir_rmdir]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.N = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
// first create the dirs
char name[256];
for (lfs_size_t i = 0; i < N; i++) {
sprintf(name, "dir%08x", i);
lfs_mkdir(&lfs, name) => 0;
}
// then remove the dirs
BENCH_START();
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (N-1-i)
: xorshift32(&prng) % N;
sprintf(name, "dir%08x", i_);
int err = lfs_remove(&lfs, name);
assert(!err || err == LFS_ERR_NOENT);
}
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''

109
benches/bench_file.toml Normal file
View File

@ -0,0 +1,109 @@
# deterministic prng
code = '''
static uint32_t xorshift32(uint32_t *state) {
uint32_t x = *state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
*state = x;
return x;
}
'''
[cases.bench_file_read]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.SIZE = '128*1024'
defines.CHUNK_SIZE = 64
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
// first write the file
lfs_file_t file;
uint8_t buffer[CHUNK_SIZE];
lfs_file_open(&lfs, &file, "file",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t i = 0; i < chunks; i++) {
uint32_t chunk_prng = i;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
buffer[j] = xorshift32(&chunk_prng);
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
lfs_file_close(&lfs, &file) => 0;
// then read the file
BENCH_START();
lfs_file_open(&lfs, &file, "file", LFS_O_RDONLY) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < chunks; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (chunks-1-i)
: xorshift32(&prng) % chunks;
lfs_file_seek(&lfs, &file, i_*CHUNK_SIZE, LFS_SEEK_SET)
=> i_*CHUNK_SIZE;
lfs_file_read(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
uint32_t chunk_prng = i_;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
assert(buffer[j] == xorshift32(&chunk_prng));
}
}
lfs_file_close(&lfs, &file) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_file_write]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.SIZE = '128*1024'
defines.CHUNK_SIZE = 64
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_size_t chunks = (SIZE+CHUNK_SIZE-1)/CHUNK_SIZE;
BENCH_START();
lfs_file_t file;
lfs_file_open(&lfs, &file, "file",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t buffer[CHUNK_SIZE];
uint32_t prng = 42;
for (lfs_size_t i = 0; i < chunks; i++) {
lfs_off_t i_
= (ORDER == 0) ? i
: (ORDER == 1) ? (chunks-1-i)
: xorshift32(&prng) % chunks;
uint32_t chunk_prng = i_;
for (lfs_size_t j = 0; j < CHUNK_SIZE; j++) {
buffer[j] = xorshift32(&chunk_prng);
}
lfs_file_seek(&lfs, &file, i_*CHUNK_SIZE, LFS_SEEK_SET)
=> i_*CHUNK_SIZE;
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''

View File

@ -0,0 +1,56 @@
[cases.bench_superblocks_found]
# support benchmarking with files
defines.N = [0, 1024]
defines.FILE_SIZE = 8
defines.CHUNK_SIZE = 8
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// create files?
lfs_mount(&lfs, cfg) => 0;
char name[256];
uint8_t buffer[CHUNK_SIZE];
for (lfs_size_t i = 0; i < N; i++) {
sprintf(name, "file%08x", i);
lfs_file_t file;
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < FILE_SIZE; j += CHUNK_SIZE) {
for (lfs_size_t k = 0; k < CHUNK_SIZE; k++) {
buffer[k] = i+j+k;
}
lfs_file_write(&lfs, &file, buffer, CHUNK_SIZE) => CHUNK_SIZE;
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
BENCH_START();
lfs_mount(&lfs, cfg) => 0;
BENCH_STOP();
lfs_unmount(&lfs) => 0;
'''
[cases.bench_superblocks_missing]
code = '''
lfs_t lfs;
BENCH_START();
int err = lfs_mount(&lfs, cfg);
assert(err != 0);
BENCH_STOP();
'''
[cases.bench_superblocks_format]
code = '''
lfs_t lfs;
BENCH_START();
lfs_format(&lfs, cfg) => 0;
BENCH_STOP();
'''

1795
runners/bench_runner.c Normal file

File diff suppressed because it is too large Load Diff

119
runners/bench_runner.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef BENCH_RUNNER_H
#define BENCH_RUNNER_H
// override LFS_TRACE
void bench_trace(const char *fmt, ...);
#define LFS_TRACE_(fmt, ...) \
bench_trace("%s:%d:trace: " fmt "%s\n", \
__FILE__, \
__LINE__, \
__VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS_EMUBD_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
// provide BENCH_START/BENCH_STOP macros
void bench_start(void);
void bench_stop(void);
#define BENCH_START() bench_start()
#define BENCH_STOP() bench_stop()
// note these are indirectly included in any generated files
#include "bd/lfs_emubd.h"
#include <stdio.h>
// give source a chance to define feature macros
#undef _FEATURES_H
#undef _STDIO_H
// generated bench configurations
struct lfs_config;
enum bench_flags {
BENCH_REENTRANT = 0x1,
};
typedef uint8_t bench_flags_t;
typedef struct bench_define {
intmax_t (*cb)(void *data);
void *data;
} bench_define_t;
struct bench_case {
const char *name;
const char *path;
bench_flags_t flags;
size_t permutations;
const bench_define_t *defines;
bool (*filter)(void);
void (*run)(struct lfs_config *cfg);
};
struct bench_suite {
const char *name;
const char *path;
bench_flags_t flags;
const char *const *define_names;
size_t define_count;
const struct bench_case *cases;
size_t case_count;
};
// access generated bench defines
intmax_t bench_define(size_t define);
#define BENCH_DEFINE(i) bench_define(i)
// a few preconfigured defines that control how benches run
#define READ_SIZE_i 0
#define PROG_SIZE_i 1
#define BLOCK_SIZE_i 2
#define BLOCK_COUNT_i 3
#define CACHE_SIZE_i 4
#define LOOKAHEAD_SIZE_i 5
#define BLOCK_CYCLES_i 6
#define ERASE_VALUE_i 7
#define ERASE_CYCLES_i 8
#define BADBLOCK_BEHAVIOR_i 9
#define POWERLOSS_BEHAVIOR_i 10
#define READ_SIZE bench_define(READ_SIZE_i)
#define PROG_SIZE bench_define(PROG_SIZE_i)
#define BLOCK_SIZE bench_define(BLOCK_SIZE_i)
#define BLOCK_COUNT bench_define(BLOCK_COUNT_i)
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
#define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i)
#define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i)
#define ERASE_VALUE bench_define(ERASE_VALUE_i)
#define ERASE_CYCLES bench_define(ERASE_CYCLES_i)
#define BADBLOCK_BEHAVIOR bench_define(BADBLOCK_BEHAVIOR_i)
#define POWERLOSS_BEHAVIOR bench_define(POWERLOSS_BEHAVIOR_i)
#define BENCH_IMPLICIT_DEFINES \
BENCH_DEF(READ_SIZE, PROG_SIZE) \
BENCH_DEF(PROG_SIZE, BLOCK_SIZE) \
BENCH_DEF(BLOCK_SIZE, 0) \
BENCH_DEF(BLOCK_COUNT, (1024*1024)/BLOCK_SIZE) \
BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
BENCH_DEF(LOOKAHEAD_SIZE, 16) \
BENCH_DEF(BLOCK_CYCLES, -1) \
BENCH_DEF(ERASE_VALUE, 0xff) \
BENCH_DEF(ERASE_CYCLES, 0) \
BENCH_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
BENCH_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
#define BENCH_GEOMETRY_DEFINE_COUNT 4
#define BENCH_IMPLICIT_DEFINE_COUNT 11
#endif

View File

@ -4,7 +4,7 @@
#endif
#include "runners/test_runner.h"
#include "bd/lfs_testbd.h"
#include "bd/lfs_emubd.h"
#include <getopt.h>
#include <sys/types.h>
@ -46,7 +46,7 @@ void *mappend(void **p,
// a quick self-terminating text-safe varint scheme
static void leb16_print(uintmax_t x) {
while (true) {
lfs_testbd_powercycles_t nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0);
char nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0);
printf("%c", (nibble < 10) ? '0'+nibble : 'a'+nibble-10);
if (x <= 0xf) {
break;
@ -101,11 +101,11 @@ typedef struct test_powerloss {
const char *long_name;
void (*run)(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
const lfs_testbd_powercycles_t *cycles;
const lfs_emubd_powercycles_t *cycles;
size_t cycle_count;
} test_powerloss_t;
@ -113,7 +113,7 @@ typedef struct test_id {
const char *name;
const test_define_t *defines;
size_t define_count;
const lfs_testbd_powercycles_t *cycles;
const lfs_emubd_powercycles_t *cycles;
size_t cycle_count;
} test_id_t;
@ -141,17 +141,19 @@ typedef struct test_define_names {
intmax_t test_define_lit(void *data) {
return (intmax_t)data;
}
#define TEST_LIT(x) {test_define_lit, (void*)(uintptr_t)(x)}
#define TEST_CONST(x) {test_define_lit, (void*)(uintptr_t)(x)}
#define TEST_LIT(x) ((test_define_t)TEST_CONST(x))
#define TEST_DEFINE(k, v) \
#define TEST_DEF(k, v) \
intmax_t test_define_##k(void *data) { \
(void)data; \
return v; \
}
TEST_IMPLICIT_DEFINES
#undef TEST_DEFINE
#undef TEST_DEF
#define TEST_DEFINE_MAP_EXPLICIT 0
#define TEST_DEFINE_MAP_OVERRIDE 1
@ -163,11 +165,11 @@ intmax_t test_define_lit(void *data) {
test_define_map_t test_define_maps[TEST_DEFINE_MAP_COUNT] = {
[TEST_DEFINE_MAP_IMPLICIT] = {
(const test_define_t[TEST_IMPLICIT_DEFINE_COUNT]) {
#define TEST_DEFINE(k, v) \
#define TEST_DEF(k, v) \
[k##_i] = {test_define_##k, NULL},
TEST_IMPLICIT_DEFINES
#undef TEST_DEFINE
#undef TEST_DEF
},
TEST_IMPLICIT_DEFINE_COUNT,
},
@ -180,11 +182,11 @@ test_define_map_t test_define_maps[TEST_DEFINE_MAP_COUNT] = {
test_define_names_t test_define_names[TEST_DEFINE_NAMES_COUNT] = {
[TEST_DEFINE_NAMES_IMPLICIT] = {
(const char *const[TEST_IMPLICIT_DEFINE_COUNT]){
#define TEST_DEFINE(k, v) \
#define TEST_DEF(k, v) \
[k##_i] = #k,
TEST_IMPLICIT_DEFINES
#undef TEST_DEFINE
#undef TEST_DEF
},
TEST_IMPLICIT_DEFINE_COUNT,
},
@ -318,7 +320,7 @@ void test_define_suite(const struct test_suite *suite) {
// define name match?
const char *name = test_define_name(d);
if (name && strcmp(name, test_overrides[i].name) == 0) {
count = d+1;
count = lfs_max(count, d+1);
permutations *= test_overrides[i].permutations;
break;
}
@ -355,10 +357,9 @@ void test_define_suite(const struct test_suite *suite) {
// scatter the define permutations based on already
// seen permutations
for (size_t j = 0; j < permutations; j++) {
test_override_defines[j*count + d]
= (test_define_t)TEST_LIT(
test_overrides[i].defines[(j/p)
% test_overrides[i].permutations]);
test_override_defines[j*count + d] = TEST_LIT(
test_overrides[i].defines[(j/p)
% test_overrides[i].permutations]);
}
// keep track of how many permutations we've seen so far
@ -426,9 +427,9 @@ const char *test_disk_path = NULL;
const char *test_trace_path = NULL;
FILE *test_trace_file = NULL;
uint32_t test_trace_cycles = 0;
lfs_testbd_sleep_t test_read_sleep = 0.0;
lfs_testbd_sleep_t test_prog_sleep = 0.0;
lfs_testbd_sleep_t test_erase_sleep = 0.0;
lfs_emubd_sleep_t test_read_sleep = 0.0;
lfs_emubd_sleep_t test_prog_sleep = 0.0;
lfs_emubd_sleep_t test_erase_sleep = 0.0;
// trace printing
@ -485,10 +486,10 @@ void test_trace(const char *fmt, ...) {
static void perm_printid(
const struct test_suite *suite,
const struct test_case *case_,
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count) {
(void)suite;
// case[:permutation[:powercycles]]]
// case[:permutation[:powercycles]]
printf("%s:", case_->name);
for (size_t d = 0;
d < lfs_max(
@ -497,7 +498,7 @@ static void perm_printid(
d++) {
if (test_define_ispermutation(d)) {
leb16_print(d);
leb16_print(test_define(d));
leb16_print(TEST_DEFINE(d));
}
}
@ -511,7 +512,7 @@ static void perm_printid(
}
static void run_powerloss_cycles(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
@ -522,7 +523,7 @@ static void case_forperm(
const struct test_case *case_,
const test_define_t *defines,
size_t define_count,
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
void (*cb)(
void *data,
@ -834,7 +835,7 @@ static void list_defines_add(
struct list_defines_defines *defines,
size_t d) {
const char *name = test_define_name(d);
intmax_t value = test_define(d);
intmax_t value = TEST_DEFINE(d);
// define already in defines?
for (size_t i = 0; i < defines->define_count; i++) {
@ -1051,11 +1052,11 @@ static void list_implicit_defines(void) {
// geometries to test
const test_geometry_t builtin_geometries[] = {
{'d', "default", {{NULL}, TEST_LIT(16), TEST_LIT(512), {NULL}}},
{'e', "eeprom", {{NULL}, TEST_LIT(1), TEST_LIT(512), {NULL}}},
{'E', "emmc", {{NULL}, {NULL}, TEST_LIT(512), {NULL}}},
{'n', "nor", {{NULL}, TEST_LIT(1), TEST_LIT(4096), {NULL}}},
{'N', "nand", {{NULL}, TEST_LIT(4096), TEST_LIT(32768), {NULL}}},
{'d', "default", {{NULL}, TEST_CONST(16), TEST_CONST(512), {NULL}}},
{'e', "eeprom", {{NULL}, TEST_CONST(1), TEST_CONST(512), {NULL}}},
{'E', "emmc", {{NULL}, {NULL}, TEST_CONST(512), {NULL}}},
{'n', "nor", {{NULL}, TEST_CONST(1), TEST_CONST(4096), {NULL}}},
{'N', "nand", {{NULL}, TEST_CONST(4096), TEST_CONST(32768), {NULL}}},
{0, NULL, {{NULL}, {NULL}, {NULL}, {NULL}}},
};
@ -1087,7 +1088,7 @@ static void list_geometries(void) {
// scenarios to run tests under power-loss
static void run_powerloss_none(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@ -1096,14 +1097,14 @@ static void run_powerloss_none(
(void)suite;
// create block device and configuration
lfs_testbd_t bd;
lfs_emubd_t bd;
struct lfs_config cfg = {
.context = &bd,
.read = lfs_testbd_read,
.prog = lfs_testbd_prog,
.erase = lfs_testbd_erase,
.sync = lfs_testbd_sync,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@ -1113,7 +1114,7 @@ static void run_powerloss_none(
.lookahead_size = LOOKAHEAD_SIZE,
};
struct lfs_testbd_config bdcfg = {
struct lfs_emubd_config bdcfg = {
.erase_value = ERASE_VALUE,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
@ -1123,7 +1124,7 @@ static void run_powerloss_none(
.erase_sleep = test_erase_sleep,
};
int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@ -1141,7 +1142,7 @@ static void run_powerloss_none(
printf("\n");
// cleanup
err = lfs_testbd_destroy(&cfg);
err = lfs_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@ -1154,7 +1155,7 @@ static void powerloss_longjmp(void *c) {
}
static void run_powerloss_linear(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@ -1163,16 +1164,16 @@ static void run_powerloss_linear(
(void)suite;
// create block device and configuration
lfs_testbd_t bd;
lfs_emubd_t bd;
jmp_buf powerloss_jmp;
volatile lfs_testbd_powercycles_t i = 1;
volatile lfs_emubd_powercycles_t i = 1;
struct lfs_config cfg = {
.context = &bd,
.read = lfs_testbd_read,
.prog = lfs_testbd_prog,
.erase = lfs_testbd_erase,
.sync = lfs_testbd_sync,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@ -1182,7 +1183,7 @@ static void run_powerloss_linear(
.lookahead_size = LOOKAHEAD_SIZE,
};
struct lfs_testbd_config bdcfg = {
struct lfs_emubd_config bdcfg = {
.erase_value = ERASE_VALUE,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
@ -1196,7 +1197,7 @@ static void run_powerloss_linear(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@ -1218,13 +1219,13 @@ static void run_powerloss_linear(
printf("powerloss ");
perm_printid(suite, case_, NULL, 0);
printf(":");
for (lfs_testbd_powercycles_t j = 1; j <= i; j++) {
for (lfs_emubd_powercycles_t j = 1; j <= i; j++) {
leb16_print(j);
}
printf("\n");
i += 1;
lfs_testbd_setpowercycles(&cfg, i);
lfs_emubd_setpowercycles(&cfg, i);
}
printf("finished ");
@ -1232,7 +1233,7 @@ static void run_powerloss_linear(
printf("\n");
// cleanup
err = lfs_testbd_destroy(&cfg);
err = lfs_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@ -1240,7 +1241,7 @@ static void run_powerloss_linear(
}
static void run_powerloss_exponential(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@ -1249,16 +1250,16 @@ static void run_powerloss_exponential(
(void)suite;
// create block device and configuration
lfs_testbd_t bd;
lfs_emubd_t bd;
jmp_buf powerloss_jmp;
volatile lfs_testbd_powercycles_t i = 1;
volatile lfs_emubd_powercycles_t i = 1;
struct lfs_config cfg = {
.context = &bd,
.read = lfs_testbd_read,
.prog = lfs_testbd_prog,
.erase = lfs_testbd_erase,
.sync = lfs_testbd_sync,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@ -1268,7 +1269,7 @@ static void run_powerloss_exponential(
.lookahead_size = LOOKAHEAD_SIZE,
};
struct lfs_testbd_config bdcfg = {
struct lfs_emubd_config bdcfg = {
.erase_value = ERASE_VALUE,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
@ -1282,7 +1283,7 @@ static void run_powerloss_exponential(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@ -1304,13 +1305,13 @@ static void run_powerloss_exponential(
printf("powerloss ");
perm_printid(suite, case_, NULL, 0);
printf(":");
for (lfs_testbd_powercycles_t j = 1; j <= i; j *= 2) {
for (lfs_emubd_powercycles_t j = 1; j <= i; j *= 2) {
leb16_print(j);
}
printf("\n");
i *= 2;
lfs_testbd_setpowercycles(&cfg, i);
lfs_emubd_setpowercycles(&cfg, i);
}
printf("finished ");
@ -1318,7 +1319,7 @@ static void run_powerloss_exponential(
printf("\n");
// cleanup
err = lfs_testbd_destroy(&cfg);
err = lfs_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@ -1326,23 +1327,23 @@ static void run_powerloss_exponential(
}
static void run_powerloss_cycles(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
(void)suite;
// create block device and configuration
lfs_testbd_t bd;
lfs_emubd_t bd;
jmp_buf powerloss_jmp;
volatile size_t i = 0;
struct lfs_config cfg = {
.context = &bd,
.read = lfs_testbd_read,
.prog = lfs_testbd_prog,
.erase = lfs_testbd_erase,
.sync = lfs_testbd_sync,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@ -1352,7 +1353,7 @@ static void run_powerloss_cycles(
.lookahead_size = LOOKAHEAD_SIZE,
};
struct lfs_testbd_config bdcfg = {
struct lfs_emubd_config bdcfg = {
.erase_value = ERASE_VALUE,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
@ -1366,7 +1367,7 @@ static void run_powerloss_cycles(
.powerloss_data = &powerloss_jmp,
};
int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@ -1391,7 +1392,7 @@ static void run_powerloss_cycles(
printf("\n");
i += 1;
lfs_testbd_setpowercycles(&cfg,
lfs_emubd_setpowercycles(&cfg,
(i < cycle_count) ? cycles[i] : 0);
}
@ -1400,7 +1401,7 @@ static void run_powerloss_cycles(
printf("\n");
// cleanup
err = lfs_testbd_destroy(&cfg);
err = lfs_emubd_destroy(&cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@ -1410,13 +1411,13 @@ static void run_powerloss_cycles(
struct powerloss_exhaustive_state {
struct lfs_config *cfg;
lfs_testbd_t *branches;
lfs_emubd_t *branches;
size_t branch_count;
size_t branch_capacity;
};
struct powerloss_exhaustive_cycles {
lfs_testbd_powercycles_t *cycles;
lfs_emubd_powercycles_t *cycles;
size_t cycle_count;
size_t cycle_capacity;
};
@ -1424,9 +1425,9 @@ struct powerloss_exhaustive_cycles {
static void powerloss_exhaustive_branch(void *c) {
struct powerloss_exhaustive_state *state = c;
// append to branches
lfs_testbd_t *branch = mappend(
lfs_emubd_t *branch = mappend(
(void**)&state->branches,
sizeof(lfs_testbd_t),
sizeof(lfs_emubd_t),
&state->branch_count,
&state->branch_capacity);
if (!branch) {
@ -1435,14 +1436,14 @@ static void powerloss_exhaustive_branch(void *c) {
}
// create copy-on-write copy
int err = lfs_testbd_copy(state->cfg, branch);
int err = lfs_emubd_copy(state->cfg, branch);
if (err) {
fprintf(stderr, "error: exhaustive: could not create bd copy\n");
exit(-1);
}
// also trigger on next power cycle
lfs_testbd_setpowercycles(state->cfg, 1);
lfs_emubd_setpowercycles(state->cfg, 1);
}
static void run_powerloss_exhaustive_layer(
@ -1450,7 +1451,7 @@ static void run_powerloss_exhaustive_layer(
const struct test_suite *suite,
const struct test_case *case_,
struct lfs_config *cfg,
struct lfs_testbd_config *bdcfg,
struct lfs_emubd_config *bdcfg,
size_t depth) {
(void)suite;
@ -1463,14 +1464,14 @@ static void run_powerloss_exhaustive_layer(
// run through the test without additional powerlosses, collecting possible
// branches as we do so
lfs_testbd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
lfs_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
bdcfg->powerloss_data = &state;
// run the tests
case_->run(cfg);
// aggressively clean up memory here to try to keep our memory usage low
int err = lfs_testbd_destroy(cfg);
int err = lfs_emubd_destroy(cfg);
if (err) {
fprintf(stderr, "error: could not destroy block device: %d\n", err);
exit(-1);
@ -1479,9 +1480,9 @@ static void run_powerloss_exhaustive_layer(
// recurse into each branch
for (size_t i = 0; i < state.branch_count; i++) {
// first push and print the branch
lfs_testbd_powercycles_t *cycle = mappend(
lfs_emubd_powercycles_t *cycle = mappend(
(void**)&cycles->cycles,
sizeof(lfs_testbd_powercycles_t),
sizeof(lfs_emubd_powercycles_t),
&cycles->cycle_count,
&cycles->cycle_capacity);
if (!cycle) {
@ -1509,7 +1510,7 @@ static void run_powerloss_exhaustive_layer(
}
static void run_powerloss_exhaustive(
const lfs_testbd_powercycles_t *cycles,
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_) {
@ -1517,14 +1518,14 @@ static void run_powerloss_exhaustive(
(void)suite;
// create block device and configuration
lfs_testbd_t bd;
lfs_emubd_t bd;
struct lfs_config cfg = {
.context = &bd,
.read = lfs_testbd_read,
.prog = lfs_testbd_prog,
.erase = lfs_testbd_erase,
.sync = lfs_testbd_sync,
.read = lfs_emubd_read,
.prog = lfs_emubd_prog,
.erase = lfs_emubd_erase,
.sync = lfs_emubd_sync,
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
@ -1534,7 +1535,7 @@ static void run_powerloss_exhaustive(
.lookahead_size = LOOKAHEAD_SIZE,
};
struct lfs_testbd_config bdcfg = {
struct lfs_emubd_config bdcfg = {
.erase_value = ERASE_VALUE,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
@ -1547,7 +1548,7 @@ static void run_powerloss_exhaustive(
.powerloss_data = NULL,
};
int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
int err = lfs_emubd_createcfg(&cfg, test_disk_path, &bdcfg);
if (err) {
fprintf(stderr, "error: could not create block device: %d\n", err);
exit(-1);
@ -1956,6 +1957,7 @@ int main(int argc, char **argv) {
if (parsed == optarg) {
goto invalid_define;
}
optarg = parsed + strspn(parsed, " ");
*(intmax_t*)mappend(
(void**)&override->defines,
sizeof(intmax_t),
@ -1965,7 +1967,6 @@ int main(int argc, char **argv) {
break;
}
optarg = parsed + strspn(parsed, " ");
if (*optarg == ',') {
optarg += 1;
}
@ -2041,24 +2042,24 @@ invalid_define:
// allow implicit r=p and p=e for common geometries
memset(geometry, 0, sizeof(test_geometry_t));
if (count >= 3) {
geometry->defines[0]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[1]
= (test_define_t)TEST_LIT(sizes[1]);
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[2]);
geometry->defines[READ_SIZE_i]
= TEST_LIT(sizes[0]);
geometry->defines[PROG_SIZE_i]
= TEST_LIT(sizes[1]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[2]);
} else if (count >= 2) {
geometry->defines[1]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[1]);
geometry->defines[PROG_SIZE_i]
= TEST_LIT(sizes[0]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[1]);
} else {
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[0]);
}
if (count >= 4) {
geometry->defines[3]
= (test_define_t)TEST_LIT(sizes[3]);
geometry->defines[BLOCK_COUNT_i]
= TEST_LIT(sizes[3]);
}
optarg = s;
goto geometry_next;
@ -2085,24 +2086,24 @@ invalid_define:
// allow implicit r=p and p=e for common geometries
memset(geometry, 0, sizeof(test_geometry_t));
if (count >= 3) {
geometry->defines[0]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[1]
= (test_define_t)TEST_LIT(sizes[1]);
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[2]);
geometry->defines[READ_SIZE_i]
= TEST_LIT(sizes[0]);
geometry->defines[PROG_SIZE_i]
= TEST_LIT(sizes[1]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[2]);
} else if (count >= 2) {
geometry->defines[1]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[1]);
geometry->defines[PROG_SIZE_i]
= TEST_LIT(sizes[0]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[1]);
} else {
geometry->defines[2]
= (test_define_t)TEST_LIT(sizes[0]);
geometry->defines[BLOCK_SIZE_i]
= TEST_LIT(sizes[0]);
}
if (count >= 4) {
geometry->defines[3]
= (test_define_t)TEST_LIT(sizes[3]);
geometry->defines[BLOCK_COUNT_i]
= TEST_LIT(sizes[3]);
}
optarg = s;
goto geometry_next;
@ -2165,16 +2166,16 @@ geometry_next:
// comma-separated permutation
if (*optarg == '{') {
lfs_testbd_powercycles_t *cycles = NULL;
lfs_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
char *s = optarg + 1;
while (true) {
char *parsed = NULL;
*(lfs_testbd_powercycles_t*)mappend(
*(lfs_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_testbd_powercycles_t),
sizeof(lfs_emubd_powercycles_t),
&cycle_count,
&cycle_capacity)
= strtoumax(s, &parsed, 0);
@ -2202,7 +2203,7 @@ geometry_next:
// leb16-encoded permutation
if (*optarg == ':') {
lfs_testbd_powercycles_t *cycles = NULL;
lfs_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
size_t cycle_capacity = 0;
@ -2214,9 +2215,9 @@ geometry_next:
break;
}
*(lfs_testbd_powercycles_t*)mappend(
*(lfs_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_testbd_powercycles_t),
sizeof(lfs_emubd_powercycles_t),
&cycle_count,
&cycle_capacity) = x;
s = parsed;
@ -2374,7 +2375,7 @@ getopt_done: ;
for (; argc > optind; optind++) {
test_define_t *defines = NULL;
size_t define_count = 0;
lfs_testbd_powercycles_t *cycles = NULL;
lfs_emubd_powercycles_t *cycles = NULL;
size_t cycle_count = 0;
// parse name, can be suite or case
@ -2422,7 +2423,7 @@ getopt_done: ;
(ncount-define_count)*sizeof(test_define_t));
define_count = ncount;
}
defines[d] = (test_define_t)TEST_LIT(v);
defines[d] = TEST_LIT(v);
}
if (cycles_) {
@ -2430,9 +2431,9 @@ getopt_done: ;
size_t cycle_capacity = 0;
while (*cycles_ != '\0') {
char *parsed = NULL;
*(lfs_testbd_powercycles_t*)mappend(
*(lfs_emubd_powercycles_t*)mappend(
(void**)&cycles,
sizeof(lfs_testbd_powercycles_t),
sizeof(lfs_emubd_powercycles_t),
&cycle_count,
&cycle_capacity)
= leb16_parse(cycles_, &parsed);

View File

@ -11,11 +11,11 @@ void test_trace(const char *fmt, ...);
__LINE__, \
__VA_ARGS__)
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS_TESTBD_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
#define LFS_EMUBD_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
// note these are indirectly included in any generated files
#include "bd/lfs_testbd.h"
#include "bd/lfs_emubd.h"
#include <stdio.h>
// give source a chance to define feature macros
@ -62,9 +62,10 @@ struct test_suite {
// access generated test defines
//intmax_t test_predefine(size_t define);
intmax_t test_define(size_t define);
#define TEST_DEFINE(i) test_define(i)
// a few preconfigured defines that control how tests run
#define READ_SIZE_i 0
@ -79,33 +80,33 @@ intmax_t test_define(size_t define);
#define BADBLOCK_BEHAVIOR_i 9
#define POWERLOSS_BEHAVIOR_i 10
#define READ_SIZE test_define(READ_SIZE_i)
#define PROG_SIZE test_define(PROG_SIZE_i)
#define BLOCK_SIZE test_define(BLOCK_SIZE_i)
#define BLOCK_COUNT test_define(BLOCK_COUNT_i)
#define CACHE_SIZE test_define(CACHE_SIZE_i)
#define LOOKAHEAD_SIZE test_define(LOOKAHEAD_SIZE_i)
#define BLOCK_CYCLES test_define(BLOCK_CYCLES_i)
#define ERASE_VALUE test_define(ERASE_VALUE_i)
#define ERASE_CYCLES test_define(ERASE_CYCLES_i)
#define BADBLOCK_BEHAVIOR test_define(BADBLOCK_BEHAVIOR_i)
#define POWERLOSS_BEHAVIOR test_define(POWERLOSS_BEHAVIOR_i)
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
#define BLOCK_SIZE TEST_DEFINE(BLOCK_SIZE_i)
#define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i)
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
#define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i)
#define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i)
#define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i)
#define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i)
#define BADBLOCK_BEHAVIOR TEST_DEFINE(BADBLOCK_BEHAVIOR_i)
#define POWERLOSS_BEHAVIOR TEST_DEFINE(POWERLOSS_BEHAVIOR_i)
#define TEST_IMPLICIT_DEFINES \
TEST_DEFINE(READ_SIZE, PROG_SIZE) \
TEST_DEFINE(PROG_SIZE, BLOCK_SIZE) \
TEST_DEFINE(BLOCK_SIZE, 0) \
TEST_DEFINE(BLOCK_COUNT, (1024*1024)/BLOCK_SIZE) \
TEST_DEFINE(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
TEST_DEFINE(LOOKAHEAD_SIZE, 16) \
TEST_DEFINE(BLOCK_CYCLES, -1) \
TEST_DEFINE(ERASE_VALUE, 0xff) \
TEST_DEFINE(ERASE_CYCLES, 0) \
TEST_DEFINE(BADBLOCK_BEHAVIOR, LFS_TESTBD_BADBLOCK_PROGERROR) \
TEST_DEFINE(POWERLOSS_BEHAVIOR, LFS_TESTBD_POWERLOSS_NOOP)
TEST_DEF(READ_SIZE, PROG_SIZE) \
TEST_DEF(PROG_SIZE, BLOCK_SIZE) \
TEST_DEF(BLOCK_SIZE, 0) \
TEST_DEF(BLOCK_COUNT, (1024*1024)/BLOCK_SIZE) \
TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
TEST_DEF(LOOKAHEAD_SIZE, 16) \
TEST_DEF(BLOCK_CYCLES, -1) \
TEST_DEF(ERASE_VALUE, 0xff) \
TEST_DEF(ERASE_CYCLES, 0) \
TEST_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \
TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
#define TEST_GEOMETRY_DEFINE_COUNT 4
#define TEST_IMPLICIT_DEFINE_COUNT 11
#define TEST_GEOMETRY_DEFINE_COUNT 4
#endif

1355
scripts/bench.py Executable file

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,8 @@ MERGES = {
'add': (
['code_size', 'data_size', 'stack_frame', 'struct_size',
'coverage_lines', 'coverage_branches',
'test_passed'],
'test_passed',
'bench_read', 'bench_prog', 'bench_erased'],
lambda xs: sum(xs[1:], start=xs[0])
),
'mul': (

View File

@ -48,7 +48,7 @@ def main(path='-', *, lines=1, sleep=0.01, keep_open=False):
if not keep_open:
break
# don't just flood open calls
time.sleep(sleep)
time.sleep(sleep or 0.1)
done = True
th.Thread(target=read, daemon=True).start()

View File

@ -388,7 +388,7 @@ def compile(test_paths, **args):
f.writeln('#define %-24s '
'TEST_IMPLICIT_DEFINE_COUNT+%d' % (define+'_i', i))
f.writeln('#define %-24s '
'test_define(%s)' % (define, define+'_i'))
'TEST_DEFINE(%s)' % (define, define+'_i'))
f.writeln('#endif')
f.writeln()
@ -486,7 +486,7 @@ def compile(test_paths, **args):
'TEST_IMPLICIT_DEFINE_COUNT+%d' % (
define+'_i', i))
f.writeln('#define %-24s '
'test_define(%s)' % (
'TEST_DEFINE(%s)' % (
define, define+'_i'))
f.writeln('#define '
'__TEST__%s__NEEDS_UNDEF' % (
@ -1018,7 +1018,9 @@ def run(runner, test_ids=[], **args):
trace = openio(args['trace'], 'w', 1)
output = None
if args.get('output'):
output = TestOutput(args['output'], ['suite', 'case'], ['test_passed'])
output = TestOutput(args['output'],
['suite', 'case'],
['test_passed'])
# measure runtime
start = time.time()

View File

@ -607,7 +607,7 @@ def main(path='-', *,
if not keep_open:
break
# don't just flood open calls
time.sleep(sleep)
time.sleep(sleep or 0.1)
except KeyboardInterrupt:
pass
else:
@ -627,7 +627,7 @@ def main(path='-', *,
if not keep_open:
break
# don't just flood open calls
time.sleep(sleep)
time.sleep(sleep or 0.1)
done = True
th.Thread(target=parse, daemon=True).start()

View File

@ -370,7 +370,7 @@ code = '''
[cases.test_alloc_bad_blocks]
in = "lfs.c"
defines.ERASE_CYCLES = 0xffffffff
defines.BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR'
defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_READERROR'
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
@ -409,7 +409,7 @@ code = '''
// but mark the head of our file as a "bad block", this is force our
// scan to bail early
lfs_testbd_setwear(cfg, fileblock, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, fileblock, 0xffffffff) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp");
size = strlen("chomp");
@ -424,7 +424,7 @@ code = '''
// now reverse the "bad block" and try to write the file again until we
// run out of space
lfs_testbd_setwear(cfg, fileblock, 0) => 0;
lfs_emubd_setwear(cfg, fileblock, 0) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp");
size = strlen("chomp");

View File

@ -6,18 +6,18 @@ defines.BLOCK_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t badblock = 2; badblock < BLOCK_COUNT; badblock++) {
lfs_testbd_setwear(cfg, badblock-1, 0) => 0;
lfs_testbd_setwear(cfg, badblock, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, badblock-1, 0) => 0;
lfs_emubd_setwear(cfg, badblock, 0xffffffff) => 0;
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
@ -86,17 +86,17 @@ defines.BLOCK_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(cfg, i+2, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, i+2, 0xffffffff) => 0;
}
lfs_t lfs;
@ -165,17 +165,17 @@ defines.BLOCK_COUNT = 256 # small bd so test runs faster
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
defines.NAMEMULT = 64
defines.FILEMULT = 1
code = '''
for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0;
}
lfs_t lfs;
@ -244,15 +244,15 @@ code = '''
defines.ERASE_CYCLES = 0xffffffff
defines.ERASE_VALUE = [0x00, 0xff, -1]
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
code = '''
lfs_testbd_setwear(cfg, 0, 0xffffffff) => 0;
lfs_testbd_setwear(cfg, 1, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, 0, 0xffffffff) => 0;
lfs_emubd_setwear(cfg, 1, 0xffffffff) => 0;
lfs_t lfs;
lfs_format(&lfs, cfg) => LFS_ERR_NOSPC;

View File

@ -4,11 +4,11 @@ defines.ERASE_CYCLES = 10
defines.BLOCK_COUNT = 256 # small bd so test runs faster
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
defines.FILES = 10
code = '''
@ -97,11 +97,11 @@ defines.ERASE_CYCLES = 10
defines.BLOCK_COUNT = 256 # small bd so test runs faster
defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2'
defines.BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_PROGERROR',
'LFS_TESTBD_BADBLOCK_ERASEERROR',
'LFS_TESTBD_BADBLOCK_READERROR',
'LFS_TESTBD_BADBLOCK_PROGNOOP',
'LFS_TESTBD_BADBLOCK_ERASENOOP',
'LFS_EMUBD_BADBLOCK_PROGERROR',
'LFS_EMUBD_BADBLOCK_ERASEERROR',
'LFS_EMUBD_BADBLOCK_READERROR',
'LFS_EMUBD_BADBLOCK_PROGNOOP',
'LFS_EMUBD_BADBLOCK_ERASENOOP',
]
defines.FILES = 10
code = '''
@ -197,7 +197,7 @@ code = '''
for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs_testbd_setwear(cfg, b,
lfs_emubd_setwear(cfg, b,
(b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0;
}
@ -297,7 +297,7 @@ code = '''
for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) {
lfs_testbd_setwear(cfg, b,
lfs_emubd_setwear(cfg, b,
(b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0;
}
@ -469,12 +469,12 @@ exhausted:
LFS_WARN("completed %d cycles", cycle);
// check the wear on our block device
lfs_testbd_wear_t minwear = -1;
lfs_testbd_wear_t totalwear = 0;
lfs_testbd_wear_t maxwear = 0;
lfs_emubd_wear_t minwear = -1;
lfs_emubd_wear_t totalwear = 0;
lfs_emubd_wear_t maxwear = 0;
// skip 0 and 1 as superblock movement is intentionally avoided
for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(cfg, b);
lfs_emubd_wear_t wear = lfs_emubd_getwear(cfg, b);
printf("%08x: wear %d\n", b, wear);
assert(wear >= 0);
if (wear < minwear) {
@ -485,17 +485,17 @@ exhausted:
}
totalwear += wear;
}
lfs_testbd_wear_t avgwear = totalwear / BLOCK_COUNT;
lfs_emubd_wear_t avgwear = totalwear / BLOCK_COUNT;
LFS_WARN("max wear: %d cycles", maxwear);
LFS_WARN("avg wear: %d cycles", totalwear / (int)BLOCK_COUNT);
LFS_WARN("min wear: %d cycles", minwear);
// find standard deviation^2
lfs_testbd_wear_t dev2 = 0;
lfs_emubd_wear_t dev2 = 0;
for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(cfg, b);
lfs_emubd_wear_t wear = lfs_emubd_getwear(cfg, b);
assert(wear >= 0);
lfs_testbd_swear_t diff = wear - avgwear;
lfs_emubd_swear_t diff = wear - avgwear;
dev2 += diff*diff;
}
dev2 /= totalwear;

View File

@ -1638,15 +1638,15 @@ code = '''
if (RELOCATIONS & 0x1) {
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/parent");
lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
if (RELOCATIONS & 0x2) {
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/parent/child");
lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
@ -1784,22 +1784,22 @@ code = '''
if (RELOCATIONS & 0x1) {
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/parent");
lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
if (RELOCATIONS & 0x2) {
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/parent/sibling");
lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
if (RELOCATIONS & 0x4) {
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/parent/child");
lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}