Эх сурвалжийг харах

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
Christopher Haster 3 жил өмнө
parent
commit
9507e6243c

+ 4 - 4
Makefile

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

+ 19 - 25
runners/bench_runner.c

@@ -90,9 +90,7 @@ static uintmax_t leb16_parse(const char *s, char **tail) {
 // bench_runner types
 
 typedef struct bench_geometry {
-    char short_name;
-    const char *long_name;
-
+    const char *name;
     bench_define_t defines[BENCH_GEOMETRY_DEFINE_COUNT];
 } bench_geometry_t;
 
@@ -1057,7 +1055,7 @@ static void list_implicit_defines(void) {
 
     // make sure to include builtin geometries here
     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_flush();
 
@@ -1089,12 +1087,12 @@ static void list_implicit_defines(void) {
 // geometries to bench
 
 const bench_geometry_t builtin_geometries[] = {
-    {'d', "default", {{NULL}, BENCH_CONST(16),   BENCH_CONST(512),   {NULL}}},
-    {'e', "eeprom",  {{NULL}, BENCH_CONST(1),    BENCH_CONST(512),   {NULL}}},
-    {'E', "emmc",    {{NULL}, {NULL},            BENCH_CONST(512),   {NULL}}},
-    {'n', "nor",     {{NULL}, BENCH_CONST(1),    BENCH_CONST(4096),  {NULL}}},
-    {'N', "nand",    {{NULL}, BENCH_CONST(4096), BENCH_CONST(32768), {NULL}}},
-    {0, NULL, {{NULL}, {NULL}, {NULL}, {NULL}}},
+    {"default", {{NULL}, BENCH_CONST(16),   BENCH_CONST(512),   {NULL}}},
+    {"eeprom",  {{NULL}, BENCH_CONST(1),    BENCH_CONST(512),   {NULL}}},
+    {"emmc",    {{NULL}, {NULL},            BENCH_CONST(512),   {NULL}}},
+    {"nor",     {{NULL}, BENCH_CONST(1),    BENCH_CONST(4096),  {NULL}}},
+    {"nand",    {{NULL}, BENCH_CONST(4096), BENCH_CONST(32768), {NULL}}},
+    {NULL, {{NULL}, {NULL}, {NULL}, {NULL}}},
 };
 
 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",
             "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_flush();
-        printf("%c,%-22s %7ju %7ju %7ju %7ju %11ju\n",
-                builtin_geometries[g].short_name,
-                builtin_geometries[g].long_name,
+        printf("%-24s %7ju %7ju %7ju %7ju %11ju\n",
+                builtin_geometries[g].name,
                 READ_SIZE,
                 PROG_SIZE,
                 BLOCK_SIZE,
@@ -1253,7 +1250,7 @@ enum opt_flags {
     OPT_LIST_IMPLICIT_DEFINES    = 5,
     OPT_LIST_GEOMETRIES          = 6,
     OPT_DEFINE                   = 'D',
-    OPT_GEOMETRY                 = 'g',
+    OPT_GEOMETRY                 = 'G',
     OPT_STEP                     = 's',
     OPT_DISK                     = 'd',
     OPT_TRACE                    = 't',
@@ -1262,7 +1259,7 @@ enum opt_flags {
     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[] = {
     {"help",             no_argument,       NULL, OPT_HELP},
@@ -1300,7 +1297,7 @@ const char *const help_text[] = {
     "List implicit defines in this bench-runner.",
     "List the available disk geometries.",
     "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).",
     "Redirect block device operations to this file.",
     "Redirect trace output to this file.",
@@ -1555,14 +1552,11 @@ invalid_define:
 
                     // named disk geometry
                     size_t len = strcspn(optarg, " ,");
-                    for (size_t i = 0; builtin_geometries[i].long_name; i++) {
-                        if ((len == 1
-                                && *optarg == builtin_geometries[i].short_name)
-                                || (len == strlen(
-                                        builtin_geometries[i].long_name)
-                                    && memcmp(optarg,
-                                        builtin_geometries[i].long_name,
-                                        len) == 0))  {
+                    for (size_t i = 0; builtin_geometries[i].name; i++) {
+                        if (len == strlen(builtin_geometries[i].name)
+                                && memcmp(optarg,
+                                    builtin_geometries[i].name,
+                                    len) == 0) {
                             *geometry = builtin_geometries[i];
                             optarg += len;
                             goto geometry_next;

+ 44 - 51
runners/test_runner.c

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

+ 9 - 6
scripts/bench.py

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

+ 31 - 25
scripts/plot.py

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

+ 10 - 10
scripts/summary.py

@@ -671,46 +671,46 @@ if __name__ == "__main__":
         help="Only show percentage change, not a full diff.")
     parser.add_argument(
         '-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 "
             "needed. Can rename fields with new_name=old_name.")
     parser.add_argument(
         '-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.")
     parser.add_argument(
         '-D', '--define',
-        type=lambda x: (lambda k,v: (k, set(v.split(','))))(*x.split('=', 1)),
         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 "
             "comma-separated options.")
     parser.add_argument(
         '--add',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Add these fields (the default).")
     parser.add_argument(
         '--mul',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Multiply these fields.")
     parser.add_argument(
         '--min',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Take the minimum of these fields.")
     parser.add_argument(
         '--max',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Take the maximum of these fields.")
     parser.add_argument(
         '--avg',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Average these fields.")
     parser.add_argument(
         '-s', '--sort',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Sort by these fields.")
     parser.add_argument(
         '-S', '--reverse-sort',
-        type=lambda x: [x.strip() for x in x.split(',')],
+        action='append',
         help="Sort by these fields, but backwards.")
     parser.add_argument(
         '-Y', '--summary',

+ 3 - 2
scripts/tailpipe.py

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

+ 12 - 10
scripts/test.py

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

+ 14 - 4
scripts/tracebd.py

@@ -853,11 +853,15 @@ if __name__ == "__main__":
         help="Render wear.")
     parser.add_argument(
         '-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.")
     parser.add_argument(
         '-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.")
     parser.add_argument(
         '-B', '--block-size',
@@ -901,24 +905,30 @@ if __name__ == "__main__":
         help="Characters to use for showing wear.")
     parser.add_argument(
         '--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.")
     parser.add_argument(
         '--wear-colors',
-        type=lambda x: x.split(','),
+        type=lambda x: [x.strip() for x in x.split(',')],
         help="Colors to use for showing wear.")
     parser.add_argument(
         '-W', '--width',
+        nargs='?',
         type=lambda x: int(x, 0),
+        const=0,
         help="Width in columns. 0 uses the terminal width. Defaults to "
             "min(terminal, 80).")
     parser.add_argument(
         '-H', '--height',
+        nargs='?',
         type=lambda x: int(x, 0),
+        const=0,
         help="Height in rows. 0 uses the terminal height. Defaults to 1.")
     parser.add_argument(
         '-n', '--lines',
+        nargs='?',
         type=lambda x: int(x, 0),
+        const=0,
         help="Show this many lines of history. 0 uses the terminal height. "
             "Defaults to 5.")
     parser.add_argument(