| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770 |
- #ifndef _POSIX_C_SOURCE
- #define _POSIX_C_SOURCE 199309L
- #endif
- #include "runners/test_runner.h"
- #include "bd/lfs_testbd.h"
- #include <getopt.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <setjmp.h>
- #include <fcntl.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <unistd.h>
- // test suites in a custom ld section
- extern struct test_suite __start__test_suites;
- extern struct test_suite __stop__test_suites;
- const struct test_suite *test_suites = &__start__test_suites;
- #define TEST_SUITE_COUNT \
- ((size_t)(&__stop__test_suites - &__start__test_suites))
- // test geometries
- struct test_geometry {
- const char *name;
- intmax_t defines[TEST_GEOMETRY_DEFINE_COUNT];
- };
- const struct test_geometry test_geometries[TEST_GEOMETRY_COUNT]
- = TEST_GEOMETRIES;
- // test define lookup and management
- const intmax_t *test_override_defines;
- intmax_t (*const *test_case_defines)(void);
- const intmax_t *test_geometry_defines;
- const intmax_t test_default_defines[TEST_PREDEFINE_COUNT]
- = TEST_DEFAULTS;
- uint8_t test_override_predefine_map[TEST_PREDEFINE_COUNT];
- uint8_t test_override_define_map[256];
- uint8_t test_case_predefine_map[TEST_PREDEFINE_COUNT];
- const char *const *test_override_names;
- size_t test_override_count;
- const char *const test_predefine_names[TEST_PREDEFINE_COUNT]
- = TEST_PREDEFINE_NAMES;
- const char *const *test_define_names;
- size_t test_define_count;
- intmax_t test_predefine(size_t define) {
- if (test_override_defines
- && test_override_predefine_map[define] != 0xff) {
- return test_override_defines[test_override_predefine_map[define]];
- } else if (test_case_defines
- && test_case_predefine_map[define] != 0xff
- && test_case_defines[test_case_predefine_map[define]]) {
- return test_case_defines[test_case_predefine_map[define]]();
- } else if (define < TEST_GEOMETRY_DEFINE_COUNT) {
- return test_geometry_defines[define];
- } else {
- return test_default_defines[define-TEST_GEOMETRY_DEFINE_COUNT];
- }
- }
- intmax_t test_define(size_t define) {
- if (test_override_defines
- && test_override_define_map[define] != 0xff) {
- return test_override_defines[test_override_define_map[define]];
- } else if (test_case_defines
- && test_case_defines[define]) {
- return test_case_defines[define]();
- }
- fprintf(stderr, "error: undefined define %s\n",
- test_define_names[define]);
- assert(false);
- exit(-1);
- }
- static void define_geometry(const struct test_geometry *geometry) {
- test_geometry_defines = geometry->defines;
- }
- static void test_define_overrides(
- const char *const *override_names,
- const intmax_t *override_defines,
- size_t override_count) {
- test_override_defines = override_defines;
- test_override_names = override_names;
- test_override_count = override_count;
- // map any override predefines
- memset(test_override_predefine_map, 0xff, TEST_PREDEFINE_COUNT);
- for (size_t i = 0; i < test_override_count; i++) {
- for (size_t j = 0; j < TEST_PREDEFINE_COUNT; j++) {
- if (strcmp(test_override_names[i], test_predefine_names[j]) == 0) {
- test_override_predefine_map[j] = i;
- }
- }
- }
- }
- static void define_suite(const struct test_suite *suite) {
- test_define_names = suite->define_names;
- test_define_count = suite->define_count;
- // map any override defines
- memset(test_override_define_map, 0xff, suite->define_count);
- for (size_t i = 0; i < test_override_count; i++) {
- for (size_t j = 0; j < suite->define_count; j++) {
- if (strcmp(test_override_names[i], suite->define_names[j]) == 0) {
- test_override_define_map[j] = i;
- }
- }
- }
- // map any suite/case predefines
- memset(test_case_predefine_map, 0xff, TEST_PREDEFINE_COUNT);
- for (size_t i = 0; i < suite->define_count; i++) {
- for (size_t j = 0; j < TEST_PREDEFINE_COUNT; j++) {
- if (strcmp(suite->define_names[i], test_predefine_names[j]) == 0) {
- test_case_predefine_map[j] = i;
- }
- }
- }
- }
- static void define_perm(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm) {
- (void)suite;
- if (case_->defines) {
- test_case_defines = case_->defines[perm];
- } else {
- test_case_defines = NULL;
- }
- }
- // a quick encoding scheme for sequences of power-loss
- static void leb16_print(
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- for (size_t i = 0; i < cycle_count; i++) {
- lfs_testbd_powercycles_t x = cycles[i];
- while (true) {
- lfs_testbd_powercycles_t nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0);
- printf("%c", (nibble < 10) ? '0'+nibble : 'a'+nibble-10);
- if (x <= 0xf) {
- break;
- }
- x >>= 4;
- }
- }
- }
- static size_t leb16_parse(const char *s, char **tail,
- lfs_testbd_powercycles_t **cycles) {
- // first lets count how many number we're dealing with
- size_t count = 0;
- size_t len = 0;
- for (size_t i = 0;; i++) {
- if ((s[i] >= '0' && s[i] <= '9')
- || (s[i] >= 'a' && s[i] <= 'f')) {
- len = i+1;
- count += 1;
- } else if ((s[i] >= 'g' && s[i] <= 'v')) {
- // do nothing
- } else {
- break;
- }
- }
- // then parse
- lfs_testbd_powercycles_t *cycles_ = malloc(
- count * sizeof(lfs_testbd_powercycles_t));
- size_t i = 0;
- lfs_testbd_powercycles_t x = 0;
- size_t k = 0;
- for (size_t j = 0; j < len; j++) {
- lfs_testbd_powercycles_t nibble = s[j];
- nibble = (nibble < 'a') ? nibble-'0' : nibble-'a'+10;
- x |= (nibble & 0xf) << (4*k);
- k += 1;
- if (!(nibble & 0x10)) {
- cycles_[i] = x;
- i += 1;
- x = 0;
- k = 0;
- }
- }
- if (tail) {
- *tail = (char*)s + len;
- }
- *cycles = cycles_;
- return count;
- }
- // test state
- typedef struct test_powerloss {
- char short_name;
- const char *long_name;
- void (*run)(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count);
- const lfs_testbd_powercycles_t *cycles;
- size_t cycle_count;
- } test_powerloss_t;
- static void run_powerloss_none(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count);
- const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
- {'0', "none", run_powerloss_none, NULL, 0},
- };
- size_t test_powerloss_count = 1;
- typedef struct test_id {
- const char *suite;
- const char *case_;
- size_t perm;
- const lfs_testbd_powercycles_t *cycles;
- size_t cycle_count;
- } test_id_t;
- const test_id_t *test_ids = (const test_id_t[]) {
- {NULL, NULL, -1, NULL, 0},
- };
- size_t test_id_count = 1;
- const char *test_geometry = NULL;
- size_t test_start = 0;
- size_t test_stop = -1;
- size_t test_step = 1;
- const char *test_disk_path = NULL;
- const char *test_trace_path = NULL;
- FILE *test_trace_file = NULL;
- uint32_t test_trace_cycles = 0;
- lfs_testbd_delay_t test_read_delay = 0.0;
- lfs_testbd_delay_t test_prog_delay = 0.0;
- lfs_testbd_delay_t test_erase_delay = 0.0;
- // trace printing
- void test_trace(const char *fmt, ...) {
- if (test_trace_path) {
- if (!test_trace_file) {
- // Tracing output is heavy and trying to open every trace
- // call is slow, so we only try to open the trace file every
- // so often. Note this doesn't affect successfully opened files
- if (test_trace_cycles % 128 != 0) {
- test_trace_cycles += 1;
- return;
- }
- test_trace_cycles += 1;
- int fd;
- if (strcmp(test_trace_path, "-") == 0) {
- fd = dup(1);
- } else {
- fd = open(
- test_trace_path,
- O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK,
- 0666);
- }
- if (fd < 0) {
- return;
- }
- FILE *f = fdopen(fd, "a");
- assert(f);
- int err = setvbuf(f, NULL, _IOLBF, BUFSIZ);
- assert(!err);
- test_trace_file = f;
- }
- va_list va;
- va_start(va, fmt);
- int res = vfprintf(test_trace_file, fmt, va);
- if (res < 0) {
- fclose(test_trace_file);
- test_trace_file = NULL;
- }
- va_end(va);
- }
- }
- // how many permutations are there actually in a test case
- static void count_perms(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count,
- size_t *perms,
- size_t *filtered) {
- (void)cycle_count;
- size_t perms_ = 0;
- size_t filtered_ = 0;
- for (size_t p = 0; p < (cycles ? 1 : test_powerloss_count); p++) {
- if (!cycles
- && test_powerlosses[p].short_name != '0'
- && !(case_->flags & TEST_REENTRANT)) {
- continue;
- }
- size_t perm_ = 0;
- for (size_t g = 0; g < TEST_GEOMETRY_COUNT; g++) {
- if (test_geometry && strcmp(
- test_geometries[g].name, test_geometry) != 0) {
- continue;
- }
- for (size_t k = 0; k < case_->permutations; k++) {
- perm_ += 1;
- if (perm != (size_t)-1 && perm_ != perm) {
- continue;
- }
- perms_ += 1;
- // setup defines
- define_perm(suite, case_, k);
- define_geometry(&test_geometries[g]);
- if (case_->filter && !case_->filter()) {
- continue;
- }
- filtered_ += 1;
- }
- }
- }
- *perms += perms_;
- *filtered += filtered_;
- }
- // operations we can do
- static void summary(void) {
- printf("%-36s %7s %7s %7s %11s\n",
- "", "flags", "suites", "cases", "perms");
- size_t cases = 0;
- test_flags_t flags = 0;
- size_t perms = 0;
- size_t filtered = 0;
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- define_suite(&test_suites[i]);
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- count_perms(&test_suites[i], &test_suites[i].cases[j],
- test_ids[t].perm,
- test_ids[t].cycles,
- test_ids[t].cycle_count,
- &perms, &filtered);
- }
- cases += test_suites[i].case_count;
- flags |= test_suites[i].flags;
- }
- }
- char perm_buf[64];
- sprintf(perm_buf, "%zu/%zu", filtered, perms);
- char flag_buf[64];
- sprintf(flag_buf, "%s%s",
- (flags & TEST_REENTRANT) ? "r" : "",
- (!flags) ? "-" : "");
- printf("%-36s %7s %7zu %7zu %11s\n",
- "TOTAL",
- flag_buf,
- TEST_SUITE_COUNT,
- cases,
- perm_buf);
- }
- static void list_suites(void) {
- printf("%-36s %7s %7s %11s\n", "suite", "flags", "cases", "perms");
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- define_suite(&test_suites[i]);
- size_t perms = 0;
- size_t filtered = 0;
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- count_perms(&test_suites[i], &test_suites[i].cases[j],
- test_ids[t].perm,
- test_ids[t].cycles,
- test_ids[t].cycle_count,
- &perms, &filtered);
- }
- char perm_buf[64];
- sprintf(perm_buf, "%zu/%zu", filtered, perms);
- char flag_buf[64];
- sprintf(flag_buf, "%s%s",
- (test_suites[i].flags & TEST_REENTRANT) ? "r" : "",
- (!test_suites[i].flags) ? "-" : "");
- printf("%-36s %7s %7zu %11s\n",
- test_suites[i].id,
- flag_buf,
- test_suites[i].case_count,
- perm_buf);
- }
- }
- }
- static void list_cases(void) {
- printf("%-36s %7s %11s\n", "case", "flags", "perms");
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- define_suite(&test_suites[i]);
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- size_t perms = 0;
- size_t filtered = 0;
- count_perms(&test_suites[i], &test_suites[i].cases[j],
- test_ids[t].perm,
- test_ids[t].cycles,
- test_ids[t].cycle_count,
- &perms, &filtered);
- char perm_buf[64];
- sprintf(perm_buf, "%zu/%zu", filtered, perms);
- char flag_buf[64];
- sprintf(flag_buf, "%s%s",
- (test_suites[i].cases[j].flags & TEST_REENTRANT)
- ? "r" : "",
- (!test_suites[i].cases[j].flags)
- ? "-" : "");
- printf("%-36s %7s %11s\n",
- test_suites[i].cases[j].id,
- flag_buf,
- perm_buf);
- }
- }
- }
- }
- static void list_paths(void) {
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- printf("%-36s %-36s\n",
- test_suites[i].cases[j].id,
- test_suites[i].cases[j].path);
- }
- }
- }
- }
- static void list_defines(void) {
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- define_suite(&test_suites[i]);
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- for (size_t p = 0;
- p < (test_ids[t].cycles ? 1 : test_powerloss_count);
- p++) {
- if (!test_ids[t].cycles
- && test_powerlosses[p].short_name != '0'
- && !(test_suites[i].cases[j].flags
- & TEST_REENTRANT)) {
- continue;
- }
- size_t perm_ = 0;
- for (size_t g = 0; g < TEST_GEOMETRY_COUNT; g++) {
- if (test_geometry && strcmp(
- test_geometries[g].name, test_geometry) != 0) {
- continue;
- }
- for (size_t k = 0;
- k < test_suites[i].cases[j].permutations;
- k++) {
- perm_ += 1;
- if (test_ids[t].perm != (size_t)-1
- && perm_ != test_ids[t].perm) {
- continue;
- }
- // setup defines
- define_perm(&test_suites[i],
- &test_suites[i].cases[j],
- k);
- define_geometry(&test_geometries[g]);
- // print the case
- char id_buf[256];
- sprintf(id_buf, "%s#%zu",
- test_suites[i].cases[j].id, perm_);
- printf("%-36s ", id_buf);
- // special case for the current geometry
- printf("GEOMETRY=%s ", test_geometries[g].name);
- // print each define
- for (size_t l = 0;
- l < test_suites[i].define_count;
- l++) {
- if (test_suites[i].cases[j].defines
- && test_suites[i].cases[j]
- .defines[k][l]) {
- printf("%s=%jd ",
- test_suites[i].define_names[l],
- test_define(l));
- }
- }
- printf("\n");
- }
- }
- }
- }
- }
- }
- }
- static void list_geometries(void) {
- for (size_t i = 0; i < TEST_GEOMETRY_COUNT; i++) {
- if (test_geometry && strcmp(
- test_geometries[i].name,
- test_geometry) != 0) {
- continue;
- }
- define_geometry(&test_geometries[i]);
- printf("%-36s ", test_geometries[i].name);
- // print each define
- for (size_t k = 0; k < TEST_GEOMETRY_DEFINE_COUNT; k++) {
- printf("%s=%jd ",
- test_predefine_names[k],
- test_predefine(k));
- }
- printf("\n");
- }
- }
- static void list_defaults(void) {
- printf("%-36s ", "defaults");
- // print each define
- for (size_t k = 0; k < TEST_DEFAULT_DEFINE_COUNT; k++) {
- printf("%s=%jd ",
- test_predefine_names[k+TEST_GEOMETRY_DEFINE_COUNT],
- test_predefine(k+TEST_GEOMETRY_DEFINE_COUNT));
- }
- printf("\n");
- }
- // scenarios to run tests under power-loss
- static void run_powerloss_none(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- (void)cycles;
- (void)cycle_count;
- (void)suite;
- // create block device and configuration
- lfs_testbd_t bd;
- struct lfs_config cfg = {
- .context = &bd,
- .read = lfs_testbd_read,
- .prog = lfs_testbd_prog,
- .erase = lfs_testbd_erase,
- .sync = lfs_testbd_sync,
- .read_size = READ_SIZE,
- .prog_size = PROG_SIZE,
- .block_size = BLOCK_SIZE,
- .block_count = BLOCK_COUNT,
- .block_cycles = BLOCK_CYCLES,
- .cache_size = CACHE_SIZE,
- .lookahead_size = LOOKAHEAD_SIZE,
- };
- struct lfs_testbd_config bdcfg = {
- .erase_value = ERASE_VALUE,
- .erase_cycles = ERASE_CYCLES,
- .badblock_behavior = BADBLOCK_BEHAVIOR,
- .disk_path = test_disk_path,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
- if (err) {
- fprintf(stderr, "error: could not create block device: %d\n", err);
- exit(-1);
- }
- // run the test
- printf("running %s#%zu\n", case_->id, perm);
- case_->run(&cfg);
- printf("finished %s#%zu\n", case_->id, perm);
- // cleanup
- err = lfs_testbd_destroy(&cfg);
- if (err) {
- fprintf(stderr, "error: could not destroy block device: %d\n", err);
- exit(-1);
- }
- }
- static void powerloss_longjmp(void *c) {
- jmp_buf *powerloss_jmp = c;
- longjmp(*powerloss_jmp, 1);
- }
- static void run_powerloss_linear(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- (void)cycles;
- (void)cycle_count;
- (void)suite;
- // create block device and configuration
- lfs_testbd_t bd;
- jmp_buf powerloss_jmp;
- volatile lfs_testbd_powercycles_t i = 1;
- struct lfs_config cfg = {
- .context = &bd,
- .read = lfs_testbd_read,
- .prog = lfs_testbd_prog,
- .erase = lfs_testbd_erase,
- .sync = lfs_testbd_sync,
- .read_size = READ_SIZE,
- .prog_size = PROG_SIZE,
- .block_size = BLOCK_SIZE,
- .block_count = BLOCK_COUNT,
- .block_cycles = BLOCK_CYCLES,
- .cache_size = CACHE_SIZE,
- .lookahead_size = LOOKAHEAD_SIZE,
- };
- struct lfs_testbd_config bdcfg = {
- .erase_value = ERASE_VALUE,
- .erase_cycles = ERASE_CYCLES,
- .badblock_behavior = BADBLOCK_BEHAVIOR,
- .disk_path = test_disk_path,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- .power_cycles = i,
- .powerloss_behavior = POWERLOSS_BEHAVIOR,
- .powerloss_cb = powerloss_longjmp,
- .powerloss_data = &powerloss_jmp,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
- if (err) {
- fprintf(stderr, "error: could not create block device: %d\n", err);
- exit(-1);
- }
- // run the test, increasing power-cycles as power-loss events occur
- printf("running %s#%zu\n", case_->id, perm);
- while (true) {
- if (!setjmp(powerloss_jmp)) {
- // run the test
- case_->run(&cfg);
- break;
- }
- // power-loss!
- printf("powerloss %s#%zu#", case_->id, perm);
- for (lfs_testbd_powercycles_t j = 1; j <= i; j++) {
- leb16_print(&j, 1);
- }
- printf("\n");
- i += 1;
- lfs_testbd_setpowercycles(&cfg, i);
- }
- printf("finished %s#%zu\n", case_->id, perm);
- // cleanup
- err = lfs_testbd_destroy(&cfg);
- if (err) {
- fprintf(stderr, "error: could not destroy block device: %d\n", err);
- exit(-1);
- }
- }
- static void run_powerloss_exponential(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- (void)cycles;
- (void)cycle_count;
- (void)suite;
- // create block device and configuration
- lfs_testbd_t bd;
- jmp_buf powerloss_jmp;
- volatile lfs_testbd_powercycles_t i = 1;
- struct lfs_config cfg = {
- .context = &bd,
- .read = lfs_testbd_read,
- .prog = lfs_testbd_prog,
- .erase = lfs_testbd_erase,
- .sync = lfs_testbd_sync,
- .read_size = READ_SIZE,
- .prog_size = PROG_SIZE,
- .block_size = BLOCK_SIZE,
- .block_count = BLOCK_COUNT,
- .block_cycles = BLOCK_CYCLES,
- .cache_size = CACHE_SIZE,
- .lookahead_size = LOOKAHEAD_SIZE,
- };
- struct lfs_testbd_config bdcfg = {
- .erase_value = ERASE_VALUE,
- .erase_cycles = ERASE_CYCLES,
- .badblock_behavior = BADBLOCK_BEHAVIOR,
- .disk_path = test_disk_path,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- .power_cycles = i,
- .powerloss_behavior = POWERLOSS_BEHAVIOR,
- .powerloss_cb = powerloss_longjmp,
- .powerloss_data = &powerloss_jmp,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
- if (err) {
- fprintf(stderr, "error: could not create block device: %d\n", err);
- exit(-1);
- }
- // run the test, increasing power-cycles as power-loss events occur
- printf("running %s#%zu\n", case_->id, perm);
- while (true) {
- if (!setjmp(powerloss_jmp)) {
- // run the test
- case_->run(&cfg);
- break;
- }
- // power-loss!
- printf("powerloss %s#%zu#", case_->id, perm);
- for (lfs_testbd_powercycles_t j = 1; j <= i; j *= 2) {
- leb16_print(&j, 1);
- }
- printf("\n");
- i *= 2;
- lfs_testbd_setpowercycles(&cfg, i);
- }
- printf("finished %s#%zu\n", case_->id, perm);
- // cleanup
- err = lfs_testbd_destroy(&cfg);
- if (err) {
- fprintf(stderr, "error: could not destroy block device: %d\n", err);
- exit(-1);
- }
- }
- static void run_powerloss_cycles(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- (void)suite;
- // create block device and configuration
- lfs_testbd_t bd;
- jmp_buf powerloss_jmp;
- volatile size_t i = 0;
- struct lfs_config cfg = {
- .context = &bd,
- .read = lfs_testbd_read,
- .prog = lfs_testbd_prog,
- .erase = lfs_testbd_erase,
- .sync = lfs_testbd_sync,
- .read_size = READ_SIZE,
- .prog_size = PROG_SIZE,
- .block_size = BLOCK_SIZE,
- .block_count = BLOCK_COUNT,
- .block_cycles = BLOCK_CYCLES,
- .cache_size = CACHE_SIZE,
- .lookahead_size = LOOKAHEAD_SIZE,
- };
- struct lfs_testbd_config bdcfg = {
- .erase_value = ERASE_VALUE,
- .erase_cycles = ERASE_CYCLES,
- .badblock_behavior = BADBLOCK_BEHAVIOR,
- .disk_path = test_disk_path,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- .power_cycles = (i < cycle_count) ? cycles[i] : 0,
- .powerloss_behavior = POWERLOSS_BEHAVIOR,
- .powerloss_cb = powerloss_longjmp,
- .powerloss_data = &powerloss_jmp,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
- if (err) {
- fprintf(stderr, "error: could not create block device: %d\n", err);
- exit(-1);
- }
- // run the test, increasing power-cycles as power-loss events occur
- printf("running %s#%zu\n", case_->id, perm);
- while (true) {
- if (!setjmp(powerloss_jmp)) {
- // run the test
- case_->run(&cfg);
- break;
- }
- // power-loss!
- assert(i <= cycle_count);
- printf("powerloss %s#%zu#", case_->id, perm);
- leb16_print(cycles, i+1);
- printf("\n");
- i += 1;
- lfs_testbd_setpowercycles(&cfg,
- (i < cycle_count) ? cycles[i] : 0);
- }
- printf("finished %s#%zu\n", case_->id, perm);
- // cleanup
- err = lfs_testbd_destroy(&cfg);
- if (err) {
- fprintf(stderr, "error: could not destroy block device: %d\n", err);
- exit(-1);
- }
- }
- struct powerloss_exhaustive_state {
- struct lfs_config *cfg;
- lfs_testbd_t *branches;
- size_t branch_count;
- size_t branch_capacity;
- };
- struct powerloss_exhaustive_cycles {
- lfs_testbd_powercycles_t *cycles;
- size_t cycle_count;
- size_t cycle_capacity;
- };
- static void powerloss_exhaustive_branch(void *c) {
- // append to branches
- struct powerloss_exhaustive_state *state = c;
- state->branch_count += 1;
- if (state->branch_count > state->branch_capacity) {
- state->branch_capacity = (2*state->branch_capacity > 4)
- ? 2*state->branch_capacity
- : 4;
- state->branches = realloc(state->branches,
- state->branch_capacity * sizeof(lfs_testbd_t));
- if (!state->branches) {
- fprintf(stderr, "error: exhaustive: out of memory\n");
- exit(-1);
- }
- }
- // create copy-on-write copy
- int err = lfs_testbd_copy(state->cfg,
- &state->branches[state->branch_count-1]);
- if (err) {
- fprintf(stderr, "error: exhaustive: could not create bd copy\n");
- exit(-1);
- }
- // also trigger on next power cycle
- lfs_testbd_setpowercycles(state->cfg, 1);
- }
- static void run_powerloss_exhaustive_layer(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- struct lfs_config *cfg,
- struct lfs_testbd_config *bdcfg,
- size_t depth,
- struct powerloss_exhaustive_cycles *cycles) {
- (void)suite;
- struct powerloss_exhaustive_state state = {
- .cfg = cfg,
- .branches = NULL,
- .branch_count = 0,
- .branch_capacity = 0,
- };
- // run through the test without additional powerlosses, collecting possible
- // branches as we do so
- lfs_testbd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
- bdcfg->powerloss_data = &state;
- // run the tests
- case_->run(cfg);
- // aggressively clean up memory here to try to keep our memory usage low
- int err = lfs_testbd_destroy(cfg);
- if (err) {
- fprintf(stderr, "error: could not destroy block device: %d\n", err);
- exit(-1);
- }
- // recurse into each branch
- for (size_t i = 0; i < state.branch_count; i++) {
- // first push and print the branch
- cycles->cycle_count += 1;
- if (cycles->cycle_count > cycles->cycle_capacity) {
- cycles->cycle_capacity = (2*cycles->cycle_capacity > 4)
- ? 2*cycles->cycle_capacity
- : 4;
- cycles->cycles = realloc(cycles->cycles,
- cycles->cycle_capacity * sizeof(lfs_testbd_powercycles_t));
- if (!cycles->cycles) {
- fprintf(stderr, "error: exhaustive: out of memory\n");
- exit(-1);
- }
- }
- cycles->cycles[cycles->cycle_count-1] = i;
- printf("powerloss %s#%zu#", case_->id, perm);
- leb16_print(cycles->cycles, cycles->cycle_count);
- printf("\n");
- // now recurse
- cfg->context = &state.branches[i];
- run_powerloss_exhaustive_layer(suite, case_, perm,
- cfg, bdcfg, depth-1, cycles);
- // pop the cycle
- cycles->cycle_count -= 1;
- }
- // clean up memory
- free(state.branches);
- }
- static void run_powerloss_exhaustive(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- (void)cycles;
- (void)suite;
- // create block device and configuration
- lfs_testbd_t bd;
- struct lfs_config cfg = {
- .context = &bd,
- .read = lfs_testbd_read,
- .prog = lfs_testbd_prog,
- .erase = lfs_testbd_erase,
- .sync = lfs_testbd_sync,
- .read_size = READ_SIZE,
- .prog_size = PROG_SIZE,
- .block_size = BLOCK_SIZE,
- .block_count = BLOCK_COUNT,
- .block_cycles = BLOCK_CYCLES,
- .cache_size = CACHE_SIZE,
- .lookahead_size = LOOKAHEAD_SIZE,
- };
- struct lfs_testbd_config bdcfg = {
- .erase_value = ERASE_VALUE,
- .erase_cycles = ERASE_CYCLES,
- .badblock_behavior = BADBLOCK_BEHAVIOR,
- .disk_path = test_disk_path,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- .powerloss_behavior = POWERLOSS_BEHAVIOR,
- .powerloss_cb = powerloss_exhaustive_branch,
- .powerloss_data = NULL,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk_path, &bdcfg);
- if (err) {
- fprintf(stderr, "error: could not create block device: %d\n", err);
- exit(-1);
- }
- // run the test, increasing power-cycles as power-loss events occur
- printf("running %s#%zu\n", case_->id, perm);
- // recursively exhaust each layer of powerlosses
- run_powerloss_exhaustive_layer(suite, case_, perm,
- &cfg, &bdcfg, cycle_count,
- &(struct powerloss_exhaustive_cycles){NULL, 0, 0});
- printf("finished %s#%zu\n", case_->id, perm);
- }
- 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},
- };
- const char *const builtin_powerlosses_help[] = {
- "Run with no power-losses.",
- "Run with linearly-decreasing power-losses.",
- "Run with exponentially-decreasing power-losses.",
- "Run a all permutations of power-losses, this may take a while.",
- "Run a all permutations of n power-losses.",
- "Run a custom comma-separated set of power-losses.",
- "Run a custom leb16-encoded set of power-losses.",
- };
- 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,
- builtin_powerlosses_help[i]);
- }
- // a couple more options with special parsing
- printf("%-24s %s\n", "1,2,3", builtin_powerlosses_help[i+0]);
- printf("%-24s %s\n", "{1,2,3}", builtin_powerlosses_help[i+1]);
- printf("%-24s %s\n", "#1248g1", builtin_powerlosses_help[i+2]);
- }
- // global test step count
- static size_t step = 0;
- // run the tests
- static void run_perms(
- const struct test_suite *suite,
- const struct test_case *case_,
- size_t perm,
- const lfs_testbd_powercycles_t *cycles,
- size_t cycle_count) {
- for (size_t p = 0; p < (cycles ? 1 : test_powerloss_count); p++) {
- if (!cycles
- && test_powerlosses[p].short_name != '0'
- && !(case_->flags & TEST_REENTRANT)) {
- continue;
- }
- size_t perm_ = 0;
- for (size_t g = 0; g < TEST_GEOMETRY_COUNT; g++) {
- if (test_geometry && strcmp(
- test_geometries[g].name, test_geometry) != 0) {
- continue;
- }
- for (size_t k = 0; k < case_->permutations; k++) {
- perm_ += 1;
- if (perm != (size_t)-1 && perm_ != perm) {
- continue;
- }
- if (!(step >= test_start
- && step < test_stop
- && (step-test_start) % test_step == 0)) {
- step += 1;
- continue;
- }
- step += 1;
- // setup defines
- define_perm(suite, case_, k);
- define_geometry(&test_geometries[g]);
- // filter?
- if (case_->filter && !case_->filter()) {
- printf("skipped %s#%zu\n", case_->id, perm_);
- continue;
- }
- if (cycles) {
- run_powerloss_cycles(
- suite, case_, perm_,
- cycles,
- cycle_count);
- } else {
- test_powerlosses[p].run(
- suite, case_, perm_,
- test_powerlosses[p].cycles,
- test_powerlosses[p].cycle_count);
- }
- }
- }
- }
- }
- static void run(void) {
- // ignore disconnected pipes
- signal(SIGPIPE, SIG_IGN);
- for (size_t t = 0; t < test_id_count; t++) {
- for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
- if (test_ids[t].suite && strcmp(
- test_suites[i].name, test_ids[t].suite) != 0) {
- continue;
- }
- define_suite(&test_suites[i]);
- for (size_t j = 0; j < test_suites[i].case_count; j++) {
- if (test_ids[t].case_ && strcmp(
- test_suites[i].cases[j].name, test_ids[t].case_) != 0) {
- continue;
- }
- run_perms(&test_suites[i], &test_suites[i].cases[j],
- test_ids[t].perm,
- test_ids[t].cycles,
- test_ids[t].cycle_count);
- }
- }
- }
- }
- // option handling
- enum opt_flags {
- OPT_HELP = 'h',
- OPT_SUMMARY = 'Y',
- OPT_LIST_SUITES = 'l',
- OPT_LIST_CASES = 'L',
- OPT_LIST_PATHS = 1,
- OPT_LIST_DEFINES = 2,
- OPT_LIST_GEOMETRIES = 3,
- OPT_LIST_DEFAULTS = 4,
- OPT_LIST_POWERLOSSES = 5,
- OPT_DEFINE = 'D',
- OPT_GEOMETRY = 'G',
- OPT_POWERLOSS = 'p',
- OPT_START = 6,
- OPT_STEP = 7,
- OPT_STOP = 8,
- OPT_DISK = 'd',
- 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 struct option long_opts[] = {
- {"help", no_argument, NULL, OPT_HELP},
- {"summary", no_argument, NULL, OPT_SUMMARY},
- {"list-suites", no_argument, NULL, OPT_LIST_SUITES},
- {"list-cases", no_argument, NULL, OPT_LIST_CASES},
- {"list-paths", no_argument, NULL, OPT_LIST_PATHS},
- {"list-defines", no_argument, NULL, OPT_LIST_DEFINES},
- {"list-geometries", no_argument, NULL, OPT_LIST_GEOMETRIES},
- {"list-defaults", no_argument, NULL, OPT_LIST_DEFAULTS},
- {"list-powerlosses", no_argument, NULL, OPT_LIST_POWERLOSSES},
- {"define", required_argument, NULL, OPT_DEFINE},
- {"geometry", required_argument, NULL, OPT_GEOMETRY},
- {"powerloss", required_argument, NULL, OPT_POWERLOSS},
- {"start", required_argument, NULL, OPT_START},
- {"stop", required_argument, NULL, OPT_STOP},
- {"step", required_argument, NULL, OPT_STEP},
- {"disk", required_argument, NULL, OPT_DISK},
- {"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},
- };
- const char *const help_text[] = {
- "Show this help message.",
- "Show quick summary.",
- "List test suites.",
- "List test cases.",
- "List the path for each test case.",
- "List the defines for each test permutation.",
- "List the disk geometries used for testing.",
- "List the default defines in this test-runner.",
- "List the available power-loss scenarios.",
- "Override a test define.",
- "Filter by geometry.",
- "Comma-separated list of power-loss scenarios to test. Defaults to 0,l.",
- "Start at the nth test.",
- "Stop before the nth test.",
- "Only run every n tests, calculated after --start and --stop.",
- "Redirect block device operations 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) {
- void (*op)(void) = run;
- const char **override_names = NULL;
- intmax_t *override_defines = NULL;
- size_t override_count = 0;
- size_t override_capacity = 0;
- size_t test_powerloss_capacity = 0;
- size_t test_id_capacity = 0;
- // parse options
- while (true) {
- int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
- switch (c) {
- // generate help message
- case OPT_HELP: {
- printf("usage: %s [options] [test_id]\n", argv[0]);
- printf("\n");
- printf("options:\n");
- size_t i = 0;
- while (long_opts[i].name) {
- size_t indent;
- if (long_opts[i].has_arg == no_argument) {
- if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
- indent = printf(" -%c, --%s ",
- long_opts[i].val,
- long_opts[i].name);
- } else {
- indent = printf(" --%s ",
- long_opts[i].name);
- }
- } else {
- if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
- indent = printf(" -%c %s, --%s %s ",
- long_opts[i].val,
- long_opts[i].name,
- long_opts[i].name,
- long_opts[i].name);
- } else {
- indent = printf(" --%s %s ",
- long_opts[i].name,
- long_opts[i].name);
- }
- }
- // a quick, hacky, byte-level method for text wrapping
- size_t len = strlen(help_text[i]);
- size_t j = 0;
- if (indent < 24) {
- printf("%*s %.80s\n",
- (int)(24-1-indent),
- "",
- &help_text[i][j]);
- j += 80;
- } else {
- printf("\n");
- }
- while (j < len) {
- printf("%24s%.80s\n", "", &help_text[i][j]);
- j += 80;
- }
- i += 1;
- }
- printf("\n");
- exit(0);
- }
- // summary/list flags
- case OPT_SUMMARY:
- op = summary;
- break;
- case OPT_LIST_SUITES:
- op = list_suites;
- break;
- case OPT_LIST_CASES:
- op = list_cases;
- break;
- case OPT_LIST_PATHS:
- op = list_paths;
- break;
- case OPT_LIST_DEFINES:
- op = list_defines;
- break;
- case OPT_LIST_GEOMETRIES:
- op = list_geometries;
- break;
- case OPT_LIST_DEFAULTS:
- op = list_defaults;
- break;
- case OPT_LIST_POWERLOSSES:
- op = list_powerlosses;
- break;
- // configuration
- case OPT_DEFINE: {
- // special case for -DGEOMETRY=<name>, we treat this the same
- // as --geometry=<name>
- if (strncmp(optarg, "GEOMETRY=", strlen("GEOMETRY=")) == 0) {
- test_geometry = &optarg[strlen("GEOMETRY=")];
- break;
- }
- // realloc if necessary
- override_count += 1;
- if (override_count > override_capacity) {
- override_capacity = (2*override_capacity > 4)
- ? 2*override_capacity
- : 4;
- override_names = realloc(override_names,
- override_capacity * sizeof(const char *));
- override_defines = realloc(override_defines,
- override_capacity * sizeof(intmax_t));
- }
- // parse into string key/intmax_t value, cannibalizing the
- // arg in the process
- char *sep = strchr(optarg, '=');
- char *parsed = NULL;
- if (!sep) {
- goto invalid_define;
- }
- override_defines[override_count-1]
- = strtoumax(sep+1, &parsed, 0);
- if (parsed == sep+1) {
- goto invalid_define;
- }
- override_names[override_count-1] = optarg;
- *sep = '\0';
- break;
- invalid_define:
- fprintf(stderr, "error: invalid define: %s\n", optarg);
- exit(-1);
- }
- case OPT_GEOMETRY:
- test_geometry = optarg;
- 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;
- // parse the comma separated list of power-loss scenarios
- while (*optarg) {
- // allocate space
- test_powerloss_count += 1;
- if (test_powerloss_count > test_powerloss_capacity) {
- test_powerloss_capacity
- = (2*test_powerloss_capacity > 4)
- ? 2*test_powerloss_capacity
- : 4;
- test_powerlosses = realloc(
- (test_powerloss_t*)test_powerlosses,
- test_powerloss_capacity
- * sizeof(test_powerloss_t));
- }
- // parse the power-loss scenario
- optarg += strspn(optarg, " ");
- // 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)) {
- ((test_powerloss_t*)test_powerlosses)[
- test_powerloss_count-1]
- = builtin_powerlosses[i];
- optarg += len;
- goto powerloss_next;
- }
- }
- // comma-separated permutation
- if (*optarg == '{') {
- // how many cycles?
- size_t count = 1;
- for (size_t i = 0; optarg[i]; i++) {
- if (optarg[i] == ',') {
- count += 1;
- }
- }
- // parse cycles
- lfs_testbd_powercycles_t *cycles = malloc(
- count * sizeof(lfs_testbd_powercycles_t));
- size_t i = 0;
- char *s = optarg + 1;
- while (true) {
- char *parsed = NULL;
- cycles[i] = strtoumax(s, &parsed, 0);
- if (parsed == s) {
- count -= 1;
- i -= 1;
- }
- i += 1;
- s = parsed + strspn(parsed, " ");
- if (*s == ',') {
- s += 1;
- continue;
- } else if (*s == '}') {
- s += 1;
- break;
- } else {
- goto powerloss_unknown;
- }
- }
- ((test_powerloss_t*)test_powerlosses)[
- test_powerloss_count-1] = (test_powerloss_t){
- .run = run_powerloss_cycles,
- .cycles = cycles,
- .cycle_count = count,
- };
- optarg = s;
- goto powerloss_next;
- }
- // leb16-encoded permutation
- if (*optarg == '#') {
- lfs_testbd_powercycles_t *cycles;
- char *parsed = NULL;
- size_t count = leb16_parse(optarg+1, &parsed, &cycles);
- if (parsed == optarg+1) {
- goto powerloss_unknown;
- }
- ((test_powerloss_t*)test_powerlosses)[
- test_powerloss_count-1] = (test_powerloss_t){
- .run = run_powerloss_cycles,
- .cycles = cycles,
- .cycle_count = count,
- };
- optarg = (char*)parsed;
- goto powerloss_next;
- }
- // exhaustive permutations
- {
- char *parsed = NULL;
- size_t count = strtoumax(optarg, &parsed, 0);
- if (parsed == optarg) {
- goto powerloss_unknown;
- }
- ((test_powerloss_t*)test_powerlosses)[
- test_powerloss_count-1] = (test_powerloss_t){
- .run = run_powerloss_exhaustive,
- .cycles = NULL,
- .cycle_count = count,
- };
- optarg = (char*)parsed;
- goto powerloss_next;
- }
- powerloss_unknown:
- // unknown scenario?
- fprintf(stderr, "error: "
- "unknown power-loss scenario: %s\n",
- optarg);
- exit(-1);
- powerloss_next:
- optarg += strcspn(optarg, ",");
- if (*optarg == ',') {
- optarg += 1;
- }
- }
- break;
- }
- case OPT_START: {
- char *parsed = NULL;
- test_start = strtoumax(optarg, &parsed, 0);
- if (parsed == optarg) {
- fprintf(stderr, "error: invalid skip: %s\n", optarg);
- exit(-1);
- }
- break;
- }
- case OPT_STOP: {
- char *parsed = NULL;
- test_stop = strtoumax(optarg, &parsed, 0);
- if (parsed == optarg) {
- fprintf(stderr, "error: invalid count: %s\n", optarg);
- exit(-1);
- }
- break;
- }
- case OPT_STEP: {
- char *parsed = NULL;
- test_step = strtoumax(optarg, &parsed, 0);
- if (parsed == optarg) {
- fprintf(stderr, "error: invalid every: %s\n", optarg);
- exit(-1);
- }
- break;
- }
- case OPT_DISK:
- test_disk_path = optarg;
- break;
- case OPT_TRACE:
- test_trace_path = optarg;
- 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
- case -1:
- goto getopt_done;
- // unknown arg, getopt prints a message for us
- default:
- exit(-1);
- }
- }
- getopt_done: ;
- if (argc > optind) {
- // reset our test identifier list
- test_ids = NULL;
- test_id_count = 0;
- test_id_capacity = 0;
- }
- // parse test identifier, if any, cannibalizing the arg in the process
- for (; argc > optind; optind++) {
- // parse suite
- char *suite = argv[optind];
- char *case_ = strchr(suite, '#');
- size_t perm = -1;
- lfs_testbd_powercycles_t *cycles = NULL;
- size_t cycle_count = 0;
- if (case_) {
- *case_ = '\0';
- case_ += 1;
- // parse case
- char *perm_ = strchr(case_, '#');
- if (perm_) {
- *perm_ = '\0';
- perm_ += 1;
- // parse power cycles
- char *cycles_ = strchr(perm_, '#');
- if (cycles_) {
- *cycles_ = '\0';
- cycles_ += 1;
- char *parsed = NULL;
- cycle_count = leb16_parse(cycles_, &parsed, &cycles);
- if (parsed == cycles_) {
- fprintf(stderr, "error: "
- "could not parse test cycles: %s\n", cycles_);
- exit(-1);
- }
- }
- char *parsed = NULL;
- perm = strtoumax(perm_, &parsed, 10);
- if (parsed == perm_) {
- fprintf(stderr, "error: "
- "could not parse test permutation: %s\n", perm_);
- exit(-1);
- }
- }
- }
- // remove optional path and .toml suffix
- char *slash = strrchr(suite, '/');
- if (slash) {
- suite = slash+1;
- }
- size_t suite_len = strlen(suite);
- if (suite_len > 5 && strcmp(&suite[suite_len-5], ".toml") == 0) {
- suite[suite_len-5] = '\0';
- }
- // append to identifier list
- test_id_count += 1;
- if (test_id_count > test_id_capacity) {
- test_id_capacity = (2*test_id_capacity > 4)
- ? 2*test_id_capacity
- : 4;
- test_ids = realloc((test_id_t*)test_ids,
- test_id_capacity * sizeof(test_id_t));
- }
- ((test_id_t*)test_ids)[test_id_count-1] = (test_id_t){
- .suite = suite,
- .case_ = case_,
- .perm = perm,
- .cycles = cycles,
- .cycle_count = cycle_count,
- };
- }
- // register overrides
- test_define_overrides(override_names, override_defines, override_count);
- // do the thing
- op();
- // cleanup (need to be done for valgrind testing)
- free(override_names);
- free(override_defines);
- if (test_powerloss_capacity) {
- for (size_t i = 0; i < test_powerloss_count; i++) {
- free((lfs_testbd_powercycles_t*)test_powerlosses[i].cycles);
- }
- free((test_powerloss_t*)test_powerlosses);
- }
- if (test_id_capacity) {
- for (size_t i = 0; i < test_id_count; i++) {
- free((lfs_testbd_powercycles_t*)test_ids[i].cycles);
- }
- free((test_id_t*)test_ids);
- }
- }
|