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 #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
powerloss_longjmp(void * c)1393 static void powerloss_longjmp(void *c) {
1394 jmp_buf *powerloss_jmp = c;
1395 longjmp(*powerloss_jmp, 1);
1396 }
1397
run_powerloss_linear(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1398 static 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
run_powerloss_log(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1491 static 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
run_powerloss_cycles(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1584 static 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
1673 struct 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
1681 struct powerloss_exhaustive_cycles {
1682 lfs_emubd_powercycles_t *cycles;
1683 size_t cycle_count;
1684 size_t cycle_capacity;
1685 };
1686
powerloss_exhaustive_branch(void * c)1687 static 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
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)1711 static 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
run_powerloss_exhaustive(const lfs_emubd_powercycles_t * cycles,size_t cycle_count,const struct test_suite * suite,const struct test_case * case_)1774 static 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
1843 const 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
1851 const 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
1863 const test_powerloss_t *test_powerlosses = (const test_powerloss_t[]){
1864 {"none", run_powerloss_none, NULL, 0},
1865 {"linear", run_powerloss_linear, NULL, 0},
1866 };
1867 size_t test_powerloss_count = 2;
1868
list_powerlosses(void)1869 static 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
1897 size_t test_step = 0;
1898
perm_run(void * data,const struct test_suite * suite,const struct test_case * case_,const test_powerloss_t * powerloss)1899 void 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
run(void)1928 static 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
1963 enum 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
1989 const char *short_opts = "hYlLD:G:P:s:d:t:";
1990
1991 const 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
2020 const 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
main(int argc,char ** argv)2046 int 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
2269 invalid_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
2400 geometry_unknown:
2401 // unknown scenario?
2402 fprintf(stderr, "error: unknown disk geometry: %s\n",
2403 optarg);
2404 exit(-1);
2405
2406 geometry_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
2536 powerloss_unknown:
2537 // unknown scenario?
2538 fprintf(stderr, "error: unknown power-loss scenario: %s\n",
2539 optarg);
2540 exit(-1);
2541
2542 powerloss_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;
2602 step_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 }
2671 getopt_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