| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721 |
- #include "runners/test_runner.h"
- #include "bd/lfs_testbd.h"
- #include <getopt.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <setjmp.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);
- static const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
- {'0', "none", run_powerloss_none, NULL, 0},
- };
- static 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;
- static const test_id_t *test_ids = (const test_id_t[]) {
- {NULL, NULL, -1, NULL, 0},
- };
- static size_t test_id_count = 1;
- static const char *test_geometry = NULL;
- static size_t test_start = 0;
- static size_t test_stop = -1;
- static size_t test_step = 1;
- static const char *test_disk = NULL;
- FILE *test_trace = NULL;
- static lfs_testbd_delay_t test_read_delay = 0.0;
- static lfs_testbd_delay_t test_prog_delay = 0.0;
- static lfs_testbd_delay_t test_erase_delay = 0.0;
- // how many permutations are there actually in a test case
- 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,
- .read_delay = test_read_delay,
- .prog_delay = test_prog_delay,
- .erase_delay = test_erase_delay,
- };
- int err = lfs_testbd_createcfg(&cfg, test_disk, &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,
- .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, &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,
- .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, &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,
- .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, &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,
- .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, &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) {
- 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 = optarg;
- break;
- case OPT_TRACE:
- if (strcmp(optarg, "-") == 0) {
- test_trace = stdout;
- } else {
- test_trace = fopen(optarg, "w");
- if (!test_trace) {
- fprintf(stderr, "error: could not open for trace: %d\n",
- -errno);
- exit(-1);
- }
- }
- 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);
- }
- }
|