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
mappend(void ** p,size_t size,size_t * count,size_t * capacity)29 void *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
leb16_print(uintmax_t x)54 static 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
leb16_parse(const char * s,char ** tail)71 static 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
113 typedef struct test_geometry {
114 const char *name;
115 test_define_t defines[TEST_GEOMETRY_DEFINE_COUNT];
116 } test_geometry_t;
117
118 typedef 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
129 typedef 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
139 extern struct test_suite __start__test_suites;
140 extern struct test_suite __stop__test_suites;
141
142 const 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
148 typedef struct test_define_map {
149 const test_define_t *defines;
150 size_t count;
151 } test_define_map_t;
152
153 typedef struct test_define_names {
154 const char *const *names;
155 size_t count;
156 } test_define_names_t;
157
test_define_lit(void * data)158 intmax_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
182 test_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
199 test_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
212 intmax_t *test_define_cache;
213 size_t test_define_cache_count;
214 unsigned *test_define_cache_mask;
215
test_define_name(size_t define)216 const 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
test_define_ispermutation(size_t define)229 bool 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
test_define(size_t define)241 intmax_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
test_define_flush(void)276 void 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
285 const test_geometry_t *test_geometry = NULL;
286
test_define_geometry(const test_geometry_t * geometry)287 void 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
293 typedef struct test_override {
294 const char *name;
295 const intmax_t *defines;
296 size_t permutations;
297 } test_override_t;
298
299 const test_override_t *test_overrides = NULL;
300 size_t test_override_count = 0;
301
302 test_define_t *test_override_defines = NULL;
303 size_t test_override_define_count = 0;
304 size_t test_override_define_permutations = 1;
305 size_t test_override_define_capacity = 0;
306
307 // suite/perm updates
test_define_suite(const struct test_suite * suite)308 void 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
test_define_perm(const struct test_suite * suite,const struct test_case * case_,size_t perm)391 void 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
test_define_override(size_t perm)405 void 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
test_define_explicit(const test_define_t * defines,size_t define_count)411 void 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
test_define_cleanup(void)418 void 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
428 extern const test_geometry_t *test_geometries;
429 extern size_t test_geometry_count;
430
431 extern const test_powerloss_t *test_powerlosses;
432 extern size_t test_powerloss_count;
433
434 const test_id_t *test_ids = (const test_id_t[]) {
435 {NULL, NULL, 0, NULL, 0},
436 };
437 size_t test_id_count = 1;
438
439 size_t test_step_start = 0;
440 size_t test_step_stop = -1;
441 size_t test_step_step = 1;
442
443 const char *test_disk_path = NULL;
444 const char *test_trace_path = NULL;
445 bool test_trace_backtrace = false;
446 uint32_t test_trace_period = 0;
447 uint32_t test_trace_freq = 0;
448 FILE *test_trace_file = NULL;
449 uint32_t test_trace_cycles = 0;
450 uint64_t test_trace_time = 0;
451 uint64_t test_trace_open_time = 0;
452 lfs_emubd_sleep_t test_read_sleep = 0.0;
453 lfs_emubd_sleep_t test_prog_sleep = 0.0;
454 lfs_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
461 void *test_trace_backtrace_buffer[
462 TEST_TRACE_BACKTRACE_BUFFER_SIZE / sizeof(void*)];
463
464 // trace printing
test_trace(const char * fmt,...)465 void 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
test_prng(uint32_t * state)563 uint32_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
perm_printid(const struct test_suite * suite,const struct test_case * case_,const lfs_emubd_powercycles_t * cycles,size_t cycle_count)576 static 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
606 typedef struct test_seen {
607 struct test_seen_branch *branches;
608 size_t branch_count;
609 size_t branch_capacity;
610 } test_seen_t;
611
612 struct test_seen_branch {
613 intmax_t define;
614 struct test_seen branch;
615 };
616
test_seen_insert(test_seen_t * seen,const struct test_suite * suite,const struct test_case * case_)617 bool 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
test_seen_cleanup(test_seen_t * seen)660 void 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
667 static 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_);
672 static 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
case_forperm(const struct test_suite * suite,const struct test_case * case_,const test_define_t * defines,size_t define_count,const lfs_emubd_powercycles_t * cycles,size_t cycle_count,void (* cb)(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss),void * data)679 static 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
769 struct perm_count_state {
770 size_t total;
771 size_t filtered;
772 };
773
perm_count(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)774 void 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
summary(void)795 static 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
list_suites(void)848 static 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
list_cases(void)911 static 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
list_suite_paths(void)968 static 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
list_case_paths(void)1010 static 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
1045 struct list_defines_define {
1046 const char *name;
1047 intmax_t *values;
1048 size_t value_count;
1049 size_t value_capacity;
1050 };
1051
1052 struct list_defines_defines {
1053 struct list_defines_define *defines;
1054 size_t define_count;
1055 size_t define_capacity;
1056 };
1057
list_defines_add(struct list_defines_defines * defines,size_t d)1058 static 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
perm_list_defines(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1097 void 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
perm_list_permutation_defines(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1119 void 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
1140 extern const test_geometry_t builtin_geometries[];
1141
list_defines(void)1142 static 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
list_permutation_defines(void)1190 static 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
list_implicit_defines(void)1238 static 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
1278 const 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
1287 const test_geometry_t *test_geometries = builtin_geometries;
1288 size_t test_geometry_count = 5;
1289
list_geometries(void)1290 static 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
run_powerloss_none(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1324 static 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 .compact_thresh = COMPACT_THRESH,
1350 .metadata_max = METADATA_MAX,
1351 .inline_max = INLINE_MAX,
1352 #ifdef LFS_MULTIVERSION
1353 .disk_version = DISK_VERSION,
1354 #endif
1355 };
1356
1357 struct lfs_emubd_config bdcfg = {
1358 .read_size = READ_SIZE,
1359 .prog_size = PROG_SIZE,
1360 .erase_size = ERASE_SIZE,
1361 .erase_count = ERASE_COUNT,
1362 .erase_value = ERASE_VALUE,
1363 .erase_cycles = ERASE_CYCLES,
1364 .badblock_behavior = BADBLOCK_BEHAVIOR,
1365 .disk_path = test_disk_path,
1366 .read_sleep = test_read_sleep,
1367 .prog_sleep = test_prog_sleep,
1368 .erase_sleep = test_erase_sleep,
1369 };
1370
1371 int err = lfs_emubd_create(&cfg, &bdcfg);
1372 if (err) {
1373 fprintf(stderr, "error: could not create block device: %d\n", err);
1374 exit(-1);
1375 }
1376
1377 // run the test
1378 printf("running ");
1379 perm_printid(suite, case_, NULL, 0);
1380 printf("\n");
1381
1382 case_->run(&cfg);
1383
1384 printf("finished ");
1385 perm_printid(suite, case_, NULL, 0);
1386 printf("\n");
1387
1388 // cleanup
1389 err = lfs_emubd_destroy(&cfg);
1390 if (err) {
1391 fprintf(stderr, "error: could not destroy block device: %d\n", err);
1392 exit(-1);
1393 }
1394 }
1395
powerloss_longjmp(void * c)1396 static void powerloss_longjmp(void *c) {
1397 jmp_buf *powerloss_jmp = c;
1398 longjmp(*powerloss_jmp, 1);
1399 }
1400
run_powerloss_linear(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1401 static void run_powerloss_linear(
1402 const lfs_emubd_powercycles_t *cycles,
1403 size_t cycle_count,
1404 const struct test_suite *suite,
1405 const struct test_case *case_) {
1406 (void)cycles;
1407 (void)cycle_count;
1408 (void)suite;
1409
1410 // create block device and configuration
1411 lfs_emubd_t bd;
1412 jmp_buf powerloss_jmp;
1413 volatile lfs_emubd_powercycles_t i = 1;
1414
1415 struct lfs_config cfg = {
1416 .context = &bd,
1417 .read = lfs_emubd_read,
1418 .prog = lfs_emubd_prog,
1419 .erase = lfs_emubd_erase,
1420 .sync = lfs_emubd_sync,
1421 .read_size = READ_SIZE,
1422 .prog_size = PROG_SIZE,
1423 .block_size = BLOCK_SIZE,
1424 .block_count = BLOCK_COUNT,
1425 .block_cycles = BLOCK_CYCLES,
1426 .cache_size = CACHE_SIZE,
1427 .lookahead_size = LOOKAHEAD_SIZE,
1428 .compact_thresh = COMPACT_THRESH,
1429 .metadata_max = METADATA_MAX,
1430 .inline_max = INLINE_MAX,
1431 #ifdef LFS_MULTIVERSION
1432 .disk_version = DISK_VERSION,
1433 #endif
1434 };
1435
1436 struct lfs_emubd_config bdcfg = {
1437 .read_size = READ_SIZE,
1438 .prog_size = PROG_SIZE,
1439 .erase_size = ERASE_SIZE,
1440 .erase_count = ERASE_COUNT,
1441 .erase_value = ERASE_VALUE,
1442 .erase_cycles = ERASE_CYCLES,
1443 .badblock_behavior = BADBLOCK_BEHAVIOR,
1444 .disk_path = test_disk_path,
1445 .read_sleep = test_read_sleep,
1446 .prog_sleep = test_prog_sleep,
1447 .erase_sleep = test_erase_sleep,
1448 .power_cycles = i,
1449 .powerloss_behavior = POWERLOSS_BEHAVIOR,
1450 .powerloss_cb = powerloss_longjmp,
1451 .powerloss_data = &powerloss_jmp,
1452 };
1453
1454 int err = lfs_emubd_create(&cfg, &bdcfg);
1455 if (err) {
1456 fprintf(stderr, "error: could not create block device: %d\n", err);
1457 exit(-1);
1458 }
1459
1460 // run the test, increasing power-cycles as power-loss events occur
1461 printf("running ");
1462 perm_printid(suite, case_, NULL, 0);
1463 printf("\n");
1464
1465 while (true) {
1466 if (!setjmp(powerloss_jmp)) {
1467 // run the test
1468 case_->run(&cfg);
1469 break;
1470 }
1471
1472 // power-loss!
1473 printf("powerloss ");
1474 perm_printid(suite, case_, NULL, 0);
1475 printf(":");
1476 for (lfs_emubd_powercycles_t j = 1; j <= i; j++) {
1477 leb16_print(j);
1478 }
1479 printf("\n");
1480
1481 i += 1;
1482 lfs_emubd_setpowercycles(&cfg, i);
1483 }
1484
1485 printf("finished ");
1486 perm_printid(suite, case_, NULL, 0);
1487 printf("\n");
1488
1489 // cleanup
1490 err = lfs_emubd_destroy(&cfg);
1491 if (err) {
1492 fprintf(stderr, "error: could not destroy block device: %d\n", err);
1493 exit(-1);
1494 }
1495 }
1496
run_powerloss_log(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1497 static void run_powerloss_log(
1498 const lfs_emubd_powercycles_t *cycles,
1499 size_t cycle_count,
1500 const struct test_suite *suite,
1501 const struct test_case *case_) {
1502 (void)cycles;
1503 (void)cycle_count;
1504 (void)suite;
1505
1506 // create block device and configuration
1507 lfs_emubd_t bd;
1508 jmp_buf powerloss_jmp;
1509 volatile lfs_emubd_powercycles_t i = 1;
1510
1511 struct lfs_config cfg = {
1512 .context = &bd,
1513 .read = lfs_emubd_read,
1514 .prog = lfs_emubd_prog,
1515 .erase = lfs_emubd_erase,
1516 .sync = lfs_emubd_sync,
1517 .read_size = READ_SIZE,
1518 .prog_size = PROG_SIZE,
1519 .block_size = BLOCK_SIZE,
1520 .block_count = BLOCK_COUNT,
1521 .block_cycles = BLOCK_CYCLES,
1522 .cache_size = CACHE_SIZE,
1523 .lookahead_size = LOOKAHEAD_SIZE,
1524 .compact_thresh = COMPACT_THRESH,
1525 .metadata_max = METADATA_MAX,
1526 .inline_max = INLINE_MAX,
1527 #ifdef LFS_MULTIVERSION
1528 .disk_version = DISK_VERSION,
1529 #endif
1530 };
1531
1532 struct lfs_emubd_config bdcfg = {
1533 .read_size = READ_SIZE,
1534 .prog_size = PROG_SIZE,
1535 .erase_size = ERASE_SIZE,
1536 .erase_count = ERASE_COUNT,
1537 .erase_value = ERASE_VALUE,
1538 .erase_cycles = ERASE_CYCLES,
1539 .badblock_behavior = BADBLOCK_BEHAVIOR,
1540 .disk_path = test_disk_path,
1541 .read_sleep = test_read_sleep,
1542 .prog_sleep = test_prog_sleep,
1543 .erase_sleep = test_erase_sleep,
1544 .power_cycles = i,
1545 .powerloss_behavior = POWERLOSS_BEHAVIOR,
1546 .powerloss_cb = powerloss_longjmp,
1547 .powerloss_data = &powerloss_jmp,
1548 };
1549
1550 int err = lfs_emubd_create(&cfg, &bdcfg);
1551 if (err) {
1552 fprintf(stderr, "error: could not create block device: %d\n", err);
1553 exit(-1);
1554 }
1555
1556 // run the test, increasing power-cycles as power-loss events occur
1557 printf("running ");
1558 perm_printid(suite, case_, NULL, 0);
1559 printf("\n");
1560
1561 while (true) {
1562 if (!setjmp(powerloss_jmp)) {
1563 // run the test
1564 case_->run(&cfg);
1565 break;
1566 }
1567
1568 // power-loss!
1569 printf("powerloss ");
1570 perm_printid(suite, case_, NULL, 0);
1571 printf(":");
1572 for (lfs_emubd_powercycles_t j = 1; j <= i; j *= 2) {
1573 leb16_print(j);
1574 }
1575 printf("\n");
1576
1577 i *= 2;
1578 lfs_emubd_setpowercycles(&cfg, i);
1579 }
1580
1581 printf("finished ");
1582 perm_printid(suite, case_, NULL, 0);
1583 printf("\n");
1584
1585 // cleanup
1586 err = lfs_emubd_destroy(&cfg);
1587 if (err) {
1588 fprintf(stderr, "error: could not destroy block device: %d\n", err);
1589 exit(-1);
1590 }
1591 }
1592
run_powerloss_cycles(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1593 static void run_powerloss_cycles(
1594 const lfs_emubd_powercycles_t *cycles,
1595 size_t cycle_count,
1596 const struct test_suite *suite,
1597 const struct test_case *case_) {
1598 (void)suite;
1599
1600 // create block device and configuration
1601 lfs_emubd_t bd;
1602 jmp_buf powerloss_jmp;
1603 volatile size_t i = 0;
1604
1605 struct lfs_config cfg = {
1606 .context = &bd,
1607 .read = lfs_emubd_read,
1608 .prog = lfs_emubd_prog,
1609 .erase = lfs_emubd_erase,
1610 .sync = lfs_emubd_sync,
1611 .read_size = READ_SIZE,
1612 .prog_size = PROG_SIZE,
1613 .block_size = BLOCK_SIZE,
1614 .block_count = BLOCK_COUNT,
1615 .block_cycles = BLOCK_CYCLES,
1616 .cache_size = CACHE_SIZE,
1617 .lookahead_size = LOOKAHEAD_SIZE,
1618 .compact_thresh = COMPACT_THRESH,
1619 .metadata_max = METADATA_MAX,
1620 .inline_max = INLINE_MAX,
1621 #ifdef LFS_MULTIVERSION
1622 .disk_version = DISK_VERSION,
1623 #endif
1624 };
1625
1626 struct lfs_emubd_config bdcfg = {
1627 .read_size = READ_SIZE,
1628 .prog_size = PROG_SIZE,
1629 .erase_size = ERASE_SIZE,
1630 .erase_count = ERASE_COUNT,
1631 .erase_value = ERASE_VALUE,
1632 .erase_cycles = ERASE_CYCLES,
1633 .badblock_behavior = BADBLOCK_BEHAVIOR,
1634 .disk_path = test_disk_path,
1635 .read_sleep = test_read_sleep,
1636 .prog_sleep = test_prog_sleep,
1637 .erase_sleep = test_erase_sleep,
1638 .power_cycles = (i < cycle_count) ? cycles[i] : 0,
1639 .powerloss_behavior = POWERLOSS_BEHAVIOR,
1640 .powerloss_cb = powerloss_longjmp,
1641 .powerloss_data = &powerloss_jmp,
1642 };
1643
1644 int err = lfs_emubd_create(&cfg, &bdcfg);
1645 if (err) {
1646 fprintf(stderr, "error: could not create block device: %d\n", err);
1647 exit(-1);
1648 }
1649
1650 // run the test, increasing power-cycles as power-loss events occur
1651 printf("running ");
1652 perm_printid(suite, case_, NULL, 0);
1653 printf("\n");
1654
1655 while (true) {
1656 if (!setjmp(powerloss_jmp)) {
1657 // run the test
1658 case_->run(&cfg);
1659 break;
1660 }
1661
1662 // power-loss!
1663 assert(i <= cycle_count);
1664 printf("powerloss ");
1665 perm_printid(suite, case_, cycles, i+1);
1666 printf("\n");
1667
1668 i += 1;
1669 lfs_emubd_setpowercycles(&cfg,
1670 (i < cycle_count) ? cycles[i] : 0);
1671 }
1672
1673 printf("finished ");
1674 perm_printid(suite, case_, NULL, 0);
1675 printf("\n");
1676
1677 // cleanup
1678 err = lfs_emubd_destroy(&cfg);
1679 if (err) {
1680 fprintf(stderr, "error: could not destroy block device: %d\n", err);
1681 exit(-1);
1682 }
1683 }
1684
1685 struct powerloss_exhaustive_state {
1686 struct lfs_config *cfg;
1687
1688 lfs_emubd_t *branches;
1689 size_t branch_count;
1690 size_t branch_capacity;
1691 };
1692
1693 struct powerloss_exhaustive_cycles {
1694 lfs_emubd_powercycles_t *cycles;
1695 size_t cycle_count;
1696 size_t cycle_capacity;
1697 };
1698
powerloss_exhaustive_branch(void * c)1699 static void powerloss_exhaustive_branch(void *c) {
1700 struct powerloss_exhaustive_state *state = c;
1701 // append to branches
1702 lfs_emubd_t *branch = mappend(
1703 (void**)&state->branches,
1704 sizeof(lfs_emubd_t),
1705 &state->branch_count,
1706 &state->branch_capacity);
1707 if (!branch) {
1708 fprintf(stderr, "error: exhaustive: out of memory\n");
1709 exit(-1);
1710 }
1711
1712 // create copy-on-write copy
1713 int err = lfs_emubd_copy(state->cfg, branch);
1714 if (err) {
1715 fprintf(stderr, "error: exhaustive: could not create bd copy\n");
1716 exit(-1);
1717 }
1718
1719 // also trigger on next power cycle
1720 lfs_emubd_setpowercycles(state->cfg, 1);
1721 }
1722
run_powerloss_exhaustive_layer(struct powerloss_exhaustive_cycles * cycles,const struct test_suite * suite,const struct test_case * case_,struct lfs_config * cfg,struct lfs_emubd_config * bdcfg,size_t depth)1723 static void run_powerloss_exhaustive_layer(
1724 struct powerloss_exhaustive_cycles *cycles,
1725 const struct test_suite *suite,
1726 const struct test_case *case_,
1727 struct lfs_config *cfg,
1728 struct lfs_emubd_config *bdcfg,
1729 size_t depth) {
1730 (void)suite;
1731
1732 struct powerloss_exhaustive_state state = {
1733 .cfg = cfg,
1734 .branches = NULL,
1735 .branch_count = 0,
1736 .branch_capacity = 0,
1737 };
1738
1739 // run through the test without additional powerlosses, collecting possible
1740 // branches as we do so
1741 lfs_emubd_setpowercycles(state.cfg, depth > 0 ? 1 : 0);
1742 bdcfg->powerloss_data = &state;
1743
1744 // run the tests
1745 case_->run(cfg);
1746
1747 // aggressively clean up memory here to try to keep our memory usage low
1748 int err = lfs_emubd_destroy(cfg);
1749 if (err) {
1750 fprintf(stderr, "error: could not destroy block device: %d\n", err);
1751 exit(-1);
1752 }
1753
1754 // recurse into each branch
1755 for (size_t i = 0; i < state.branch_count; i++) {
1756 // first push and print the branch
1757 lfs_emubd_powercycles_t *cycle = mappend(
1758 (void**)&cycles->cycles,
1759 sizeof(lfs_emubd_powercycles_t),
1760 &cycles->cycle_count,
1761 &cycles->cycle_capacity);
1762 if (!cycle) {
1763 fprintf(stderr, "error: exhaustive: out of memory\n");
1764 exit(-1);
1765 }
1766 *cycle = i+1;
1767
1768 printf("powerloss ");
1769 perm_printid(suite, case_, cycles->cycles, cycles->cycle_count);
1770 printf("\n");
1771
1772 // now recurse
1773 cfg->context = &state.branches[i];
1774 run_powerloss_exhaustive_layer(cycles,
1775 suite, case_,
1776 cfg, bdcfg, depth-1);
1777
1778 // pop the cycle
1779 cycles->cycle_count -= 1;
1780 }
1781
1782 // clean up memory
1783 free(state.branches);
1784 }
1785
run_powerloss_exhaustive(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1786 static void run_powerloss_exhaustive(
1787 const lfs_emubd_powercycles_t *cycles,
1788 size_t cycle_count,
1789 const struct test_suite *suite,
1790 const struct test_case *case_) {
1791 (void)cycles;
1792 (void)suite;
1793
1794 // create block device and configuration
1795 lfs_emubd_t bd;
1796
1797 struct lfs_config cfg = {
1798 .context = &bd,
1799 .read = lfs_emubd_read,
1800 .prog = lfs_emubd_prog,
1801 .erase = lfs_emubd_erase,
1802 .sync = lfs_emubd_sync,
1803 .read_size = READ_SIZE,
1804 .prog_size = PROG_SIZE,
1805 .block_size = BLOCK_SIZE,
1806 .block_count = BLOCK_COUNT,
1807 .block_cycles = BLOCK_CYCLES,
1808 .cache_size = CACHE_SIZE,
1809 .lookahead_size = LOOKAHEAD_SIZE,
1810 .compact_thresh = COMPACT_THRESH,
1811 .metadata_max = METADATA_MAX,
1812 .inline_max = INLINE_MAX,
1813 #ifdef LFS_MULTIVERSION
1814 .disk_version = DISK_VERSION,
1815 #endif
1816 };
1817
1818 struct lfs_emubd_config bdcfg = {
1819 .read_size = READ_SIZE,
1820 .prog_size = PROG_SIZE,
1821 .erase_size = ERASE_SIZE,
1822 .erase_count = ERASE_COUNT,
1823 .erase_value = ERASE_VALUE,
1824 .erase_cycles = ERASE_CYCLES,
1825 .badblock_behavior = BADBLOCK_BEHAVIOR,
1826 .disk_path = test_disk_path,
1827 .read_sleep = test_read_sleep,
1828 .prog_sleep = test_prog_sleep,
1829 .erase_sleep = test_erase_sleep,
1830 .powerloss_behavior = POWERLOSS_BEHAVIOR,
1831 .powerloss_cb = powerloss_exhaustive_branch,
1832 .powerloss_data = NULL,
1833 };
1834
1835 int err = lfs_emubd_create(&cfg, &bdcfg);
1836 if (err) {
1837 fprintf(stderr, "error: could not create block device: %d\n", err);
1838 exit(-1);
1839 }
1840
1841 // run the test, increasing power-cycles as power-loss events occur
1842 printf("running ");
1843 perm_printid(suite, case_, NULL, 0);
1844 printf("\n");
1845
1846 // recursively exhaust each layer of powerlosses
1847 run_powerloss_exhaustive_layer(
1848 &(struct powerloss_exhaustive_cycles){NULL, 0, 0},
1849 suite, case_,
1850 &cfg, &bdcfg, cycle_count);
1851
1852 printf("finished ");
1853 perm_printid(suite, case_, NULL, 0);
1854 printf("\n");
1855 }
1856
1857
1858 const test_powerloss_t builtin_powerlosses[] = {
1859 {"none", run_powerloss_none, NULL, 0},
1860 {"log", run_powerloss_log, NULL, 0},
1861 {"linear", run_powerloss_linear, NULL, 0},
1862 {"exhaustive", run_powerloss_exhaustive, NULL, SIZE_MAX},
1863 {NULL, NULL, NULL, 0},
1864 };
1865
1866 const char *const builtin_powerlosses_help[] = {
1867 "Run with no power-losses.",
1868 "Run with exponentially-decreasing power-losses.",
1869 "Run with linearly-decreasing power-losses.",
1870 "Run a all permutations of power-losses, this may take a while.",
1871 "Run a all permutations of n power-losses.",
1872 "Run a custom comma-separated set of power-losses.",
1873 "Run a custom leb16-encoded set of power-losses.",
1874 };
1875
1876 // default to -Pnone,linear, which provides a good heuristic while still
1877 // running quickly
1878 const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
1879 {"none", run_powerloss_none, NULL, 0},
1880 {"linear", run_powerloss_linear, NULL, 0},
1881 };
1882 size_t test_powerloss_count = 2;
1883
list_powerlosses(void)1884 static void list_powerlosses(void) {
1885 // at least size so that names fit
1886 unsigned name_width = 23;
1887 for (size_t i = 0; builtin_powerlosses[i].name; i++) {
1888 size_t len = strlen(builtin_powerlosses[i].name);
1889 if (len > name_width) {
1890 name_width = len;
1891 }
1892 }
1893 name_width = 4*((name_width+1+4-1)/4)-1;
1894
1895 printf("%-*s %s\n", name_width, "scenario", "description");
1896 size_t i = 0;
1897 for (; builtin_powerlosses[i].name; i++) {
1898 printf("%-*s %s\n",
1899 name_width,
1900 builtin_powerlosses[i].name,
1901 builtin_powerlosses_help[i]);
1902 }
1903
1904 // a couple more options with special parsing
1905 printf("%-*s %s\n", name_width, "1,2,3", builtin_powerlosses_help[i+0]);
1906 printf("%-*s %s\n", name_width, "{1,2,3}", builtin_powerlosses_help[i+1]);
1907 printf("%-*s %s\n", name_width, ":1248g1", builtin_powerlosses_help[i+2]);
1908 }
1909
1910
1911 // global test step count
1912 size_t test_step = 0;
1913
perm_run(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1914 void perm_run(
1915 void *data,
1916 const struct test_suite *suite,
1917 const struct test_case *case_,
1918 const test_powerloss_t *powerloss) {
1919 (void)data;
1920
1921 // skip this step?
1922 if (!(test_step >= test_step_start
1923 && test_step < test_step_stop
1924 && (test_step-test_step_start) % test_step_step == 0)) {
1925 test_step += 1;
1926 return;
1927 }
1928 test_step += 1;
1929
1930 // filter?
1931 if (case_->filter && !case_->filter()) {
1932 printf("skipped ");
1933 perm_printid(suite, case_, NULL, 0);
1934 printf("\n");
1935 return;
1936 }
1937
1938 powerloss->run(
1939 powerloss->cycles, powerloss->cycle_count,
1940 suite, case_);
1941 }
1942
run(void)1943 static void run(void) {
1944 // ignore disconnected pipes
1945 signal(SIGPIPE, SIG_IGN);
1946
1947 for (size_t t = 0; t < test_id_count; t++) {
1948 for (size_t i = 0; i < TEST_SUITE_COUNT; i++) {
1949 test_define_suite(&test_suites[i]);
1950
1951 for (size_t j = 0; j < test_suites[i].case_count; j++) {
1952 // does neither suite nor case name match?
1953 if (test_ids[t].name && !(
1954 strcmp(test_ids[t].name,
1955 test_suites[i].name) == 0
1956 || strcmp(test_ids[t].name,
1957 test_suites[i].cases[j].name) == 0)) {
1958 continue;
1959 }
1960
1961 case_forperm(
1962 &test_suites[i],
1963 &test_suites[i].cases[j],
1964 test_ids[t].defines,
1965 test_ids[t].define_count,
1966 test_ids[t].cycles,
1967 test_ids[t].cycle_count,
1968 perm_run,
1969 NULL);
1970 }
1971 }
1972 }
1973 }
1974
1975
1976
1977 // option handling
1978 enum opt_flags {
1979 OPT_HELP = 'h',
1980 OPT_SUMMARY = 'Y',
1981 OPT_LIST_SUITES = 'l',
1982 OPT_LIST_CASES = 'L',
1983 OPT_LIST_SUITE_PATHS = 1,
1984 OPT_LIST_CASE_PATHS = 2,
1985 OPT_LIST_DEFINES = 3,
1986 OPT_LIST_PERMUTATION_DEFINES = 4,
1987 OPT_LIST_IMPLICIT_DEFINES = 5,
1988 OPT_LIST_GEOMETRIES = 6,
1989 OPT_LIST_POWERLOSSES = 7,
1990 OPT_DEFINE = 'D',
1991 OPT_GEOMETRY = 'G',
1992 OPT_POWERLOSS = 'P',
1993 OPT_STEP = 's',
1994 OPT_DISK = 'd',
1995 OPT_TRACE = 't',
1996 OPT_TRACE_BACKTRACE = 8,
1997 OPT_TRACE_PERIOD = 9,
1998 OPT_TRACE_FREQ = 10,
1999 OPT_READ_SLEEP = 11,
2000 OPT_PROG_SLEEP = 12,
2001 OPT_ERASE_SLEEP = 13,
2002 };
2003
2004 const char *short_opts = "hYlLD:G:P:s:d:t:";
2005
2006 const struct option long_opts[] = {
2007 {"help", no_argument, NULL, OPT_HELP},
2008 {"summary", no_argument, NULL, OPT_SUMMARY},
2009 {"list-suites", no_argument, NULL, OPT_LIST_SUITES},
2010 {"list-cases", no_argument, NULL, OPT_LIST_CASES},
2011 {"list-suite-paths", no_argument, NULL, OPT_LIST_SUITE_PATHS},
2012 {"list-case-paths", no_argument, NULL, OPT_LIST_CASE_PATHS},
2013 {"list-defines", no_argument, NULL, OPT_LIST_DEFINES},
2014 {"list-permutation-defines",
2015 no_argument, NULL, OPT_LIST_PERMUTATION_DEFINES},
2016 {"list-implicit-defines",
2017 no_argument, NULL, OPT_LIST_IMPLICIT_DEFINES},
2018 {"list-geometries", no_argument, NULL, OPT_LIST_GEOMETRIES},
2019 {"list-powerlosses", no_argument, NULL, OPT_LIST_POWERLOSSES},
2020 {"define", required_argument, NULL, OPT_DEFINE},
2021 {"geometry", required_argument, NULL, OPT_GEOMETRY},
2022 {"powerloss", required_argument, NULL, OPT_POWERLOSS},
2023 {"step", required_argument, NULL, OPT_STEP},
2024 {"disk", required_argument, NULL, OPT_DISK},
2025 {"trace", required_argument, NULL, OPT_TRACE},
2026 {"trace-backtrace", no_argument, NULL, OPT_TRACE_BACKTRACE},
2027 {"trace-period", required_argument, NULL, OPT_TRACE_PERIOD},
2028 {"trace-freq", required_argument, NULL, OPT_TRACE_FREQ},
2029 {"read-sleep", required_argument, NULL, OPT_READ_SLEEP},
2030 {"prog-sleep", required_argument, NULL, OPT_PROG_SLEEP},
2031 {"erase-sleep", required_argument, NULL, OPT_ERASE_SLEEP},
2032 {NULL, 0, NULL, 0},
2033 };
2034
2035 const char *const help_text[] = {
2036 "Show this help message.",
2037 "Show quick summary.",
2038 "List test suites.",
2039 "List test cases.",
2040 "List the path for each test suite.",
2041 "List the path and line number for each test case.",
2042 "List all defines in this test-runner.",
2043 "List explicit defines in this test-runner.",
2044 "List implicit defines in this test-runner.",
2045 "List the available disk geometries.",
2046 "List the available power-loss scenarios.",
2047 "Override a test define.",
2048 "Comma-separated list of disk geometries to test.",
2049 "Comma-separated list of power-loss scenarios to test.",
2050 "Comma-separated range of test permutations to run (start,stop,step).",
2051 "Direct block device operations to this file.",
2052 "Direct trace output to this file.",
2053 "Include a backtrace with every trace statement.",
2054 "Sample trace output at this period in cycles.",
2055 "Sample trace output at this frequency in hz.",
2056 "Artificial read delay in seconds.",
2057 "Artificial prog delay in seconds.",
2058 "Artificial erase delay in seconds.",
2059 };
2060
main(int argc,char ** argv)2061 int main(int argc, char **argv) {
2062 void (*op)(void) = run;
2063
2064 size_t test_override_capacity = 0;
2065 size_t test_geometry_capacity = 0;
2066 size_t test_powerloss_capacity = 0;
2067 size_t test_id_capacity = 0;
2068
2069 // parse options
2070 while (true) {
2071 int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
2072 switch (c) {
2073 // generate help message
2074 case OPT_HELP: {
2075 printf("usage: %s [options] [test_id]\n", argv[0]);
2076 printf("\n");
2077
2078 printf("options:\n");
2079 size_t i = 0;
2080 while (long_opts[i].name) {
2081 size_t indent;
2082 if (long_opts[i].has_arg == no_argument) {
2083 if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
2084 indent = printf(" -%c, --%s ",
2085 long_opts[i].val,
2086 long_opts[i].name);
2087 } else {
2088 indent = printf(" --%s ",
2089 long_opts[i].name);
2090 }
2091 } else {
2092 if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
2093 indent = printf(" -%c %s, --%s %s ",
2094 long_opts[i].val,
2095 long_opts[i].name,
2096 long_opts[i].name,
2097 long_opts[i].name);
2098 } else {
2099 indent = printf(" --%s %s ",
2100 long_opts[i].name,
2101 long_opts[i].name);
2102 }
2103 }
2104
2105 // a quick, hacky, byte-level method for text wrapping
2106 size_t len = strlen(help_text[i]);
2107 size_t j = 0;
2108 if (indent < 24) {
2109 printf("%*s %.80s\n",
2110 (int)(24-1-indent),
2111 "",
2112 &help_text[i][j]);
2113 j += 80;
2114 } else {
2115 printf("\n");
2116 }
2117
2118 while (j < len) {
2119 printf("%24s%.80s\n", "", &help_text[i][j]);
2120 j += 80;
2121 }
2122
2123 i += 1;
2124 }
2125
2126 printf("\n");
2127 exit(0);
2128 }
2129 // summary/list flags
2130 case OPT_SUMMARY:
2131 op = summary;
2132 break;
2133 case OPT_LIST_SUITES:
2134 op = list_suites;
2135 break;
2136 case OPT_LIST_CASES:
2137 op = list_cases;
2138 break;
2139 case OPT_LIST_SUITE_PATHS:
2140 op = list_suite_paths;
2141 break;
2142 case OPT_LIST_CASE_PATHS:
2143 op = list_case_paths;
2144 break;
2145 case OPT_LIST_DEFINES:
2146 op = list_defines;
2147 break;
2148 case OPT_LIST_PERMUTATION_DEFINES:
2149 op = list_permutation_defines;
2150 break;
2151 case OPT_LIST_IMPLICIT_DEFINES:
2152 op = list_implicit_defines;
2153 break;
2154 case OPT_LIST_GEOMETRIES:
2155 op = list_geometries;
2156 break;
2157 case OPT_LIST_POWERLOSSES:
2158 op = list_powerlosses;
2159 break;
2160 // configuration
2161 case OPT_DEFINE: {
2162 // allocate space
2163 test_override_t *override = mappend(
2164 (void**)&test_overrides,
2165 sizeof(test_override_t),
2166 &test_override_count,
2167 &test_override_capacity);
2168
2169 // parse into string key/intmax_t value, cannibalizing the
2170 // arg in the process
2171 char *sep = strchr(optarg, '=');
2172 char *parsed = NULL;
2173 if (!sep) {
2174 goto invalid_define;
2175 }
2176 *sep = '\0';
2177 override->name = optarg;
2178 optarg = sep+1;
2179
2180 // parse comma-separated permutations
2181 {
2182 override->defines = NULL;
2183 override->permutations = 0;
2184 size_t override_capacity = 0;
2185 while (true) {
2186 optarg += strspn(optarg, " ");
2187
2188 if (strncmp(optarg, "range", strlen("range")) == 0) {
2189 // range of values
2190 optarg += strlen("range");
2191 optarg += strspn(optarg, " ");
2192 if (*optarg != '(') {
2193 goto invalid_define;
2194 }
2195 optarg += 1;
2196
2197 intmax_t start = strtoumax(optarg, &parsed, 0);
2198 intmax_t stop = -1;
2199 intmax_t step = 1;
2200 // allow empty string for start=0
2201 if (parsed == optarg) {
2202 start = 0;
2203 }
2204 optarg = parsed + strspn(parsed, " ");
2205
2206 if (*optarg != ',' && *optarg != ')') {
2207 goto invalid_define;
2208 }
2209
2210 if (*optarg == ',') {
2211 optarg += 1;
2212 stop = strtoumax(optarg, &parsed, 0);
2213 // allow empty string for stop=end
2214 if (parsed == optarg) {
2215 stop = -1;
2216 }
2217 optarg = parsed + strspn(parsed, " ");
2218
2219 if (*optarg != ',' && *optarg != ')') {
2220 goto invalid_define;
2221 }
2222
2223 if (*optarg == ',') {
2224 optarg += 1;
2225 step = strtoumax(optarg, &parsed, 0);
2226 // allow empty string for stop=1
2227 if (parsed == optarg) {
2228 step = 1;
2229 }
2230 optarg = parsed + strspn(parsed, " ");
2231
2232 if (*optarg != ')') {
2233 goto invalid_define;
2234 }
2235 }
2236 } else {
2237 // single value = stop only
2238 stop = start;
2239 start = 0;
2240 }
2241
2242 if (*optarg != ')') {
2243 goto invalid_define;
2244 }
2245 optarg += 1;
2246
2247 // calculate the range of values
2248 assert(step != 0);
2249 for (intmax_t i = start;
2250 (step < 0)
2251 ? i > stop
2252 : (uintmax_t)i < (uintmax_t)stop;
2253 i += step) {
2254 *(intmax_t*)mappend(
2255 (void**)&override->defines,
2256 sizeof(intmax_t),
2257 &override->permutations,
2258 &override_capacity) = i;
2259 }
2260 } else if (*optarg != '\0') {
2261 // single value
2262 intmax_t define = strtoimax(optarg, &parsed, 0);
2263 if (parsed == optarg) {
2264 goto invalid_define;
2265 }
2266 optarg = parsed + strspn(parsed, " ");
2267 *(intmax_t*)mappend(
2268 (void**)&override->defines,
2269 sizeof(intmax_t),
2270 &override->permutations,
2271 &override_capacity) = define;
2272 } else {
2273 break;
2274 }
2275
2276 if (*optarg == ',') {
2277 optarg += 1;
2278 }
2279 }
2280 }
2281 assert(override->permutations > 0);
2282 break;
2283
2284 invalid_define:
2285 fprintf(stderr, "error: invalid define: %s\n", optarg);
2286 exit(-1);
2287 }
2288 case OPT_GEOMETRY: {
2289 // reset our geometry scenarios
2290 if (test_geometry_capacity > 0) {
2291 free((test_geometry_t*)test_geometries);
2292 }
2293 test_geometries = NULL;
2294 test_geometry_count = 0;
2295 test_geometry_capacity = 0;
2296
2297 // parse the comma separated list of disk geometries
2298 while (*optarg) {
2299 // allocate space
2300 test_geometry_t *geometry = mappend(
2301 (void**)&test_geometries,
2302 sizeof(test_geometry_t),
2303 &test_geometry_count,
2304 &test_geometry_capacity);
2305
2306 // parse the disk geometry
2307 optarg += strspn(optarg, " ");
2308
2309 // named disk geometry
2310 size_t len = strcspn(optarg, " ,");
2311 for (size_t i = 0; builtin_geometries[i].name; i++) {
2312 if (len == strlen(builtin_geometries[i].name)
2313 && memcmp(optarg,
2314 builtin_geometries[i].name,
2315 len) == 0) {
2316 *geometry = builtin_geometries[i];
2317 optarg += len;
2318 goto geometry_next;
2319 }
2320 }
2321
2322 // comma-separated read/prog/erase/count
2323 if (*optarg == '{') {
2324 lfs_size_t sizes[4];
2325 size_t count = 0;
2326
2327 char *s = optarg + 1;
2328 while (count < 4) {
2329 char *parsed = NULL;
2330 sizes[count] = strtoumax(s, &parsed, 0);
2331 count += 1;
2332
2333 s = parsed + strspn(parsed, " ");
2334 if (*s == ',') {
2335 s += 1;
2336 continue;
2337 } else if (*s == '}') {
2338 s += 1;
2339 break;
2340 } else {
2341 goto geometry_unknown;
2342 }
2343 }
2344
2345 // allow implicit r=p and p=e for common geometries
2346 memset(geometry, 0, sizeof(test_geometry_t));
2347 if (count >= 3) {
2348 geometry->defines[READ_SIZE_i]
2349 = TEST_LIT(sizes[0]);
2350 geometry->defines[PROG_SIZE_i]
2351 = TEST_LIT(sizes[1]);
2352 geometry->defines[ERASE_SIZE_i]
2353 = TEST_LIT(sizes[2]);
2354 } else if (count >= 2) {
2355 geometry->defines[PROG_SIZE_i]
2356 = TEST_LIT(sizes[0]);
2357 geometry->defines[ERASE_SIZE_i]
2358 = TEST_LIT(sizes[1]);
2359 } else {
2360 geometry->defines[ERASE_SIZE_i]
2361 = TEST_LIT(sizes[0]);
2362 }
2363 if (count >= 4) {
2364 geometry->defines[ERASE_COUNT_i]
2365 = TEST_LIT(sizes[3]);
2366 }
2367 optarg = s;
2368 goto geometry_next;
2369 }
2370
2371 // leb16-encoded read/prog/erase/count
2372 if (*optarg == ':') {
2373 lfs_size_t sizes[4];
2374 size_t count = 0;
2375
2376 char *s = optarg + 1;
2377 while (true) {
2378 char *parsed = NULL;
2379 uintmax_t x = leb16_parse(s, &parsed);
2380 if (parsed == s || count >= 4) {
2381 break;
2382 }
2383
2384 sizes[count] = x;
2385 count += 1;
2386 s = parsed;
2387 }
2388
2389 // allow implicit r=p and p=e for common geometries
2390 memset(geometry, 0, sizeof(test_geometry_t));
2391 if (count >= 3) {
2392 geometry->defines[READ_SIZE_i]
2393 = TEST_LIT(sizes[0]);
2394 geometry->defines[PROG_SIZE_i]
2395 = TEST_LIT(sizes[1]);
2396 geometry->defines[ERASE_SIZE_i]
2397 = TEST_LIT(sizes[2]);
2398 } else if (count >= 2) {
2399 geometry->defines[PROG_SIZE_i]
2400 = TEST_LIT(sizes[0]);
2401 geometry->defines[ERASE_SIZE_i]
2402 = TEST_LIT(sizes[1]);
2403 } else {
2404 geometry->defines[ERASE_SIZE_i]
2405 = TEST_LIT(sizes[0]);
2406 }
2407 if (count >= 4) {
2408 geometry->defines[ERASE_COUNT_i]
2409 = TEST_LIT(sizes[3]);
2410 }
2411 optarg = s;
2412 goto geometry_next;
2413 }
2414
2415 geometry_unknown:
2416 // unknown scenario?
2417 fprintf(stderr, "error: unknown disk geometry: %s\n",
2418 optarg);
2419 exit(-1);
2420
2421 geometry_next:
2422 optarg += strspn(optarg, " ");
2423 if (*optarg == ',') {
2424 optarg += 1;
2425 } else if (*optarg == '\0') {
2426 break;
2427 } else {
2428 goto geometry_unknown;
2429 }
2430 }
2431 break;
2432 }
2433 case OPT_POWERLOSS: {
2434 // reset our powerloss scenarios
2435 if (test_powerloss_capacity > 0) {
2436 free((test_powerloss_t*)test_powerlosses);
2437 }
2438 test_powerlosses = NULL;
2439 test_powerloss_count = 0;
2440 test_powerloss_capacity = 0;
2441
2442 // parse the comma separated list of power-loss scenarios
2443 while (*optarg) {
2444 // allocate space
2445 test_powerloss_t *powerloss = mappend(
2446 (void**)&test_powerlosses,
2447 sizeof(test_powerloss_t),
2448 &test_powerloss_count,
2449 &test_powerloss_capacity);
2450
2451 // parse the power-loss scenario
2452 optarg += strspn(optarg, " ");
2453
2454 // named power-loss scenario
2455 size_t len = strcspn(optarg, " ,");
2456 for (size_t i = 0; builtin_powerlosses[i].name; i++) {
2457 if (len == strlen(builtin_powerlosses[i].name)
2458 && memcmp(optarg,
2459 builtin_powerlosses[i].name,
2460 len) == 0) {
2461 *powerloss = builtin_powerlosses[i];
2462 optarg += len;
2463 goto powerloss_next;
2464 }
2465 }
2466
2467 // comma-separated permutation
2468 if (*optarg == '{') {
2469 lfs_emubd_powercycles_t *cycles = NULL;
2470 size_t cycle_count = 0;
2471 size_t cycle_capacity = 0;
2472
2473 char *s = optarg + 1;
2474 while (true) {
2475 char *parsed = NULL;
2476 *(lfs_emubd_powercycles_t*)mappend(
2477 (void**)&cycles,
2478 sizeof(lfs_emubd_powercycles_t),
2479 &cycle_count,
2480 &cycle_capacity)
2481 = strtoumax(s, &parsed, 0);
2482
2483 s = parsed + strspn(parsed, " ");
2484 if (*s == ',') {
2485 s += 1;
2486 continue;
2487 } else if (*s == '}') {
2488 s += 1;
2489 break;
2490 } else {
2491 goto powerloss_unknown;
2492 }
2493 }
2494
2495 *powerloss = (test_powerloss_t){
2496 .run = run_powerloss_cycles,
2497 .cycles = cycles,
2498 .cycle_count = cycle_count,
2499 };
2500 optarg = s;
2501 goto powerloss_next;
2502 }
2503
2504 // leb16-encoded permutation
2505 if (*optarg == ':') {
2506 lfs_emubd_powercycles_t *cycles = NULL;
2507 size_t cycle_count = 0;
2508 size_t cycle_capacity = 0;
2509
2510 char *s = optarg + 1;
2511 while (true) {
2512 char *parsed = NULL;
2513 uintmax_t x = leb16_parse(s, &parsed);
2514 if (parsed == s) {
2515 break;
2516 }
2517
2518 *(lfs_emubd_powercycles_t*)mappend(
2519 (void**)&cycles,
2520 sizeof(lfs_emubd_powercycles_t),
2521 &cycle_count,
2522 &cycle_capacity) = x;
2523 s = parsed;
2524 }
2525
2526 *powerloss = (test_powerloss_t){
2527 .run = run_powerloss_cycles,
2528 .cycles = cycles,
2529 .cycle_count = cycle_count,
2530 };
2531 optarg = s;
2532 goto powerloss_next;
2533 }
2534
2535 // exhaustive permutations
2536 {
2537 char *parsed = NULL;
2538 size_t count = strtoumax(optarg, &parsed, 0);
2539 if (parsed == optarg) {
2540 goto powerloss_unknown;
2541 }
2542 *powerloss = (test_powerloss_t){
2543 .run = run_powerloss_exhaustive,
2544 .cycles = NULL,
2545 .cycle_count = count,
2546 };
2547 optarg = (char*)parsed;
2548 goto powerloss_next;
2549 }
2550
2551 powerloss_unknown:
2552 // unknown scenario?
2553 fprintf(stderr, "error: unknown power-loss scenario: %s\n",
2554 optarg);
2555 exit(-1);
2556
2557 powerloss_next:
2558 optarg += strspn(optarg, " ");
2559 if (*optarg == ',') {
2560 optarg += 1;
2561 } else if (*optarg == '\0') {
2562 break;
2563 } else {
2564 goto powerloss_unknown;
2565 }
2566 }
2567 break;
2568 }
2569 case OPT_STEP: {
2570 char *parsed = NULL;
2571 test_step_start = strtoumax(optarg, &parsed, 0);
2572 test_step_stop = -1;
2573 test_step_step = 1;
2574 // allow empty string for start=0
2575 if (parsed == optarg) {
2576 test_step_start = 0;
2577 }
2578 optarg = parsed + strspn(parsed, " ");
2579
2580 if (*optarg != ',' && *optarg != '\0') {
2581 goto step_unknown;
2582 }
2583
2584 if (*optarg == ',') {
2585 optarg += 1;
2586 test_step_stop = strtoumax(optarg, &parsed, 0);
2587 // allow empty string for stop=end
2588 if (parsed == optarg) {
2589 test_step_stop = -1;
2590 }
2591 optarg = parsed + strspn(parsed, " ");
2592
2593 if (*optarg != ',' && *optarg != '\0') {
2594 goto step_unknown;
2595 }
2596
2597 if (*optarg == ',') {
2598 optarg += 1;
2599 test_step_step = strtoumax(optarg, &parsed, 0);
2600 // allow empty string for stop=1
2601 if (parsed == optarg) {
2602 test_step_step = 1;
2603 }
2604 optarg = parsed + strspn(parsed, " ");
2605
2606 if (*optarg != '\0') {
2607 goto step_unknown;
2608 }
2609 }
2610 } else {
2611 // single value = stop only
2612 test_step_stop = test_step_start;
2613 test_step_start = 0;
2614 }
2615
2616 break;
2617 step_unknown:
2618 fprintf(stderr, "error: invalid step: %s\n", optarg);
2619 exit(-1);
2620 }
2621 case OPT_DISK:
2622 test_disk_path = optarg;
2623 break;
2624 case OPT_TRACE:
2625 test_trace_path = optarg;
2626 break;
2627 case OPT_TRACE_BACKTRACE:
2628 test_trace_backtrace = true;
2629 break;
2630 case OPT_TRACE_PERIOD: {
2631 char *parsed = NULL;
2632 test_trace_period = strtoumax(optarg, &parsed, 0);
2633 if (parsed == optarg) {
2634 fprintf(stderr, "error: invalid trace-period: %s\n", optarg);
2635 exit(-1);
2636 }
2637 break;
2638 }
2639 case OPT_TRACE_FREQ: {
2640 char *parsed = NULL;
2641 test_trace_freq = strtoumax(optarg, &parsed, 0);
2642 if (parsed == optarg) {
2643 fprintf(stderr, "error: invalid trace-freq: %s\n", optarg);
2644 exit(-1);
2645 }
2646 break;
2647 }
2648 case OPT_READ_SLEEP: {
2649 char *parsed = NULL;
2650 double read_sleep = strtod(optarg, &parsed);
2651 if (parsed == optarg) {
2652 fprintf(stderr, "error: invalid read-sleep: %s\n", optarg);
2653 exit(-1);
2654 }
2655 test_read_sleep = read_sleep*1.0e9;
2656 break;
2657 }
2658 case OPT_PROG_SLEEP: {
2659 char *parsed = NULL;
2660 double prog_sleep = strtod(optarg, &parsed);
2661 if (parsed == optarg) {
2662 fprintf(stderr, "error: invalid prog-sleep: %s\n", optarg);
2663 exit(-1);
2664 }
2665 test_prog_sleep = prog_sleep*1.0e9;
2666 break;
2667 }
2668 case OPT_ERASE_SLEEP: {
2669 char *parsed = NULL;
2670 double erase_sleep = strtod(optarg, &parsed);
2671 if (parsed == optarg) {
2672 fprintf(stderr, "error: invalid erase-sleep: %s\n", optarg);
2673 exit(-1);
2674 }
2675 test_erase_sleep = erase_sleep*1.0e9;
2676 break;
2677 }
2678 // done parsing
2679 case -1:
2680 goto getopt_done;
2681 // unknown arg, getopt prints a message for us
2682 default:
2683 exit(-1);
2684 }
2685 }
2686 getopt_done: ;
2687
2688 if (argc > optind) {
2689 // reset our test identifier list
2690 test_ids = NULL;
2691 test_id_count = 0;
2692 test_id_capacity = 0;
2693 }
2694
2695 // parse test identifier, if any, cannibalizing the arg in the process
2696 for (; argc > optind; optind++) {
2697 test_define_t *defines = NULL;
2698 size_t define_count = 0;
2699 lfs_emubd_powercycles_t *cycles = NULL;
2700 size_t cycle_count = 0;
2701
2702 // parse name, can be suite or case
2703 char *name = argv[optind];
2704 char *defines_ = strchr(name, ':');
2705 if (defines_) {
2706 *defines_ = '\0';
2707 defines_ += 1;
2708 }
2709
2710 // remove optional path and .toml suffix
2711 char *slash = strrchr(name, '/');
2712 if (slash) {
2713 name = slash+1;
2714 }
2715
2716 size_t name_len = strlen(name);
2717 if (name_len > 5 && strcmp(&name[name_len-5], ".toml") == 0) {
2718 name[name_len-5] = '\0';
2719 }
2720
2721 if (defines_) {
2722 // parse defines
2723 char *cycles_ = strchr(defines_, ':');
2724 if (cycles_) {
2725 *cycles_ = '\0';
2726 cycles_ += 1;
2727 }
2728
2729 while (true) {
2730 char *parsed;
2731 size_t d = leb16_parse(defines_, &parsed);
2732 intmax_t v = leb16_parse(parsed, &parsed);
2733 if (parsed == defines_) {
2734 break;
2735 }
2736 defines_ = parsed;
2737
2738 if (d >= define_count) {
2739 // align to power of two to avoid any superlinear growth
2740 size_t ncount = 1 << lfs_npw2(d+1);
2741 defines = realloc(defines,
2742 ncount*sizeof(test_define_t));
2743 memset(defines+define_count, 0,
2744 (ncount-define_count)*sizeof(test_define_t));
2745 define_count = ncount;
2746 }
2747 defines[d] = TEST_LIT(v);
2748 }
2749
2750 if (cycles_) {
2751 // parse power cycles
2752 size_t cycle_capacity = 0;
2753 while (*cycles_ != '\0') {
2754 char *parsed = NULL;
2755 *(lfs_emubd_powercycles_t*)mappend(
2756 (void**)&cycles,
2757 sizeof(lfs_emubd_powercycles_t),
2758 &cycle_count,
2759 &cycle_capacity)
2760 = leb16_parse(cycles_, &parsed);
2761 if (parsed == cycles_) {
2762 fprintf(stderr, "error: "
2763 "could not parse test cycles: %s\n",
2764 cycles_);
2765 exit(-1);
2766 }
2767 cycles_ = parsed;
2768 }
2769 }
2770 }
2771
2772 // append to identifier list
2773 *(test_id_t*)mappend(
2774 (void**)&test_ids,
2775 sizeof(test_id_t),
2776 &test_id_count,
2777 &test_id_capacity) = (test_id_t){
2778 .name = name,
2779 .defines = defines,
2780 .define_count = define_count,
2781 .cycles = cycles,
2782 .cycle_count = cycle_count,
2783 };
2784 }
2785
2786 // do the thing
2787 op();
2788
2789 // cleanup (need to be done for valgrind testing)
2790 test_define_cleanup();
2791 if (test_overrides) {
2792 for (size_t i = 0; i < test_override_count; i++) {
2793 free((void*)test_overrides[i].defines);
2794 }
2795 free((void*)test_overrides);
2796 }
2797 if (test_geometry_capacity) {
2798 free((void*)test_geometries);
2799 }
2800 if (test_powerloss_capacity) {
2801 for (size_t i = 0; i < test_powerloss_count; i++) {
2802 free((void*)test_powerlosses[i].cycles);
2803 }
2804 free((void*)test_powerlosses);
2805 }
2806 if (test_id_capacity) {
2807 for (size_t i = 0; i < test_id_count; i++) {
2808 free((void*)test_ids[i].defines);
2809 free((void*)test_ids[i].cycles);
2810 }
2811 free((void*)test_ids);
2812 }
2813 }
2814