1/* 2 * Runner for littlefs tests 3 * 4 * Copyright (c) 2022, The littlefs authors. 5 * SPDX-License-Identifier: BSD-3-Clause 6 */ 7#ifndef _POSIX_C_SOURCE 8#define _POSIX_C_SOURCE 199309L 9#endif 10 11#include "runners/test_runner.h" 12#include "bd/lfs_emubd.h" 13 14#include <getopt.h> 15#include <sys/types.h> 16#include <errno.h> 17#include <setjmp.h> 18#include <fcntl.h> 19#include <stdarg.h> 20#include <stdio.h> 21#include <unistd.h> 22#include <time.h> 23#include <execinfo.h> 24 25 26// some helpers 27 28// append to an array with amortized doubling 29void *mappend(void **p, 30 size_t size, 31 size_t *count, 32 size_t *capacity) { 33 uint8_t *p_ = *p; 34 size_t count_ = *count; 35 size_t capacity_ = *capacity; 36 37 count_ += 1; 38 if (count_ > capacity_) { 39 capacity_ = (2*capacity_ < 4) ? 4 : 2*capacity_; 40 41 p_ = realloc(p_, capacity_*size); 42 if (!p_) { 43 return NULL; 44 } 45 } 46 47 *p = p_; 48 *count = count_; 49 *capacity = capacity_; 50 return &p_[(count_-1)*size]; 51} 52 53// a quick self-terminating text-safe varint scheme 54static void leb16_print(uintmax_t x) { 55 // allow 'w' to indicate negative numbers 56 if ((intmax_t)x < 0) { 57 printf("w"); 58 x = -x; 59 } 60 61 while (true) { 62 char nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0); 63 printf("%c", (nibble < 10) ? '0'+nibble : 'a'+nibble-10); 64 if (x <= 0xf) { 65 break; 66 } 67 x >>= 4; 68 } 69} 70 71static uintmax_t leb16_parse(const char *s, char **tail) { 72 bool neg = false; 73 uintmax_t x = 0; 74 if (tail) { 75 *tail = (char*)s; 76 } 77 78 if (s[0] == 'w') { 79 neg = true; 80 s = s+1; 81 } 82 83 size_t i = 0; 84 while (true) { 85 uintmax_t nibble = s[i]; 86 if (nibble >= '0' && nibble <= '9') { 87 nibble = nibble - '0'; 88 } else if (nibble >= 'a' && nibble <= 'v') { 89 nibble = nibble - 'a' + 10; 90 } else { 91 // invalid? 92 return 0; 93 } 94 95 x |= (nibble & 0xf) << (4*i); 96 i += 1; 97 if (!(nibble & 0x10)) { 98 s = s + i; 99 break; 100 } 101 } 102 103 if (tail) { 104 *tail = (char*)s; 105 } 106 return neg ? -x : x; 107} 108 109 110 111// test_runner types 112 113typedef struct test_geometry { 114 const char *name; 115 test_define_t defines[TEST_GEOMETRY_DEFINE_COUNT]; 116} test_geometry_t; 117 118typedef struct test_powerloss { 119 const char *name; 120 void (*run)( 121 const lfs_emubd_powercycles_t *cycles, 122 size_t cycle_count, 123 const struct test_suite *suite, 124 const struct test_case *case_); 125 const lfs_emubd_powercycles_t *cycles; 126 size_t cycle_count; 127} test_powerloss_t; 128 129typedef struct test_id { 130 const char *name; 131 const test_define_t *defines; 132 size_t define_count; 133 const lfs_emubd_powercycles_t *cycles; 134 size_t cycle_count; 135} test_id_t; 136 137 138// test suites are linked into a custom ld section 139extern struct test_suite __start__test_suites; 140extern struct test_suite __stop__test_suites; 141 142const struct test_suite *test_suites = &__start__test_suites; 143#define TEST_SUITE_COUNT \ 144 ((size_t)(&__stop__test_suites - &__start__test_suites)) 145 146 147// test define management 148typedef struct test_define_map { 149 const test_define_t *defines; 150 size_t count; 151} test_define_map_t; 152 153typedef struct test_define_names { 154 const char *const *names; 155 size_t count; 156} test_define_names_t; 157 158intmax_t test_define_lit(void *data) { 159 return (intptr_t)data; 160} 161 162#define TEST_CONST(x) {test_define_lit, (void*)(uintptr_t)(x)} 163#define TEST_LIT(x) ((test_define_t)TEST_CONST(x)) 164 165 166#define TEST_DEF(k, v) \ 167 intmax_t test_define_##k(void *data) { \ 168 (void)data; \ 169 return v; \ 170 } 171 172 TEST_IMPLICIT_DEFINES 173#undef TEST_DEF 174 175#define TEST_DEFINE_MAP_OVERRIDE 0 176#define TEST_DEFINE_MAP_EXPLICIT 1 177#define TEST_DEFINE_MAP_PERMUTATION 2 178#define TEST_DEFINE_MAP_GEOMETRY 3 179#define TEST_DEFINE_MAP_IMPLICIT 4 180#define TEST_DEFINE_MAP_COUNT 5 181 182test_define_map_t test_define_maps[TEST_DEFINE_MAP_COUNT] = { 183 [TEST_DEFINE_MAP_IMPLICIT] = { 184 (const test_define_t[TEST_IMPLICIT_DEFINE_COUNT]) { 185 #define TEST_DEF(k, v) \ 186 [k##_i] = {test_define_##k, NULL}, 187 188 TEST_IMPLICIT_DEFINES 189 #undef TEST_DEF 190 }, 191 TEST_IMPLICIT_DEFINE_COUNT, 192 }, 193}; 194 195#define TEST_DEFINE_NAMES_SUITE 0 196#define TEST_DEFINE_NAMES_IMPLICIT 1 197#define TEST_DEFINE_NAMES_COUNT 2 198 199test_define_names_t test_define_names[TEST_DEFINE_NAMES_COUNT] = { 200 [TEST_DEFINE_NAMES_IMPLICIT] = { 201 (const char *const[TEST_IMPLICIT_DEFINE_COUNT]){ 202 #define TEST_DEF(k, v) \ 203 [k##_i] = #k, 204 205 TEST_IMPLICIT_DEFINES 206 #undef TEST_DEF 207 }, 208 TEST_IMPLICIT_DEFINE_COUNT, 209 }, 210}; 211 212intmax_t *test_define_cache; 213size_t test_define_cache_count; 214unsigned *test_define_cache_mask; 215 216const char *test_define_name(size_t define) { 217 // lookup in our test names 218 for (size_t i = 0; i < TEST_DEFINE_NAMES_COUNT; i++) { 219 if (define < test_define_names[i].count 220 && test_define_names[i].names 221 && test_define_names[i].names[define]) { 222 return test_define_names[i].names[define]; 223 } 224 } 225 226 return NULL; 227} 228 229bool test_define_ispermutation(size_t define) { 230 // is this define specific to the permutation? 231 for (size_t i = 0; i < TEST_DEFINE_MAP_IMPLICIT; i++) { 232 if (define < test_define_maps[i].count 233 && test_define_maps[i].defines[define].cb) { 234 return true; 235 } 236 } 237 238 return false; 239} 240 241intmax_t test_define(size_t define) { 242 // is the define in our cache? 243 if (define < test_define_cache_count 244 && (test_define_cache_mask[define/(8*sizeof(unsigned))] 245 & (1 << (define%(8*sizeof(unsigned)))))) { 246 return test_define_cache[define]; 247 } 248 249 // lookup in our test defines 250 for (size_t i = 0; i < TEST_DEFINE_MAP_COUNT; i++) { 251 if (define < test_define_maps[i].count 252 && test_define_maps[i].defines[define].cb) { 253 intmax_t v = test_define_maps[i].defines[define].cb( 254 test_define_maps[i].defines[define].data); 255 256 // insert into cache! 257 test_define_cache[define] = v; 258 test_define_cache_mask[define / (8*sizeof(unsigned))] 259 |= 1 << (define%(8*sizeof(unsigned))); 260 261 return v; 262 } 263 } 264 265 return 0; 266 267 // not found? 268 const char *name = test_define_name(define); 269 fprintf(stderr, "error: undefined define %s (%zd)\n", 270 name ? name : "(unknown)", 271 define); 272 assert(false); 273 exit(-1); 274} 275 276void test_define_flush(void) { 277 // clear cache between permutations 278 memset(test_define_cache_mask, 0, 279 sizeof(unsigned)*( 280 (test_define_cache_count+(8*sizeof(unsigned))-1) 281 / (8*sizeof(unsigned)))); 282} 283 284// geometry updates 285const test_geometry_t *test_geometry = NULL; 286 287void test_define_geometry(const test_geometry_t *geometry) { 288 test_define_maps[TEST_DEFINE_MAP_GEOMETRY] = (test_define_map_t){ 289 geometry->defines, TEST_GEOMETRY_DEFINE_COUNT}; 290} 291 292// override updates 293typedef struct test_override { 294 const char *name; 295 const intmax_t *defines; 296 size_t permutations; 297} test_override_t; 298 299const test_override_t *test_overrides = NULL; 300size_t test_override_count = 0; 301 302test_define_t *test_override_defines = NULL; 303size_t test_override_define_count = 0; 304size_t test_override_define_permutations = 1; 305size_t test_override_define_capacity = 0; 306 307// suite/perm updates 308void test_define_suite(const struct test_suite *suite) { 309 test_define_names[TEST_DEFINE_NAMES_SUITE] = (test_define_names_t){ 310 suite->define_names, suite->define_count}; 311 312 // make sure our cache is large enough 313 if (lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT) 314 > test_define_cache_count) { 315 // align to power of two to avoid any superlinear growth 316 size_t ncount = 1 << lfs_npw2( 317 lfs_max(suite->define_count, TEST_IMPLICIT_DEFINE_COUNT)); 318 test_define_cache = realloc(test_define_cache, ncount*sizeof(intmax_t)); 319 test_define_cache_mask = realloc(test_define_cache_mask, 320 sizeof(unsigned)*( 321 (ncount+(8*sizeof(unsigned))-1) 322 / (8*sizeof(unsigned)))); 323 test_define_cache_count = ncount; 324 } 325 326 // map any overrides 327 if (test_override_count > 0) { 328 // first figure out the total size of override permutations 329 size_t count = 0; 330 size_t permutations = 1; 331 for (size_t i = 0; i < test_override_count; i++) { 332 for (size_t d = 0; 333 d < lfs_max( 334 suite->define_count, 335 TEST_IMPLICIT_DEFINE_COUNT); 336 d++) { 337 // define name match? 338 const char *name = test_define_name(d); 339 if (name && strcmp(name, test_overrides[i].name) == 0) { 340 count = lfs_max(count, d+1); 341 permutations *= test_overrides[i].permutations; 342 break; 343 } 344 } 345 } 346 test_override_define_count = count; 347 test_override_define_permutations = permutations; 348 349 // make sure our override arrays are big enough 350 if (count * permutations > test_override_define_capacity) { 351 // align to power of two to avoid any superlinear growth 352 size_t ncapacity = 1 << lfs_npw2(count * permutations); 353 test_override_defines = realloc( 354 test_override_defines, 355 sizeof(test_define_t)*ncapacity); 356 test_override_define_capacity = ncapacity; 357 } 358 359 // zero unoverridden defines 360 memset(test_override_defines, 0, 361 sizeof(test_define_t) * count * permutations); 362 363 // compute permutations 364 size_t p = 1; 365 for (size_t i = 0; i < test_override_count; i++) { 366 for (size_t d = 0; 367 d < lfs_max( 368 suite->define_count, 369 TEST_IMPLICIT_DEFINE_COUNT); 370 d++) { 371 // define name match? 372 const char *name = test_define_name(d); 373 if (name && strcmp(name, test_overrides[i].name) == 0) { 374 // scatter the define permutations based on already 375 // seen permutations 376 for (size_t j = 0; j < permutations; j++) { 377 test_override_defines[j*count + d] = TEST_LIT( 378 test_overrides[i].defines[(j/p) 379 % test_overrides[i].permutations]); 380 } 381 382 // keep track of how many permutations we've seen so far 383 p *= test_overrides[i].permutations; 384 break; 385 } 386 } 387 } 388 } 389} 390 391void test_define_perm( 392 const struct test_suite *suite, 393 const struct test_case *case_, 394 size_t perm) { 395 if (case_->defines) { 396 test_define_maps[TEST_DEFINE_MAP_PERMUTATION] = (test_define_map_t){ 397 case_->defines + perm*suite->define_count, 398 suite->define_count}; 399 } else { 400 test_define_maps[TEST_DEFINE_MAP_PERMUTATION] = (test_define_map_t){ 401 NULL, 0}; 402 } 403} 404 405void test_define_override(size_t perm) { 406 test_define_maps[TEST_DEFINE_MAP_OVERRIDE] = (test_define_map_t){ 407 test_override_defines + perm*test_override_define_count, 408 test_override_define_count}; 409} 410 411void test_define_explicit( 412 const test_define_t *defines, 413 size_t define_count) { 414 test_define_maps[TEST_DEFINE_MAP_EXPLICIT] = (test_define_map_t){ 415 defines, define_count}; 416} 417 418void test_define_cleanup(void) { 419 // test define management can allocate a few things 420 free(test_define_cache); 421 free(test_define_cache_mask); 422 free(test_override_defines); 423} 424 425 426 427// test state 428extern const test_geometry_t *test_geometries; 429extern size_t test_geometry_count; 430 431extern const test_powerloss_t *test_powerlosses; 432extern size_t test_powerloss_count; 433 434const test_id_t *test_ids = (const test_id_t[]) { 435 {NULL, NULL, 0, NULL, 0}, 436}; 437size_t test_id_count = 1; 438 439size_t test_step_start = 0; 440size_t test_step_stop = -1; 441size_t test_step_step = 1; 442 443const char *test_disk_path = NULL; 444const char *test_trace_path = NULL; 445bool test_trace_backtrace = false; 446uint32_t test_trace_period = 0; 447uint32_t test_trace_freq = 0; 448FILE *test_trace_file = NULL; 449uint32_t test_trace_cycles = 0; 450uint64_t test_trace_time = 0; 451uint64_t test_trace_open_time = 0; 452lfs_emubd_sleep_t test_read_sleep = 0.0; 453lfs_emubd_sleep_t test_prog_sleep = 0.0; 454lfs_emubd_sleep_t test_erase_sleep = 0.0; 455 456// this determines both the backtrace buffer and the trace printf buffer, if 457// trace ends up interleaved or truncated this may need to be increased 458#ifndef TEST_TRACE_BACKTRACE_BUFFER_SIZE 459#define TEST_TRACE_BACKTRACE_BUFFER_SIZE 8192 460#endif 461void *test_trace_backtrace_buffer[ 462 TEST_TRACE_BACKTRACE_BUFFER_SIZE / sizeof(void*)]; 463 464// trace printing 465void test_trace(const char *fmt, ...) { 466 if (test_trace_path) { 467 // sample at a specific period? 468 if (test_trace_period) { 469 if (test_trace_cycles % test_trace_period != 0) { 470 test_trace_cycles += 1; 471 return; 472 } 473 test_trace_cycles += 1; 474 } 475 476 // sample at a specific frequency? 477 if (test_trace_freq) { 478 struct timespec t; 479 clock_gettime(CLOCK_MONOTONIC, &t); 480 uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000 481 + (uint64_t)t.tv_nsec; 482 if (now - test_trace_time < (1000*1000*1000) / test_trace_freq) { 483 return; 484 } 485 test_trace_time = now; 486 } 487 488 if (!test_trace_file) { 489 // Tracing output is heavy and trying to open every trace 490 // call is slow, so we only try to open the trace file every 491 // so often. Note this doesn't affect successfully opened files 492 struct timespec t; 493 clock_gettime(CLOCK_MONOTONIC, &t); 494 uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000 495 + (uint64_t)t.tv_nsec; 496 if (now - test_trace_open_time < 100*1000*1000) { 497 return; 498 } 499 test_trace_open_time = now; 500 501 // try to open the trace file 502 int fd; 503 if (strcmp(test_trace_path, "-") == 0) { 504 fd = dup(1); 505 if (fd < 0) { 506 return; 507 } 508 } else { 509 fd = open( 510 test_trace_path, 511 O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK, 512 0666); 513 if (fd < 0) { 514 return; 515 } 516 int err = fcntl(fd, F_SETFL, O_WRONLY | O_CREAT | O_APPEND); 517 assert(!err); 518 } 519 520 FILE *f = fdopen(fd, "a"); 521 assert(f); 522 int err = setvbuf(f, NULL, _IOFBF, 523 TEST_TRACE_BACKTRACE_BUFFER_SIZE); 524 assert(!err); 525 test_trace_file = f; 526 } 527 528 // print trace 529 va_list va; 530 va_start(va, fmt); 531 int res = vfprintf(test_trace_file, fmt, va); 532 va_end(va); 533 if (res < 0) { 534 fclose(test_trace_file); 535 test_trace_file = NULL; 536 return; 537 } 538 539 if (test_trace_backtrace) { 540 // print backtrace 541 size_t count = backtrace( 542 test_trace_backtrace_buffer, 543 TEST_TRACE_BACKTRACE_BUFFER_SIZE); 544 // note we skip our own stack frame 545 for (size_t i = 1; i < count; i++) { 546 res = fprintf(test_trace_file, "\tat %p\n", 547 test_trace_backtrace_buffer[i]); 548 if (res < 0) { 549 fclose(test_trace_file); 550 test_trace_file = NULL; 551 return; 552 } 553 } 554 } 555 556 // flush immediately 557 fflush(test_trace_file); 558 } 559} 560 561 562// test prng 563uint32_t test_prng(uint32_t *state) { 564 // A simple xorshift32 generator, easily reproducible. Keep in mind 565 // determinism is much more important than actual randomness here. 566 uint32_t x = *state; 567 x ^= x << 13; 568 x ^= x >> 17; 569 x ^= x << 5; 570 *state = x; 571 return x; 572} 573 574 575// encode our permutation into a reusable id 576static void perm_printid( 577 const struct test_suite *suite, 578 const struct test_case *case_, 579 const lfs_emubd_powercycles_t *cycles, 580 size_t cycle_count) { 581 (void)suite; 582 // case[:permutation[:powercycles]] 583 printf("%s:", case_->name); 584 for (size_t d = 0; 585 d < lfs_max( 586 suite->define_count, 587 TEST_IMPLICIT_DEFINE_COUNT); 588 d++) { 589 if (test_define_ispermutation(d)) { 590 leb16_print(d); 591 leb16_print(TEST_DEFINE(d)); 592 } 593 } 594 595 // only print power-cycles if any occured 596 if (cycles) { 597 printf(":"); 598 for (size_t i = 0; i < cycle_count; i++) { 599 leb16_print(cycles[i]); 600 } 601 } 602} 603 604 605// a quick trie for keeping track of permutations we've seen 606typedef struct test_seen { 607 struct test_seen_branch *branches; 608 size_t branch_count; 609 size_t branch_capacity; 610} test_seen_t; 611 612struct test_seen_branch { 613 intmax_t define; 614 struct test_seen branch; 615}; 616 617bool test_seen_insert( 618 test_seen_t *seen, 619 const struct test_suite *suite, 620 const struct test_case *case_) { 621 (void)case_; 622 bool was_seen = true; 623 624 // use the currently set defines 625 for (size_t d = 0; 626 d < lfs_max( 627 suite->define_count, 628 TEST_IMPLICIT_DEFINE_COUNT); 629 d++) { 630 // treat unpermuted defines the same as 0 631 intmax_t define = test_define_ispermutation(d) ? TEST_DEFINE(d) : 0; 632 633 // already seen? 634 struct test_seen_branch *branch = NULL; 635 for (size_t i = 0; i < seen->branch_count; i++) { 636 if (seen->branches[i].define == define) { 637 branch = &seen->branches[i]; 638 break; 639 } 640 } 641 642 // need to create a new node 643 if (!branch) { 644 was_seen = false; 645 branch = mappend( 646 (void**)&seen->branches, 647 sizeof(struct test_seen_branch), 648 &seen->branch_count, 649 &seen->branch_capacity); 650 branch->define = define; 651 branch->branch = (test_seen_t){NULL, 0, 0}; 652 } 653 654 seen = &branch->branch; 655 } 656 657 return was_seen; 658} 659 660void test_seen_cleanup(test_seen_t *seen) { 661 for (size_t i = 0; i < seen->branch_count; i++) { 662 test_seen_cleanup(&seen->branches[i].branch); 663 } 664 free(seen->branches); 665} 666 667static void run_powerloss_none( 668 const lfs_emubd_powercycles_t *cycles, 669 size_t cycle_count, 670 const struct test_suite *suite, 671 const struct test_case *case_); 672static void run_powerloss_cycles( 673 const lfs_emubd_powercycles_t *cycles, 674 size_t cycle_count, 675 const struct test_suite *suite, 676 const struct test_case *case_); 677 678// iterate through permutations in a test case 679static void case_forperm( 680 const struct test_suite *suite, 681 const struct test_case *case_, 682 const test_define_t *defines, 683 size_t define_count, 684 const lfs_emubd_powercycles_t *cycles, 685 size_t cycle_count, 686 void (*cb)( 687 void *data, 688 const struct test_suite *suite, 689 const struct test_case *case_, 690 const test_powerloss_t *powerloss), 691 void *data) { 692 // explicit permutation? 693 if (defines) { 694 test_define_explicit(defines, define_count); 695 696 for (size_t v = 0; v < test_override_define_permutations; v++) { 697 // define override permutation 698 test_define_override(v); 699 test_define_flush(); 700 701 // explicit powerloss cycles? 702 if (cycles) { 703 cb(data, suite, case_, &(test_powerloss_t){ 704 .run=run_powerloss_cycles, 705 .cycles=cycles, 706 .cycle_count=cycle_count}); 707 } else { 708 for (size_t p = 0; p < test_powerloss_count; p++) { 709 // skip non-reentrant tests when powerloss testing 710 if (test_powerlosses[p].run != run_powerloss_none 711 && !(case_->flags & TEST_REENTRANT)) { 712 continue; 713 } 714 715 cb(data, suite, case_, &test_powerlosses[p]); 716 } 717 } 718 } 719 720 return; 721 } 722 723 test_seen_t seen = {NULL, 0, 0}; 724 725 for (size_t k = 0; k < case_->permutations; k++) { 726 // define permutation 727 test_define_perm(suite, case_, k); 728 729 for (size_t v = 0; v < test_override_define_permutations; v++) { 730 // define override permutation 731 test_define_override(v); 732 733 for (size_t g = 0; g < test_geometry_count; g++) { 734 // define geometry 735 test_define_geometry(&test_geometries[g]); 736 test_define_flush(); 737 738 // have we seen this permutation before? 739 bool was_seen = test_seen_insert(&seen, suite, case_); 740 if (!(k == 0 && v == 0 && g == 0) && was_seen) { 741 continue; 742 } 743 744 if (cycles) { 745 cb(data, suite, case_, &(test_powerloss_t){ 746 .run=run_powerloss_cycles, 747 .cycles=cycles, 748 .cycle_count=cycle_count}); 749 } else { 750 for (size_t p = 0; p < test_powerloss_count; p++) { 751 // skip non-reentrant tests when powerloss testing 752 if (test_powerlosses[p].run != run_powerloss_none 753 && !(case_->flags & TEST_REENTRANT)) { 754 continue; 755 } 756 757 cb(data, suite, case_, &test_powerlosses[p]); 758 } 759 } 760 } 761 } 762 } 763 764 test_seen_cleanup(&seen); 765} 766 767 768// how many permutations are there actually in a test case 769struct perm_count_state { 770 size_t total; 771 size_t filtered; 772}; 773 774void perm_count( 775 void *data, 776 const struct test_suite *suite, 777 const struct test_case *case_, 778 const test_powerloss_t *powerloss) { 779 struct perm_count_state *state = data; 780 (void)suite; 781 (void)case_; 782 (void)powerloss; 783 784 state->total += 1; 785 786 if (case_->filter && !case_->filter()) { 787 return; 788 } 789 790 state->filtered += 1; 791} 792 793 794// operations we can do 795static void summary(void) { 796 printf("%-23s %7s %7s %7s %11s\n", 797 "", "flags", "suites", "cases", "perms"); 798 size_t suites = 0; 799 size_t cases = 0; 800 test_flags_t flags = 0; 801 struct perm_count_state perms = {0, 0}; 802 803 for (size_t t = 0; t < test_id_count; t++) { 804 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 805 test_define_suite(&test_suites[i]); 806 807 for (size_t j = 0; j < test_suites[i].case_count; j++) { 808 // does neither suite nor case name match? 809 if (test_ids[t].name && !( 810 strcmp(test_ids[t].name, 811 test_suites[i].name) == 0 812 || strcmp(test_ids[t].name, 813 test_suites[i].cases[j].name) == 0)) { 814 continue; 815 } 816 817 cases += 1; 818 case_forperm( 819 &test_suites[i], 820 &test_suites[i].cases[j], 821 test_ids[t].defines, 822 test_ids[t].define_count, 823 test_ids[t].cycles, 824 test_ids[t].cycle_count, 825 perm_count, 826 &perms); 827 } 828 829 suites += 1; 830 flags |= test_suites[i].flags; 831 } 832 } 833 834 char perm_buf[64]; 835 sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total); 836 char flag_buf[64]; 837 sprintf(flag_buf, "%s%s", 838 (flags & TEST_REENTRANT) ? "r" : "", 839 (!flags) ? "-" : ""); 840 printf("%-23s %7s %7zu %7zu %11s\n", 841 "TOTAL", 842 flag_buf, 843 suites, 844 cases, 845 perm_buf); 846} 847 848static void list_suites(void) { 849 // at least size so that names fit 850 unsigned name_width = 23; 851 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 852 size_t len = strlen(test_suites[i].name); 853 if (len > name_width) { 854 name_width = len; 855 } 856 } 857 name_width = 4*((name_width+1+4-1)/4)-1; 858 859 printf("%-*s %7s %7s %11s\n", 860 name_width, "suite", "flags", "cases", "perms"); 861 for (size_t t = 0; t < test_id_count; t++) { 862 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 863 test_define_suite(&test_suites[i]); 864 865 size_t cases = 0; 866 struct perm_count_state perms = {0, 0}; 867 868 for (size_t j = 0; j < test_suites[i].case_count; j++) { 869 // does neither suite nor case name match? 870 if (test_ids[t].name && !( 871 strcmp(test_ids[t].name, 872 test_suites[i].name) == 0 873 || strcmp(test_ids[t].name, 874 test_suites[i].cases[j].name) == 0)) { 875 continue; 876 } 877 878 cases += 1; 879 case_forperm( 880 &test_suites[i], 881 &test_suites[i].cases[j], 882 test_ids[t].defines, 883 test_ids[t].define_count, 884 test_ids[t].cycles, 885 test_ids[t].cycle_count, 886 perm_count, 887 &perms); 888 } 889 890 // no tests found? 891 if (!cases) { 892 continue; 893 } 894 895 char perm_buf[64]; 896 sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total); 897 char flag_buf[64]; 898 sprintf(flag_buf, "%s%s", 899 (test_suites[i].flags & TEST_REENTRANT) ? "r" : "", 900 (!test_suites[i].flags) ? "-" : ""); 901 printf("%-*s %7s %7zu %11s\n", 902 name_width, 903 test_suites[i].name, 904 flag_buf, 905 cases, 906 perm_buf); 907 } 908 } 909} 910 911static void list_cases(void) { 912 // at least size so that names fit 913 unsigned name_width = 23; 914 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 915 for (size_t j = 0; j < test_suites[i].case_count; j++) { 916 size_t len = strlen(test_suites[i].cases[j].name); 917 if (len > name_width) { 918 name_width = len; 919 } 920 } 921 } 922 name_width = 4*((name_width+1+4-1)/4)-1; 923 924 printf("%-*s %7s %11s\n", name_width, "case", "flags", "perms"); 925 for (size_t t = 0; t < test_id_count; t++) { 926 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 927 test_define_suite(&test_suites[i]); 928 929 for (size_t j = 0; j < test_suites[i].case_count; j++) { 930 // does neither suite nor case name match? 931 if (test_ids[t].name && !( 932 strcmp(test_ids[t].name, 933 test_suites[i].name) == 0 934 || strcmp(test_ids[t].name, 935 test_suites[i].cases[j].name) == 0)) { 936 continue; 937 } 938 939 struct perm_count_state perms = {0, 0}; 940 case_forperm( 941 &test_suites[i], 942 &test_suites[i].cases[j], 943 test_ids[t].defines, 944 test_ids[t].define_count, 945 test_ids[t].cycles, 946 test_ids[t].cycle_count, 947 perm_count, 948 &perms); 949 950 char perm_buf[64]; 951 sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total); 952 char flag_buf[64]; 953 sprintf(flag_buf, "%s%s", 954 (test_suites[i].cases[j].flags & TEST_REENTRANT) 955 ? "r" : "", 956 (!test_suites[i].cases[j].flags) 957 ? "-" : ""); 958 printf("%-*s %7s %11s\n", 959 name_width, 960 test_suites[i].cases[j].name, 961 flag_buf, 962 perm_buf); 963 } 964 } 965 } 966} 967 968static void list_suite_paths(void) { 969 // at least size so that names fit 970 unsigned name_width = 23; 971 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 972 size_t len = strlen(test_suites[i].name); 973 if (len > name_width) { 974 name_width = len; 975 } 976 } 977 name_width = 4*((name_width+1+4-1)/4)-1; 978 979 printf("%-*s %s\n", name_width, "suite", "path"); 980 for (size_t t = 0; t < test_id_count; t++) { 981 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 982 size_t cases = 0; 983 984 for (size_t j = 0; j < test_suites[i].case_count; j++) { 985 // does neither suite nor case name match? 986 if (test_ids[t].name && !( 987 strcmp(test_ids[t].name, 988 test_suites[i].name) == 0 989 || strcmp(test_ids[t].name, 990 test_suites[i].cases[j].name) == 0)) { 991 continue; 992 } 993 994 cases += 1; 995 } 996 997 // no tests found? 998 if (!cases) { 999 continue; 1000 } 1001 1002 printf("%-*s %s\n", 1003 name_width, 1004 test_suites[i].name, 1005 test_suites[i].path); 1006 } 1007 } 1008} 1009 1010static void list_case_paths(void) { 1011 // at least size so that names fit 1012 unsigned name_width = 23; 1013 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 1014 for (size_t j = 0; j < test_suites[i].case_count; j++) { 1015 size_t len = strlen(test_suites[i].cases[j].name); 1016 if (len > name_width) { 1017 name_width = len; 1018 } 1019 } 1020 } 1021 name_width = 4*((name_width+1+4-1)/4)-1; 1022 1023 printf("%-*s %s\n", name_width, "case", "path"); 1024 for (size_t t = 0; t < test_id_count; t++) { 1025 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 1026 for (size_t j = 0; j < test_suites[i].case_count; j++) { 1027 // does neither suite nor case name match? 1028 if (test_ids[t].name && !( 1029 strcmp(test_ids[t].name, 1030 test_suites[i].name) == 0 1031 || strcmp(test_ids[t].name, 1032 test_suites[i].cases[j].name) == 0)) { 1033 continue; 1034 } 1035 1036 printf("%-*s %s\n", 1037 name_width, 1038 test_suites[i].cases[j].name, 1039 test_suites[i].cases[j].path); 1040 } 1041 } 1042 } 1043} 1044 1045struct list_defines_define { 1046 const char *name; 1047 intmax_t *values; 1048 size_t value_count; 1049 size_t value_capacity; 1050}; 1051 1052struct list_defines_defines { 1053 struct list_defines_define *defines; 1054 size_t define_count; 1055 size_t define_capacity; 1056}; 1057 1058static void list_defines_add( 1059 struct list_defines_defines *defines, 1060 size_t d) { 1061 const char *name = test_define_name(d); 1062 intmax_t value = TEST_DEFINE(d); 1063 1064 // define already in defines? 1065 for (size_t i = 0; i < defines->define_count; i++) { 1066 if (strcmp(defines->defines[i].name, name) == 0) { 1067 // value already in values? 1068 for (size_t j = 0; j < defines->defines[i].value_count; j++) { 1069 if (defines->defines[i].values[j] == value) { 1070 return; 1071 } 1072 } 1073 1074 *(intmax_t*)mappend( 1075 (void**)&defines->defines[i].values, 1076 sizeof(intmax_t), 1077 &defines->defines[i].value_count, 1078 &defines->defines[i].value_capacity) = value; 1079 1080 return; 1081 } 1082 } 1083 1084 // new define? 1085 struct list_defines_define *define = mappend( 1086 (void**)&defines->defines, 1087 sizeof(struct list_defines_define), 1088 &defines->define_count, 1089 &defines->define_capacity); 1090 define->name = name; 1091 define->values = malloc(sizeof(intmax_t)); 1092 define->values[0] = value; 1093 define->value_count = 1; 1094 define->value_capacity = 1; 1095} 1096 1097void perm_list_defines( 1098 void *data, 1099 const struct test_suite *suite, 1100 const struct test_case *case_, 1101 const test_powerloss_t *powerloss) { 1102 struct list_defines_defines *defines = data; 1103 (void)suite; 1104 (void)case_; 1105 (void)powerloss; 1106 1107 // collect defines 1108 for (size_t d = 0; 1109 d < lfs_max(suite->define_count, 1110 TEST_IMPLICIT_DEFINE_COUNT); 1111 d++) { 1112 if (d < TEST_IMPLICIT_DEFINE_COUNT 1113 || test_define_ispermutation(d)) { 1114 list_defines_add(defines, d); 1115 } 1116 } 1117} 1118 1119void perm_list_permutation_defines( 1120 void *data, 1121 const struct test_suite *suite, 1122 const struct test_case *case_, 1123 const test_powerloss_t *powerloss) { 1124 struct list_defines_defines *defines = data; 1125 (void)suite; 1126 (void)case_; 1127 (void)powerloss; 1128 1129 // collect permutation_defines 1130 for (size_t d = 0; 1131 d < lfs_max(suite->define_count, 1132 TEST_IMPLICIT_DEFINE_COUNT); 1133 d++) { 1134 if (test_define_ispermutation(d)) { 1135 list_defines_add(defines, d); 1136 } 1137 } 1138} 1139 1140extern const test_geometry_t builtin_geometries[]; 1141 1142static void list_defines(void) { 1143 struct list_defines_defines defines = {NULL, 0, 0}; 1144 1145 // add defines 1146 for (size_t t = 0; t < test_id_count; t++) { 1147 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 1148 test_define_suite(&test_suites[i]); 1149 1150 for (size_t j = 0; j < test_suites[i].case_count; j++) { 1151 // does neither suite nor case name match? 1152 if (test_ids[t].name && !( 1153 strcmp(test_ids[t].name, 1154 test_suites[i].name) == 0 1155 || strcmp(test_ids[t].name, 1156 test_suites[i].cases[j].name) == 0)) { 1157 continue; 1158 } 1159 1160 case_forperm( 1161 &test_suites[i], 1162 &test_suites[i].cases[j], 1163 test_ids[t].defines, 1164 test_ids[t].define_count, 1165 test_ids[t].cycles, 1166 test_ids[t].cycle_count, 1167 perm_list_defines, 1168 &defines); 1169 } 1170 } 1171 } 1172 1173 for (size_t i = 0; i < defines.define_count; i++) { 1174 printf("%s=", defines.defines[i].name); 1175 for (size_t j = 0; j < defines.defines[i].value_count; j++) { 1176 printf("%jd", defines.defines[i].values[j]); 1177 if (j != defines.defines[i].value_count-1) { 1178 printf(","); 1179 } 1180 } 1181 printf("\n"); 1182 } 1183 1184 for (size_t i = 0; i < defines.define_count; i++) { 1185 free(defines.defines[i].values); 1186 } 1187 free(defines.defines); 1188} 1189 1190static void list_permutation_defines(void) { 1191 struct list_defines_defines defines = {NULL, 0, 0}; 1192 1193 // add permutation defines 1194 for (size_t t = 0; t < test_id_count; t++) { 1195 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 1196 test_define_suite(&test_suites[i]); 1197 1198 for (size_t j = 0; j < test_suites[i].case_count; j++) { 1199 // does neither suite nor case name match? 1200 if (test_ids[t].name && !( 1201 strcmp(test_ids[t].name, 1202 test_suites[i].name) == 0 1203 || strcmp(test_ids[t].name, 1204 test_suites[i].cases[j].name) == 0)) { 1205 continue; 1206 } 1207 1208 case_forperm( 1209 &test_suites[i], 1210 &test_suites[i].cases[j], 1211 test_ids[t].defines, 1212 test_ids[t].define_count, 1213 test_ids[t].cycles, 1214 test_ids[t].cycle_count, 1215 perm_list_permutation_defines, 1216 &defines); 1217 } 1218 } 1219 } 1220 1221 for (size_t i = 0; i < defines.define_count; i++) { 1222 printf("%s=", defines.defines[i].name); 1223 for (size_t j = 0; j < defines.defines[i].value_count; j++) { 1224 printf("%jd", defines.defines[i].values[j]); 1225 if (j != defines.defines[i].value_count-1) { 1226 printf(","); 1227 } 1228 } 1229 printf("\n"); 1230 } 1231 1232 for (size_t i = 0; i < defines.define_count; i++) { 1233 free(defines.defines[i].values); 1234 } 1235 free(defines.defines); 1236} 1237 1238static void list_implicit_defines(void) { 1239 struct list_defines_defines defines = {NULL, 0, 0}; 1240 1241 // yes we do need to define a suite, this does a bit of bookeeping 1242 // such as setting up the define cache 1243 test_define_suite(&(const struct test_suite){0}); 1244 1245 // make sure to include builtin geometries here 1246 extern const test_geometry_t builtin_geometries[]; 1247 for (size_t g = 0; builtin_geometries[g].name; g++) { 1248 test_define_geometry(&builtin_geometries[g]); 1249 test_define_flush(); 1250 1251 // add implicit defines 1252 for (size_t d = 0; d < TEST_IMPLICIT_DEFINE_COUNT; d++) { 1253 list_defines_add(&defines, d); 1254 } 1255 } 1256 1257 for (size_t i = 0; i < defines.define_count; i++) { 1258 printf("%s=", defines.defines[i].name); 1259 for (size_t j = 0; j < defines.defines[i].value_count; j++) { 1260 printf("%jd", defines.defines[i].values[j]); 1261 if (j != defines.defines[i].value_count-1) { 1262 printf(","); 1263 } 1264 } 1265 printf("\n"); 1266 } 1267 1268 for (size_t i = 0; i < defines.define_count; i++) { 1269 free(defines.defines[i].values); 1270 } 1271 free(defines.defines); 1272} 1273 1274 1275 1276// geometries to test 1277 1278const test_geometry_t builtin_geometries[] = { 1279 {"default", {{0}, TEST_CONST(16), TEST_CONST(512), {0}}}, 1280 {"eeprom", {{0}, TEST_CONST(1), TEST_CONST(512), {0}}}, 1281 {"emmc", {{0}, {0}, TEST_CONST(512), {0}}}, 1282 {"nor", {{0}, TEST_CONST(1), TEST_CONST(4096), {0}}}, 1283 {"nand", {{0}, TEST_CONST(4096), TEST_CONST(32768), {0}}}, 1284 {NULL, {{0}, {0}, {0}, {0}}}, 1285}; 1286 1287const test_geometry_t *test_geometries = builtin_geometries; 1288size_t test_geometry_count = 5; 1289 1290static void list_geometries(void) { 1291 // at least size so that names fit 1292 unsigned name_width = 23; 1293 for (size_t g = 0; builtin_geometries[g].name; g++) { 1294 size_t len = strlen(builtin_geometries[g].name); 1295 if (len > name_width) { 1296 name_width = len; 1297 } 1298 } 1299 name_width = 4*((name_width+1+4-1)/4)-1; 1300 1301 // yes we do need to define a suite, this does a bit of bookeeping 1302 // such as setting up the define cache 1303 test_define_suite(&(const struct test_suite){0}); 1304 1305 printf("%-*s %7s %7s %7s %7s %11s\n", 1306 name_width, "geometry", "read", "prog", "erase", "count", "size"); 1307 for (size_t g = 0; builtin_geometries[g].name; g++) { 1308 test_define_geometry(&builtin_geometries[g]); 1309 test_define_flush(); 1310 printf("%-*s %7ju %7ju %7ju %7ju %11ju\n", 1311 name_width, 1312 builtin_geometries[g].name, 1313 READ_SIZE, 1314 PROG_SIZE, 1315 ERASE_SIZE, 1316 ERASE_COUNT, 1317 ERASE_SIZE*ERASE_COUNT); 1318 } 1319} 1320 1321 1322// scenarios to run tests under power-loss 1323 1324static void run_powerloss_none( 1325 const lfs_emubd_powercycles_t *cycles, 1326 size_t cycle_count, 1327 const struct test_suite *suite, 1328 const struct test_case *case_) { 1329 (void)cycles; 1330 (void)cycle_count; 1331 (void)suite; 1332 1333 // create block device and configuration 1334 lfs_emubd_t bd; 1335 1336 struct lfs_config cfg = { 1337 .context = &bd, 1338 .read = lfs_emubd_read, 1339 .prog = lfs_emubd_prog, 1340 .erase = lfs_emubd_erase, 1341 .sync = lfs_emubd_sync, 1342 .read_size = READ_SIZE, 1343 .prog_size = PROG_SIZE, 1344 .block_size = BLOCK_SIZE, 1345 .block_count = BLOCK_COUNT, 1346 .block_cycles = BLOCK_CYCLES, 1347 .cache_size = CACHE_SIZE, 1348 .lookahead_size = LOOKAHEAD_SIZE, 1349 #ifdef LFS_MULTIVERSION 1350 .disk_version = DISK_VERSION, 1351 #endif 1352 }; 1353 1354 struct lfs_emubd_config bdcfg = { 1355 .read_size = READ_SIZE, 1356 .prog_size = PROG_SIZE, 1357 .erase_size = ERASE_SIZE, 1358 .erase_count = ERASE_COUNT, 1359 .erase_value = ERASE_VALUE, 1360 .erase_cycles = ERASE_CYCLES, 1361 .badblock_behavior = BADBLOCK_BEHAVIOR, 1362 .disk_path = test_disk_path, 1363 .read_sleep = test_read_sleep, 1364 .prog_sleep = test_prog_sleep, 1365 .erase_sleep = test_erase_sleep, 1366 }; 1367 1368 int err = lfs_emubd_create(&cfg, &bdcfg); 1369 if (err) { 1370 fprintf(stderr, "error: could not create block device: %d\n", err); 1371 exit(-1); 1372 } 1373 1374 // run the test 1375 printf("running "); 1376 perm_printid(suite, case_, NULL, 0); 1377 printf("\n"); 1378 1379 case_->run(&cfg); 1380 1381 printf("finished "); 1382 perm_printid(suite, case_, NULL, 0); 1383 printf("\n"); 1384 1385 // cleanup 1386 err = lfs_emubd_destroy(&cfg); 1387 if (err) { 1388 fprintf(stderr, "error: could not destroy block device: %d\n", err); 1389 exit(-1); 1390 } 1391} 1392 1393static void powerloss_longjmp(void *c) { 1394 jmp_buf *powerloss_jmp = c; 1395 longjmp(*powerloss_jmp, 1); 1396} 1397 1398static void run_powerloss_linear( 1399 const lfs_emubd_powercycles_t *cycles, 1400 size_t cycle_count, 1401 const struct test_suite *suite, 1402 const struct test_case *case_) { 1403 (void)cycles; 1404 (void)cycle_count; 1405 (void)suite; 1406 1407 // create block device and configuration 1408 lfs_emubd_t bd; 1409 jmp_buf powerloss_jmp; 1410 volatile lfs_emubd_powercycles_t i = 1; 1411 1412 struct lfs_config cfg = { 1413 .context = &bd, 1414 .read = lfs_emubd_read, 1415 .prog = lfs_emubd_prog, 1416 .erase = lfs_emubd_erase, 1417 .sync = lfs_emubd_sync, 1418 .read_size = READ_SIZE, 1419 .prog_size = PROG_SIZE, 1420 .block_size = BLOCK_SIZE, 1421 .block_count = BLOCK_COUNT, 1422 .block_cycles = BLOCK_CYCLES, 1423 .cache_size = CACHE_SIZE, 1424 .lookahead_size = LOOKAHEAD_SIZE, 1425 #ifdef LFS_MULTIVERSION 1426 .disk_version = DISK_VERSION, 1427 #endif 1428 }; 1429 1430 struct lfs_emubd_config bdcfg = { 1431 .read_size = READ_SIZE, 1432 .prog_size = PROG_SIZE, 1433 .erase_size = ERASE_SIZE, 1434 .erase_count = ERASE_COUNT, 1435 .erase_value = ERASE_VALUE, 1436 .erase_cycles = ERASE_CYCLES, 1437 .badblock_behavior = BADBLOCK_BEHAVIOR, 1438 .disk_path = test_disk_path, 1439 .read_sleep = test_read_sleep, 1440 .prog_sleep = test_prog_sleep, 1441 .erase_sleep = test_erase_sleep, 1442 .power_cycles = i, 1443 .powerloss_behavior = POWERLOSS_BEHAVIOR, 1444 .powerloss_cb = powerloss_longjmp, 1445 .powerloss_data = &powerloss_jmp, 1446 }; 1447 1448 int err = lfs_emubd_create(&cfg, &bdcfg); 1449 if (err) { 1450 fprintf(stderr, "error: could not create block device: %d\n", err); 1451 exit(-1); 1452 } 1453 1454 // run the test, increasing power-cycles as power-loss events occur 1455 printf("running "); 1456 perm_printid(suite, case_, NULL, 0); 1457 printf("\n"); 1458 1459 while (true) { 1460 if (!setjmp(powerloss_jmp)) { 1461 // run the test 1462 case_->run(&cfg); 1463 break; 1464 } 1465 1466 // power-loss! 1467 printf("powerloss "); 1468 perm_printid(suite, case_, NULL, 0); 1469 printf(":"); 1470 for (lfs_emubd_powercycles_t j = 1; j <= i; j++) { 1471 leb16_print(j); 1472 } 1473 printf("\n"); 1474 1475 i += 1; 1476 lfs_emubd_setpowercycles(&cfg, i); 1477 } 1478 1479 printf("finished "); 1480 perm_printid(suite, case_, NULL, 0); 1481 printf("\n"); 1482 1483 // cleanup 1484 err = lfs_emubd_destroy(&cfg); 1485 if (err) { 1486 fprintf(stderr, "error: could not destroy block device: %d\n", err); 1487 exit(-1); 1488 } 1489} 1490 1491static void run_powerloss_log( 1492 const lfs_emubd_powercycles_t *cycles, 1493 size_t cycle_count, 1494 const struct test_suite *suite, 1495 const struct test_case *case_) { 1496 (void)cycles; 1497 (void)cycle_count; 1498 (void)suite; 1499 1500 // create block device and configuration 1501 lfs_emubd_t bd; 1502 jmp_buf powerloss_jmp; 1503 volatile lfs_emubd_powercycles_t i = 1; 1504 1505 struct lfs_config cfg = { 1506 .context = &bd, 1507 .read = lfs_emubd_read, 1508 .prog = lfs_emubd_prog, 1509 .erase = lfs_emubd_erase, 1510 .sync = lfs_emubd_sync, 1511 .read_size = READ_SIZE, 1512 .prog_size = PROG_SIZE, 1513 .block_size = BLOCK_SIZE, 1514 .block_count = BLOCK_COUNT, 1515 .block_cycles = BLOCK_CYCLES, 1516 .cache_size = CACHE_SIZE, 1517 .lookahead_size = LOOKAHEAD_SIZE, 1518 #ifdef LFS_MULTIVERSION 1519 .disk_version = DISK_VERSION, 1520 #endif 1521 }; 1522 1523 struct lfs_emubd_config bdcfg = { 1524 .read_size = READ_SIZE, 1525 .prog_size = PROG_SIZE, 1526 .erase_size = ERASE_SIZE, 1527 .erase_count = ERASE_COUNT, 1528 .erase_value = ERASE_VALUE, 1529 .erase_cycles = ERASE_CYCLES, 1530 .badblock_behavior = BADBLOCK_BEHAVIOR, 1531 .disk_path = test_disk_path, 1532 .read_sleep = test_read_sleep, 1533 .prog_sleep = test_prog_sleep, 1534 .erase_sleep = test_erase_sleep, 1535 .power_cycles = i, 1536 .powerloss_behavior = POWERLOSS_BEHAVIOR, 1537 .powerloss_cb = powerloss_longjmp, 1538 .powerloss_data = &powerloss_jmp, 1539 }; 1540 1541 int err = lfs_emubd_create(&cfg, &bdcfg); 1542 if (err) { 1543 fprintf(stderr, "error: could not create block device: %d\n", err); 1544 exit(-1); 1545 } 1546 1547 // run the test, increasing power-cycles as power-loss events occur 1548 printf("running "); 1549 perm_printid(suite, case_, NULL, 0); 1550 printf("\n"); 1551 1552 while (true) { 1553 if (!setjmp(powerloss_jmp)) { 1554 // run the test 1555 case_->run(&cfg); 1556 break; 1557 } 1558 1559 // power-loss! 1560 printf("powerloss "); 1561 perm_printid(suite, case_, NULL, 0); 1562 printf(":"); 1563 for (lfs_emubd_powercycles_t j = 1; j <= i; j *= 2) { 1564 leb16_print(j); 1565 } 1566 printf("\n"); 1567 1568 i *= 2; 1569 lfs_emubd_setpowercycles(&cfg, i); 1570 } 1571 1572 printf("finished "); 1573 perm_printid(suite, case_, NULL, 0); 1574 printf("\n"); 1575 1576 // cleanup 1577 err = lfs_emubd_destroy(&cfg); 1578 if (err) { 1579 fprintf(stderr, "error: could not destroy block device: %d\n", err); 1580 exit(-1); 1581 } 1582} 1583 1584static void run_powerloss_cycles( 1585 const lfs_emubd_powercycles_t *cycles, 1586 size_t cycle_count, 1587 const struct test_suite *suite, 1588 const struct test_case *case_) { 1589 (void)suite; 1590 1591 // create block device and configuration 1592 lfs_emubd_t bd; 1593 jmp_buf powerloss_jmp; 1594 volatile size_t i = 0; 1595 1596 struct lfs_config cfg = { 1597 .context = &bd, 1598 .read = lfs_emubd_read, 1599 .prog = lfs_emubd_prog, 1600 .erase = lfs_emubd_erase, 1601 .sync = lfs_emubd_sync, 1602 .read_size = READ_SIZE, 1603 .prog_size = PROG_SIZE, 1604 .block_size = BLOCK_SIZE, 1605 .block_count = BLOCK_COUNT, 1606 .block_cycles = BLOCK_CYCLES, 1607 .cache_size = CACHE_SIZE, 1608 .lookahead_size = LOOKAHEAD_SIZE, 1609 #ifdef LFS_MULTIVERSION 1610 .disk_version = DISK_VERSION, 1611 #endif 1612 }; 1613 1614 struct lfs_emubd_config bdcfg = { 1615 .read_size = READ_SIZE, 1616 .prog_size = PROG_SIZE, 1617 .erase_size = ERASE_SIZE, 1618 .erase_count = ERASE_COUNT, 1619 .erase_value = ERASE_VALUE, 1620 .erase_cycles = ERASE_CYCLES, 1621 .badblock_behavior = BADBLOCK_BEHAVIOR, 1622 .disk_path = test_disk_path, 1623 .read_sleep = test_read_sleep, 1624 .prog_sleep = test_prog_sleep, 1625 .erase_sleep = test_erase_sleep, 1626 .power_cycles = (i < cycle_count) ? cycles[i] : 0, 1627 .powerloss_behavior = POWERLOSS_BEHAVIOR, 1628 .powerloss_cb = powerloss_longjmp, 1629 .powerloss_data = &powerloss_jmp, 1630 }; 1631 1632 int err = lfs_emubd_create(&cfg, &bdcfg); 1633 if (err) { 1634 fprintf(stderr, "error: could not create block device: %d\n", err); 1635 exit(-1); 1636 } 1637 1638 // run the test, increasing power-cycles as power-loss events occur 1639 printf("running "); 1640 perm_printid(suite, case_, NULL, 0); 1641 printf("\n"); 1642 1643 while (true) { 1644 if (!setjmp(powerloss_jmp)) { 1645 // run the test 1646 case_->run(&cfg); 1647 break; 1648 } 1649 1650 // power-loss! 1651 assert(i <= cycle_count); 1652 printf("powerloss "); 1653 perm_printid(suite, case_, cycles, i+1); 1654 printf("\n"); 1655 1656 i += 1; 1657 lfs_emubd_setpowercycles(&cfg, 1658 (i < cycle_count) ? cycles[i] : 0); 1659 } 1660 1661 printf("finished "); 1662 perm_printid(suite, case_, NULL, 0); 1663 printf("\n"); 1664 1665 // cleanup 1666 err = lfs_emubd_destroy(&cfg); 1667 if (err) { 1668 fprintf(stderr, "error: could not destroy block device: %d\n", err); 1669 exit(-1); 1670 } 1671} 1672 1673struct powerloss_exhaustive_state { 1674 struct lfs_config *cfg; 1675 1676 lfs_emubd_t *branches; 1677 size_t branch_count; 1678 size_t branch_capacity; 1679}; 1680 1681struct powerloss_exhaustive_cycles { 1682 lfs_emubd_powercycles_t *cycles; 1683 size_t cycle_count; 1684 size_t cycle_capacity; 1685}; 1686 1687static void powerloss_exhaustive_branch(void *c) { 1688 struct powerloss_exhaustive_state *state = c; 1689 // append to branches 1690 lfs_emubd_t *branch = mappend( 1691 (void**)&state->branches, 1692 sizeof(lfs_emubd_t), 1693 &state->branch_count, 1694 &state->branch_capacity); 1695 if (!branch) { 1696 fprintf(stderr, "error: exhaustive: out of memory\n"); 1697 exit(-1); 1698 } 1699 1700 // create copy-on-write copy 1701 int err = lfs_emubd_copy(state->cfg, branch); 1702 if (err) { 1703 fprintf(stderr, "error: exhaustive: could not create bd copy\n"); 1704 exit(-1); 1705 } 1706 1707 // also trigger on next power cycle 1708 lfs_emubd_setpowercycles(state->cfg, 1); 1709} 1710 1711static void run_powerloss_exhaustive_layer( 1712 struct powerloss_exhaustive_cycles *cycles, 1713 const struct test_suite *suite, 1714 const struct test_case *case_, 1715 struct lfs_config *cfg, 1716 struct lfs_emubd_config *bdcfg, 1717 size_t depth) { 1718 (void)suite; 1719 1720 struct powerloss_exhaustive_state state = { 1721 .cfg = cfg, 1722 .branches = NULL, 1723 .branch_count = 0, 1724 .branch_capacity = 0, 1725 }; 1726 1727 // run through the test without additional powerlosses, collecting possible 1728 // branches as we do so 1729 lfs_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0); 1730 bdcfg->powerloss_data = &state; 1731 1732 // run the tests 1733 case_->run(cfg); 1734 1735 // aggressively clean up memory here to try to keep our memory usage low 1736 int err = lfs_emubd_destroy(cfg); 1737 if (err) { 1738 fprintf(stderr, "error: could not destroy block device: %d\n", err); 1739 exit(-1); 1740 } 1741 1742 // recurse into each branch 1743 for (size_t i = 0; i < state.branch_count; i++) { 1744 // first push and print the branch 1745 lfs_emubd_powercycles_t *cycle = mappend( 1746 (void**)&cycles->cycles, 1747 sizeof(lfs_emubd_powercycles_t), 1748 &cycles->cycle_count, 1749 &cycles->cycle_capacity); 1750 if (!cycle) { 1751 fprintf(stderr, "error: exhaustive: out of memory\n"); 1752 exit(-1); 1753 } 1754 *cycle = i+1; 1755 1756 printf("powerloss "); 1757 perm_printid(suite, case_, cycles->cycles, cycles->cycle_count); 1758 printf("\n"); 1759 1760 // now recurse 1761 cfg->context = &state.branches[i]; 1762 run_powerloss_exhaustive_layer(cycles, 1763 suite, case_, 1764 cfg, bdcfg, depth-1); 1765 1766 // pop the cycle 1767 cycles->cycle_count -= 1; 1768 } 1769 1770 // clean up memory 1771 free(state.branches); 1772} 1773 1774static void run_powerloss_exhaustive( 1775 const lfs_emubd_powercycles_t *cycles, 1776 size_t cycle_count, 1777 const struct test_suite *suite, 1778 const struct test_case *case_) { 1779 (void)cycles; 1780 (void)suite; 1781 1782 // create block device and configuration 1783 lfs_emubd_t bd; 1784 1785 struct lfs_config cfg = { 1786 .context = &bd, 1787 .read = lfs_emubd_read, 1788 .prog = lfs_emubd_prog, 1789 .erase = lfs_emubd_erase, 1790 .sync = lfs_emubd_sync, 1791 .read_size = READ_SIZE, 1792 .prog_size = PROG_SIZE, 1793 .block_size = BLOCK_SIZE, 1794 .block_count = BLOCK_COUNT, 1795 .block_cycles = BLOCK_CYCLES, 1796 .cache_size = CACHE_SIZE, 1797 .lookahead_size = LOOKAHEAD_SIZE, 1798 #ifdef LFS_MULTIVERSION 1799 .disk_version = DISK_VERSION, 1800 #endif 1801 }; 1802 1803 struct lfs_emubd_config bdcfg = { 1804 .read_size = READ_SIZE, 1805 .prog_size = PROG_SIZE, 1806 .erase_size = ERASE_SIZE, 1807 .erase_count = ERASE_COUNT, 1808 .erase_value = ERASE_VALUE, 1809 .erase_cycles = ERASE_CYCLES, 1810 .badblock_behavior = BADBLOCK_BEHAVIOR, 1811 .disk_path = test_disk_path, 1812 .read_sleep = test_read_sleep, 1813 .prog_sleep = test_prog_sleep, 1814 .erase_sleep = test_erase_sleep, 1815 .powerloss_behavior = POWERLOSS_BEHAVIOR, 1816 .powerloss_cb = powerloss_exhaustive_branch, 1817 .powerloss_data = NULL, 1818 }; 1819 1820 int err = lfs_emubd_create(&cfg, &bdcfg); 1821 if (err) { 1822 fprintf(stderr, "error: could not create block device: %d\n", err); 1823 exit(-1); 1824 } 1825 1826 // run the test, increasing power-cycles as power-loss events occur 1827 printf("running "); 1828 perm_printid(suite, case_, NULL, 0); 1829 printf("\n"); 1830 1831 // recursively exhaust each layer of powerlosses 1832 run_powerloss_exhaustive_layer( 1833 &(struct powerloss_exhaustive_cycles){NULL, 0, 0}, 1834 suite, case_, 1835 &cfg, &bdcfg, cycle_count); 1836 1837 printf("finished "); 1838 perm_printid(suite, case_, NULL, 0); 1839 printf("\n"); 1840} 1841 1842 1843const test_powerloss_t builtin_powerlosses[] = { 1844 {"none", run_powerloss_none, NULL, 0}, 1845 {"log", run_powerloss_log, NULL, 0}, 1846 {"linear", run_powerloss_linear, NULL, 0}, 1847 {"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX}, 1848 {NULL, NULL, NULL, 0}, 1849}; 1850 1851const char *const builtin_powerlosses_help[] = { 1852 "Run with no power-losses.", 1853 "Run with exponentially-decreasing power-losses.", 1854 "Run with linearly-decreasing power-losses.", 1855 "Run a all permutations of power-losses, this may take a while.", 1856 "Run a all permutations of n power-losses.", 1857 "Run a custom comma-separated set of power-losses.", 1858 "Run a custom leb16-encoded set of power-losses.", 1859}; 1860 1861// default to -Pnone,linear, which provides a good heuristic while still 1862// running quickly 1863const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){ 1864 {"none", run_powerloss_none, NULL, 0}, 1865 {"linear", run_powerloss_linear, NULL, 0}, 1866}; 1867size_t test_powerloss_count = 2; 1868 1869static void list_powerlosses(void) { 1870 // at least size so that names fit 1871 unsigned name_width = 23; 1872 for (size_t i = 0; builtin_powerlosses[i].name; i++) { 1873 size_t len = strlen(builtin_powerlosses[i].name); 1874 if (len > name_width) { 1875 name_width = len; 1876 } 1877 } 1878 name_width = 4*((name_width+1+4-1)/4)-1; 1879 1880 printf("%-*s %s\n", name_width, "scenario", "description"); 1881 size_t i = 0; 1882 for (; builtin_powerlosses[i].name; i++) { 1883 printf("%-*s %s\n", 1884 name_width, 1885 builtin_powerlosses[i].name, 1886 builtin_powerlosses_help[i]); 1887 } 1888 1889 // a couple more options with special parsing 1890 printf("%-*s %s\n", name_width, "1,2,3", builtin_powerlosses_help[i+0]); 1891 printf("%-*s %s\n", name_width, "{1,2,3}", builtin_powerlosses_help[i+1]); 1892 printf("%-*s %s\n", name_width, ":1248g1", builtin_powerlosses_help[i+2]); 1893} 1894 1895 1896// global test step count 1897size_t test_step = 0; 1898 1899void perm_run( 1900 void *data, 1901 const struct test_suite *suite, 1902 const struct test_case *case_, 1903 const test_powerloss_t *powerloss) { 1904 (void)data; 1905 1906 // skip this step? 1907 if (!(test_step >= test_step_start 1908 && test_step < test_step_stop 1909 && (test_step-test_step_start) % test_step_step == 0)) { 1910 test_step += 1; 1911 return; 1912 } 1913 test_step += 1; 1914 1915 // filter? 1916 if (case_->filter && !case_->filter()) { 1917 printf("skipped "); 1918 perm_printid(suite, case_, NULL, 0); 1919 printf("\n"); 1920 return; 1921 } 1922 1923 powerloss->run( 1924 powerloss->cycles, powerloss->cycle_count, 1925 suite, case_); 1926} 1927 1928static void run(void) { 1929 // ignore disconnected pipes 1930 signal(SIGPIPE, SIG_IGN); 1931 1932 for (size_t t = 0; t < test_id_count; t++) { 1933 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) { 1934 test_define_suite(&test_suites[i]); 1935 1936 for (size_t j = 0; j < test_suites[i].case_count; j++) { 1937 // does neither suite nor case name match? 1938 if (test_ids[t].name && !( 1939 strcmp(test_ids[t].name, 1940 test_suites[i].name) == 0 1941 || strcmp(test_ids[t].name, 1942 test_suites[i].cases[j].name) == 0)) { 1943 continue; 1944 } 1945 1946 case_forperm( 1947 &test_suites[i], 1948 &test_suites[i].cases[j], 1949 test_ids[t].defines, 1950 test_ids[t].define_count, 1951 test_ids[t].cycles, 1952 test_ids[t].cycle_count, 1953 perm_run, 1954 NULL); 1955 } 1956 } 1957 } 1958} 1959 1960 1961 1962// option handling 1963enum opt_flags { 1964 OPT_HELP = 'h', 1965 OPT_SUMMARY = 'Y', 1966 OPT_LIST_SUITES = 'l', 1967 OPT_LIST_CASES = 'L', 1968 OPT_LIST_SUITE_PATHS = 1, 1969 OPT_LIST_CASE_PATHS = 2, 1970 OPT_LIST_DEFINES = 3, 1971 OPT_LIST_PERMUTATION_DEFINES = 4, 1972 OPT_LIST_IMPLICIT_DEFINES = 5, 1973 OPT_LIST_GEOMETRIES = 6, 1974 OPT_LIST_POWERLOSSES = 7, 1975 OPT_DEFINE = 'D', 1976 OPT_GEOMETRY = 'G', 1977 OPT_POWERLOSS = 'P', 1978 OPT_STEP = 's', 1979 OPT_DISK = 'd', 1980 OPT_TRACE = 't', 1981 OPT_TRACE_BACKTRACE = 8, 1982 OPT_TRACE_PERIOD = 9, 1983 OPT_TRACE_FREQ = 10, 1984 OPT_READ_SLEEP = 11, 1985 OPT_PROG_SLEEP = 12, 1986 OPT_ERASE_SLEEP = 13, 1987}; 1988 1989const char *short_opts = "hYlLD:G:P:s:d:t:"; 1990 1991const struct option long_opts[] = { 1992 {"help", no_argument, NULL, OPT_HELP}, 1993 {"summary", no_argument, NULL, OPT_SUMMARY}, 1994 {"list-suites", no_argument, NULL, OPT_LIST_SUITES}, 1995 {"list-cases", no_argument, NULL, OPT_LIST_CASES}, 1996 {"list-suite-paths", no_argument, NULL, OPT_LIST_SUITE_PATHS}, 1997 {"list-case-paths", no_argument, NULL, OPT_LIST_CASE_PATHS}, 1998 {"list-defines", no_argument, NULL, OPT_LIST_DEFINES}, 1999 {"list-permutation-defines", 2000 no_argument, NULL, OPT_LIST_PERMUTATION_DEFINES}, 2001 {"list-implicit-defines", 2002 no_argument, NULL, OPT_LIST_IMPLICIT_DEFINES}, 2003 {"list-geometries", no_argument, NULL, OPT_LIST_GEOMETRIES}, 2004 {"list-powerlosses", no_argument, NULL, OPT_LIST_POWERLOSSES}, 2005 {"define", required_argument, NULL, OPT_DEFINE}, 2006 {"geometry", required_argument, NULL, OPT_GEOMETRY}, 2007 {"powerloss", required_argument, NULL, OPT_POWERLOSS}, 2008 {"step", required_argument, NULL, OPT_STEP}, 2009 {"disk", required_argument, NULL, OPT_DISK}, 2010 {"trace", required_argument, NULL, OPT_TRACE}, 2011 {"trace-backtrace", no_argument, NULL, OPT_TRACE_BACKTRACE}, 2012 {"trace-period", required_argument, NULL, OPT_TRACE_PERIOD}, 2013 {"trace-freq", required_argument, NULL, OPT_TRACE_FREQ}, 2014 {"read-sleep", required_argument, NULL, OPT_READ_SLEEP}, 2015 {"prog-sleep", required_argument, NULL, OPT_PROG_SLEEP}, 2016 {"erase-sleep", required_argument, NULL, OPT_ERASE_SLEEP}, 2017 {NULL, 0, NULL, 0}, 2018}; 2019 2020const char *const help_text[] = { 2021 "Show this help message.", 2022 "Show quick summary.", 2023 "List test suites.", 2024 "List test cases.", 2025 "List the path for each test suite.", 2026 "List the path and line number for each test case.", 2027 "List all defines in this test-runner.", 2028 "List explicit defines in this test-runner.", 2029 "List implicit defines in this test-runner.", 2030 "List the available disk geometries.", 2031 "List the available power-loss scenarios.", 2032 "Override a test define.", 2033 "Comma-separated list of disk geometries to test.", 2034 "Comma-separated list of power-loss scenarios to test.", 2035 "Comma-separated range of test permutations to run (start,stop,step).", 2036 "Direct block device operations to this file.", 2037 "Direct trace output to this file.", 2038 "Include a backtrace with every trace statement.", 2039 "Sample trace output at this period in cycles.", 2040 "Sample trace output at this frequency in hz.", 2041 "Artificial read delay in seconds.", 2042 "Artificial prog delay in seconds.", 2043 "Artificial erase delay in seconds.", 2044}; 2045 2046int main(int argc, char **argv) { 2047 void (*op)(void) = run; 2048 2049 size_t test_override_capacity = 0; 2050 size_t test_geometry_capacity = 0; 2051 size_t test_powerloss_capacity = 0; 2052 size_t test_id_capacity = 0; 2053 2054 // parse options 2055 while (true) { 2056 int c = getopt_long(argc, argv, short_opts, long_opts, NULL); 2057 switch (c) { 2058 // generate help message 2059 case OPT_HELP: { 2060 printf("usage: %s [options] [test_id]\n", argv[0]); 2061 printf("\n"); 2062 2063 printf("options:\n"); 2064 size_t i = 0; 2065 while (long_opts[i].name) { 2066 size_t indent; 2067 if (long_opts[i].has_arg == no_argument) { 2068 if (long_opts[i].val >= '0' && long_opts[i].val < 'z') { 2069 indent = printf(" -%c, --%s ", 2070 long_opts[i].val, 2071 long_opts[i].name); 2072 } else { 2073 indent = printf(" --%s ", 2074 long_opts[i].name); 2075 } 2076 } else { 2077 if (long_opts[i].val >= '0' && long_opts[i].val < 'z') { 2078 indent = printf(" -%c %s, --%s %s ", 2079 long_opts[i].val, 2080 long_opts[i].name, 2081 long_opts[i].name, 2082 long_opts[i].name); 2083 } else { 2084 indent = printf(" --%s %s ", 2085 long_opts[i].name, 2086 long_opts[i].name); 2087 } 2088 } 2089 2090 // a quick, hacky, byte-level method for text wrapping 2091 size_t len = strlen(help_text[i]); 2092 size_t j = 0; 2093 if (indent < 24) { 2094 printf("%*s %.80s\n", 2095 (int)(24-1-indent), 2096 "", 2097 &help_text[i][j]); 2098 j += 80; 2099 } else { 2100 printf("\n"); 2101 } 2102 2103 while (j < len) { 2104 printf("%24s%.80s\n", "", &help_text[i][j]); 2105 j += 80; 2106 } 2107 2108 i += 1; 2109 } 2110 2111 printf("\n"); 2112 exit(0); 2113 } 2114 // summary/list flags 2115 case OPT_SUMMARY: 2116 op = summary; 2117 break; 2118 case OPT_LIST_SUITES: 2119 op = list_suites; 2120 break; 2121 case OPT_LIST_CASES: 2122 op = list_cases; 2123 break; 2124 case OPT_LIST_SUITE_PATHS: 2125 op = list_suite_paths; 2126 break; 2127 case OPT_LIST_CASE_PATHS: 2128 op = list_case_paths; 2129 break; 2130 case OPT_LIST_DEFINES: 2131 op = list_defines; 2132 break; 2133 case OPT_LIST_PERMUTATION_DEFINES: 2134 op = list_permutation_defines; 2135 break; 2136 case OPT_LIST_IMPLICIT_DEFINES: 2137 op = list_implicit_defines; 2138 break; 2139 case OPT_LIST_GEOMETRIES: 2140 op = list_geometries; 2141 break; 2142 case OPT_LIST_POWERLOSSES: 2143 op = list_powerlosses; 2144 break; 2145 // configuration 2146 case OPT_DEFINE: { 2147 // allocate space 2148 test_override_t *override = mappend( 2149 (void**)&test_overrides, 2150 sizeof(test_override_t), 2151 &test_override_count, 2152 &test_override_capacity); 2153 2154 // parse into string key/intmax_t value, cannibalizing the 2155 // arg in the process 2156 char *sep = strchr(optarg, '='); 2157 char *parsed = NULL; 2158 if (!sep) { 2159 goto invalid_define; 2160 } 2161 *sep = '\0'; 2162 override->name = optarg; 2163 optarg = sep+1; 2164 2165 // parse comma-separated permutations 2166 { 2167 override->defines = NULL; 2168 override->permutations = 0; 2169 size_t override_capacity = 0; 2170 while (true) { 2171 optarg += strspn(optarg, " "); 2172 2173 if (strncmp(optarg, "range", strlen("range")) == 0) { 2174 // range of values 2175 optarg += strlen("range"); 2176 optarg += strspn(optarg, " "); 2177 if (*optarg != '(') { 2178 goto invalid_define; 2179 } 2180 optarg += 1; 2181 2182 intmax_t start = strtoumax(optarg, &parsed, 0); 2183 intmax_t stop = -1; 2184 intmax_t step = 1; 2185 // allow empty string for start=0 2186 if (parsed == optarg) { 2187 start = 0; 2188 } 2189 optarg = parsed + strspn(parsed, " "); 2190 2191 if (*optarg != ',' && *optarg != ')') { 2192 goto invalid_define; 2193 } 2194 2195 if (*optarg == ',') { 2196 optarg += 1; 2197 stop = strtoumax(optarg, &parsed, 0); 2198 // allow empty string for stop=end 2199 if (parsed == optarg) { 2200 stop = -1; 2201 } 2202 optarg = parsed + strspn(parsed, " "); 2203 2204 if (*optarg != ',' && *optarg != ')') { 2205 goto invalid_define; 2206 } 2207 2208 if (*optarg == ',') { 2209 optarg += 1; 2210 step = strtoumax(optarg, &parsed, 0); 2211 // allow empty string for stop=1 2212 if (parsed == optarg) { 2213 step = 1; 2214 } 2215 optarg = parsed + strspn(parsed, " "); 2216 2217 if (*optarg != ')') { 2218 goto invalid_define; 2219 } 2220 } 2221 } else { 2222 // single value = stop only 2223 stop = start; 2224 start = 0; 2225 } 2226 2227 if (*optarg != ')') { 2228 goto invalid_define; 2229 } 2230 optarg += 1; 2231 2232 // calculate the range of values 2233 assert(step != 0); 2234 for (intmax_t i = start; 2235 (step < 0) 2236 ? i > stop 2237 : (uintmax_t)i < (uintmax_t)stop; 2238 i += step) { 2239 *(intmax_t*)mappend( 2240 (void**)&override->defines, 2241 sizeof(intmax_t), 2242 &override->permutations, 2243 &override_capacity) = i; 2244 } 2245 } else if (*optarg != '\0') { 2246 // single value 2247 intmax_t define = strtoimax(optarg, &parsed, 0); 2248 if (parsed == optarg) { 2249 goto invalid_define; 2250 } 2251 optarg = parsed + strspn(parsed, " "); 2252 *(intmax_t*)mappend( 2253 (void**)&override->defines, 2254 sizeof(intmax_t), 2255 &override->permutations, 2256 &override_capacity) = define; 2257 } else { 2258 break; 2259 } 2260 2261 if (*optarg == ',') { 2262 optarg += 1; 2263 } 2264 } 2265 } 2266 assert(override->permutations > 0); 2267 break; 2268 2269invalid_define: 2270 fprintf(stderr, "error: invalid define: %s\n", optarg); 2271 exit(-1); 2272 } 2273 case OPT_GEOMETRY: { 2274 // reset our geometry scenarios 2275 if (test_geometry_capacity > 0) { 2276 free((test_geometry_t*)test_geometries); 2277 } 2278 test_geometries = NULL; 2279 test_geometry_count = 0; 2280 test_geometry_capacity = 0; 2281 2282 // parse the comma separated list of disk geometries 2283 while (*optarg) { 2284 // allocate space 2285 test_geometry_t *geometry = mappend( 2286 (void**)&test_geometries, 2287 sizeof(test_geometry_t), 2288 &test_geometry_count, 2289 &test_geometry_capacity); 2290 2291 // parse the disk geometry 2292 optarg += strspn(optarg, " "); 2293 2294 // named disk geometry 2295 size_t len = strcspn(optarg, " ,"); 2296 for (size_t i = 0; builtin_geometries[i].name; i++) { 2297 if (len == strlen(builtin_geometries[i].name) 2298 && memcmp(optarg, 2299 builtin_geometries[i].name, 2300 len) == 0) { 2301 *geometry = builtin_geometries[i]; 2302 optarg += len; 2303 goto geometry_next; 2304 } 2305 } 2306 2307 // comma-separated read/prog/erase/count 2308 if (*optarg == '{') { 2309 lfs_size_t sizes[4]; 2310 size_t count = 0; 2311 2312 char *s = optarg + 1; 2313 while (count < 4) { 2314 char *parsed = NULL; 2315 sizes[count] = strtoumax(s, &parsed, 0); 2316 count += 1; 2317 2318 s = parsed + strspn(parsed, " "); 2319 if (*s == ',') { 2320 s += 1; 2321 continue; 2322 } else if (*s == '}') { 2323 s += 1; 2324 break; 2325 } else { 2326 goto geometry_unknown; 2327 } 2328 } 2329 2330 // allow implicit r=p and p=e for common geometries 2331 memset(geometry, 0, sizeof(test_geometry_t)); 2332 if (count >= 3) { 2333 geometry->defines[READ_SIZE_i] 2334 = TEST_LIT(sizes[0]); 2335 geometry->defines[PROG_SIZE_i] 2336 = TEST_LIT(sizes[1]); 2337 geometry->defines[ERASE_SIZE_i] 2338 = TEST_LIT(sizes[2]); 2339 } else if (count >= 2) { 2340 geometry->defines[PROG_SIZE_i] 2341 = TEST_LIT(sizes[0]); 2342 geometry->defines[ERASE_SIZE_i] 2343 = TEST_LIT(sizes[1]); 2344 } else { 2345 geometry->defines[ERASE_SIZE_i] 2346 = TEST_LIT(sizes[0]); 2347 } 2348 if (count >= 4) { 2349 geometry->defines[ERASE_COUNT_i] 2350 = TEST_LIT(sizes[3]); 2351 } 2352 optarg = s; 2353 goto geometry_next; 2354 } 2355 2356 // leb16-encoded read/prog/erase/count 2357 if (*optarg == ':') { 2358 lfs_size_t sizes[4]; 2359 size_t count = 0; 2360 2361 char *s = optarg + 1; 2362 while (true) { 2363 char *parsed = NULL; 2364 uintmax_t x = leb16_parse(s, &parsed); 2365 if (parsed == s || count >= 4) { 2366 break; 2367 } 2368 2369 sizes[count] = x; 2370 count += 1; 2371 s = parsed; 2372 } 2373 2374 // allow implicit r=p and p=e for common geometries 2375 memset(geometry, 0, sizeof(test_geometry_t)); 2376 if (count >= 3) { 2377 geometry->defines[READ_SIZE_i] 2378 = TEST_LIT(sizes[0]); 2379 geometry->defines[PROG_SIZE_i] 2380 = TEST_LIT(sizes[1]); 2381 geometry->defines[ERASE_SIZE_i] 2382 = TEST_LIT(sizes[2]); 2383 } else if (count >= 2) { 2384 geometry->defines[PROG_SIZE_i] 2385 = TEST_LIT(sizes[0]); 2386 geometry->defines[ERASE_SIZE_i] 2387 = TEST_LIT(sizes[1]); 2388 } else { 2389 geometry->defines[ERASE_SIZE_i] 2390 = TEST_LIT(sizes[0]); 2391 } 2392 if (count >= 4) { 2393 geometry->defines[ERASE_COUNT_i] 2394 = TEST_LIT(sizes[3]); 2395 } 2396 optarg = s; 2397 goto geometry_next; 2398 } 2399 2400geometry_unknown: 2401 // unknown scenario? 2402 fprintf(stderr, "error: unknown disk geometry: %s\n", 2403 optarg); 2404 exit(-1); 2405 2406geometry_next: 2407 optarg += strspn(optarg, " "); 2408 if (*optarg == ',') { 2409 optarg += 1; 2410 } else if (*optarg == '\0') { 2411 break; 2412 } else { 2413 goto geometry_unknown; 2414 } 2415 } 2416 break; 2417 } 2418 case OPT_POWERLOSS: { 2419 // reset our powerloss scenarios 2420 if (test_powerloss_capacity > 0) { 2421 free((test_powerloss_t*)test_powerlosses); 2422 } 2423 test_powerlosses = NULL; 2424 test_powerloss_count = 0; 2425 test_powerloss_capacity = 0; 2426 2427 // parse the comma separated list of power-loss scenarios 2428 while (*optarg) { 2429 // allocate space 2430 test_powerloss_t *powerloss = mappend( 2431 (void**)&test_powerlosses, 2432 sizeof(test_powerloss_t), 2433 &test_powerloss_count, 2434 &test_powerloss_capacity); 2435 2436 // parse the power-loss scenario 2437 optarg += strspn(optarg, " "); 2438 2439 // named power-loss scenario 2440 size_t len = strcspn(optarg, " ,"); 2441 for (size_t i = 0; builtin_powerlosses[i].name; i++) { 2442 if (len == strlen(builtin_powerlosses[i].name) 2443 && memcmp(optarg, 2444 builtin_powerlosses[i].name, 2445 len) == 0) { 2446 *powerloss = builtin_powerlosses[i]; 2447 optarg += len; 2448 goto powerloss_next; 2449 } 2450 } 2451 2452 // comma-separated permutation 2453 if (*optarg == '{') { 2454 lfs_emubd_powercycles_t *cycles = NULL; 2455 size_t cycle_count = 0; 2456 size_t cycle_capacity = 0; 2457 2458 char *s = optarg + 1; 2459 while (true) { 2460 char *parsed = NULL; 2461 *(lfs_emubd_powercycles_t*)mappend( 2462 (void**)&cycles, 2463 sizeof(lfs_emubd_powercycles_t), 2464 &cycle_count, 2465 &cycle_capacity) 2466 = strtoumax(s, &parsed, 0); 2467 2468 s = parsed + strspn(parsed, " "); 2469 if (*s == ',') { 2470 s += 1; 2471 continue; 2472 } else if (*s == '}') { 2473 s += 1; 2474 break; 2475 } else { 2476 goto powerloss_unknown; 2477 } 2478 } 2479 2480 *powerloss = (test_powerloss_t){ 2481 .run = run_powerloss_cycles, 2482 .cycles = cycles, 2483 .cycle_count = cycle_count, 2484 }; 2485 optarg = s; 2486 goto powerloss_next; 2487 } 2488 2489 // leb16-encoded permutation 2490 if (*optarg == ':') { 2491 lfs_emubd_powercycles_t *cycles = NULL; 2492 size_t cycle_count = 0; 2493 size_t cycle_capacity = 0; 2494 2495 char *s = optarg + 1; 2496 while (true) { 2497 char *parsed = NULL; 2498 uintmax_t x = leb16_parse(s, &parsed); 2499 if (parsed == s) { 2500 break; 2501 } 2502 2503 *(lfs_emubd_powercycles_t*)mappend( 2504 (void**)&cycles, 2505 sizeof(lfs_emubd_powercycles_t), 2506 &cycle_count, 2507 &cycle_capacity) = x; 2508 s = parsed; 2509 } 2510 2511 *powerloss = (test_powerloss_t){ 2512 .run = run_powerloss_cycles, 2513 .cycles = cycles, 2514 .cycle_count = cycle_count, 2515 }; 2516 optarg = s; 2517 goto powerloss_next; 2518 } 2519 2520 // exhaustive permutations 2521 { 2522 char *parsed = NULL; 2523 size_t count = strtoumax(optarg, &parsed, 0); 2524 if (parsed == optarg) { 2525 goto powerloss_unknown; 2526 } 2527 *powerloss = (test_powerloss_t){ 2528 .run = run_powerloss_exhaustive, 2529 .cycles = NULL, 2530 .cycle_count = count, 2531 }; 2532 optarg = (char*)parsed; 2533 goto powerloss_next; 2534 } 2535 2536powerloss_unknown: 2537 // unknown scenario? 2538 fprintf(stderr, "error: unknown power-loss scenario: %s\n", 2539 optarg); 2540 exit(-1); 2541 2542powerloss_next: 2543 optarg += strspn(optarg, " "); 2544 if (*optarg == ',') { 2545 optarg += 1; 2546 } else if (*optarg == '\0') { 2547 break; 2548 } else { 2549 goto powerloss_unknown; 2550 } 2551 } 2552 break; 2553 } 2554 case OPT_STEP: { 2555 char *parsed = NULL; 2556 test_step_start = strtoumax(optarg, &parsed, 0); 2557 test_step_stop = -1; 2558 test_step_step = 1; 2559 // allow empty string for start=0 2560 if (parsed == optarg) { 2561 test_step_start = 0; 2562 } 2563 optarg = parsed + strspn(parsed, " "); 2564 2565 if (*optarg != ',' && *optarg != '\0') { 2566 goto step_unknown; 2567 } 2568 2569 if (*optarg == ',') { 2570 optarg += 1; 2571 test_step_stop = strtoumax(optarg, &parsed, 0); 2572 // allow empty string for stop=end 2573 if (parsed == optarg) { 2574 test_step_stop = -1; 2575 } 2576 optarg = parsed + strspn(parsed, " "); 2577 2578 if (*optarg != ',' && *optarg != '\0') { 2579 goto step_unknown; 2580 } 2581 2582 if (*optarg == ',') { 2583 optarg += 1; 2584 test_step_step = strtoumax(optarg, &parsed, 0); 2585 // allow empty string for stop=1 2586 if (parsed == optarg) { 2587 test_step_step = 1; 2588 } 2589 optarg = parsed + strspn(parsed, " "); 2590 2591 if (*optarg != '\0') { 2592 goto step_unknown; 2593 } 2594 } 2595 } else { 2596 // single value = stop only 2597 test_step_stop = test_step_start; 2598 test_step_start = 0; 2599 } 2600 2601 break; 2602step_unknown: 2603 fprintf(stderr, "error: invalid step: %s\n", optarg); 2604 exit(-1); 2605 } 2606 case OPT_DISK: 2607 test_disk_path = optarg; 2608 break; 2609 case OPT_TRACE: 2610 test_trace_path = optarg; 2611 break; 2612 case OPT_TRACE_BACKTRACE: 2613 test_trace_backtrace = true; 2614 break; 2615 case OPT_TRACE_PERIOD: { 2616 char *parsed = NULL; 2617 test_trace_period = strtoumax(optarg, &parsed, 0); 2618 if (parsed == optarg) { 2619 fprintf(stderr, "error: invalid trace-period: %s\n", optarg); 2620 exit(-1); 2621 } 2622 break; 2623 } 2624 case OPT_TRACE_FREQ: { 2625 char *parsed = NULL; 2626 test_trace_freq = strtoumax(optarg, &parsed, 0); 2627 if (parsed == optarg) { 2628 fprintf(stderr, "error: invalid trace-freq: %s\n", optarg); 2629 exit(-1); 2630 } 2631 break; 2632 } 2633 case OPT_READ_SLEEP: { 2634 char *parsed = NULL; 2635 double read_sleep = strtod(optarg, &parsed); 2636 if (parsed == optarg) { 2637 fprintf(stderr, "error: invalid read-sleep: %s\n", optarg); 2638 exit(-1); 2639 } 2640 test_read_sleep = read_sleep*1.0e9; 2641 break; 2642 } 2643 case OPT_PROG_SLEEP: { 2644 char *parsed = NULL; 2645 double prog_sleep = strtod(optarg, &parsed); 2646 if (parsed == optarg) { 2647 fprintf(stderr, "error: invalid prog-sleep: %s\n", optarg); 2648 exit(-1); 2649 } 2650 test_prog_sleep = prog_sleep*1.0e9; 2651 break; 2652 } 2653 case OPT_ERASE_SLEEP: { 2654 char *parsed = NULL; 2655 double erase_sleep = strtod(optarg, &parsed); 2656 if (parsed == optarg) { 2657 fprintf(stderr, "error: invalid erase-sleep: %s\n", optarg); 2658 exit(-1); 2659 } 2660 test_erase_sleep = erase_sleep*1.0e9; 2661 break; 2662 } 2663 // done parsing 2664 case -1: 2665 goto getopt_done; 2666 // unknown arg, getopt prints a message for us 2667 default: 2668 exit(-1); 2669 } 2670 } 2671getopt_done: ; 2672 2673 if (argc > optind) { 2674 // reset our test identifier list 2675 test_ids = NULL; 2676 test_id_count = 0; 2677 test_id_capacity = 0; 2678 } 2679 2680 // parse test identifier, if any, cannibalizing the arg in the process 2681 for (; argc > optind; optind++) { 2682 test_define_t *defines = NULL; 2683 size_t define_count = 0; 2684 lfs_emubd_powercycles_t *cycles = NULL; 2685 size_t cycle_count = 0; 2686 2687 // parse name, can be suite or case 2688 char *name = argv[optind]; 2689 char *defines_ = strchr(name, ':'); 2690 if (defines_) { 2691 *defines_ = '\0'; 2692 defines_ += 1; 2693 } 2694 2695 // remove optional path and .toml suffix 2696 char *slash = strrchr(name, '/'); 2697 if (slash) { 2698 name = slash+1; 2699 } 2700 2701 size_t name_len = strlen(name); 2702 if (name_len > 5 && strcmp(&name[name_len-5], ".toml") == 0) { 2703 name[name_len-5] = '\0'; 2704 } 2705 2706 if (defines_) { 2707 // parse defines 2708 char *cycles_ = strchr(defines_, ':'); 2709 if (cycles_) { 2710 *cycles_ = '\0'; 2711 cycles_ += 1; 2712 } 2713 2714 while (true) { 2715 char *parsed; 2716 size_t d = leb16_parse(defines_, &parsed); 2717 intmax_t v = leb16_parse(parsed, &parsed); 2718 if (parsed == defines_) { 2719 break; 2720 } 2721 defines_ = parsed; 2722 2723 if (d >= define_count) { 2724 // align to power of two to avoid any superlinear growth 2725 size_t ncount = 1 << lfs_npw2(d+1); 2726 defines = realloc(defines, 2727 ncount*sizeof(test_define_t)); 2728 memset(defines+define_count, 0, 2729 (ncount-define_count)*sizeof(test_define_t)); 2730 define_count = ncount; 2731 } 2732 defines[d] = TEST_LIT(v); 2733 } 2734 2735 if (cycles_) { 2736 // parse power cycles 2737 size_t cycle_capacity = 0; 2738 while (*cycles_ != '\0') { 2739 char *parsed = NULL; 2740 *(lfs_emubd_powercycles_t*)mappend( 2741 (void**)&cycles, 2742 sizeof(lfs_emubd_powercycles_t), 2743 &cycle_count, 2744 &cycle_capacity) 2745 = leb16_parse(cycles_, &parsed); 2746 if (parsed == cycles_) { 2747 fprintf(stderr, "error: " 2748 "could not parse test cycles: %s\n", 2749 cycles_); 2750 exit(-1); 2751 } 2752 cycles_ = parsed; 2753 } 2754 } 2755 } 2756 2757 // append to identifier list 2758 *(test_id_t*)mappend( 2759 (void**)&test_ids, 2760 sizeof(test_id_t), 2761 &test_id_count, 2762 &test_id_capacity) = (test_id_t){ 2763 .name = name, 2764 .defines = defines, 2765 .define_count = define_count, 2766 .cycles = cycles, 2767 .cycle_count = cycle_count, 2768 }; 2769 } 2770 2771 // do the thing 2772 op(); 2773 2774 // cleanup (need to be done for valgrind testing) 2775 test_define_cleanup(); 2776 if (test_overrides) { 2777 for (size_t i = 0; i < test_override_count; i++) { 2778 free((void*)test_overrides[i].defines); 2779 } 2780 free((void*)test_overrides); 2781 } 2782 if (test_geometry_capacity) { 2783 free((void*)test_geometries); 2784 } 2785 if (test_powerloss_capacity) { 2786 for (size_t i = 0; i < test_powerloss_count; i++) { 2787 free((void*)test_powerlosses[i].cycles); 2788 } 2789 free((void*)test_powerlosses); 2790 } 2791 if (test_id_capacity) { 2792 for (size_t i = 0; i < test_id_count; i++) { 2793 free((void*)test_ids[i].defines); 2794 free((void*)test_ids[i].cycles); 2795 } 2796 free((void*)test_ids); 2797 } 2798} 2799