From 5a271da7eb4034b68aaafc0dd023b0dbe6177cce Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 9 Feb 2026 18:30:09 -0600 Subject: [PATCH] runners: test: Reworked -P/--powerloss to use another expr-like grammar This reworks -P/--powerloss to be more consistent with other flexible flags (-D/--define, -S/--probe, etc): - Tweaks -P/--powerloss to accept multiple flags (-Pnone -Plinear) instead of a comma-separated list (-Pnone,linear) - Adopts an expr-like grammar similar to -Dx='range(3)', -Sx=123shz, etc (see below) - Generalizes run_powerloss_linear and run_powerloss_log to accept start/stop/step conditions, allowing for range and logrange exprs with minimal work --- The new expr-like grammar follows what's worked well for -D/--define, -S/--probe, etc, in which parens can be used to parameterize some of the more complex scenarios. This makes the -P/--powerloss grammar more consistent, less ad-hoc, easier to parse, while also providing flexibility for future powerloss exprs. As an example, bounded range/logrange variants of linear/log were easy to add without each needing their own little syntax: - none -> none - Run with no powerlosses - linear -> linear - Run with linearly-decreasing powerlosses - log -> log - Run with exponentially-decreasing pls - n -> permute(n) - Run all permutations of n powerlosses - exhaustive -> exhaustive - Run all powerloss permutations - {1,2,3} -> list(1,2,3) - Run explicit list of powerlosses - added range(a,b,s) - Run explicit range of powerlosses - added logrange(a,b,s) - Run explicit range of 2^n powerlosses - :1248g1 -> :1248g1 - Run custom leb128-encoded set of pls Note we still keep :-prefixed leb128-encoded powerlosses as is. This is enough of its own syntax that trying to map it to an expr doesn't really make sense. And is humorously compatible with most future grammars. --- runners/bench_runner.c | 18 +- runners/test_runner.c | 564 ++++++++++++++++++++--------------------- runners/test_runner.h | 2 +- scripts/test.py | 6 +- 4 files changed, 292 insertions(+), 298 deletions(-) diff --git a/runners/bench_runner.c b/runners/bench_runner.c index 16134ef3..6b6fbe1f 100644 --- a/runners/bench_runner.c +++ b/runners/bench_runner.c @@ -52,9 +52,9 @@ void *mappend(void **p, } // a quick self-terminating text-safe varint scheme -static void leb16_print(uintmax_t x) { +static void leb16_print(intmax_t x) { // allow 'w' to indicate negative numbers - if ((intmax_t)x < 0) { + if (x < 0) { printf("w"); x = -x; } @@ -69,7 +69,7 @@ static void leb16_print(uintmax_t x) { } } -static uintmax_t leb16_parse(const char *s, char **tail) { +static intmax_t leb16_parse(const char *s, char **tail) { bool neg = false; uintmax_t x = 0; if (tail) { @@ -392,13 +392,13 @@ intmax_t bench_override_cb(void *data, size_t i) { if (v->step) { size_t range_count; if (v->step > 0) { - range_count = (v->stop-1 - v->start) / v->step + 1; + range_count = (v->stop-1 - v->start) / +v->step + 1; } else { range_count = (v->start-1 - v->stop) / -v->step + 1; } if (i < range_count) { - return i*v->step + v->start; + return v->start + i*v->step; } i -= range_count; // value? @@ -903,7 +903,7 @@ int __wrap_vprintf(const char *fmt, va_list args) { // bench probe/recording state typedef struct bench_probe { - const char *probe; + const char *name; size_t step; double runfreq; double simfreq; @@ -1017,7 +1017,7 @@ bench_record_t *bench_find(const char *probe) { // find probe descriptor, if there is one bench_probe_t *probe_ = NULL; for (size_t i = 0; i < bench_probe_count; i++) { - if (strcmp(bench_probes[i].probe, probe) == 0) { + if (strcmp(bench_probes[i].name, probe) == 0) { probe_ = &bench_probes[i]; break; } @@ -2711,7 +2711,7 @@ int main(int argc, char **argv) { if (*optarg == ',') { optarg += 1; step = strtoumax(optarg, &parsed, 0); - // allow empty string for stop=1 + // allow empty string for step=1 if (parsed == optarg) { step = 1; } @@ -2814,7 +2814,7 @@ int main(int argc, char **argv) { // parse into string key/intmax_t value, cannibalizing the // arg in the process - probe->probe = optarg; + probe->name = optarg; sep = strchr(optarg, '='); if (sep) { *sep = '\0'; diff --git a/runners/test_runner.c b/runners/test_runner.c index b248ce94..d3aa228b 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -52,9 +52,9 @@ void *mappend(void **p, } // a quick self-terminating text-safe varint scheme -static void leb16_print(uintmax_t x) { +static void leb16_print(intmax_t x) { // allow 'w' to indicate negative numbers - if ((intmax_t)x < 0) { + if (x < 0) { printf("w"); x = -x; } @@ -69,7 +69,7 @@ static void leb16_print(uintmax_t x) { } } -static uintmax_t leb16_parse(const char *s, char **tail) { +static intmax_t leb16_parse(const char *s, char **tail) { bool neg = false; uintmax_t x = 0; if (tail) { @@ -83,7 +83,7 @@ static uintmax_t leb16_parse(const char *s, char **tail) { size_t i = 0; while (true) { - uintmax_t nibble = s[i]; + intmax_t nibble = s[i]; if (nibble >= '0' && nibble <= '9') { nibble = nibble - '0'; } else if (nibble >= 'a' && nibble <= 'v') { @@ -117,7 +117,7 @@ typedef struct test_powerloss { const struct test_powerloss *powerloss, const struct test_suite *suite, const struct test_case *case_); - const test_powercycles_t *cycles; + const test_spowercycles_t *cycles; size_t cycle_count; } test_powerloss_t; @@ -403,13 +403,13 @@ intmax_t test_override_cb(void *data, size_t i) { if (v->step) { size_t range_count; if (v->step > 0) { - range_count = (v->stop-1 - v->start) / v->step + 1; + range_count = (v->stop-1 - v->start) / +v->step + 1; } else { range_count = (v->start-1 - v->stop) / -v->step + 1; } if (i < range_count) { - return i*v->step + v->start; + return v->start + i*v->step; } i -= range_count; // value? @@ -456,10 +456,14 @@ test_ns_t test_read_sleep = 0.0; test_ns_t test_prog_sleep = 0.0; test_ns_t test_erase_sleep = 0.0; -volatile size_t TEST_PLS = 0; // incremented every powerloss +const test_powerloss_t *test_powerlosses = NULL; +size_t test_powerloss_count = 0; +size_t test_powerloss_capacity = 0; +extern const test_powerloss_t test_default_powerlosses[]; +extern const size_t test_default_powerloss_count; + +volatile test_powercycles_t TEST_PLS = 0; // incremented every powerloss -extern const test_powerloss_t *test_powerlosses; -extern size_t test_powerloss_count; // this determines both the backtrace buffer and the trace printf buffer, if @@ -647,7 +651,7 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size) { static void perm_printid( const struct test_suite *suite, const struct test_case *case_, - const test_powercycles_t *cycles, + const test_spowercycles_t *cycles, size_t cycle_count) { (void)suite; (void)cycles; @@ -733,7 +737,7 @@ static void run_powerloss_none( const struct test_suite *suite, const struct test_case *case_); #ifndef TEST_KIWIBD -static void run_powerloss_cycles( +static void run_powerloss_list( const test_powerloss_t *powerloss, const struct test_suite *suite, const struct test_case *case_); @@ -750,6 +754,14 @@ static void case_forperm( const struct test_case *case_, const test_powerloss_t *powerloss), void *data) { + // default powerlosses? + const test_powerloss_t *powerlosses = test_powerlosses; + size_t powerloss_count = test_powerloss_count; + if (!powerlosses) { + powerlosses = test_default_powerlosses; + powerloss_count = test_default_powerloss_count; + } + // explicit permutation? if (id && id->defines) { // define case permutation, the exact case perm doesn't matter here @@ -764,14 +776,14 @@ static void case_forperm( if (id && id->powerloss.run) { cb(data, suite, case_, &id->powerloss); } else { - for (size_t p = 0; p < test_powerloss_count; p++) { + for (size_t p = 0; p < powerloss_count; p++) { // skip non-reentrant tests when powerloss testing - if (test_powerlosses[p].run != run_powerloss_none + if (powerlosses[p].run != run_powerloss_none && !(case_->flags & TEST_REENTRANT)) { continue; } - cb(data, suite, case_, &test_powerlosses[p]); + cb(data, suite, case_, &powerlosses[p]); } } } @@ -807,14 +819,14 @@ static void case_forperm( if (id && id->powerloss.run) { cb(data, suite, case_, &id->powerloss); } else { - for (size_t p = 0; p < test_powerloss_count; p++) { + for (size_t p = 0; p < powerloss_count; p++) { // skip non-reentrant tests when powerloss testing - if (test_powerlosses[p].run != run_powerloss_none + if (powerlosses[p].run != run_powerloss_none && !(case_->flags & TEST_REENTRANT)) { continue; } - cb(data, suite, case_, &test_powerlosses[p]); + cb(data, suite, case_, &powerlosses[p]); } } } @@ -1444,6 +1456,21 @@ static void run_powerloss_linear( const test_powerloss_t *powerloss, const struct test_suite *suite, const struct test_case *case_) { + test_spowercycles_t start + = (powerloss->cycle_count >= 2) + ? powerloss->cycles[0] + : 0; + test_spowercycles_t stop + = (powerloss->cycle_count >= 2) + ? powerloss->cycles[1] + : (powerloss->cycle_count >= 1) + ? powerloss->cycles[0] + : -1; + test_spowercycles_t step + = (powerloss->cycle_count >= 3) + ? powerloss->cycles[2] + : 1; + // zero pls TEST_PLS = 0; @@ -1467,9 +1494,6 @@ static void run_powerloss_linear( .read_sleep = test_read_sleep, \ .prog_sleep = test_prog_sleep, \ .erase_sleep = test_erase_sleep, \ - .power_cycles = (TEST_PLS < powerloss->cycle_count) \ - ? TEST_PLS+1 \ - : 0, \ .powerloss_cb = powerloss_longjmp, \ .powerloss_data = &powerloss_jmp, #include TEST_STRINGIFY(TEST_DEFINES) @@ -1488,6 +1512,15 @@ static void run_powerloss_linear( printf("\n"); while (true) { + lfs3_emubd_setpowercycles(CFG, + (powerloss->cycle_count == 0 + || (test_spowercycles_t)TEST_PLS < ( + (step > 0) + ? (stop-1 - start) / +step + 1 + : (start-1 - stop) / -step + 1)) + ? 1 + (start + TEST_PLS*step) + : 0); + if (!setjmp(powerloss_jmp)) { // run the test @@ -1500,14 +1533,13 @@ static void run_powerloss_linear( printf("powerloss "); perm_printid(suite, case_, NULL, 0); printf(":x"); - leb16_print(TEST_PLS+1); + leb16_print(start); + leb16_print(start + (TEST_PLS+1)*step); + leb16_print(step); printf("\n"); // increment pls TEST_PLS += 1; - lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count) - ? TEST_PLS+1 - : 0); } printf("finished "); @@ -1528,6 +1560,21 @@ static void run_powerloss_log( const test_powerloss_t *powerloss, const struct test_suite *suite, const struct test_case *case_) { + test_spowercycles_t start + = (powerloss->cycle_count >= 2) + ? powerloss->cycles[0] + : 0; + test_spowercycles_t stop + = (powerloss->cycle_count >= 2) + ? powerloss->cycles[1] + : (powerloss->cycle_count >= 1) + ? powerloss->cycles[0] + : -1; + test_spowercycles_t step + = (powerloss->cycle_count >= 3) + ? powerloss->cycles[2] + : 1; + // zero pls TEST_PLS = 0; @@ -1551,9 +1598,6 @@ static void run_powerloss_log( .read_sleep = test_read_sleep, \ .prog_sleep = test_prog_sleep, \ .erase_sleep = test_erase_sleep, \ - .power_cycles = (TEST_PLS < powerloss->cycle_count) \ - ? 1 << TEST_PLS \ - : 0, \ .powerloss_cb = powerloss_longjmp, \ .powerloss_data = &powerloss_jmp, #include TEST_STRINGIFY(TEST_DEFINES) @@ -1572,6 +1616,15 @@ static void run_powerloss_log( printf("\n"); while (true) { + lfs3_emubd_setpowercycles(CFG, + (powerloss->cycle_count == 0 + || (test_spowercycles_t)TEST_PLS < ( + (step > 0) + ? (stop-1 - start) / +step + 1 + : (start-1 - stop) / -step + 1)) + ? 1 << (start + (TEST_PLS+1)*step) + : 0); + if (!setjmp(powerloss_jmp)) { // run the test @@ -1584,14 +1637,13 @@ static void run_powerloss_log( printf("powerloss "); perm_printid(suite, case_, NULL, 0); printf(":y"); - leb16_print(TEST_PLS+1); + leb16_print(start); + leb16_print(start + (TEST_PLS+1)*step); + leb16_print(step); printf("\n"); // increment pls TEST_PLS += 1; - lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count) - ? 1 << TEST_PLS - : 0); } printf("finished "); @@ -1608,7 +1660,7 @@ static void run_powerloss_log( #endif #ifndef TEST_KIWIBD -static void run_powerloss_cycles( +static void run_powerloss_list( const test_powerloss_t *powerloss, const struct test_suite *suite, const struct test_case *case_) { @@ -1635,9 +1687,10 @@ static void run_powerloss_cycles( .read_sleep = test_read_sleep, \ .prog_sleep = test_prog_sleep, \ .erase_sleep = test_erase_sleep, \ - .power_cycles = (TEST_PLS < powerloss->cycle_count) \ - ? powerloss->cycles[TEST_PLS] \ - : 0, \ + .power_cycles = \ + (0 < powerloss->cycle_count) \ + ? powerloss->cycles[0] \ + : 0, \ .powerloss_cb = powerloss_longjmp, \ .powerloss_data = &powerloss_jmp, #include TEST_STRINGIFY(TEST_DEFINES) @@ -1672,9 +1725,10 @@ static void run_powerloss_cycles( // increment pls TEST_PLS += 1; - lfs3_emubd_setpowercycles(CFG, (TEST_PLS < powerloss->cycle_count) - ? powerloss->cycles[TEST_PLS] - : 0); + lfs3_emubd_setpowercycles(CFG, + (TEST_PLS < powerloss->cycle_count) + ? powerloss->cycles[TEST_PLS] + : 0); } printf("finished "); @@ -1783,7 +1837,9 @@ static void run_powerloss_exhaustive_layer( *cycle = i+1; printf("powerloss "); - perm_printid(suite, case_, cycles->cycles, cycles->cycle_count); + perm_printid(suite, case_, + (test_spowercycles_t*)cycles->cycles, + cycles->cycle_count); printf("\n"); // now recurse @@ -1846,7 +1902,11 @@ static void run_powerloss_exhaustive( run_powerloss_exhaustive_layer( &(struct powerloss_exhaustive_cycles){NULL, 0, 0}, suite, case_, - CFG, BDCFG, powerloss->cycle_count, 0); + CFG, BDCFG, + (powerloss->cycle_count == 0) + ? SIZE_MAX + : (size_t)powerloss->cycles[0], + 0); printf("finished "); perm_printid(suite, case_, NULL, 0); @@ -1855,48 +1915,52 @@ static void run_powerloss_exhaustive( #endif -const test_powerloss_t builtin_powerlosses[] = { - {"none", run_powerloss_none, NULL, 0}, +const test_powerloss_t test_builtin_powerlosses[] = { + {"none", run_powerloss_none, NULL, 0}, #ifndef TEST_KIWIBD - {"log", run_powerloss_log, NULL, SIZE_MAX}, - {"linear", run_powerloss_linear, NULL, SIZE_MAX}, - {"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX}, + {"linear", run_powerloss_linear, NULL, 0}, + {"log", run_powerloss_log, NULL, 0}, + {"permute(n)", run_powerloss_exhaustive, NULL, 1}, + {"exhaustive", run_powerloss_exhaustive, NULL, 0}, + {"list(1,2,3)", run_powerloss_list, NULL, SIZE_MAX}, + {"range(a,b,s)", run_powerloss_linear, NULL, 3}, + {"logrange(a,b,s)", run_powerloss_log, NULL, 3}, + {":1248g1", NULL, NULL, SIZE_MAX}, #endif {NULL, NULL, NULL, 0}, }; -const char *const builtin_powerlosses_help[] = { +const char *const test_builtin_powerlosses_help[] = { "Run with no powerlosses.", #ifndef TEST_KIWIBD - "Run with exponentially-decreasing powerlosses.", "Run with linearly-decreasing powerlosses.", - "Run all powerloss permutations, this may take a while.", + "Run with exponentially-decreasing powerlosses.", "Run all permutations of n powerlosses.", - "Run custom comma-separated set of powerlosses.", + "Run all powerloss permutations, this may take a while.", + "Run explicit list of powerlosses.", + "Run explicit range of powerlosses.", + "Run explicit range of 2^n powerlosses.", "Run custom leb16-encoded set of powerlosses.", #endif }; -// default to -Pnone,linear, which provides a good heuristic while still -// running quickly -const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){ - {"none", run_powerloss_none, NULL, 0}, +// default to -Pnone -Plinear, which provides a good heuristic while +// still running quickly +const test_powerloss_t test_default_powerlosses[] = { + {"none", run_powerloss_none, NULL, 0}, #ifndef TEST_KIWIBD - {"linear", run_powerloss_linear, NULL, SIZE_MAX}, + {"linear", run_powerloss_linear, NULL, 0}, #endif }; -#ifndef TEST_KIWIBD -size_t test_powerloss_count = 2; -#else -size_t test_powerloss_count = 1; -#endif -size_t test_powerloss_capacity = 0; +const size_t test_default_powerloss_count + = sizeof(test_default_powerlosses) + / sizeof(test_powerloss_t); static void list_powerlosses(void) { // at least size so that names fit unsigned name_width = 23; - for (size_t i = 0; builtin_powerlosses[i].name; i++) { - size_t len = strlen(builtin_powerlosses[i].name); + for (size_t i = 0; test_builtin_powerlosses[i].name; i++) { + size_t len = strlen(test_builtin_powerlosses[i].name); if (len > name_width) { name_width = len; } @@ -1905,19 +1969,12 @@ static void list_powerlosses(void) { printf("%-*s %s\n", name_width, "scenario", "description"); size_t i = 0; - for (; builtin_powerlosses[i].name; i++) { + for (; test_builtin_powerlosses[i].name; i++) { printf("%-*s %s\n", name_width, - builtin_powerlosses[i].name, - builtin_powerlosses_help[i]); + test_builtin_powerlosses[i].name, + test_builtin_powerlosses_help[i]); } - - // a couple more options with special parsing - #ifndef TEST_KIWIBD - printf("%-*s %s\n", name_width, "1,2,3", builtin_powerlosses_help[i++]); - printf("%-*s %s\n", name_width, "{1,2,3}", builtin_powerlosses_help[i++]); - printf("%-*s %s\n", name_width, ":1248g1", builtin_powerlosses_help[i++]); - #endif } @@ -2066,7 +2123,7 @@ const char *const help_text[] = { "List the available powerloss scenarios.", "Override a test define.", "How deep to evaluate recursive defines before erroring.", - "Comma-separated list of powerloss scenarios to test.", + "Specify a powerloss scenario to test.", "Comma-separated range of permutations to run.", "Ignore test filters.", "Don't run internal tests.", @@ -2249,7 +2306,7 @@ int main(int argc, char **argv) { if (*optarg == ',') { optarg += 1; step = strtoumax(optarg, &parsed, 0); - // allow empty string for stop=1 + // allow empty string for step=1 if (parsed == optarg) { step = 1; } @@ -2343,172 +2400,131 @@ int main(int argc, char **argv) { break; case OPT_POWERLOSS:; - // reset our powerloss scenarios - if (test_powerloss_capacity > 0) { - free((test_powerloss_t*)test_powerlosses); - } - test_powerlosses = NULL; - test_powerloss_count = 0; - test_powerloss_capacity = 0; + // allocate space + test_powerloss_t *powerloss = mappend( + (void**)&test_powerlosses, + sizeof(test_powerloss_t), + &test_powerloss_count, + &test_powerloss_capacity); - // parse the comma separated list of powerloss scenarios - while (*optarg) { - // allocate space - test_powerloss_t *powerloss = mappend( - (void**)&test_powerlosses, - sizeof(test_powerloss_t), - &test_powerloss_count, - &test_powerloss_capacity); + // leb16-encoded permutation? + #ifndef TEST_KIWIBD + if (*optarg == ':') { + optarg += 1; + powerloss->name = "leb16"; + powerloss->run = run_powerloss_list; + powerloss->cycles = NULL; + powerloss->cycle_count = 0; - // parse the powerloss scenario - optarg += strspn(optarg, " "); - - // named powerloss scenario - size_t len = strcspn(optarg, " ,"); - 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; - } - } - - // comma-separated permutation - #ifndef TEST_KIWIBD - if (*optarg == '{') { - test_powercycles_t *cycles = NULL; - size_t cycle_count = 0; - size_t cycle_capacity = 0; - - char *s = optarg + 1; - while (true) { - parsed = NULL; - *(test_powercycles_t*)mappend( - (void**)&cycles, - sizeof(test_powercycles_t), - &cycle_count, - &cycle_capacity) - = strtoumax(s, &parsed, 0); - - s = parsed + strspn(parsed, " "); - if (*s == ',') { - s += 1; - continue; - } else if (*s == '}') { - s += 1; - break; - } else { - goto powerloss_unknown; - } - } - - *powerloss = (test_powerloss_t){ - "explicit", - run_powerloss_cycles, - cycles, - cycle_count}; - optarg = s; - goto powerloss_next; - } - #endif - - // leb16-encoded permutation - #ifndef TEST_KIWIBD - if (*optarg == ':') { - // special case for linear power cycles - if (optarg[1] == 'x') { - size_t cycle_count = leb16_parse(optarg+2, &optarg); - - *powerloss = (test_powerloss_t){ - "linear", - run_powerloss_linear, - NULL, - cycle_count}; - goto powerloss_next; - - // special case for log power cycles - } else if (optarg[1] == 'y') { - size_t cycle_count = leb16_parse(optarg+2, &optarg); - - *powerloss = (test_powerloss_t){ - "log", - run_powerloss_log, - NULL, - cycle_count}; - goto powerloss_next; - - // otherwise explicit power cycles - } else { - test_powercycles_t *cycles = NULL; - size_t cycle_count = 0; - size_t cycle_capacity = 0; - - char *s = optarg + 1; - while (true) { - parsed = NULL; - uintmax_t x = leb16_parse(s, &parsed); - if (parsed == s) { - break; - } - - *(test_powercycles_t*)mappend( - (void**)&cycles, - sizeof(test_powercycles_t), - &cycle_count, - &cycle_capacity) = x; - s = parsed; - } - - *powerloss = (test_powerloss_t){ - "explicit", - run_powerloss_cycles, - cycles, - cycle_count}; - optarg = s; - goto powerloss_next; - } - } - #endif - - // exhaustive permutations - #ifndef TEST_KIWIBD - { - parsed = NULL; - size_t count = strtoumax(optarg, &parsed, 0); - if (parsed == optarg) { - goto powerloss_unknown; - } - *powerloss = (test_powerloss_t){ - "exhaustive", - run_powerloss_exhaustive, - NULL, - count}; - optarg = (char*)parsed; - goto powerloss_next; - } - #endif - - powerloss_unknown:; - // unknown scenario? - fprintf(stderr, "error: unknown powerloss scenario: %s\n", - optarg); - exit(-1); - - powerloss_next:; - optarg += strspn(optarg, " "); - if (*optarg == ',') { + // special case for linear power cycles + if (*optarg == 'x') { + powerloss->run = run_powerloss_linear; + optarg += 1; + + // special case for log power cycles + } else if (*optarg == 'y') { + powerloss->run = run_powerloss_log; optarg += 1; - } else if (*optarg == '\0') { - break; - } else { - goto powerloss_unknown; } + + // parse power cycles + test_spowercycles_t *cycles = NULL; + size_t cycle_count = 0; + size_t cycle_capacity = 0; + + while (true) { + parsed = NULL; + intmax_t x = leb16_parse(optarg, &parsed); + if (parsed == optarg) { + break; + } + + *(test_spowercycles_t*)mappend( + (void**)&cycles, + sizeof(test_spowercycles_t), + &cycle_count, + &cycle_capacity) = x; + optarg = parsed; + } + + powerloss->cycles = cycles; + powerloss->cycle_count = cycle_count; + break; + } + #endif + + // parse powerloss scenario + size_t len = strcspn(optarg, " ("); + const test_powerloss_t *scenario = NULL; + for (size_t i = 0; test_builtin_powerlosses[i].name; i++) { + if (len == strcspn(test_builtin_powerlosses[i].name, " (") + && memcmp( + optarg, + test_builtin_powerlosses[i].name, + len) == 0) { + scenario = &test_builtin_powerlosses[i]; + break; + } + } + if (!scenario) { + goto invalid_powerloss; + } + + // parse into string name + args, cannibalizing the + // arg in the process + powerloss->name = optarg; + char *paren = strchr(optarg + strspn(optarg, " "), '('); + optarg[len] = '\0'; + powerloss->run = scenario->run; + powerloss->cycles = NULL; + powerloss->cycle_count = 0; + + if ((paren && scenario->cycle_count == 0) + || (!paren && scenario->cycle_count > 0)) { + goto invalid_powerloss; + } + + if (paren) { + optarg = paren+1; + + // parse comma-separated powerloss args + test_spowercycles_t *cycles = NULL; + size_t cycle_count = 0; + size_t cycle_capacity = 0; + + while (cycle_count < scenario->cycle_count) { + parsed = NULL; + *(test_spowercycles_t*)mappend( + (void**)&cycles, + sizeof(test_spowercycles_t), + &cycle_count, + &cycle_capacity) + = strtoumax(optarg, &parsed, 0); + if (parsed == optarg) { + goto invalid_powerloss; + } + optarg = parsed + strspn(parsed, " "); + + if (*optarg != ',') { + break; + } + optarg += 1; + } + + if (*optarg != ')') { + goto invalid_powerloss; + } + optarg += 1; + + powerloss->cycles = cycles; + powerloss->cycle_count = cycle_count; } break; + invalid_powerloss:; + fprintf(stderr, "error: invalid powerloss: %s\n", optarg); + exit(-1); + case OPT_STEP:; parsed = NULL; test_step_start = strtoumax(optarg, &parsed, 0); @@ -2713,69 +2729,45 @@ getopt_done:; // special case for linear power cycles #ifndef TEST_KIWIBD - if (cycles_ && *cycles_ == 'x') { - char *parsed = NULL; - size_t cycle_count = leb16_parse(cycles_+1, &parsed); - if (parsed == cycles_+1) { - fprintf(stderr, "error: " - "could not parse test cycles: %s\n", - cycles_); - exit(-1); + if (cycles_) { + powerloss.name = "leb16"; + powerloss.run = run_powerloss_list; + powerloss.cycles = NULL; + powerloss.cycle_count = 0; + + // special case for linear power cycles + if (*cycles_ == 'x') { + powerloss.run = run_powerloss_linear; + cycles_ += 1; + + // special case for log power cycles + } else if (*cycles_ == 'y') { + powerloss.run = run_powerloss_log; + cycles_ += 1; } - cycles_ = parsed; - powerloss = (test_powerloss_t){ - "linear", - run_powerloss_linear, - NULL, - cycle_count}; - - // special case for log power cycles - } else if (cycles_ && *cycles_ == 'y') { - char *parsed = NULL; - size_t cycle_count = leb16_parse(cycles_+1, &parsed); - if (parsed == cycles_+1) { - fprintf(stderr, "error: " - "could not parse test cycles: %s\n", - cycles_); - exit(-1); - } - cycles_ = parsed; - - powerloss = (test_powerloss_t){ - "log", - run_powerloss_log, - NULL, - cycle_count}; - - // otherwise explicit power cycles - } else if (cycles_) { // parse power cycles - test_powercycles_t *cycles = NULL; + test_spowercycles_t *cycles = NULL; size_t cycle_count = 0; size_t cycle_capacity = 0; - while (*cycles_ != '\0') { + + while (true) { char *parsed = NULL; - *(test_powercycles_t*)mappend( - (void**)&cycles, - sizeof(test_powercycles_t), - &cycle_count, - &cycle_capacity) - = leb16_parse(cycles_, &parsed); + intmax_t x = leb16_parse(cycles_, &parsed); if (parsed == cycles_) { - fprintf(stderr, "error: " - "could not parse test cycles: %s\n", - cycles_); - exit(-1); + break; } + + *(test_spowercycles_t*)mappend( + (void**)&cycles, + sizeof(test_spowercycles_t), + &cycle_count, + &cycle_capacity) = x; cycles_ = parsed; } - powerloss = (test_powerloss_t){ - "explicit", - run_powerloss_cycles, - cycles, - cycle_count}; + powerloss.cycles = cycles; + powerloss.cycle_count = cycle_count; } #endif } diff --git a/runners/test_runner.h b/runners/test_runner.h index 7b86a588..1fb943f4 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -139,7 +139,7 @@ extern const size_t test_suite_count; // this variable tracks the number of powerlosses triggered during the // current test permutation, this is useful for both tests and debugging -extern volatile size_t TEST_PLS; +extern volatile test_powercycles_t TEST_PLS; // deterministic prng for pseudo-randomness in tests uint32_t test_prng(uint32_t *state); diff --git a/scripts/test.py b/scripts/test.py index ecdd4edf..e403d739 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -820,7 +820,8 @@ def find_runner(runner, id=None, main=True, **args): if args.get('define_depth'): cmd.append('--define-depth=%s' % args['define_depth']) if args.get('powerloss'): - cmd.append('-P%s' % args['powerloss']) + for powerloss in args['powerloss']: + cmd.append('-P%s' % powerloss) if args.get('force'): cmd.append('--force') if args.get('no_internal'): @@ -1733,7 +1734,8 @@ if __name__ == "__main__": help="How deep to evaluate recursive defines before erroring.") test_parser.add_argument( '-P', '--powerloss', - help="Comma-separated list of powerloss scenarios to test.") + action='append', + help="Specify a powerloss scenario to test.") test_parser.add_argument( '--force', action='store_true',