Added optional read/prog/erase delays to testbd

These have no real purpose other than slowing down the simulation
for inspection/fun.

Note this did reveal an issue in pretty_asserts.py which was clobbering
feature macros. Added explicit, and maybe a bit hacky, #undef _FEATURE_H
to avoid this.
This commit is contained in:
Christopher Haster
2022-08-23 17:01:04 -05:00
parent 3f4f85986e
commit 552336eba9
5 changed files with 129 additions and 1 deletions

View File

@@ -7,12 +7,17 @@
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#include "bd/lfs_testbd.h" #include "bd/lfs_testbd.h"
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <time.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@@ -238,6 +243,18 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
size); size);
} }
if (bd->cfg->read_delay) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->read_delay/1000000000,
.tv_nsec=bd->cfg->read_delay%1000000000},
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
return err;
}
}
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", 0); LFS_TESTBD_TRACE("lfs_testbd_read -> %d", 0);
return 0; return 0;
} }
@@ -306,6 +323,18 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
} }
} }
if (bd->cfg->prog_delay) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->prog_delay/1000000000,
.tv_nsec=bd->cfg->prog_delay%1000000000},
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
return err;
}
}
// lose power? // lose power?
if (bd->power_cycles > 0) { if (bd->power_cycles > 0) {
bd->power_cycles -= 1; bd->power_cycles -= 1;
@@ -386,6 +415,18 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
} }
} }
if (bd->cfg->erase_delay) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->erase_delay/1000000000,
.tv_nsec=bd->cfg->erase_delay%1000000000},
NULL);
if (err) {
err = -errno;
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
return err;
}
}
// lose power? // lose power?
if (bd->power_cycles > 0) { if (bd->power_cycles > 0) {
bd->power_cycles -= 1; bd->power_cycles -= 1;

View File

@@ -57,6 +57,10 @@ typedef int32_t lfs_testbd_swear_t;
typedef uint32_t lfs_testbd_powercycles_t; typedef uint32_t lfs_testbd_powercycles_t;
typedef int32_t lfs_testbd_spowercycles_t; typedef int32_t lfs_testbd_spowercycles_t;
// Type for delays in nanoseconds
typedef uint64_t lfs_testbd_delay_t;
typedef int64_t lfs_testbd_sdelay_t;
// testbd config, this is required for testing // testbd config, this is required for testing
struct lfs_testbd_config { struct lfs_testbd_config {
// 8-bit erase value to use for simulating erases. -1 does not simulate // 8-bit erase value to use for simulating erases. -1 does not simulate
@@ -93,6 +97,18 @@ struct lfs_testbd_config {
// Path to file to use as a mirror of the disk. This provides a way to view // Path to file to use as a mirror of the disk. This provides a way to view
// the current state of the block device. // the current state of the block device.
const char *disk_path; const char *disk_path;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_delay_t read_delay;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_delay_t prog_delay;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs_testbd_delay_t erase_delay;
}; };
// A reference counted block // A reference counted block

View File

@@ -247,6 +247,9 @@ static size_t test_step = 1;
static const char *test_disk = NULL; static const char *test_disk = NULL;
FILE *test_trace = NULL; FILE *test_trace = NULL;
static lfs_testbd_delay_t test_read_delay = 0.0;
static lfs_testbd_delay_t test_prog_delay = 0.0;
static lfs_testbd_delay_t test_erase_delay = 0.0;
// how many permutations are there actually in a test case // how many permutations are there actually in a test case
@@ -611,6 +614,9 @@ static void run_powerloss_none(
.erase_cycles = ERASE_CYCLES, .erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR, .badblock_behavior = BADBLOCK_BEHAVIOR,
.disk_path = test_disk, .disk_path = test_disk,
.read_delay = test_read_delay,
.prog_delay = test_prog_delay,
.erase_delay = test_erase_delay,
}; };
int err = lfs_testbd_createcfg(&cfg, test_disk, &bdcfg); int err = lfs_testbd_createcfg(&cfg, test_disk, &bdcfg);
@@ -674,6 +680,9 @@ static void run_powerloss_linear(
.erase_cycles = ERASE_CYCLES, .erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR, .badblock_behavior = BADBLOCK_BEHAVIOR,
.disk_path = test_disk, .disk_path = test_disk,
.read_delay = test_read_delay,
.prog_delay = test_prog_delay,
.erase_delay = test_erase_delay,
.power_cycles = i, .power_cycles = i,
.powerloss_behavior = POWERLOSS_BEHAVIOR, .powerloss_behavior = POWERLOSS_BEHAVIOR,
.powerloss_cb = powerloss_longjmp, .powerloss_cb = powerloss_longjmp,
@@ -751,6 +760,9 @@ static void run_powerloss_exponential(
.erase_cycles = ERASE_CYCLES, .erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR, .badblock_behavior = BADBLOCK_BEHAVIOR,
.disk_path = test_disk, .disk_path = test_disk,
.read_delay = test_read_delay,
.prog_delay = test_prog_delay,
.erase_delay = test_erase_delay,
.power_cycles = i, .power_cycles = i,
.powerloss_behavior = POWERLOSS_BEHAVIOR, .powerloss_behavior = POWERLOSS_BEHAVIOR,
.powerloss_cb = powerloss_longjmp, .powerloss_cb = powerloss_longjmp,
@@ -826,6 +838,9 @@ static void run_powerloss_cycles(
.erase_cycles = ERASE_CYCLES, .erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR, .badblock_behavior = BADBLOCK_BEHAVIOR,
.disk_path = test_disk, .disk_path = test_disk,
.read_delay = test_read_delay,
.prog_delay = test_prog_delay,
.erase_delay = test_erase_delay,
.power_cycles = (i < cycle_count) ? cycles[i] : 0, .power_cycles = (i < cycle_count) ? cycles[i] : 0,
.powerloss_behavior = POWERLOSS_BEHAVIOR, .powerloss_behavior = POWERLOSS_BEHAVIOR,
.powerloss_cb = powerloss_longjmp, .powerloss_cb = powerloss_longjmp,
@@ -1018,6 +1033,9 @@ enum opt_flags {
OPT_STOP = 8, OPT_STOP = 8,
OPT_DISK = 'd', OPT_DISK = 'd',
OPT_TRACE = 't', OPT_TRACE = 't',
OPT_READ_DELAY = 9,
OPT_PROG_DELAY = 10,
OPT_ERASE_DELAY = 11,
}; };
const char *short_opts = "hYlLD:G:p:nrVd:t:"; const char *short_opts = "hYlLD:G:p:nrVd:t:";
@@ -1040,6 +1058,9 @@ const struct option long_opts[] = {
{"step", required_argument, NULL, OPT_STEP}, {"step", required_argument, NULL, OPT_STEP},
{"disk", required_argument, NULL, OPT_DISK}, {"disk", required_argument, NULL, OPT_DISK},
{"trace", required_argument, NULL, OPT_TRACE}, {"trace", required_argument, NULL, OPT_TRACE},
{"read-delay", required_argument, NULL, OPT_READ_DELAY},
{"prog-delay", required_argument, NULL, OPT_PROG_DELAY},
{"erase-delay", required_argument, NULL, OPT_ERASE_DELAY},
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
@@ -1061,6 +1082,9 @@ const char *const help_text[] = {
"Only run every n tests, calculated after --start and --stop.", "Only run every n tests, calculated after --start and --stop.",
"Redirect block device operations to this file.", "Redirect block device operations to this file.",
"Redirect trace output to this file.", "Redirect trace output to this file.",
"Artificial read delay in seconds.",
"Artificial prog delay in seconds.",
"Artificial erase delay in seconds.",
}; };
int main(int argc, char **argv) { int main(int argc, char **argv) {
@@ -1374,6 +1398,36 @@ powerloss_next:
} }
} }
break; break;
case OPT_READ_DELAY: {
char *parsed = NULL;
double read_delay = strtod(optarg, &parsed);
if (parsed == optarg) {
fprintf(stderr, "error: invalid read-delay: %s\n", optarg);
exit(-1);
}
test_read_delay = read_delay*1.0e9;
break;
}
case OPT_PROG_DELAY: {
char *parsed = NULL;
double prog_delay = strtod(optarg, &parsed);
if (parsed == optarg) {
fprintf(stderr, "error: invalid prog-delay: %s\n", optarg);
exit(-1);
}
test_prog_delay = prog_delay*1.0e9;
break;
}
case OPT_ERASE_DELAY: {
char *parsed = NULL;
double erase_delay = strtod(optarg, &parsed);
if (parsed == optarg) {
fprintf(stderr, "error: invalid erase-delay: %s\n", optarg);
exit(-1);
}
test_erase_delay = erase_delay*1.0e9;
break;
}
// done parsing // done parsing
case -1: case -1:
goto getopt_done; goto getopt_done;

View File

@@ -48,11 +48,14 @@ def write_header(f, limit=LIMIT):
f.writeln("//") f.writeln("//")
f.writeln() f.writeln()
f.writeln("#include <stdio.h>")
f.writeln("#include <stdbool.h>") f.writeln("#include <stdbool.h>")
f.writeln("#include <stdint.h>") f.writeln("#include <stdint.h>")
f.writeln("#include <inttypes.h>") f.writeln("#include <inttypes.h>")
f.writeln("#include <stdio.h>")
f.writeln("#include <string.h>")
f.writeln("#include <signal.h>") f.writeln("#include <signal.h>")
# give source a chance to define feature macros
f.writeln("#undef _FEATURES_H")
f.writeln() f.writeln()
# write print macros # write print macros

View File

@@ -498,6 +498,7 @@ def list_(**args):
if args.get('list_cases'): cmd.append('--list-cases') if args.get('list_cases'): cmd.append('--list-cases')
if args.get('list_paths'): cmd.append('--list-paths') if args.get('list_paths'): cmd.append('--list-paths')
if args.get('list_defines'): cmd.append('--list-defines') if args.get('list_defines'): cmd.append('--list-defines')
if args.get('list_defaults'): cmd.append('--list-defaults')
if args.get('list_geometries'): cmd.append('--list-geometries') if args.get('list_geometries'): cmd.append('--list-geometries')
if args.get('list_powerlosses'): cmd.append('--list-powerlosses') if args.get('list_powerlosses'): cmd.append('--list-powerlosses')
@@ -641,8 +642,15 @@ def run_stage(name, runner_, **args):
cmd.append('--disk=%s' % args['disk']) cmd.append('--disk=%s' % args['disk'])
if args.get('trace'): if args.get('trace'):
cmd.append('--trace=%s' % args['trace']) cmd.append('--trace=%s' % args['trace'])
if args.get('read_delay'):
cmd.append('--read-delay=%s' % args['read_delay'])
if args.get('prog_delay'):
cmd.append('--prog-delay=%s' % args['prog_delay'])
if args.get('erase_delay'):
cmd.append('--erase-delay=%s' % args['erase_delay'])
if args.get('verbose'): if args.get('verbose'):
print(' '.join(shlex.quote(c) for c in cmd)) print(' '.join(shlex.quote(c) for c in cmd))
mpty, spty = pty.openpty() mpty, spty = pty.openpty()
proc = sp.Popen(cmd, stdout=spty, stderr=spty) proc = sp.Popen(cmd, stdout=spty, stderr=spty)
os.close(spty) os.close(spty)
@@ -1010,6 +1018,12 @@ if __name__ == "__main__":
help="Redirect trace output to this file.") help="Redirect trace output to this file.")
test_parser.add_argument('-o', '--output', test_parser.add_argument('-o', '--output',
help="Redirect stdout and stderr to this file.") help="Redirect stdout and stderr to this file.")
test_parser.add_argument('--read-delay',
help="Artificial read delay in seconds.")
test_parser.add_argument('--prog-delay',
help="Artificial prog delay in seconds.")
test_parser.add_argument('--erase-delay',
help="Artificial erase delay in seconds.")
test_parser.add_argument('--runner', default=[RUNNER_PATH], test_parser.add_argument('--runner', default=[RUNNER_PATH],
type=lambda x: x.split(), type=lambda x: x.split(),
help="Path to runner, defaults to %r" % RUNNER_PATH) help="Path to runner, defaults to %r" % RUNNER_PATH)