Several tweaks to script flags

- Changed multi-field flags to action=append instead of comma-separated.
- Dropped short-names for geometries/powerlosses
- Renamed -Pexponential -> -Plog
- Allowed omitting the 0 for -W0/-H0/-n0 and made -j0 consistent
- Better handling of --xlim/--ylim
This commit is contained in:
Christopher Haster 2022-09-26 19:19:40 -05:00
parent 42d889e141
commit 9507e6243c
9 changed files with 146 additions and 137 deletions

View File

@ -170,10 +170,10 @@ coverage: $(GCDA)
.PHONY: summary sizes .PHONY: summary sizes
summary sizes: $(BUILDDIR)lfs.csv summary sizes: $(BUILDDIR)lfs.csv
$(strip ./scripts/summary.py -Y $^ \ $(strip ./scripts/summary.py -Y $^ \
-fcode=code_size,$\ -fcode=code_size \
data=data_size,$\ -fdata=data_size \
stack=stack_limit,$\ -fstack=stack_limit \
struct=struct_size \ -fstruct=struct_size \
--max=stack \ --max=stack \
$(SUMMARYFLAGS)) $(SUMMARYFLAGS))

View File

@ -90,9 +90,7 @@ static uintmax_t leb16_parse(const char *s, char **tail) {
// bench_runner types // bench_runner types
typedef struct bench_geometry { typedef struct bench_geometry {
char short_name; const char *name;
const char *long_name;
bench_define_t defines[BENCH_GEOMETRY_DEFINE_COUNT]; bench_define_t defines[BENCH_GEOMETRY_DEFINE_COUNT];
} bench_geometry_t; } bench_geometry_t;
@ -1057,7 +1055,7 @@ static void list_implicit_defines(void) {
// make sure to include builtin geometries here // make sure to include builtin geometries here
extern const bench_geometry_t builtin_geometries[]; extern const bench_geometry_t builtin_geometries[];
for (size_t g = 0; builtin_geometries[g].long_name; g++) { for (size_t g = 0; builtin_geometries[g].name; g++) {
bench_define_geometry(&builtin_geometries[g]); bench_define_geometry(&builtin_geometries[g]);
bench_define_flush(); bench_define_flush();
@ -1089,12 +1087,12 @@ static void list_implicit_defines(void) {
// geometries to bench // geometries to bench
const bench_geometry_t builtin_geometries[] = { const bench_geometry_t builtin_geometries[] = {
{'d', "default", {{NULL}, BENCH_CONST(16), BENCH_CONST(512), {NULL}}}, {"default", {{NULL}, BENCH_CONST(16), BENCH_CONST(512), {NULL}}},
{'e', "eeprom", {{NULL}, BENCH_CONST(1), BENCH_CONST(512), {NULL}}}, {"eeprom", {{NULL}, BENCH_CONST(1), BENCH_CONST(512), {NULL}}},
{'E', "emmc", {{NULL}, {NULL}, BENCH_CONST(512), {NULL}}}, {"emmc", {{NULL}, {NULL}, BENCH_CONST(512), {NULL}}},
{'n', "nor", {{NULL}, BENCH_CONST(1), BENCH_CONST(4096), {NULL}}}, {"nor", {{NULL}, BENCH_CONST(1), BENCH_CONST(4096), {NULL}}},
{'N', "nand", {{NULL}, BENCH_CONST(4096), BENCH_CONST(32768), {NULL}}}, {"nand", {{NULL}, BENCH_CONST(4096), BENCH_CONST(32768), {NULL}}},
{0, NULL, {{NULL}, {NULL}, {NULL}, {NULL}}}, {NULL, {{NULL}, {NULL}, {NULL}, {NULL}}},
}; };
const bench_geometry_t *bench_geometries = builtin_geometries; const bench_geometry_t *bench_geometries = builtin_geometries;
@ -1107,12 +1105,11 @@ static void list_geometries(void) {
printf("%-24s %7s %7s %7s %7s %11s\n", printf("%-24s %7s %7s %7s %7s %11s\n",
"geometry", "read", "prog", "erase", "count", "size"); "geometry", "read", "prog", "erase", "count", "size");
for (size_t g = 0; builtin_geometries[g].long_name; g++) { for (size_t g = 0; builtin_geometries[g].name; g++) {
bench_define_geometry(&builtin_geometries[g]); bench_define_geometry(&builtin_geometries[g]);
bench_define_flush(); bench_define_flush();
printf("%c,%-22s %7ju %7ju %7ju %7ju %11ju\n", printf("%-24s %7ju %7ju %7ju %7ju %11ju\n",
builtin_geometries[g].short_name, builtin_geometries[g].name,
builtin_geometries[g].long_name,
READ_SIZE, READ_SIZE,
PROG_SIZE, PROG_SIZE,
BLOCK_SIZE, BLOCK_SIZE,
@ -1253,7 +1250,7 @@ enum opt_flags {
OPT_LIST_IMPLICIT_DEFINES = 5, OPT_LIST_IMPLICIT_DEFINES = 5,
OPT_LIST_GEOMETRIES = 6, OPT_LIST_GEOMETRIES = 6,
OPT_DEFINE = 'D', OPT_DEFINE = 'D',
OPT_GEOMETRY = 'g', OPT_GEOMETRY = 'G',
OPT_STEP = 's', OPT_STEP = 's',
OPT_DISK = 'd', OPT_DISK = 'd',
OPT_TRACE = 't', OPT_TRACE = 't',
@ -1262,7 +1259,7 @@ enum opt_flags {
OPT_ERASE_SLEEP = 9, OPT_ERASE_SLEEP = 9,
}; };
const char *short_opts = "hYlLD:g:s:d:t:"; const char *short_opts = "hYlLD:G:s:d:t:";
const struct option long_opts[] = { const struct option long_opts[] = {
{"help", no_argument, NULL, OPT_HELP}, {"help", no_argument, NULL, OPT_HELP},
@ -1300,7 +1297,7 @@ const char *const help_text[] = {
"List implicit defines in this bench-runner.", "List implicit defines in this bench-runner.",
"List the available disk geometries.", "List the available disk geometries.",
"Override a bench define.", "Override a bench define.",
"Comma-separated list of disk geometries to bench. Defaults to d,e,E,n,N.", "Comma-separated list of disk geometries to bench.",
"Comma-separated range of bench permutations to run (start,stop,step).", "Comma-separated range of bench permutations to run (start,stop,step).",
"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.",
@ -1555,14 +1552,11 @@ invalid_define:
// named disk geometry // named disk geometry
size_t len = strcspn(optarg, " ,"); size_t len = strcspn(optarg, " ,");
for (size_t i = 0; builtin_geometries[i].long_name; i++) { for (size_t i = 0; builtin_geometries[i].name; i++) {
if ((len == 1 if (len == strlen(builtin_geometries[i].name)
&& *optarg == builtin_geometries[i].short_name) && memcmp(optarg,
|| (len == strlen( builtin_geometries[i].name,
builtin_geometries[i].long_name) len) == 0) {
&& memcmp(optarg,
builtin_geometries[i].long_name,
len) == 0)) {
*geometry = builtin_geometries[i]; *geometry = builtin_geometries[i];
optarg += len; optarg += len;
goto geometry_next; goto geometry_next;

View File

@ -90,16 +90,12 @@ static uintmax_t leb16_parse(const char *s, char **tail) {
// test_runner types // test_runner types
typedef struct test_geometry { typedef struct test_geometry {
char short_name; const char *name;
const char *long_name;
test_define_t defines[TEST_GEOMETRY_DEFINE_COUNT]; test_define_t defines[TEST_GEOMETRY_DEFINE_COUNT];
} test_geometry_t; } test_geometry_t;
typedef struct test_powerloss { typedef struct test_powerloss {
char short_name; const char *name;
const char *long_name;
void (*run)( void (*run)(
const lfs_emubd_powercycles_t *cycles, const lfs_emubd_powercycles_t *cycles,
size_t cycle_count, size_t cycle_count,
@ -574,6 +570,11 @@ void test_seen_cleanup(test_seen_t *seen) {
free(seen->branches); free(seen->branches);
} }
static void run_powerloss_none(
const lfs_emubd_powercycles_t *cycles,
size_t cycle_count,
const struct test_suite *suite,
const struct test_case *case_);
static void run_powerloss_cycles( static void run_powerloss_cycles(
const lfs_emubd_powercycles_t *cycles, const lfs_emubd_powercycles_t *cycles,
size_t cycle_count, size_t cycle_count,
@ -606,7 +607,7 @@ static void case_forperm(
} else { } else {
for (size_t p = 0; p < test_powerloss_count; p++) { for (size_t p = 0; p < test_powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing // skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].short_name != '0' if (test_powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) { && !(case_->flags & TEST_REENTRANT)) {
continue; continue;
} }
@ -646,7 +647,7 @@ static void case_forperm(
} else { } else {
for (size_t p = 0; p < test_powerloss_count; p++) { for (size_t p = 0; p < test_powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing // skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].short_name != '0' if (test_powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) { && !(case_->flags & TEST_REENTRANT)) {
continue; continue;
} }
@ -1094,7 +1095,7 @@ static void list_implicit_defines(void) {
// make sure to include builtin geometries here // make sure to include builtin geometries here
extern const test_geometry_t builtin_geometries[]; extern const test_geometry_t builtin_geometries[];
for (size_t g = 0; builtin_geometries[g].long_name; g++) { for (size_t g = 0; builtin_geometries[g].name; g++) {
test_define_geometry(&builtin_geometries[g]); test_define_geometry(&builtin_geometries[g]);
test_define_flush(); test_define_flush();
@ -1126,12 +1127,12 @@ static void list_implicit_defines(void) {
// geometries to test // geometries to test
const test_geometry_t builtin_geometries[] = { const test_geometry_t builtin_geometries[] = {
{'d', "default", {{NULL}, TEST_CONST(16), TEST_CONST(512), {NULL}}}, {"default", {{NULL}, TEST_CONST(16), TEST_CONST(512), {NULL}}},
{'e', "eeprom", {{NULL}, TEST_CONST(1), TEST_CONST(512), {NULL}}}, {"eeprom", {{NULL}, TEST_CONST(1), TEST_CONST(512), {NULL}}},
{'E', "emmc", {{NULL}, {NULL}, TEST_CONST(512), {NULL}}}, {"emmc", {{NULL}, {NULL}, TEST_CONST(512), {NULL}}},
{'n', "nor", {{NULL}, TEST_CONST(1), TEST_CONST(4096), {NULL}}}, {"nor", {{NULL}, TEST_CONST(1), TEST_CONST(4096), {NULL}}},
{'N', "nand", {{NULL}, TEST_CONST(4096), TEST_CONST(32768), {NULL}}}, {"nand", {{NULL}, TEST_CONST(4096), TEST_CONST(32768), {NULL}}},
{0, NULL, {{NULL}, {NULL}, {NULL}, {NULL}}}, {NULL, {{NULL}, {NULL}, {NULL}, {NULL}}},
}; };
const test_geometry_t *test_geometries = builtin_geometries; const test_geometry_t *test_geometries = builtin_geometries;
@ -1144,12 +1145,11 @@ static void list_geometries(void) {
printf("%-24s %7s %7s %7s %7s %11s\n", printf("%-24s %7s %7s %7s %7s %11s\n",
"geometry", "read", "prog", "erase", "count", "size"); "geometry", "read", "prog", "erase", "count", "size");
for (size_t g = 0; builtin_geometries[g].long_name; g++) { for (size_t g = 0; builtin_geometries[g].name; g++) {
test_define_geometry(&builtin_geometries[g]); test_define_geometry(&builtin_geometries[g]);
test_define_flush(); test_define_flush();
printf("%c,%-22s %7ju %7ju %7ju %7ju %11ju\n", printf("%-24s %7ju %7ju %7ju %7ju %11ju\n",
builtin_geometries[g].short_name, builtin_geometries[g].name,
builtin_geometries[g].long_name,
READ_SIZE, READ_SIZE,
PROG_SIZE, PROG_SIZE,
BLOCK_SIZE, BLOCK_SIZE,
@ -1314,7 +1314,7 @@ static void run_powerloss_linear(
} }
} }
static void run_powerloss_exponential( static void run_powerloss_log(
const lfs_emubd_powercycles_t *cycles, const lfs_emubd_powercycles_t *cycles,
size_t cycle_count, size_t cycle_count,
const struct test_suite *suite, const struct test_suite *suite,
@ -1646,11 +1646,11 @@ static void run_powerloss_exhaustive(
const test_powerloss_t builtin_powerlosses[] = { const test_powerloss_t builtin_powerlosses[] = {
{'0', "none", run_powerloss_none, NULL, 0}, {"none", run_powerloss_none, NULL, 0},
{'e', "exponential", run_powerloss_exponential, NULL, 0}, {"log", run_powerloss_log, NULL, 0},
{'l', "linear", run_powerloss_linear, NULL, 0}, {"linear", run_powerloss_linear, NULL, 0},
{'x', "exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX}, {"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX},
{0, NULL, NULL, NULL, 0}, {NULL, NULL, NULL, 0},
}; };
const char *const builtin_powerlosses_help[] = { const char *const builtin_powerlosses_help[] = {
@ -1664,17 +1664,16 @@ const char *const builtin_powerlosses_help[] = {
}; };
const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){ const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
{'0', "none", run_powerloss_none, NULL, 0}, {"none", run_powerloss_none, NULL, 0},
}; };
size_t test_powerloss_count = 1; size_t test_powerloss_count = 1;
static void list_powerlosses(void) { static void list_powerlosses(void) {
printf("%-24s %s\n", "scenario", "description"); printf("%-24s %s\n", "scenario", "description");
size_t i = 0; size_t i = 0;
for (; builtin_powerlosses[i].long_name; i++) { for (; builtin_powerlosses[i].name; i++) {
printf("%c,%-22s %s\n", printf("%-24s %s\n",
builtin_powerlosses[i].short_name, builtin_powerlosses[i].name,
builtin_powerlosses[i].long_name,
builtin_powerlosses_help[i]); builtin_powerlosses_help[i]);
} }
@ -1765,8 +1764,8 @@ enum opt_flags {
OPT_LIST_GEOMETRIES = 6, OPT_LIST_GEOMETRIES = 6,
OPT_LIST_POWERLOSSES = 7, OPT_LIST_POWERLOSSES = 7,
OPT_DEFINE = 'D', OPT_DEFINE = 'D',
OPT_GEOMETRY = 'g', OPT_GEOMETRY = 'G',
OPT_POWERLOSS = 'p', OPT_POWERLOSS = 'P',
OPT_STEP = 's', OPT_STEP = 's',
OPT_DISK = 'd', OPT_DISK = 'd',
OPT_TRACE = 't', OPT_TRACE = 't',
@ -1775,7 +1774,7 @@ enum opt_flags {
OPT_ERASE_SLEEP = 10, OPT_ERASE_SLEEP = 10,
}; };
const char *short_opts = "hYlLD:g:p:s:d:t:"; const char *short_opts = "hYlLD:G:P:s:d:t:";
const struct option long_opts[] = { const struct option long_opts[] = {
{"help", no_argument, NULL, OPT_HELP}, {"help", no_argument, NULL, OPT_HELP},
@ -1816,8 +1815,8 @@ const char *const help_text[] = {
"List the available disk geometries.", "List the available disk geometries.",
"List the available power-loss scenarios.", "List the available power-loss scenarios.",
"Override a test define.", "Override a test define.",
"Comma-separated list of disk geometries to test. Defaults to d,e,E,n,N.", "Comma-separated list of disk geometries to test.",
"Comma-separated list of power-loss scenarios to test. Defaults to 0,l.", "Comma-separated list of power-loss scenarios to test.",
"Comma-separated range of test permutations to run (start,stop,step).", "Comma-separated range of test permutations to run (start,stop,step).",
"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.",
@ -2076,14 +2075,11 @@ invalid_define:
// named disk geometry // named disk geometry
size_t len = strcspn(optarg, " ,"); size_t len = strcspn(optarg, " ,");
for (size_t i = 0; builtin_geometries[i].long_name; i++) { for (size_t i = 0; builtin_geometries[i].name; i++) {
if ((len == 1 if (len == strlen(builtin_geometries[i].name)
&& *optarg == builtin_geometries[i].short_name) && memcmp(optarg,
|| (len == strlen( builtin_geometries[i].name,
builtin_geometries[i].long_name) len) == 0) {
&& memcmp(optarg,
builtin_geometries[i].long_name,
len) == 0)) {
*geometry = builtin_geometries[i]; *geometry = builtin_geometries[i];
optarg += len; optarg += len;
goto geometry_next; goto geometry_next;
@ -2224,14 +2220,11 @@ geometry_next:
// named power-loss scenario // named power-loss scenario
size_t len = strcspn(optarg, " ,"); size_t len = strcspn(optarg, " ,");
for (size_t i = 0; builtin_powerlosses[i].long_name; i++) { for (size_t i = 0; builtin_powerlosses[i].name; i++) {
if ((len == 1 if (len == strlen(builtin_powerlosses[i].name)
&& *optarg == builtin_powerlosses[i].short_name) && memcmp(optarg,
|| (len == strlen( builtin_powerlosses[i].name,
builtin_powerlosses[i].long_name) len) == 0) {
&& memcmp(optarg,
builtin_powerlosses[i].long_name,
len) == 0)) {
*powerloss = builtin_powerlosses[i]; *powerloss = builtin_powerlosses[i];
optarg += len; optarg += len;
goto powerloss_next; goto powerloss_next;

View File

@ -511,7 +511,7 @@ def find_runner(runner, **args):
# other context # other context
if args.get('geometry'): if args.get('geometry'):
cmd.append('-g%s' % args['geometry']) cmd.append('-G%s' % args['geometry'])
if args.get('disk'): if args.get('disk'):
cmd.append('-d%s' % args['disk']) cmd.append('-d%s' % args['disk'])
if args.get('trace'): if args.get('trace'):
@ -1003,6 +1003,10 @@ def run(runner, bench_ids=[], **args):
total_perms)) total_perms))
print() print()
# automatic job detection?
if args.get('jobs') == 0:
args['jobs'] = len(os.sched_getaffinity(0))
# truncate and open logs here so they aren't disconnected between benches # truncate and open logs here so they aren't disconnected between benches
stdout = None stdout = None
if args.get('stdout'): if args.get('stdout'):
@ -1246,9 +1250,8 @@ if __name__ == "__main__":
action='append', action='append',
help="Override a bench define.") help="Override a bench define.")
bench_parser.add_argument( bench_parser.add_argument(
'-g', '--geometry', '-G', '--geometry',
help="Comma-separated list of disk geometries to bench. " help="Comma-separated list of disk geometries to bench.")
"Defaults to d,e,E,n,N.")
bench_parser.add_argument( bench_parser.add_argument(
'-d', '--disk', '-d', '--disk',
help="Direct block device operations to this file.") help="Direct block device operations to this file.")
@ -1274,8 +1277,8 @@ if __name__ == "__main__":
'-j', '--jobs', '-j', '--jobs',
nargs='?', nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=len(os.sched_getaffinity(0)), const=0,
help="Number of parallel runners to run.") help="Number of parallel runners to run. 0 runs one runner per core.")
bench_parser.add_argument( bench_parser.add_argument(
'-k', '--keep-going', '-k', '--keep-going',
action='store_true', action='store_true',

View File

@ -479,8 +479,8 @@ def main(csv_paths, *,
x=None, x=None,
y=None, y=None,
define=[], define=[],
xlim=None, xlim=(None,None),
ylim=None, ylim=(None,None),
width=None, width=None,
height=17, height=17,
cat=False, cat=False,
@ -489,7 +489,7 @@ def main(csv_paths, *,
colors=None, colors=None,
chars=None, chars=None,
line_chars=None, line_chars=None,
no_lines=False, points=False,
legend=None, legend=None,
keep_open=False, keep_open=False,
sleep=None, sleep=None,
@ -503,9 +503,9 @@ def main(csv_paths, *,
color = False color = False
# allow shortened ranges # allow shortened ranges
if xlim is not None and len(xlim) == 1: if len(xlim) == 1:
xlim = (0, xlim[0]) xlim = (0, xlim[0])
if ylim is not None and len(ylim) == 1: if len(ylim) == 1:
ylim = (0, ylim[0]) ylim = (0, ylim[0])
# separate out renames # separate out renames
@ -544,7 +544,7 @@ def main(csv_paths, *,
if line_chars is not None: if line_chars is not None:
line_chars_ = line_chars line_chars_ = line_chars
elif not no_lines: elif not points:
line_chars_ = [True] line_chars_ = [True]
else: else:
line_chars_ = [False] line_chars_ = [False]
@ -567,28 +567,26 @@ def main(csv_paths, *,
legend_width = max(legend_width, len(label)+1) legend_width = max(legend_width, len(label)+1)
# find xlim/ylim # find xlim/ylim
if xlim is not None: xlim_ = (
xlim_ = xlim xlim[0] if xlim[0] is not None
else: else min(it.chain([0], (k
xlim_ = (
min(it.chain([0], (k
for r in datasets_.values() for r in datasets_.values()
for k, v in r.items() for k, v in r.items()
if v is not None))), if v is not None))),
max(it.chain([0], (k xlim[1] if xlim[1] is not None
else max(it.chain([0], (k
for r in datasets_.values() for r in datasets_.values()
for k, v in r.items() for k, v in r.items()
if v is not None)))) if v is not None))))
if ylim is not None: ylim_ = (
ylim_ = ylim ylim[0] if ylim[0] is not None
else: else min(it.chain([0], (v
ylim_ = (
min(it.chain([0], (v
for r in datasets_.values() for r in datasets_.values()
for _, v in r.items() for _, v in r.items()
if v is not None))), if v is not None))),
max(it.chain([0], (v ylim[1] if ylim[1] is not None
else max(it.chain([0], (v
for r in datasets_.values() for r in datasets_.values()
for _, v in r.items() for _, v in r.items()
if v is not None)))) if v is not None))))
@ -740,17 +738,17 @@ if __name__ == "__main__":
"or list of paths. Defaults to %r." % CSV_PATHS) "or list of paths. Defaults to %r." % CSV_PATHS)
parser.add_argument( parser.add_argument(
'-b', '--by', '-b', '--by',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Fields to render as separate plots. All other fields will be " help="Fields to render as separate plots. All other fields will be "
"summed as needed. Can rename fields with new_name=old_name.") "summed as needed. Can rename fields with new_name=old_name.")
parser.add_argument( parser.add_argument(
'-x', '-x',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Fields to use for the x-axis. Can rename fields with " help="Fields to use for the x-axis. Can rename fields with "
"new_name=old_name.") "new_name=old_name.")
parser.add_argument( parser.add_argument(
'-y', '-y',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Fields to use for the y-axis. Can rename fields with " help="Fields to use for the y-axis. Can rename fields with "
"new_name=old_name.") "new_name=old_name.")
parser.add_argument( parser.add_argument(
@ -771,7 +769,7 @@ if __name__ == "__main__":
"sometimes suffer from inconsistent widths.") "sometimes suffer from inconsistent widths.")
parser.add_argument( parser.add_argument(
'--colors', '--colors',
type=lambda x: x.split(','), type=lambda x: [x.strip() for x in x.split(',')],
help="Colors to use.") help="Colors to use.")
parser.add_argument( parser.add_argument(
'--chars', '--chars',
@ -780,17 +778,21 @@ if __name__ == "__main__":
'--line-chars', '--line-chars',
help="Characters to use for lines.") help="Characters to use for lines.")
parser.add_argument( parser.add_argument(
'-L', '--no-lines', '-.', '--points',
action='store_true', action='store_true',
help="Only draw the data points.") help="Only draw the data points.")
parser.add_argument( parser.add_argument(
'-W', '--width', '-W', '--width',
nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Width in columns. 0 uses the terminal width. Defaults to " help="Width in columns. 0 uses the terminal width. Defaults to "
"min(terminal, 80).") "min(terminal, 80).")
parser.add_argument( parser.add_argument(
'-H', '--height', '-H', '--height',
nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Height in rows. 0 uses the terminal height. Defaults to 17.") help="Height in rows. 0 uses the terminal height. Defaults to 17.")
parser.add_argument( parser.add_argument(
'-z', '--cat', '-z', '--cat',
@ -798,11 +800,15 @@ if __name__ == "__main__":
help="Pipe directly to stdout.") help="Pipe directly to stdout.")
parser.add_argument( parser.add_argument(
'-X', '--xlim', '-X', '--xlim',
type=lambda x: tuple(dat(x) if x else None for x in x.split(',')), type=lambda x: tuple(
dat(x) if x.strip() else None
for x in x.split(',')),
help="Range for the x-axis.") help="Range for the x-axis.")
parser.add_argument( parser.add_argument(
'-Y', '--ylim', '-Y', '--ylim',
type=lambda x: tuple(dat(x) if x else None for x in x.split(',')), type=lambda x: tuple(
dat(x) if x.strip() else None
for x in x.split(',')),
help="Range for the y-axis.") help="Range for the y-axis.")
parser.add_argument( parser.add_argument(
'--xlog', '--xlog',

View File

@ -671,46 +671,46 @@ if __name__ == "__main__":
help="Only show percentage change, not a full diff.") help="Only show percentage change, not a full diff.")
parser.add_argument( parser.add_argument(
'-b', '--by', '-b', '--by',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Group by these fields. All other fields will be merged as " help="Group by these fields. All other fields will be merged as "
"needed. Can rename fields with new_name=old_name.") "needed. Can rename fields with new_name=old_name.")
parser.add_argument( parser.add_argument(
'-f', '--fields', '-f', '--fields',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Use these fields. Can rename fields with new_name=old_name.") help="Use these fields. Can rename fields with new_name=old_name.")
parser.add_argument( parser.add_argument(
'-D', '--define', '-D', '--define',
type=lambda x: (lambda k,v: (k, set(v.split(','))))(*x.split('=', 1)),
action='append', action='append',
type=lambda x: (lambda k,v: (k, set(v.split(','))))(*x.split('=', 1)),
help="Only include rows where this field is this value. May include " help="Only include rows where this field is this value. May include "
"comma-separated options.") "comma-separated options.")
parser.add_argument( parser.add_argument(
'--add', '--add',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Add these fields (the default).") help="Add these fields (the default).")
parser.add_argument( parser.add_argument(
'--mul', '--mul',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Multiply these fields.") help="Multiply these fields.")
parser.add_argument( parser.add_argument(
'--min', '--min',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Take the minimum of these fields.") help="Take the minimum of these fields.")
parser.add_argument( parser.add_argument(
'--max', '--max',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Take the maximum of these fields.") help="Take the maximum of these fields.")
parser.add_argument( parser.add_argument(
'--avg', '--avg',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Average these fields.") help="Average these fields.")
parser.add_argument( parser.add_argument(
'-s', '--sort', '-s', '--sort',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Sort by these fields.") help="Sort by these fields.")
parser.add_argument( parser.add_argument(
'-S', '--reverse-sort', '-S', '--reverse-sort',
type=lambda x: [x.strip() for x in x.split(',')], action='append',
help="Sort by these fields, but backwards.") help="Sort by these fields, but backwards.")
parser.add_argument( parser.add_argument(
'-Y', '--summary', '-Y', '--summary',

View File

@ -121,9 +121,10 @@ if __name__ == "__main__":
nargs='?', nargs='?',
help="Path to read from.") help="Path to read from.")
parser.add_argument( parser.add_argument(
'-n', '-n', '--lines',
'--lines', nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Show this many lines of history. 0 uses the terminal height. " help="Show this many lines of history. 0 uses the terminal height. "
"Defaults to 5.") "Defaults to 5.")
parser.add_argument( parser.add_argument(

View File

@ -525,9 +525,9 @@ def find_runner(runner, **args):
# other context # other context
if args.get('geometry'): if args.get('geometry'):
cmd.append('-g%s' % args['geometry']) cmd.append('-G%s' % args['geometry'])
if args.get('powerloss'): if args.get('powerloss'):
cmd.append('-p%s' % args['powerloss']) cmd.append('-P%s' % args['powerloss'])
if args.get('disk'): if args.get('disk'):
cmd.append('-d%s' % args['disk']) cmd.append('-d%s' % args['disk'])
if args.get('trace'): if args.get('trace'):
@ -1009,6 +1009,10 @@ def run(runner, test_ids=[], **args):
total_perms)) total_perms))
print() print()
# automatic job detection?
if args.get('jobs') == 0:
args['jobs'] = len(os.sched_getaffinity(0))
# truncate and open logs here so they aren't disconnected between tests # truncate and open logs here so they aren't disconnected between tests
stdout = None stdout = None
if args.get('stdout'): if args.get('stdout'):
@ -1251,13 +1255,11 @@ if __name__ == "__main__":
action='append', action='append',
help="Override a test define.") help="Override a test define.")
test_parser.add_argument( test_parser.add_argument(
'-g', '--geometry', '-G', '--geometry',
help="Comma-separated list of disk geometries to test. " help="Comma-separated list of disk geometries to test.")
"Defaults to d,e,E,n,N.")
test_parser.add_argument( test_parser.add_argument(
'-p', '--powerloss', '-P', '--powerloss',
help="Comma-separated list of power-loss scenarios to test. " help="Comma-separated list of power-loss scenarios to test.")
"Defaults to 0,l.")
test_parser.add_argument( test_parser.add_argument(
'-d', '--disk', '-d', '--disk',
help="Direct block device operations to this file.") help="Direct block device operations to this file.")
@ -1283,8 +1285,8 @@ if __name__ == "__main__":
'-j', '--jobs', '-j', '--jobs',
nargs='?', nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=len(os.sched_getaffinity(0)), const=0,
help="Number of parallel runners to run.") help="Number of parallel runners to run. 0 runs one runner per core.")
test_parser.add_argument( test_parser.add_argument(
'-k', '--keep-going', '-k', '--keep-going',
action='store_true', action='store_true',

View File

@ -853,11 +853,15 @@ if __name__ == "__main__":
help="Render wear.") help="Render wear.")
parser.add_argument( parser.add_argument(
'-b', '--block', '-b', '--block',
type=lambda x: tuple(int(x,0) if x else None for x in x.split(',',1)), type=lambda x: tuple(
int(x, 0) if x.strip() else None
for x in x.split(',')),
help="Show a specific block or range of blocks.") help="Show a specific block or range of blocks.")
parser.add_argument( parser.add_argument(
'-i', '--off', '-i', '--off',
type=lambda x: tuple(int(x,0) if x else None for x in x.split(',',1)), type=lambda x: tuple(
int(x, 0) if x.strip() else None
for x in x.split(',')),
help="Show a specific offset or range of offsets.") help="Show a specific offset or range of offsets.")
parser.add_argument( parser.add_argument(
'-B', '--block-size', '-B', '--block-size',
@ -901,24 +905,30 @@ if __name__ == "__main__":
help="Characters to use for showing wear.") help="Characters to use for showing wear.")
parser.add_argument( parser.add_argument(
'--colors', '--colors',
type=lambda x: x.split(','), type=lambda x: [x.strip() for x in x.split(',')],
help="Colors to use for read, prog, erase, noop operations.") help="Colors to use for read, prog, erase, noop operations.")
parser.add_argument( parser.add_argument(
'--wear-colors', '--wear-colors',
type=lambda x: x.split(','), type=lambda x: [x.strip() for x in x.split(',')],
help="Colors to use for showing wear.") help="Colors to use for showing wear.")
parser.add_argument( parser.add_argument(
'-W', '--width', '-W', '--width',
nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Width in columns. 0 uses the terminal width. Defaults to " help="Width in columns. 0 uses the terminal width. Defaults to "
"min(terminal, 80).") "min(terminal, 80).")
parser.add_argument( parser.add_argument(
'-H', '--height', '-H', '--height',
nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Height in rows. 0 uses the terminal height. Defaults to 1.") help="Height in rows. 0 uses the terminal height. Defaults to 1.")
parser.add_argument( parser.add_argument(
'-n', '--lines', '-n', '--lines',
nargs='?',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),
const=0,
help="Show this many lines of history. 0 uses the terminal height. " help="Show this many lines of history. 0 uses the terminal height. "
"Defaults to 5.") "Defaults to 5.")
parser.add_argument( parser.add_argument(