119ea8026Sopenharmony_ci/*
219ea8026Sopenharmony_ci * Runner for littlefs benchmarks
319ea8026Sopenharmony_ci *
419ea8026Sopenharmony_ci * Copyright (c) 2022, The littlefs authors.
519ea8026Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause
619ea8026Sopenharmony_ci */
719ea8026Sopenharmony_ci#ifndef _POSIX_C_SOURCE
819ea8026Sopenharmony_ci#define _POSIX_C_SOURCE 199309L
919ea8026Sopenharmony_ci#endif
1019ea8026Sopenharmony_ci
1119ea8026Sopenharmony_ci#include "runners/bench_runner.h"
1219ea8026Sopenharmony_ci#include "bd/lfs_emubd.h"
1319ea8026Sopenharmony_ci
1419ea8026Sopenharmony_ci#include <getopt.h>
1519ea8026Sopenharmony_ci#include <sys/types.h>
1619ea8026Sopenharmony_ci#include <errno.h>
1719ea8026Sopenharmony_ci#include <setjmp.h>
1819ea8026Sopenharmony_ci#include <fcntl.h>
1919ea8026Sopenharmony_ci#include <stdarg.h>
2019ea8026Sopenharmony_ci#include <stdio.h>
2119ea8026Sopenharmony_ci#include <unistd.h>
2219ea8026Sopenharmony_ci#include <execinfo.h>
2319ea8026Sopenharmony_ci#include <time.h>
2419ea8026Sopenharmony_ci
2519ea8026Sopenharmony_ci
2619ea8026Sopenharmony_ci// some helpers
2719ea8026Sopenharmony_ci
2819ea8026Sopenharmony_ci// append to an array with amortized doubling
2919ea8026Sopenharmony_civoid *mappend(void **p,
3019ea8026Sopenharmony_ci        size_t size,
3119ea8026Sopenharmony_ci        size_t *count,
3219ea8026Sopenharmony_ci        size_t *capacity) {
3319ea8026Sopenharmony_ci    uint8_t *p_ = *p;
3419ea8026Sopenharmony_ci    size_t count_ = *count;
3519ea8026Sopenharmony_ci    size_t capacity_ = *capacity;
3619ea8026Sopenharmony_ci
3719ea8026Sopenharmony_ci    count_ += 1;
3819ea8026Sopenharmony_ci    if (count_ > capacity_) {
3919ea8026Sopenharmony_ci        capacity_ = (2*capacity_ < 4) ? 4 : 2*capacity_;
4019ea8026Sopenharmony_ci
4119ea8026Sopenharmony_ci        p_ = realloc(p_, capacity_*size);
4219ea8026Sopenharmony_ci        if (!p_) {
4319ea8026Sopenharmony_ci            return NULL;
4419ea8026Sopenharmony_ci        }
4519ea8026Sopenharmony_ci    }
4619ea8026Sopenharmony_ci
4719ea8026Sopenharmony_ci    *p = p_;
4819ea8026Sopenharmony_ci    *count = count_;
4919ea8026Sopenharmony_ci    *capacity = capacity_;
5019ea8026Sopenharmony_ci    return &p_[(count_-1)*size];
5119ea8026Sopenharmony_ci}
5219ea8026Sopenharmony_ci
5319ea8026Sopenharmony_ci// a quick self-terminating text-safe varint scheme
5419ea8026Sopenharmony_cistatic void leb16_print(uintmax_t x) {
5519ea8026Sopenharmony_ci    // allow 'w' to indicate negative numbers
5619ea8026Sopenharmony_ci    if ((intmax_t)x < 0) {
5719ea8026Sopenharmony_ci        printf("w");
5819ea8026Sopenharmony_ci        x = -x;
5919ea8026Sopenharmony_ci    }
6019ea8026Sopenharmony_ci
6119ea8026Sopenharmony_ci    while (true) {
6219ea8026Sopenharmony_ci        char nibble = (x & 0xf) | (x > 0xf ? 0x10 : 0);
6319ea8026Sopenharmony_ci        printf("%c", (nibble < 10) ? '0'+nibble : 'a'+nibble-10);
6419ea8026Sopenharmony_ci        if (x <= 0xf) {
6519ea8026Sopenharmony_ci            break;
6619ea8026Sopenharmony_ci        }
6719ea8026Sopenharmony_ci        x >>= 4;
6819ea8026Sopenharmony_ci    }
6919ea8026Sopenharmony_ci}
7019ea8026Sopenharmony_ci
7119ea8026Sopenharmony_cistatic uintmax_t leb16_parse(const char *s, char **tail) {
7219ea8026Sopenharmony_ci    bool neg = false;
7319ea8026Sopenharmony_ci    uintmax_t x = 0;
7419ea8026Sopenharmony_ci    if (tail) {
7519ea8026Sopenharmony_ci        *tail = (char*)s;
7619ea8026Sopenharmony_ci    }
7719ea8026Sopenharmony_ci
7819ea8026Sopenharmony_ci    if (s[0] == 'w') {
7919ea8026Sopenharmony_ci        neg = true;
8019ea8026Sopenharmony_ci        s = s+1;
8119ea8026Sopenharmony_ci    }
8219ea8026Sopenharmony_ci
8319ea8026Sopenharmony_ci    size_t i = 0;
8419ea8026Sopenharmony_ci    while (true) {
8519ea8026Sopenharmony_ci        uintmax_t nibble = s[i];
8619ea8026Sopenharmony_ci        if (nibble >= '0' && nibble <= '9') {
8719ea8026Sopenharmony_ci            nibble = nibble - '0';
8819ea8026Sopenharmony_ci        } else if (nibble >= 'a' && nibble <= 'v') {
8919ea8026Sopenharmony_ci            nibble = nibble - 'a' + 10;
9019ea8026Sopenharmony_ci        } else {
9119ea8026Sopenharmony_ci            // invalid?
9219ea8026Sopenharmony_ci            return 0;
9319ea8026Sopenharmony_ci        }
9419ea8026Sopenharmony_ci
9519ea8026Sopenharmony_ci        x |= (nibble & 0xf) << (4*i);
9619ea8026Sopenharmony_ci        i += 1;
9719ea8026Sopenharmony_ci        if (!(nibble & 0x10)) {
9819ea8026Sopenharmony_ci            s = s + i;
9919ea8026Sopenharmony_ci            break;
10019ea8026Sopenharmony_ci        }
10119ea8026Sopenharmony_ci    }
10219ea8026Sopenharmony_ci
10319ea8026Sopenharmony_ci    if (tail) {
10419ea8026Sopenharmony_ci        *tail = (char*)s;
10519ea8026Sopenharmony_ci    }
10619ea8026Sopenharmony_ci    return neg ? -x : x;
10719ea8026Sopenharmony_ci}
10819ea8026Sopenharmony_ci
10919ea8026Sopenharmony_ci
11019ea8026Sopenharmony_ci
11119ea8026Sopenharmony_ci// bench_runner types
11219ea8026Sopenharmony_ci
11319ea8026Sopenharmony_citypedef struct bench_geometry {
11419ea8026Sopenharmony_ci    const char *name;
11519ea8026Sopenharmony_ci    bench_define_t defines[BENCH_GEOMETRY_DEFINE_COUNT];
11619ea8026Sopenharmony_ci} bench_geometry_t;
11719ea8026Sopenharmony_ci
11819ea8026Sopenharmony_citypedef struct bench_id {
11919ea8026Sopenharmony_ci    const char *name;
12019ea8026Sopenharmony_ci    const bench_define_t *defines;
12119ea8026Sopenharmony_ci    size_t define_count;
12219ea8026Sopenharmony_ci} bench_id_t;
12319ea8026Sopenharmony_ci
12419ea8026Sopenharmony_ci
12519ea8026Sopenharmony_ci// bench suites are linked into a custom ld section
12619ea8026Sopenharmony_ciextern struct bench_suite __start__bench_suites;
12719ea8026Sopenharmony_ciextern struct bench_suite __stop__bench_suites;
12819ea8026Sopenharmony_ci
12919ea8026Sopenharmony_ciconst struct bench_suite *bench_suites = &__start__bench_suites;
13019ea8026Sopenharmony_ci#define BENCH_SUITE_COUNT \
13119ea8026Sopenharmony_ci    ((size_t)(&__stop__bench_suites - &__start__bench_suites))
13219ea8026Sopenharmony_ci
13319ea8026Sopenharmony_ci
13419ea8026Sopenharmony_ci// bench define management
13519ea8026Sopenharmony_citypedef struct bench_define_map {
13619ea8026Sopenharmony_ci    const bench_define_t *defines;
13719ea8026Sopenharmony_ci    size_t count;
13819ea8026Sopenharmony_ci} bench_define_map_t;
13919ea8026Sopenharmony_ci
14019ea8026Sopenharmony_citypedef struct bench_define_names {
14119ea8026Sopenharmony_ci    const char *const *names;
14219ea8026Sopenharmony_ci    size_t count;
14319ea8026Sopenharmony_ci} bench_define_names_t;
14419ea8026Sopenharmony_ci
14519ea8026Sopenharmony_ciintmax_t bench_define_lit(void *data) {
14619ea8026Sopenharmony_ci    return (intptr_t)data;
14719ea8026Sopenharmony_ci}
14819ea8026Sopenharmony_ci
14919ea8026Sopenharmony_ci#define BENCH_CONST(x) {bench_define_lit, (void*)(uintptr_t)(x)}
15019ea8026Sopenharmony_ci#define BENCH_LIT(x) ((bench_define_t)BENCH_CONST(x))
15119ea8026Sopenharmony_ci
15219ea8026Sopenharmony_ci
15319ea8026Sopenharmony_ci#define BENCH_DEF(k, v) \
15419ea8026Sopenharmony_ci    intmax_t bench_define_##k(void *data) { \
15519ea8026Sopenharmony_ci        (void)data; \
15619ea8026Sopenharmony_ci        return v; \
15719ea8026Sopenharmony_ci    }
15819ea8026Sopenharmony_ci
15919ea8026Sopenharmony_ci    BENCH_IMPLICIT_DEFINES
16019ea8026Sopenharmony_ci#undef BENCH_DEF
16119ea8026Sopenharmony_ci
16219ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_OVERRIDE    0
16319ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_EXPLICIT    1
16419ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_PERMUTATION 2
16519ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_GEOMETRY    3
16619ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_IMPLICIT    4
16719ea8026Sopenharmony_ci#define BENCH_DEFINE_MAP_COUNT       5
16819ea8026Sopenharmony_ci
16919ea8026Sopenharmony_cibench_define_map_t bench_define_maps[BENCH_DEFINE_MAP_COUNT] = {
17019ea8026Sopenharmony_ci    [BENCH_DEFINE_MAP_IMPLICIT] = {
17119ea8026Sopenharmony_ci        (const bench_define_t[BENCH_IMPLICIT_DEFINE_COUNT]) {
17219ea8026Sopenharmony_ci            #define BENCH_DEF(k, v) \
17319ea8026Sopenharmony_ci                [k##_i] = {bench_define_##k, NULL},
17419ea8026Sopenharmony_ci
17519ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINES
17619ea8026Sopenharmony_ci            #undef BENCH_DEF
17719ea8026Sopenharmony_ci        },
17819ea8026Sopenharmony_ci        BENCH_IMPLICIT_DEFINE_COUNT,
17919ea8026Sopenharmony_ci    },
18019ea8026Sopenharmony_ci};
18119ea8026Sopenharmony_ci
18219ea8026Sopenharmony_ci#define BENCH_DEFINE_NAMES_SUITE    0
18319ea8026Sopenharmony_ci#define BENCH_DEFINE_NAMES_IMPLICIT 1
18419ea8026Sopenharmony_ci#define BENCH_DEFINE_NAMES_COUNT    2
18519ea8026Sopenharmony_ci
18619ea8026Sopenharmony_cibench_define_names_t bench_define_names[BENCH_DEFINE_NAMES_COUNT] = {
18719ea8026Sopenharmony_ci    [BENCH_DEFINE_NAMES_IMPLICIT] = {
18819ea8026Sopenharmony_ci        (const char *const[BENCH_IMPLICIT_DEFINE_COUNT]){
18919ea8026Sopenharmony_ci            #define BENCH_DEF(k, v) \
19019ea8026Sopenharmony_ci                [k##_i] = #k,
19119ea8026Sopenharmony_ci
19219ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINES
19319ea8026Sopenharmony_ci            #undef BENCH_DEF
19419ea8026Sopenharmony_ci        },
19519ea8026Sopenharmony_ci        BENCH_IMPLICIT_DEFINE_COUNT,
19619ea8026Sopenharmony_ci    },
19719ea8026Sopenharmony_ci};
19819ea8026Sopenharmony_ci
19919ea8026Sopenharmony_ciintmax_t *bench_define_cache;
20019ea8026Sopenharmony_cisize_t bench_define_cache_count;
20119ea8026Sopenharmony_ciunsigned *bench_define_cache_mask;
20219ea8026Sopenharmony_ci
20319ea8026Sopenharmony_ciconst char *bench_define_name(size_t define) {
20419ea8026Sopenharmony_ci    // lookup in our bench names
20519ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_DEFINE_NAMES_COUNT; i++) {
20619ea8026Sopenharmony_ci        if (define < bench_define_names[i].count
20719ea8026Sopenharmony_ci                && bench_define_names[i].names
20819ea8026Sopenharmony_ci                && bench_define_names[i].names[define]) {
20919ea8026Sopenharmony_ci            return bench_define_names[i].names[define];
21019ea8026Sopenharmony_ci        }
21119ea8026Sopenharmony_ci    }
21219ea8026Sopenharmony_ci
21319ea8026Sopenharmony_ci    return NULL;
21419ea8026Sopenharmony_ci}
21519ea8026Sopenharmony_ci
21619ea8026Sopenharmony_cibool bench_define_ispermutation(size_t define) {
21719ea8026Sopenharmony_ci    // is this define specific to the permutation?
21819ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_DEFINE_MAP_IMPLICIT; i++) {
21919ea8026Sopenharmony_ci        if (define < bench_define_maps[i].count
22019ea8026Sopenharmony_ci                && bench_define_maps[i].defines[define].cb) {
22119ea8026Sopenharmony_ci            return true;
22219ea8026Sopenharmony_ci        }
22319ea8026Sopenharmony_ci    }
22419ea8026Sopenharmony_ci
22519ea8026Sopenharmony_ci    return false;
22619ea8026Sopenharmony_ci}
22719ea8026Sopenharmony_ci
22819ea8026Sopenharmony_ciintmax_t bench_define(size_t define) {
22919ea8026Sopenharmony_ci    // is the define in our cache?
23019ea8026Sopenharmony_ci    if (define < bench_define_cache_count
23119ea8026Sopenharmony_ci            && (bench_define_cache_mask[define/(8*sizeof(unsigned))]
23219ea8026Sopenharmony_ci                & (1 << (define%(8*sizeof(unsigned)))))) {
23319ea8026Sopenharmony_ci        return bench_define_cache[define];
23419ea8026Sopenharmony_ci    }
23519ea8026Sopenharmony_ci
23619ea8026Sopenharmony_ci    // lookup in our bench defines
23719ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_DEFINE_MAP_COUNT; i++) {
23819ea8026Sopenharmony_ci        if (define < bench_define_maps[i].count
23919ea8026Sopenharmony_ci                && bench_define_maps[i].defines[define].cb) {
24019ea8026Sopenharmony_ci            intmax_t v = bench_define_maps[i].defines[define].cb(
24119ea8026Sopenharmony_ci                    bench_define_maps[i].defines[define].data);
24219ea8026Sopenharmony_ci
24319ea8026Sopenharmony_ci            // insert into cache!
24419ea8026Sopenharmony_ci            bench_define_cache[define] = v;
24519ea8026Sopenharmony_ci            bench_define_cache_mask[define / (8*sizeof(unsigned))]
24619ea8026Sopenharmony_ci                    |= 1 << (define%(8*sizeof(unsigned)));
24719ea8026Sopenharmony_ci
24819ea8026Sopenharmony_ci            return v;
24919ea8026Sopenharmony_ci        }
25019ea8026Sopenharmony_ci    }
25119ea8026Sopenharmony_ci
25219ea8026Sopenharmony_ci    return 0;
25319ea8026Sopenharmony_ci
25419ea8026Sopenharmony_ci    // not found?
25519ea8026Sopenharmony_ci    const char *name = bench_define_name(define);
25619ea8026Sopenharmony_ci    fprintf(stderr, "error: undefined define %s (%zd)\n",
25719ea8026Sopenharmony_ci            name ? name : "(unknown)",
25819ea8026Sopenharmony_ci            define);
25919ea8026Sopenharmony_ci    assert(false);
26019ea8026Sopenharmony_ci    exit(-1);
26119ea8026Sopenharmony_ci}
26219ea8026Sopenharmony_ci
26319ea8026Sopenharmony_civoid bench_define_flush(void) {
26419ea8026Sopenharmony_ci    // clear cache between permutations
26519ea8026Sopenharmony_ci    memset(bench_define_cache_mask, 0,
26619ea8026Sopenharmony_ci            sizeof(unsigned)*(
26719ea8026Sopenharmony_ci                (bench_define_cache_count+(8*sizeof(unsigned))-1)
26819ea8026Sopenharmony_ci                / (8*sizeof(unsigned))));
26919ea8026Sopenharmony_ci}
27019ea8026Sopenharmony_ci
27119ea8026Sopenharmony_ci// geometry updates
27219ea8026Sopenharmony_ciconst bench_geometry_t *bench_geometry = NULL;
27319ea8026Sopenharmony_ci
27419ea8026Sopenharmony_civoid bench_define_geometry(const bench_geometry_t *geometry) {
27519ea8026Sopenharmony_ci    bench_define_maps[BENCH_DEFINE_MAP_GEOMETRY] = (bench_define_map_t){
27619ea8026Sopenharmony_ci            geometry->defines, BENCH_GEOMETRY_DEFINE_COUNT};
27719ea8026Sopenharmony_ci}
27819ea8026Sopenharmony_ci
27919ea8026Sopenharmony_ci// override updates
28019ea8026Sopenharmony_citypedef struct bench_override {
28119ea8026Sopenharmony_ci    const char *name;
28219ea8026Sopenharmony_ci    const intmax_t *defines;
28319ea8026Sopenharmony_ci    size_t permutations;
28419ea8026Sopenharmony_ci} bench_override_t;
28519ea8026Sopenharmony_ci
28619ea8026Sopenharmony_ciconst bench_override_t *bench_overrides = NULL;
28719ea8026Sopenharmony_cisize_t bench_override_count = 0;
28819ea8026Sopenharmony_ci
28919ea8026Sopenharmony_cibench_define_t *bench_override_defines = NULL;
29019ea8026Sopenharmony_cisize_t bench_override_define_count = 0;
29119ea8026Sopenharmony_cisize_t bench_override_define_permutations = 1;
29219ea8026Sopenharmony_cisize_t bench_override_define_capacity = 0;
29319ea8026Sopenharmony_ci
29419ea8026Sopenharmony_ci// suite/perm updates
29519ea8026Sopenharmony_civoid bench_define_suite(const struct bench_suite *suite) {
29619ea8026Sopenharmony_ci    bench_define_names[BENCH_DEFINE_NAMES_SUITE] = (bench_define_names_t){
29719ea8026Sopenharmony_ci            suite->define_names, suite->define_count};
29819ea8026Sopenharmony_ci
29919ea8026Sopenharmony_ci    // make sure our cache is large enough
30019ea8026Sopenharmony_ci    if (lfs_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT)
30119ea8026Sopenharmony_ci            > bench_define_cache_count) {
30219ea8026Sopenharmony_ci        // align to power of two to avoid any superlinear growth
30319ea8026Sopenharmony_ci        size_t ncount = 1 << lfs_npw2(
30419ea8026Sopenharmony_ci                lfs_max(suite->define_count, BENCH_IMPLICIT_DEFINE_COUNT));
30519ea8026Sopenharmony_ci        bench_define_cache = realloc(bench_define_cache, ncount*sizeof(intmax_t));
30619ea8026Sopenharmony_ci        bench_define_cache_mask = realloc(bench_define_cache_mask,
30719ea8026Sopenharmony_ci                sizeof(unsigned)*(
30819ea8026Sopenharmony_ci                    (ncount+(8*sizeof(unsigned))-1)
30919ea8026Sopenharmony_ci                    / (8*sizeof(unsigned))));
31019ea8026Sopenharmony_ci        bench_define_cache_count = ncount;
31119ea8026Sopenharmony_ci    }
31219ea8026Sopenharmony_ci
31319ea8026Sopenharmony_ci    // map any overrides
31419ea8026Sopenharmony_ci    if (bench_override_count > 0) {
31519ea8026Sopenharmony_ci        // first figure out the total size of override permutations
31619ea8026Sopenharmony_ci        size_t count = 0;
31719ea8026Sopenharmony_ci        size_t permutations = 1;
31819ea8026Sopenharmony_ci        for (size_t i = 0; i < bench_override_count; i++) {
31919ea8026Sopenharmony_ci            for (size_t d = 0;
32019ea8026Sopenharmony_ci                    d < lfs_max(
32119ea8026Sopenharmony_ci                        suite->define_count,
32219ea8026Sopenharmony_ci                        BENCH_IMPLICIT_DEFINE_COUNT);
32319ea8026Sopenharmony_ci                    d++) {
32419ea8026Sopenharmony_ci                // define name match?
32519ea8026Sopenharmony_ci                const char *name = bench_define_name(d);
32619ea8026Sopenharmony_ci                if (name && strcmp(name, bench_overrides[i].name) == 0) {
32719ea8026Sopenharmony_ci                    count = lfs_max(count, d+1);
32819ea8026Sopenharmony_ci                    permutations *= bench_overrides[i].permutations;
32919ea8026Sopenharmony_ci                    break;
33019ea8026Sopenharmony_ci                }
33119ea8026Sopenharmony_ci            }
33219ea8026Sopenharmony_ci        }
33319ea8026Sopenharmony_ci        bench_override_define_count = count;
33419ea8026Sopenharmony_ci        bench_override_define_permutations = permutations;
33519ea8026Sopenharmony_ci
33619ea8026Sopenharmony_ci        // make sure our override arrays are big enough
33719ea8026Sopenharmony_ci        if (count * permutations > bench_override_define_capacity) {
33819ea8026Sopenharmony_ci            // align to power of two to avoid any superlinear growth
33919ea8026Sopenharmony_ci            size_t ncapacity = 1 << lfs_npw2(count * permutations);
34019ea8026Sopenharmony_ci            bench_override_defines = realloc(
34119ea8026Sopenharmony_ci                    bench_override_defines,
34219ea8026Sopenharmony_ci                    sizeof(bench_define_t)*ncapacity);
34319ea8026Sopenharmony_ci            bench_override_define_capacity = ncapacity;
34419ea8026Sopenharmony_ci        }
34519ea8026Sopenharmony_ci
34619ea8026Sopenharmony_ci        // zero unoverridden defines
34719ea8026Sopenharmony_ci        memset(bench_override_defines, 0,
34819ea8026Sopenharmony_ci                sizeof(bench_define_t) * count * permutations);
34919ea8026Sopenharmony_ci
35019ea8026Sopenharmony_ci        // compute permutations
35119ea8026Sopenharmony_ci        size_t p = 1;
35219ea8026Sopenharmony_ci        for (size_t i = 0; i < bench_override_count; i++) {
35319ea8026Sopenharmony_ci            for (size_t d = 0;
35419ea8026Sopenharmony_ci                    d < lfs_max(
35519ea8026Sopenharmony_ci                        suite->define_count,
35619ea8026Sopenharmony_ci                        BENCH_IMPLICIT_DEFINE_COUNT);
35719ea8026Sopenharmony_ci                    d++) {
35819ea8026Sopenharmony_ci                // define name match?
35919ea8026Sopenharmony_ci                const char *name = bench_define_name(d);
36019ea8026Sopenharmony_ci                if (name && strcmp(name, bench_overrides[i].name) == 0) {
36119ea8026Sopenharmony_ci                    // scatter the define permutations based on already
36219ea8026Sopenharmony_ci                    // seen permutations
36319ea8026Sopenharmony_ci                    for (size_t j = 0; j < permutations; j++) {
36419ea8026Sopenharmony_ci                        bench_override_defines[j*count + d] = BENCH_LIT(
36519ea8026Sopenharmony_ci                                bench_overrides[i].defines[(j/p)
36619ea8026Sopenharmony_ci                                    % bench_overrides[i].permutations]);
36719ea8026Sopenharmony_ci                    }
36819ea8026Sopenharmony_ci
36919ea8026Sopenharmony_ci                    // keep track of how many permutations we've seen so far
37019ea8026Sopenharmony_ci                    p *= bench_overrides[i].permutations;
37119ea8026Sopenharmony_ci                    break;
37219ea8026Sopenharmony_ci                }
37319ea8026Sopenharmony_ci            }
37419ea8026Sopenharmony_ci        }
37519ea8026Sopenharmony_ci    }
37619ea8026Sopenharmony_ci}
37719ea8026Sopenharmony_ci
37819ea8026Sopenharmony_civoid bench_define_perm(
37919ea8026Sopenharmony_ci        const struct bench_suite *suite,
38019ea8026Sopenharmony_ci        const struct bench_case *case_,
38119ea8026Sopenharmony_ci        size_t perm) {
38219ea8026Sopenharmony_ci    if (case_->defines) {
38319ea8026Sopenharmony_ci        bench_define_maps[BENCH_DEFINE_MAP_PERMUTATION] = (bench_define_map_t){
38419ea8026Sopenharmony_ci                case_->defines + perm*suite->define_count,
38519ea8026Sopenharmony_ci                suite->define_count};
38619ea8026Sopenharmony_ci    } else {
38719ea8026Sopenharmony_ci        bench_define_maps[BENCH_DEFINE_MAP_PERMUTATION] = (bench_define_map_t){
38819ea8026Sopenharmony_ci                NULL, 0};
38919ea8026Sopenharmony_ci    }
39019ea8026Sopenharmony_ci}
39119ea8026Sopenharmony_ci
39219ea8026Sopenharmony_civoid bench_define_override(size_t perm) {
39319ea8026Sopenharmony_ci    bench_define_maps[BENCH_DEFINE_MAP_OVERRIDE] = (bench_define_map_t){
39419ea8026Sopenharmony_ci            bench_override_defines + perm*bench_override_define_count,
39519ea8026Sopenharmony_ci            bench_override_define_count};
39619ea8026Sopenharmony_ci}
39719ea8026Sopenharmony_ci
39819ea8026Sopenharmony_civoid bench_define_explicit(
39919ea8026Sopenharmony_ci        const bench_define_t *defines,
40019ea8026Sopenharmony_ci        size_t define_count) {
40119ea8026Sopenharmony_ci    bench_define_maps[BENCH_DEFINE_MAP_EXPLICIT] = (bench_define_map_t){
40219ea8026Sopenharmony_ci            defines, define_count};
40319ea8026Sopenharmony_ci}
40419ea8026Sopenharmony_ci
40519ea8026Sopenharmony_civoid bench_define_cleanup(void) {
40619ea8026Sopenharmony_ci    // bench define management can allocate a few things
40719ea8026Sopenharmony_ci    free(bench_define_cache);
40819ea8026Sopenharmony_ci    free(bench_define_cache_mask);
40919ea8026Sopenharmony_ci    free(bench_override_defines);
41019ea8026Sopenharmony_ci}
41119ea8026Sopenharmony_ci
41219ea8026Sopenharmony_ci
41319ea8026Sopenharmony_ci
41419ea8026Sopenharmony_ci// bench state
41519ea8026Sopenharmony_ciextern const bench_geometry_t *bench_geometries;
41619ea8026Sopenharmony_ciextern size_t bench_geometry_count;
41719ea8026Sopenharmony_ci
41819ea8026Sopenharmony_ciconst bench_id_t *bench_ids = (const bench_id_t[]) {
41919ea8026Sopenharmony_ci    {NULL, NULL, 0},
42019ea8026Sopenharmony_ci};
42119ea8026Sopenharmony_cisize_t bench_id_count = 1;
42219ea8026Sopenharmony_ci
42319ea8026Sopenharmony_cisize_t bench_step_start = 0;
42419ea8026Sopenharmony_cisize_t bench_step_stop = -1;
42519ea8026Sopenharmony_cisize_t bench_step_step = 1;
42619ea8026Sopenharmony_ci
42719ea8026Sopenharmony_ciconst char *bench_disk_path = NULL;
42819ea8026Sopenharmony_ciconst char *bench_trace_path = NULL;
42919ea8026Sopenharmony_cibool bench_trace_backtrace = false;
43019ea8026Sopenharmony_ciuint32_t bench_trace_period = 0;
43119ea8026Sopenharmony_ciuint32_t bench_trace_freq = 0;
43219ea8026Sopenharmony_ciFILE *bench_trace_file = NULL;
43319ea8026Sopenharmony_ciuint32_t bench_trace_cycles = 0;
43419ea8026Sopenharmony_ciuint64_t bench_trace_time = 0;
43519ea8026Sopenharmony_ciuint64_t bench_trace_open_time = 0;
43619ea8026Sopenharmony_cilfs_emubd_sleep_t bench_read_sleep = 0.0;
43719ea8026Sopenharmony_cilfs_emubd_sleep_t bench_prog_sleep = 0.0;
43819ea8026Sopenharmony_cilfs_emubd_sleep_t bench_erase_sleep = 0.0;
43919ea8026Sopenharmony_ci
44019ea8026Sopenharmony_ci// this determines both the backtrace buffer and the trace printf buffer, if
44119ea8026Sopenharmony_ci// trace ends up interleaved or truncated this may need to be increased
44219ea8026Sopenharmony_ci#ifndef BENCH_TRACE_BACKTRACE_BUFFER_SIZE
44319ea8026Sopenharmony_ci#define BENCH_TRACE_BACKTRACE_BUFFER_SIZE 8192
44419ea8026Sopenharmony_ci#endif
44519ea8026Sopenharmony_civoid *bench_trace_backtrace_buffer[
44619ea8026Sopenharmony_ci    BENCH_TRACE_BACKTRACE_BUFFER_SIZE / sizeof(void*)];
44719ea8026Sopenharmony_ci
44819ea8026Sopenharmony_ci// trace printing
44919ea8026Sopenharmony_civoid bench_trace(const char *fmt, ...) {
45019ea8026Sopenharmony_ci    if (bench_trace_path) {
45119ea8026Sopenharmony_ci        // sample at a specific period?
45219ea8026Sopenharmony_ci        if (bench_trace_period) {
45319ea8026Sopenharmony_ci            if (bench_trace_cycles % bench_trace_period != 0) {
45419ea8026Sopenharmony_ci                bench_trace_cycles += 1;
45519ea8026Sopenharmony_ci                return;
45619ea8026Sopenharmony_ci            }
45719ea8026Sopenharmony_ci            bench_trace_cycles += 1;
45819ea8026Sopenharmony_ci        }
45919ea8026Sopenharmony_ci
46019ea8026Sopenharmony_ci        // sample at a specific frequency?
46119ea8026Sopenharmony_ci        if (bench_trace_freq) {
46219ea8026Sopenharmony_ci            struct timespec t;
46319ea8026Sopenharmony_ci            clock_gettime(CLOCK_MONOTONIC, &t);
46419ea8026Sopenharmony_ci            uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000
46519ea8026Sopenharmony_ci                    + (uint64_t)t.tv_nsec;
46619ea8026Sopenharmony_ci            if (now - bench_trace_time < (1000*1000*1000) / bench_trace_freq) {
46719ea8026Sopenharmony_ci                return;
46819ea8026Sopenharmony_ci            }
46919ea8026Sopenharmony_ci            bench_trace_time = now;
47019ea8026Sopenharmony_ci        }
47119ea8026Sopenharmony_ci
47219ea8026Sopenharmony_ci        if (!bench_trace_file) {
47319ea8026Sopenharmony_ci            // Tracing output is heavy and trying to open every trace
47419ea8026Sopenharmony_ci            // call is slow, so we only try to open the trace file every
47519ea8026Sopenharmony_ci            // so often. Note this doesn't affect successfully opened files
47619ea8026Sopenharmony_ci            struct timespec t;
47719ea8026Sopenharmony_ci            clock_gettime(CLOCK_MONOTONIC, &t);
47819ea8026Sopenharmony_ci            uint64_t now = (uint64_t)t.tv_sec*1000*1000*1000
47919ea8026Sopenharmony_ci                    + (uint64_t)t.tv_nsec;
48019ea8026Sopenharmony_ci            if (now - bench_trace_open_time < 100*1000*1000) {
48119ea8026Sopenharmony_ci                return;
48219ea8026Sopenharmony_ci            }
48319ea8026Sopenharmony_ci            bench_trace_open_time = now;
48419ea8026Sopenharmony_ci
48519ea8026Sopenharmony_ci            // try to open the trace file
48619ea8026Sopenharmony_ci            int fd;
48719ea8026Sopenharmony_ci            if (strcmp(bench_trace_path, "-") == 0) {
48819ea8026Sopenharmony_ci                fd = dup(1);
48919ea8026Sopenharmony_ci                if (fd < 0) {
49019ea8026Sopenharmony_ci                    return;
49119ea8026Sopenharmony_ci                }
49219ea8026Sopenharmony_ci            } else {
49319ea8026Sopenharmony_ci                fd = open(
49419ea8026Sopenharmony_ci                        bench_trace_path,
49519ea8026Sopenharmony_ci                        O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK,
49619ea8026Sopenharmony_ci                        0666);
49719ea8026Sopenharmony_ci                if (fd < 0) {
49819ea8026Sopenharmony_ci                    return;
49919ea8026Sopenharmony_ci                }
50019ea8026Sopenharmony_ci                int err = fcntl(fd, F_SETFL, O_WRONLY | O_CREAT | O_APPEND);
50119ea8026Sopenharmony_ci                assert(!err);
50219ea8026Sopenharmony_ci            }
50319ea8026Sopenharmony_ci
50419ea8026Sopenharmony_ci            FILE *f = fdopen(fd, "a");
50519ea8026Sopenharmony_ci            assert(f);
50619ea8026Sopenharmony_ci            int err = setvbuf(f, NULL, _IOFBF,
50719ea8026Sopenharmony_ci                    BENCH_TRACE_BACKTRACE_BUFFER_SIZE);
50819ea8026Sopenharmony_ci            assert(!err);
50919ea8026Sopenharmony_ci            bench_trace_file = f;
51019ea8026Sopenharmony_ci        }
51119ea8026Sopenharmony_ci
51219ea8026Sopenharmony_ci        // print trace
51319ea8026Sopenharmony_ci        va_list va;
51419ea8026Sopenharmony_ci        va_start(va, fmt);
51519ea8026Sopenharmony_ci        int res = vfprintf(bench_trace_file, fmt, va);
51619ea8026Sopenharmony_ci        va_end(va);
51719ea8026Sopenharmony_ci        if (res < 0) {
51819ea8026Sopenharmony_ci            fclose(bench_trace_file);
51919ea8026Sopenharmony_ci            bench_trace_file = NULL;
52019ea8026Sopenharmony_ci            return;
52119ea8026Sopenharmony_ci        }
52219ea8026Sopenharmony_ci
52319ea8026Sopenharmony_ci        if (bench_trace_backtrace) {
52419ea8026Sopenharmony_ci            // print backtrace
52519ea8026Sopenharmony_ci            size_t count = backtrace(
52619ea8026Sopenharmony_ci                    bench_trace_backtrace_buffer,
52719ea8026Sopenharmony_ci                    BENCH_TRACE_BACKTRACE_BUFFER_SIZE);
52819ea8026Sopenharmony_ci            // note we skip our own stack frame
52919ea8026Sopenharmony_ci            for (size_t i = 1; i < count; i++) {
53019ea8026Sopenharmony_ci                res = fprintf(bench_trace_file, "\tat %p\n",
53119ea8026Sopenharmony_ci                        bench_trace_backtrace_buffer[i]);
53219ea8026Sopenharmony_ci                if (res < 0) {
53319ea8026Sopenharmony_ci                    fclose(bench_trace_file);
53419ea8026Sopenharmony_ci                    bench_trace_file = NULL;
53519ea8026Sopenharmony_ci                    return;
53619ea8026Sopenharmony_ci                }
53719ea8026Sopenharmony_ci            }
53819ea8026Sopenharmony_ci        }
53919ea8026Sopenharmony_ci
54019ea8026Sopenharmony_ci        // flush immediately
54119ea8026Sopenharmony_ci        fflush(bench_trace_file);
54219ea8026Sopenharmony_ci    }
54319ea8026Sopenharmony_ci}
54419ea8026Sopenharmony_ci
54519ea8026Sopenharmony_ci
54619ea8026Sopenharmony_ci// bench prng
54719ea8026Sopenharmony_ciuint32_t bench_prng(uint32_t *state) {
54819ea8026Sopenharmony_ci    // A simple xorshift32 generator, easily reproducible. Keep in mind
54919ea8026Sopenharmony_ci    // determinism is much more important than actual randomness here.
55019ea8026Sopenharmony_ci    uint32_t x = *state;
55119ea8026Sopenharmony_ci    x ^= x << 13;
55219ea8026Sopenharmony_ci    x ^= x >> 17;
55319ea8026Sopenharmony_ci    x ^= x << 5;
55419ea8026Sopenharmony_ci    *state = x;
55519ea8026Sopenharmony_ci    return x;
55619ea8026Sopenharmony_ci}
55719ea8026Sopenharmony_ci
55819ea8026Sopenharmony_ci
55919ea8026Sopenharmony_ci// bench recording state
56019ea8026Sopenharmony_cistatic struct lfs_config *bench_cfg = NULL;
56119ea8026Sopenharmony_cistatic lfs_emubd_io_t bench_last_readed = 0;
56219ea8026Sopenharmony_cistatic lfs_emubd_io_t bench_last_proged = 0;
56319ea8026Sopenharmony_cistatic lfs_emubd_io_t bench_last_erased = 0;
56419ea8026Sopenharmony_cilfs_emubd_io_t bench_readed = 0;
56519ea8026Sopenharmony_cilfs_emubd_io_t bench_proged = 0;
56619ea8026Sopenharmony_cilfs_emubd_io_t bench_erased = 0;
56719ea8026Sopenharmony_ci
56819ea8026Sopenharmony_civoid bench_reset(void) {
56919ea8026Sopenharmony_ci    bench_readed = 0;
57019ea8026Sopenharmony_ci    bench_proged = 0;
57119ea8026Sopenharmony_ci    bench_erased = 0;
57219ea8026Sopenharmony_ci    bench_last_readed = 0;
57319ea8026Sopenharmony_ci    bench_last_proged = 0;
57419ea8026Sopenharmony_ci    bench_last_erased = 0;
57519ea8026Sopenharmony_ci}
57619ea8026Sopenharmony_ci
57719ea8026Sopenharmony_civoid bench_start(void) {
57819ea8026Sopenharmony_ci    assert(bench_cfg);
57919ea8026Sopenharmony_ci    lfs_emubd_sio_t readed = lfs_emubd_readed(bench_cfg);
58019ea8026Sopenharmony_ci    assert(readed >= 0);
58119ea8026Sopenharmony_ci    lfs_emubd_sio_t proged = lfs_emubd_proged(bench_cfg);
58219ea8026Sopenharmony_ci    assert(proged >= 0);
58319ea8026Sopenharmony_ci    lfs_emubd_sio_t erased = lfs_emubd_erased(bench_cfg);
58419ea8026Sopenharmony_ci    assert(erased >= 0);
58519ea8026Sopenharmony_ci
58619ea8026Sopenharmony_ci    bench_last_readed = readed;
58719ea8026Sopenharmony_ci    bench_last_proged = proged;
58819ea8026Sopenharmony_ci    bench_last_erased = erased;
58919ea8026Sopenharmony_ci}
59019ea8026Sopenharmony_ci
59119ea8026Sopenharmony_civoid bench_stop(void) {
59219ea8026Sopenharmony_ci    assert(bench_cfg);
59319ea8026Sopenharmony_ci    lfs_emubd_sio_t readed = lfs_emubd_readed(bench_cfg);
59419ea8026Sopenharmony_ci    assert(readed >= 0);
59519ea8026Sopenharmony_ci    lfs_emubd_sio_t proged = lfs_emubd_proged(bench_cfg);
59619ea8026Sopenharmony_ci    assert(proged >= 0);
59719ea8026Sopenharmony_ci    lfs_emubd_sio_t erased = lfs_emubd_erased(bench_cfg);
59819ea8026Sopenharmony_ci    assert(erased >= 0);
59919ea8026Sopenharmony_ci
60019ea8026Sopenharmony_ci    bench_readed += readed - bench_last_readed;
60119ea8026Sopenharmony_ci    bench_proged += proged - bench_last_proged;
60219ea8026Sopenharmony_ci    bench_erased += erased - bench_last_erased;
60319ea8026Sopenharmony_ci}
60419ea8026Sopenharmony_ci
60519ea8026Sopenharmony_ci
60619ea8026Sopenharmony_ci// encode our permutation into a reusable id
60719ea8026Sopenharmony_cistatic void perm_printid(
60819ea8026Sopenharmony_ci        const struct bench_suite *suite,
60919ea8026Sopenharmony_ci        const struct bench_case *case_) {
61019ea8026Sopenharmony_ci    (void)suite;
61119ea8026Sopenharmony_ci    // case[:permutation]
61219ea8026Sopenharmony_ci    printf("%s:", case_->name);
61319ea8026Sopenharmony_ci    for (size_t d = 0;
61419ea8026Sopenharmony_ci            d < lfs_max(
61519ea8026Sopenharmony_ci                suite->define_count,
61619ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINE_COUNT);
61719ea8026Sopenharmony_ci            d++) {
61819ea8026Sopenharmony_ci        if (bench_define_ispermutation(d)) {
61919ea8026Sopenharmony_ci            leb16_print(d);
62019ea8026Sopenharmony_ci            leb16_print(BENCH_DEFINE(d));
62119ea8026Sopenharmony_ci        }
62219ea8026Sopenharmony_ci    }
62319ea8026Sopenharmony_ci}
62419ea8026Sopenharmony_ci
62519ea8026Sopenharmony_ci// a quick trie for keeping track of permutations we've seen
62619ea8026Sopenharmony_citypedef struct bench_seen {
62719ea8026Sopenharmony_ci    struct bench_seen_branch *branches;
62819ea8026Sopenharmony_ci    size_t branch_count;
62919ea8026Sopenharmony_ci    size_t branch_capacity;
63019ea8026Sopenharmony_ci} bench_seen_t;
63119ea8026Sopenharmony_ci
63219ea8026Sopenharmony_cistruct bench_seen_branch {
63319ea8026Sopenharmony_ci    intmax_t define;
63419ea8026Sopenharmony_ci    struct bench_seen branch;
63519ea8026Sopenharmony_ci};
63619ea8026Sopenharmony_ci
63719ea8026Sopenharmony_cibool bench_seen_insert(
63819ea8026Sopenharmony_ci        bench_seen_t *seen,
63919ea8026Sopenharmony_ci        const struct bench_suite *suite,
64019ea8026Sopenharmony_ci        const struct bench_case *case_) {
64119ea8026Sopenharmony_ci    (void)case_;
64219ea8026Sopenharmony_ci    bool was_seen = true;
64319ea8026Sopenharmony_ci
64419ea8026Sopenharmony_ci    // use the currently set defines
64519ea8026Sopenharmony_ci    for (size_t d = 0;
64619ea8026Sopenharmony_ci            d < lfs_max(
64719ea8026Sopenharmony_ci                suite->define_count,
64819ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINE_COUNT);
64919ea8026Sopenharmony_ci            d++) {
65019ea8026Sopenharmony_ci        // treat unpermuted defines the same as 0
65119ea8026Sopenharmony_ci        intmax_t define = bench_define_ispermutation(d) ? BENCH_DEFINE(d) : 0;
65219ea8026Sopenharmony_ci
65319ea8026Sopenharmony_ci        // already seen?
65419ea8026Sopenharmony_ci        struct bench_seen_branch *branch = NULL;
65519ea8026Sopenharmony_ci        for (size_t i = 0; i < seen->branch_count; i++) {
65619ea8026Sopenharmony_ci            if (seen->branches[i].define == define) {
65719ea8026Sopenharmony_ci                branch = &seen->branches[i];
65819ea8026Sopenharmony_ci                break;
65919ea8026Sopenharmony_ci            }
66019ea8026Sopenharmony_ci        }
66119ea8026Sopenharmony_ci
66219ea8026Sopenharmony_ci        // need to create a new node
66319ea8026Sopenharmony_ci        if (!branch) {
66419ea8026Sopenharmony_ci            was_seen = false;
66519ea8026Sopenharmony_ci            branch = mappend(
66619ea8026Sopenharmony_ci                    (void**)&seen->branches,
66719ea8026Sopenharmony_ci                    sizeof(struct bench_seen_branch),
66819ea8026Sopenharmony_ci                    &seen->branch_count,
66919ea8026Sopenharmony_ci                    &seen->branch_capacity);
67019ea8026Sopenharmony_ci            branch->define = define;
67119ea8026Sopenharmony_ci            branch->branch = (bench_seen_t){NULL, 0, 0};
67219ea8026Sopenharmony_ci        }
67319ea8026Sopenharmony_ci
67419ea8026Sopenharmony_ci        seen = &branch->branch;
67519ea8026Sopenharmony_ci    }
67619ea8026Sopenharmony_ci
67719ea8026Sopenharmony_ci    return was_seen;
67819ea8026Sopenharmony_ci}
67919ea8026Sopenharmony_ci
68019ea8026Sopenharmony_civoid bench_seen_cleanup(bench_seen_t *seen) {
68119ea8026Sopenharmony_ci    for (size_t i = 0; i < seen->branch_count; i++) {
68219ea8026Sopenharmony_ci        bench_seen_cleanup(&seen->branches[i].branch);
68319ea8026Sopenharmony_ci    }
68419ea8026Sopenharmony_ci    free(seen->branches);
68519ea8026Sopenharmony_ci}
68619ea8026Sopenharmony_ci
68719ea8026Sopenharmony_ci// iterate through permutations in a bench case
68819ea8026Sopenharmony_cistatic void case_forperm(
68919ea8026Sopenharmony_ci        const struct bench_suite *suite,
69019ea8026Sopenharmony_ci        const struct bench_case *case_,
69119ea8026Sopenharmony_ci        const bench_define_t *defines,
69219ea8026Sopenharmony_ci        size_t define_count,
69319ea8026Sopenharmony_ci        void (*cb)(
69419ea8026Sopenharmony_ci            void *data,
69519ea8026Sopenharmony_ci            const struct bench_suite *suite,
69619ea8026Sopenharmony_ci            const struct bench_case *case_),
69719ea8026Sopenharmony_ci        void *data) {
69819ea8026Sopenharmony_ci    // explicit permutation?
69919ea8026Sopenharmony_ci    if (defines) {
70019ea8026Sopenharmony_ci        bench_define_explicit(defines, define_count);
70119ea8026Sopenharmony_ci
70219ea8026Sopenharmony_ci        for (size_t v = 0; v < bench_override_define_permutations; v++) {
70319ea8026Sopenharmony_ci            // define override permutation
70419ea8026Sopenharmony_ci            bench_define_override(v);
70519ea8026Sopenharmony_ci            bench_define_flush();
70619ea8026Sopenharmony_ci
70719ea8026Sopenharmony_ci            cb(data, suite, case_);
70819ea8026Sopenharmony_ci        }
70919ea8026Sopenharmony_ci
71019ea8026Sopenharmony_ci        return;
71119ea8026Sopenharmony_ci    }
71219ea8026Sopenharmony_ci
71319ea8026Sopenharmony_ci    bench_seen_t seen = {NULL, 0, 0};
71419ea8026Sopenharmony_ci
71519ea8026Sopenharmony_ci    for (size_t k = 0; k < case_->permutations; k++) {
71619ea8026Sopenharmony_ci        // define permutation
71719ea8026Sopenharmony_ci        bench_define_perm(suite, case_, k);
71819ea8026Sopenharmony_ci
71919ea8026Sopenharmony_ci        for (size_t v = 0; v < bench_override_define_permutations; v++) {
72019ea8026Sopenharmony_ci            // define override permutation
72119ea8026Sopenharmony_ci            bench_define_override(v);
72219ea8026Sopenharmony_ci
72319ea8026Sopenharmony_ci            for (size_t g = 0; g < bench_geometry_count; g++) {
72419ea8026Sopenharmony_ci                // define geometry
72519ea8026Sopenharmony_ci                bench_define_geometry(&bench_geometries[g]);
72619ea8026Sopenharmony_ci                bench_define_flush();
72719ea8026Sopenharmony_ci
72819ea8026Sopenharmony_ci                // have we seen this permutation before?
72919ea8026Sopenharmony_ci                bool was_seen = bench_seen_insert(&seen, suite, case_);
73019ea8026Sopenharmony_ci                if (!(k == 0 && v == 0 && g == 0) && was_seen) {
73119ea8026Sopenharmony_ci                    continue;
73219ea8026Sopenharmony_ci                }
73319ea8026Sopenharmony_ci
73419ea8026Sopenharmony_ci                cb(data, suite, case_);
73519ea8026Sopenharmony_ci            }
73619ea8026Sopenharmony_ci        }
73719ea8026Sopenharmony_ci    }
73819ea8026Sopenharmony_ci
73919ea8026Sopenharmony_ci    bench_seen_cleanup(&seen);
74019ea8026Sopenharmony_ci}
74119ea8026Sopenharmony_ci
74219ea8026Sopenharmony_ci
74319ea8026Sopenharmony_ci// how many permutations are there actually in a bench case
74419ea8026Sopenharmony_cistruct perm_count_state {
74519ea8026Sopenharmony_ci    size_t total;
74619ea8026Sopenharmony_ci    size_t filtered;
74719ea8026Sopenharmony_ci};
74819ea8026Sopenharmony_ci
74919ea8026Sopenharmony_civoid perm_count(
75019ea8026Sopenharmony_ci        void *data,
75119ea8026Sopenharmony_ci        const struct bench_suite *suite,
75219ea8026Sopenharmony_ci        const struct bench_case *case_) {
75319ea8026Sopenharmony_ci    struct perm_count_state *state = data;
75419ea8026Sopenharmony_ci    (void)suite;
75519ea8026Sopenharmony_ci    (void)case_;
75619ea8026Sopenharmony_ci
75719ea8026Sopenharmony_ci    state->total += 1;
75819ea8026Sopenharmony_ci
75919ea8026Sopenharmony_ci    if (case_->filter && !case_->filter()) {
76019ea8026Sopenharmony_ci        return;
76119ea8026Sopenharmony_ci    }
76219ea8026Sopenharmony_ci
76319ea8026Sopenharmony_ci    state->filtered += 1;
76419ea8026Sopenharmony_ci}
76519ea8026Sopenharmony_ci
76619ea8026Sopenharmony_ci
76719ea8026Sopenharmony_ci// operations we can do
76819ea8026Sopenharmony_cistatic void summary(void) {
76919ea8026Sopenharmony_ci    printf("%-23s  %7s %7s %7s %11s\n",
77019ea8026Sopenharmony_ci            "", "flags", "suites", "cases", "perms");
77119ea8026Sopenharmony_ci    size_t suites = 0;
77219ea8026Sopenharmony_ci    size_t cases = 0;
77319ea8026Sopenharmony_ci    bench_flags_t flags = 0;
77419ea8026Sopenharmony_ci    struct perm_count_state perms = {0, 0};
77519ea8026Sopenharmony_ci
77619ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
77719ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
77819ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
77919ea8026Sopenharmony_ci
78019ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
78119ea8026Sopenharmony_ci                // does neither suite nor case name match?
78219ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
78319ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
78419ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
78519ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
78619ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
78719ea8026Sopenharmony_ci                    continue;
78819ea8026Sopenharmony_ci                }
78919ea8026Sopenharmony_ci
79019ea8026Sopenharmony_ci                cases += 1;
79119ea8026Sopenharmony_ci                case_forperm(
79219ea8026Sopenharmony_ci                        &bench_suites[i],
79319ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
79419ea8026Sopenharmony_ci                        bench_ids[t].defines,
79519ea8026Sopenharmony_ci                        bench_ids[t].define_count,
79619ea8026Sopenharmony_ci                        perm_count,
79719ea8026Sopenharmony_ci                        &perms);
79819ea8026Sopenharmony_ci            }
79919ea8026Sopenharmony_ci
80019ea8026Sopenharmony_ci            suites += 1;
80119ea8026Sopenharmony_ci            flags |= bench_suites[i].flags;
80219ea8026Sopenharmony_ci        }
80319ea8026Sopenharmony_ci    }
80419ea8026Sopenharmony_ci
80519ea8026Sopenharmony_ci    char perm_buf[64];
80619ea8026Sopenharmony_ci    sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
80719ea8026Sopenharmony_ci    char flag_buf[64];
80819ea8026Sopenharmony_ci    sprintf(flag_buf, "%s%s",
80919ea8026Sopenharmony_ci            (flags & BENCH_REENTRANT) ? "r" : "",
81019ea8026Sopenharmony_ci            (!flags) ? "-" : "");
81119ea8026Sopenharmony_ci    printf("%-23s  %7s %7zu %7zu %11s\n",
81219ea8026Sopenharmony_ci            "TOTAL",
81319ea8026Sopenharmony_ci            flag_buf,
81419ea8026Sopenharmony_ci            suites,
81519ea8026Sopenharmony_ci            cases,
81619ea8026Sopenharmony_ci            perm_buf);
81719ea8026Sopenharmony_ci}
81819ea8026Sopenharmony_ci
81919ea8026Sopenharmony_cistatic void list_suites(void) {
82019ea8026Sopenharmony_ci    // at least size so that names fit
82119ea8026Sopenharmony_ci    unsigned name_width = 23;
82219ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
82319ea8026Sopenharmony_ci        size_t len = strlen(bench_suites[i].name);
82419ea8026Sopenharmony_ci        if (len > name_width) {
82519ea8026Sopenharmony_ci            name_width = len;
82619ea8026Sopenharmony_ci        }
82719ea8026Sopenharmony_ci    }
82819ea8026Sopenharmony_ci    name_width = 4*((name_width+1+4-1)/4)-1;
82919ea8026Sopenharmony_ci
83019ea8026Sopenharmony_ci    printf("%-*s  %7s %7s %11s\n",
83119ea8026Sopenharmony_ci            name_width, "suite", "flags", "cases", "perms");
83219ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
83319ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
83419ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
83519ea8026Sopenharmony_ci
83619ea8026Sopenharmony_ci            size_t cases = 0;
83719ea8026Sopenharmony_ci            struct perm_count_state perms = {0, 0};
83819ea8026Sopenharmony_ci
83919ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
84019ea8026Sopenharmony_ci                // does neither suite nor case name match?
84119ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
84219ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
84319ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
84419ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
84519ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
84619ea8026Sopenharmony_ci                    continue;
84719ea8026Sopenharmony_ci                }
84819ea8026Sopenharmony_ci
84919ea8026Sopenharmony_ci                cases += 1;
85019ea8026Sopenharmony_ci                case_forperm(
85119ea8026Sopenharmony_ci                        &bench_suites[i],
85219ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
85319ea8026Sopenharmony_ci                        bench_ids[t].defines,
85419ea8026Sopenharmony_ci                        bench_ids[t].define_count,
85519ea8026Sopenharmony_ci                        perm_count,
85619ea8026Sopenharmony_ci                        &perms);
85719ea8026Sopenharmony_ci            }
85819ea8026Sopenharmony_ci
85919ea8026Sopenharmony_ci            // no benches found?
86019ea8026Sopenharmony_ci            if (!cases) {
86119ea8026Sopenharmony_ci                continue;
86219ea8026Sopenharmony_ci            }
86319ea8026Sopenharmony_ci
86419ea8026Sopenharmony_ci            char perm_buf[64];
86519ea8026Sopenharmony_ci            sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
86619ea8026Sopenharmony_ci            char flag_buf[64];
86719ea8026Sopenharmony_ci            sprintf(flag_buf, "%s%s",
86819ea8026Sopenharmony_ci                    (bench_suites[i].flags & BENCH_REENTRANT) ? "r" : "",
86919ea8026Sopenharmony_ci                    (!bench_suites[i].flags) ? "-" : "");
87019ea8026Sopenharmony_ci            printf("%-*s  %7s %7zu %11s\n",
87119ea8026Sopenharmony_ci                    name_width,
87219ea8026Sopenharmony_ci                    bench_suites[i].name,
87319ea8026Sopenharmony_ci                    flag_buf,
87419ea8026Sopenharmony_ci                    cases,
87519ea8026Sopenharmony_ci                    perm_buf);
87619ea8026Sopenharmony_ci        }
87719ea8026Sopenharmony_ci    }
87819ea8026Sopenharmony_ci}
87919ea8026Sopenharmony_ci
88019ea8026Sopenharmony_cistatic void list_cases(void) {
88119ea8026Sopenharmony_ci    // at least size so that names fit
88219ea8026Sopenharmony_ci    unsigned name_width = 23;
88319ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
88419ea8026Sopenharmony_ci        for (size_t j = 0; j < bench_suites[i].case_count; j++) {
88519ea8026Sopenharmony_ci            size_t len = strlen(bench_suites[i].cases[j].name);
88619ea8026Sopenharmony_ci            if (len > name_width) {
88719ea8026Sopenharmony_ci                name_width = len;
88819ea8026Sopenharmony_ci            }
88919ea8026Sopenharmony_ci        }
89019ea8026Sopenharmony_ci    }
89119ea8026Sopenharmony_ci    name_width = 4*((name_width+1+4-1)/4)-1;
89219ea8026Sopenharmony_ci
89319ea8026Sopenharmony_ci    printf("%-*s  %7s %11s\n", name_width, "case", "flags", "perms");
89419ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
89519ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
89619ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
89719ea8026Sopenharmony_ci
89819ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
89919ea8026Sopenharmony_ci                // does neither suite nor case name match?
90019ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
90119ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
90219ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
90319ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
90419ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
90519ea8026Sopenharmony_ci                    continue;
90619ea8026Sopenharmony_ci                }
90719ea8026Sopenharmony_ci
90819ea8026Sopenharmony_ci                struct perm_count_state perms = {0, 0};
90919ea8026Sopenharmony_ci                case_forperm(
91019ea8026Sopenharmony_ci                        &bench_suites[i],
91119ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
91219ea8026Sopenharmony_ci                        bench_ids[t].defines,
91319ea8026Sopenharmony_ci                        bench_ids[t].define_count,
91419ea8026Sopenharmony_ci                        perm_count,
91519ea8026Sopenharmony_ci                        &perms);
91619ea8026Sopenharmony_ci
91719ea8026Sopenharmony_ci                char perm_buf[64];
91819ea8026Sopenharmony_ci                sprintf(perm_buf, "%zu/%zu", perms.filtered, perms.total);
91919ea8026Sopenharmony_ci                char flag_buf[64];
92019ea8026Sopenharmony_ci                sprintf(flag_buf, "%s%s",
92119ea8026Sopenharmony_ci                        (bench_suites[i].cases[j].flags & BENCH_REENTRANT)
92219ea8026Sopenharmony_ci                            ? "r" : "",
92319ea8026Sopenharmony_ci                        (!bench_suites[i].cases[j].flags)
92419ea8026Sopenharmony_ci                            ? "-" : "");
92519ea8026Sopenharmony_ci                printf("%-*s  %7s %11s\n",
92619ea8026Sopenharmony_ci                        name_width,
92719ea8026Sopenharmony_ci                        bench_suites[i].cases[j].name,
92819ea8026Sopenharmony_ci                        flag_buf,
92919ea8026Sopenharmony_ci                        perm_buf);
93019ea8026Sopenharmony_ci            }
93119ea8026Sopenharmony_ci        }
93219ea8026Sopenharmony_ci    }
93319ea8026Sopenharmony_ci}
93419ea8026Sopenharmony_ci
93519ea8026Sopenharmony_cistatic void list_suite_paths(void) {
93619ea8026Sopenharmony_ci    // at least size so that names fit
93719ea8026Sopenharmony_ci    unsigned name_width = 23;
93819ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
93919ea8026Sopenharmony_ci        size_t len = strlen(bench_suites[i].name);
94019ea8026Sopenharmony_ci        if (len > name_width) {
94119ea8026Sopenharmony_ci            name_width = len;
94219ea8026Sopenharmony_ci        }
94319ea8026Sopenharmony_ci    }
94419ea8026Sopenharmony_ci    name_width = 4*((name_width+1+4-1)/4)-1;
94519ea8026Sopenharmony_ci
94619ea8026Sopenharmony_ci    printf("%-*s  %s\n", name_width, "suite", "path");
94719ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
94819ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
94919ea8026Sopenharmony_ci            size_t cases = 0;
95019ea8026Sopenharmony_ci
95119ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
95219ea8026Sopenharmony_ci                // does neither suite nor case name match?
95319ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
95419ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
95519ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
95619ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
95719ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
95819ea8026Sopenharmony_ci                    continue;
95919ea8026Sopenharmony_ci
96019ea8026Sopenharmony_ci                    cases += 1;
96119ea8026Sopenharmony_ci                }
96219ea8026Sopenharmony_ci            }
96319ea8026Sopenharmony_ci
96419ea8026Sopenharmony_ci            // no benches found?
96519ea8026Sopenharmony_ci            if (!cases) {
96619ea8026Sopenharmony_ci                continue;
96719ea8026Sopenharmony_ci            }
96819ea8026Sopenharmony_ci
96919ea8026Sopenharmony_ci            printf("%-*s  %s\n",
97019ea8026Sopenharmony_ci                    name_width,
97119ea8026Sopenharmony_ci                    bench_suites[i].name,
97219ea8026Sopenharmony_ci                    bench_suites[i].path);
97319ea8026Sopenharmony_ci        }
97419ea8026Sopenharmony_ci    }
97519ea8026Sopenharmony_ci}
97619ea8026Sopenharmony_ci
97719ea8026Sopenharmony_cistatic void list_case_paths(void) {
97819ea8026Sopenharmony_ci    // at least size so that names fit
97919ea8026Sopenharmony_ci    unsigned name_width = 23;
98019ea8026Sopenharmony_ci    for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
98119ea8026Sopenharmony_ci        for (size_t j = 0; j < bench_suites[i].case_count; j++) {
98219ea8026Sopenharmony_ci            size_t len = strlen(bench_suites[i].cases[j].name);
98319ea8026Sopenharmony_ci            if (len > name_width) {
98419ea8026Sopenharmony_ci                name_width = len;
98519ea8026Sopenharmony_ci            }
98619ea8026Sopenharmony_ci        }
98719ea8026Sopenharmony_ci    }
98819ea8026Sopenharmony_ci    name_width = 4*((name_width+1+4-1)/4)-1;
98919ea8026Sopenharmony_ci
99019ea8026Sopenharmony_ci    printf("%-*s  %s\n", name_width, "case", "path");
99119ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
99219ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
99319ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
99419ea8026Sopenharmony_ci                // does neither suite nor case name match?
99519ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
99619ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
99719ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
99819ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
99919ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
100019ea8026Sopenharmony_ci                    continue;
100119ea8026Sopenharmony_ci                }
100219ea8026Sopenharmony_ci
100319ea8026Sopenharmony_ci                printf("%-*s  %s\n",
100419ea8026Sopenharmony_ci                        name_width,
100519ea8026Sopenharmony_ci                        bench_suites[i].cases[j].name,
100619ea8026Sopenharmony_ci                        bench_suites[i].cases[j].path);
100719ea8026Sopenharmony_ci            }
100819ea8026Sopenharmony_ci        }
100919ea8026Sopenharmony_ci    }
101019ea8026Sopenharmony_ci}
101119ea8026Sopenharmony_ci
101219ea8026Sopenharmony_cistruct list_defines_define {
101319ea8026Sopenharmony_ci    const char *name;
101419ea8026Sopenharmony_ci    intmax_t *values;
101519ea8026Sopenharmony_ci    size_t value_count;
101619ea8026Sopenharmony_ci    size_t value_capacity;
101719ea8026Sopenharmony_ci};
101819ea8026Sopenharmony_ci
101919ea8026Sopenharmony_cistruct list_defines_defines {
102019ea8026Sopenharmony_ci    struct list_defines_define *defines;
102119ea8026Sopenharmony_ci    size_t define_count;
102219ea8026Sopenharmony_ci    size_t define_capacity;
102319ea8026Sopenharmony_ci};
102419ea8026Sopenharmony_ci
102519ea8026Sopenharmony_cistatic void list_defines_add(
102619ea8026Sopenharmony_ci        struct list_defines_defines *defines,
102719ea8026Sopenharmony_ci        size_t d) {
102819ea8026Sopenharmony_ci    const char *name = bench_define_name(d);
102919ea8026Sopenharmony_ci    intmax_t value = BENCH_DEFINE(d);
103019ea8026Sopenharmony_ci
103119ea8026Sopenharmony_ci    // define already in defines?
103219ea8026Sopenharmony_ci    for (size_t i = 0; i < defines->define_count; i++) {
103319ea8026Sopenharmony_ci        if (strcmp(defines->defines[i].name, name) == 0) {
103419ea8026Sopenharmony_ci            // value already in values?
103519ea8026Sopenharmony_ci            for (size_t j = 0; j < defines->defines[i].value_count; j++) {
103619ea8026Sopenharmony_ci                if (defines->defines[i].values[j] == value) {
103719ea8026Sopenharmony_ci                    return;
103819ea8026Sopenharmony_ci                }
103919ea8026Sopenharmony_ci            }
104019ea8026Sopenharmony_ci
104119ea8026Sopenharmony_ci            *(intmax_t*)mappend(
104219ea8026Sopenharmony_ci                (void**)&defines->defines[i].values,
104319ea8026Sopenharmony_ci                sizeof(intmax_t),
104419ea8026Sopenharmony_ci                &defines->defines[i].value_count,
104519ea8026Sopenharmony_ci                &defines->defines[i].value_capacity) = value;
104619ea8026Sopenharmony_ci
104719ea8026Sopenharmony_ci            return;
104819ea8026Sopenharmony_ci        }
104919ea8026Sopenharmony_ci    }
105019ea8026Sopenharmony_ci
105119ea8026Sopenharmony_ci    // new define?
105219ea8026Sopenharmony_ci    struct list_defines_define *define = mappend(
105319ea8026Sopenharmony_ci            (void**)&defines->defines,
105419ea8026Sopenharmony_ci            sizeof(struct list_defines_define),
105519ea8026Sopenharmony_ci            &defines->define_count,
105619ea8026Sopenharmony_ci            &defines->define_capacity);
105719ea8026Sopenharmony_ci    define->name = name;
105819ea8026Sopenharmony_ci    define->values = malloc(sizeof(intmax_t));
105919ea8026Sopenharmony_ci    define->values[0] = value;
106019ea8026Sopenharmony_ci    define->value_count = 1;
106119ea8026Sopenharmony_ci    define->value_capacity = 1;
106219ea8026Sopenharmony_ci}
106319ea8026Sopenharmony_ci
106419ea8026Sopenharmony_civoid perm_list_defines(
106519ea8026Sopenharmony_ci        void *data,
106619ea8026Sopenharmony_ci        const struct bench_suite *suite,
106719ea8026Sopenharmony_ci        const struct bench_case *case_) {
106819ea8026Sopenharmony_ci    struct list_defines_defines *defines = data;
106919ea8026Sopenharmony_ci    (void)suite;
107019ea8026Sopenharmony_ci    (void)case_;
107119ea8026Sopenharmony_ci
107219ea8026Sopenharmony_ci    // collect defines
107319ea8026Sopenharmony_ci    for (size_t d = 0;
107419ea8026Sopenharmony_ci            d < lfs_max(suite->define_count,
107519ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINE_COUNT);
107619ea8026Sopenharmony_ci            d++) {
107719ea8026Sopenharmony_ci        if (d < BENCH_IMPLICIT_DEFINE_COUNT
107819ea8026Sopenharmony_ci                || bench_define_ispermutation(d)) {
107919ea8026Sopenharmony_ci            list_defines_add(defines, d);
108019ea8026Sopenharmony_ci        }
108119ea8026Sopenharmony_ci    }
108219ea8026Sopenharmony_ci}
108319ea8026Sopenharmony_ci
108419ea8026Sopenharmony_civoid perm_list_permutation_defines(
108519ea8026Sopenharmony_ci        void *data,
108619ea8026Sopenharmony_ci        const struct bench_suite *suite,
108719ea8026Sopenharmony_ci        const struct bench_case *case_) {
108819ea8026Sopenharmony_ci    struct list_defines_defines *defines = data;
108919ea8026Sopenharmony_ci    (void)suite;
109019ea8026Sopenharmony_ci    (void)case_;
109119ea8026Sopenharmony_ci
109219ea8026Sopenharmony_ci    // collect permutation_defines
109319ea8026Sopenharmony_ci    for (size_t d = 0;
109419ea8026Sopenharmony_ci            d < lfs_max(suite->define_count,
109519ea8026Sopenharmony_ci                BENCH_IMPLICIT_DEFINE_COUNT);
109619ea8026Sopenharmony_ci            d++) {
109719ea8026Sopenharmony_ci        if (bench_define_ispermutation(d)) {
109819ea8026Sopenharmony_ci            list_defines_add(defines, d);
109919ea8026Sopenharmony_ci        }
110019ea8026Sopenharmony_ci    }
110119ea8026Sopenharmony_ci}
110219ea8026Sopenharmony_ci
110319ea8026Sopenharmony_ciextern const bench_geometry_t builtin_geometries[];
110419ea8026Sopenharmony_ci
110519ea8026Sopenharmony_cistatic void list_defines(void) {
110619ea8026Sopenharmony_ci    struct list_defines_defines defines = {NULL, 0, 0};
110719ea8026Sopenharmony_ci
110819ea8026Sopenharmony_ci    // add defines
110919ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
111019ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
111119ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
111219ea8026Sopenharmony_ci
111319ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
111419ea8026Sopenharmony_ci                // does neither suite nor case name match?
111519ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
111619ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
111719ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
111819ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
111919ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
112019ea8026Sopenharmony_ci                    continue;
112119ea8026Sopenharmony_ci                }
112219ea8026Sopenharmony_ci
112319ea8026Sopenharmony_ci                case_forperm(
112419ea8026Sopenharmony_ci                        &bench_suites[i],
112519ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
112619ea8026Sopenharmony_ci                        bench_ids[t].defines,
112719ea8026Sopenharmony_ci                        bench_ids[t].define_count,
112819ea8026Sopenharmony_ci                        perm_list_defines,
112919ea8026Sopenharmony_ci                        &defines);
113019ea8026Sopenharmony_ci            }
113119ea8026Sopenharmony_ci        }
113219ea8026Sopenharmony_ci    }
113319ea8026Sopenharmony_ci
113419ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
113519ea8026Sopenharmony_ci        printf("%s=", defines.defines[i].name);
113619ea8026Sopenharmony_ci        for (size_t j = 0; j < defines.defines[i].value_count; j++) {
113719ea8026Sopenharmony_ci            printf("%jd", defines.defines[i].values[j]);
113819ea8026Sopenharmony_ci            if (j != defines.defines[i].value_count-1) {
113919ea8026Sopenharmony_ci                printf(",");
114019ea8026Sopenharmony_ci            }
114119ea8026Sopenharmony_ci        }
114219ea8026Sopenharmony_ci        printf("\n");
114319ea8026Sopenharmony_ci    }
114419ea8026Sopenharmony_ci
114519ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
114619ea8026Sopenharmony_ci        free(defines.defines[i].values);
114719ea8026Sopenharmony_ci    }
114819ea8026Sopenharmony_ci    free(defines.defines);
114919ea8026Sopenharmony_ci}
115019ea8026Sopenharmony_ci
115119ea8026Sopenharmony_cistatic void list_permutation_defines(void) {
115219ea8026Sopenharmony_ci    struct list_defines_defines defines = {NULL, 0, 0};
115319ea8026Sopenharmony_ci
115419ea8026Sopenharmony_ci    // add permutation defines
115519ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
115619ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
115719ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
115819ea8026Sopenharmony_ci
115919ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
116019ea8026Sopenharmony_ci                // does neither suite nor case name match?
116119ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
116219ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
116319ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
116419ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
116519ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
116619ea8026Sopenharmony_ci                    continue;
116719ea8026Sopenharmony_ci                }
116819ea8026Sopenharmony_ci
116919ea8026Sopenharmony_ci                case_forperm(
117019ea8026Sopenharmony_ci                        &bench_suites[i],
117119ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
117219ea8026Sopenharmony_ci                        bench_ids[t].defines,
117319ea8026Sopenharmony_ci                        bench_ids[t].define_count,
117419ea8026Sopenharmony_ci                        perm_list_permutation_defines,
117519ea8026Sopenharmony_ci                        &defines);
117619ea8026Sopenharmony_ci            }
117719ea8026Sopenharmony_ci        }
117819ea8026Sopenharmony_ci    }
117919ea8026Sopenharmony_ci
118019ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
118119ea8026Sopenharmony_ci        printf("%s=", defines.defines[i].name);
118219ea8026Sopenharmony_ci        for (size_t j = 0; j < defines.defines[i].value_count; j++) {
118319ea8026Sopenharmony_ci            printf("%jd", defines.defines[i].values[j]);
118419ea8026Sopenharmony_ci            if (j != defines.defines[i].value_count-1) {
118519ea8026Sopenharmony_ci                printf(",");
118619ea8026Sopenharmony_ci            }
118719ea8026Sopenharmony_ci        }
118819ea8026Sopenharmony_ci        printf("\n");
118919ea8026Sopenharmony_ci    }
119019ea8026Sopenharmony_ci
119119ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
119219ea8026Sopenharmony_ci        free(defines.defines[i].values);
119319ea8026Sopenharmony_ci    }
119419ea8026Sopenharmony_ci    free(defines.defines);
119519ea8026Sopenharmony_ci}
119619ea8026Sopenharmony_ci
119719ea8026Sopenharmony_cistatic void list_implicit_defines(void) {
119819ea8026Sopenharmony_ci    struct list_defines_defines defines = {NULL, 0, 0};
119919ea8026Sopenharmony_ci
120019ea8026Sopenharmony_ci    // yes we do need to define a suite, this does a bit of bookeeping
120119ea8026Sopenharmony_ci    // such as setting up the define cache
120219ea8026Sopenharmony_ci    bench_define_suite(&(const struct bench_suite){0});
120319ea8026Sopenharmony_ci
120419ea8026Sopenharmony_ci    // make sure to include builtin geometries here
120519ea8026Sopenharmony_ci    extern const bench_geometry_t builtin_geometries[];
120619ea8026Sopenharmony_ci    for (size_t g = 0; builtin_geometries[g].name; g++) {
120719ea8026Sopenharmony_ci        bench_define_geometry(&builtin_geometries[g]);
120819ea8026Sopenharmony_ci        bench_define_flush();
120919ea8026Sopenharmony_ci
121019ea8026Sopenharmony_ci        // add implicit defines
121119ea8026Sopenharmony_ci        for (size_t d = 0; d < BENCH_IMPLICIT_DEFINE_COUNT; d++) {
121219ea8026Sopenharmony_ci            list_defines_add(&defines, d);
121319ea8026Sopenharmony_ci        }
121419ea8026Sopenharmony_ci    }
121519ea8026Sopenharmony_ci
121619ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
121719ea8026Sopenharmony_ci        printf("%s=", defines.defines[i].name);
121819ea8026Sopenharmony_ci        for (size_t j = 0; j < defines.defines[i].value_count; j++) {
121919ea8026Sopenharmony_ci            printf("%jd", defines.defines[i].values[j]);
122019ea8026Sopenharmony_ci            if (j != defines.defines[i].value_count-1) {
122119ea8026Sopenharmony_ci                printf(",");
122219ea8026Sopenharmony_ci            }
122319ea8026Sopenharmony_ci        }
122419ea8026Sopenharmony_ci        printf("\n");
122519ea8026Sopenharmony_ci    }
122619ea8026Sopenharmony_ci
122719ea8026Sopenharmony_ci    for (size_t i = 0; i < defines.define_count; i++) {
122819ea8026Sopenharmony_ci        free(defines.defines[i].values);
122919ea8026Sopenharmony_ci    }
123019ea8026Sopenharmony_ci    free(defines.defines);
123119ea8026Sopenharmony_ci}
123219ea8026Sopenharmony_ci
123319ea8026Sopenharmony_ci
123419ea8026Sopenharmony_ci
123519ea8026Sopenharmony_ci// geometries to bench
123619ea8026Sopenharmony_ci
123719ea8026Sopenharmony_ciconst bench_geometry_t builtin_geometries[] = {
123819ea8026Sopenharmony_ci    {"default", {{0}, BENCH_CONST(16),   BENCH_CONST(512),   {0}}},
123919ea8026Sopenharmony_ci    {"eeprom",  {{0}, BENCH_CONST(1),    BENCH_CONST(512),   {0}}},
124019ea8026Sopenharmony_ci    {"emmc",    {{0}, {0},               BENCH_CONST(512),   {0}}},
124119ea8026Sopenharmony_ci    {"nor",     {{0}, BENCH_CONST(1),    BENCH_CONST(4096),  {0}}},
124219ea8026Sopenharmony_ci    {"nand",    {{0}, BENCH_CONST(4096), BENCH_CONST(32768), {0}}},
124319ea8026Sopenharmony_ci    {NULL, {{0}, {0}, {0}, {0}}},
124419ea8026Sopenharmony_ci};
124519ea8026Sopenharmony_ci
124619ea8026Sopenharmony_ciconst bench_geometry_t *bench_geometries = builtin_geometries;
124719ea8026Sopenharmony_cisize_t bench_geometry_count = 5;
124819ea8026Sopenharmony_ci
124919ea8026Sopenharmony_cistatic void list_geometries(void) {
125019ea8026Sopenharmony_ci    // at least size so that names fit
125119ea8026Sopenharmony_ci    unsigned name_width = 23;
125219ea8026Sopenharmony_ci    for (size_t g = 0; builtin_geometries[g].name; g++) {
125319ea8026Sopenharmony_ci        size_t len = strlen(builtin_geometries[g].name);
125419ea8026Sopenharmony_ci        if (len > name_width) {
125519ea8026Sopenharmony_ci            name_width = len;
125619ea8026Sopenharmony_ci        }
125719ea8026Sopenharmony_ci    }
125819ea8026Sopenharmony_ci    name_width = 4*((name_width+1+4-1)/4)-1;
125919ea8026Sopenharmony_ci
126019ea8026Sopenharmony_ci    // yes we do need to define a suite, this does a bit of bookeeping
126119ea8026Sopenharmony_ci    // such as setting up the define cache
126219ea8026Sopenharmony_ci    bench_define_suite(&(const struct bench_suite){0});
126319ea8026Sopenharmony_ci
126419ea8026Sopenharmony_ci    printf("%-*s  %7s %7s %7s %7s %11s\n",
126519ea8026Sopenharmony_ci            name_width, "geometry", "read", "prog", "erase", "count", "size");
126619ea8026Sopenharmony_ci    for (size_t g = 0; builtin_geometries[g].name; g++) {
126719ea8026Sopenharmony_ci        bench_define_geometry(&builtin_geometries[g]);
126819ea8026Sopenharmony_ci        bench_define_flush();
126919ea8026Sopenharmony_ci        printf("%-*s  %7ju %7ju %7ju %7ju %11ju\n",
127019ea8026Sopenharmony_ci                name_width,
127119ea8026Sopenharmony_ci                builtin_geometries[g].name,
127219ea8026Sopenharmony_ci                READ_SIZE,
127319ea8026Sopenharmony_ci                PROG_SIZE,
127419ea8026Sopenharmony_ci                ERASE_SIZE,
127519ea8026Sopenharmony_ci                ERASE_COUNT,
127619ea8026Sopenharmony_ci                ERASE_SIZE*ERASE_COUNT);
127719ea8026Sopenharmony_ci    }
127819ea8026Sopenharmony_ci}
127919ea8026Sopenharmony_ci
128019ea8026Sopenharmony_ci
128119ea8026Sopenharmony_ci
128219ea8026Sopenharmony_ci// global bench step count
128319ea8026Sopenharmony_cisize_t bench_step = 0;
128419ea8026Sopenharmony_ci
128519ea8026Sopenharmony_civoid perm_run(
128619ea8026Sopenharmony_ci        void *data,
128719ea8026Sopenharmony_ci        const struct bench_suite *suite,
128819ea8026Sopenharmony_ci        const struct bench_case *case_) {
128919ea8026Sopenharmony_ci    (void)data;
129019ea8026Sopenharmony_ci
129119ea8026Sopenharmony_ci    // skip this step?
129219ea8026Sopenharmony_ci    if (!(bench_step >= bench_step_start
129319ea8026Sopenharmony_ci            && bench_step < bench_step_stop
129419ea8026Sopenharmony_ci            && (bench_step-bench_step_start) % bench_step_step == 0)) {
129519ea8026Sopenharmony_ci        bench_step += 1;
129619ea8026Sopenharmony_ci        return;
129719ea8026Sopenharmony_ci    }
129819ea8026Sopenharmony_ci    bench_step += 1;
129919ea8026Sopenharmony_ci
130019ea8026Sopenharmony_ci    // filter?
130119ea8026Sopenharmony_ci    if (case_->filter && !case_->filter()) {
130219ea8026Sopenharmony_ci        printf("skipped ");
130319ea8026Sopenharmony_ci        perm_printid(suite, case_);
130419ea8026Sopenharmony_ci        printf("\n");
130519ea8026Sopenharmony_ci        return;
130619ea8026Sopenharmony_ci    }
130719ea8026Sopenharmony_ci
130819ea8026Sopenharmony_ci    // create block device and configuration
130919ea8026Sopenharmony_ci    lfs_emubd_t bd;
131019ea8026Sopenharmony_ci
131119ea8026Sopenharmony_ci    struct lfs_config cfg = {
131219ea8026Sopenharmony_ci        .context            = &bd,
131319ea8026Sopenharmony_ci        .read               = lfs_emubd_read,
131419ea8026Sopenharmony_ci        .prog               = lfs_emubd_prog,
131519ea8026Sopenharmony_ci        .erase              = lfs_emubd_erase,
131619ea8026Sopenharmony_ci        .sync               = lfs_emubd_sync,
131719ea8026Sopenharmony_ci        .read_size          = READ_SIZE,
131819ea8026Sopenharmony_ci        .prog_size          = PROG_SIZE,
131919ea8026Sopenharmony_ci        .block_size         = BLOCK_SIZE,
132019ea8026Sopenharmony_ci        .block_count        = BLOCK_COUNT,
132119ea8026Sopenharmony_ci        .block_cycles       = BLOCK_CYCLES,
132219ea8026Sopenharmony_ci        .cache_size         = CACHE_SIZE,
132319ea8026Sopenharmony_ci        .lookahead_size     = LOOKAHEAD_SIZE,
132419ea8026Sopenharmony_ci    };
132519ea8026Sopenharmony_ci
132619ea8026Sopenharmony_ci    struct lfs_emubd_config bdcfg = {
132719ea8026Sopenharmony_ci        .read_size          = READ_SIZE,
132819ea8026Sopenharmony_ci        .prog_size          = PROG_SIZE,
132919ea8026Sopenharmony_ci        .erase_size         = ERASE_SIZE,
133019ea8026Sopenharmony_ci        .erase_count        = ERASE_COUNT,
133119ea8026Sopenharmony_ci        .erase_value        = ERASE_VALUE,
133219ea8026Sopenharmony_ci        .erase_cycles       = ERASE_CYCLES,
133319ea8026Sopenharmony_ci        .badblock_behavior  = BADBLOCK_BEHAVIOR,
133419ea8026Sopenharmony_ci        .disk_path          = bench_disk_path,
133519ea8026Sopenharmony_ci        .read_sleep         = bench_read_sleep,
133619ea8026Sopenharmony_ci        .prog_sleep         = bench_prog_sleep,
133719ea8026Sopenharmony_ci        .erase_sleep        = bench_erase_sleep,
133819ea8026Sopenharmony_ci    };
133919ea8026Sopenharmony_ci
134019ea8026Sopenharmony_ci    int err = lfs_emubd_create(&cfg, &bdcfg);
134119ea8026Sopenharmony_ci    if (err) {
134219ea8026Sopenharmony_ci        fprintf(stderr, "error: could not create block device: %d\n", err);
134319ea8026Sopenharmony_ci        exit(-1);
134419ea8026Sopenharmony_ci    }
134519ea8026Sopenharmony_ci
134619ea8026Sopenharmony_ci    // run the bench
134719ea8026Sopenharmony_ci    bench_cfg = &cfg;
134819ea8026Sopenharmony_ci    bench_reset();
134919ea8026Sopenharmony_ci    printf("running ");
135019ea8026Sopenharmony_ci    perm_printid(suite, case_);
135119ea8026Sopenharmony_ci    printf("\n");
135219ea8026Sopenharmony_ci
135319ea8026Sopenharmony_ci    case_->run(&cfg);
135419ea8026Sopenharmony_ci
135519ea8026Sopenharmony_ci    printf("finished ");
135619ea8026Sopenharmony_ci    perm_printid(suite, case_);
135719ea8026Sopenharmony_ci    printf(" %"PRIu64" %"PRIu64" %"PRIu64,
135819ea8026Sopenharmony_ci        bench_readed,
135919ea8026Sopenharmony_ci        bench_proged,
136019ea8026Sopenharmony_ci        bench_erased);
136119ea8026Sopenharmony_ci    printf("\n");
136219ea8026Sopenharmony_ci
136319ea8026Sopenharmony_ci    // cleanup
136419ea8026Sopenharmony_ci    err = lfs_emubd_destroy(&cfg);
136519ea8026Sopenharmony_ci    if (err) {
136619ea8026Sopenharmony_ci        fprintf(stderr, "error: could not destroy block device: %d\n", err);
136719ea8026Sopenharmony_ci        exit(-1);
136819ea8026Sopenharmony_ci    }
136919ea8026Sopenharmony_ci}
137019ea8026Sopenharmony_ci
137119ea8026Sopenharmony_cistatic void run(void) {
137219ea8026Sopenharmony_ci    // ignore disconnected pipes
137319ea8026Sopenharmony_ci    signal(SIGPIPE, SIG_IGN);
137419ea8026Sopenharmony_ci
137519ea8026Sopenharmony_ci    for (size_t t = 0; t < bench_id_count; t++) {
137619ea8026Sopenharmony_ci        for (size_t i = 0; i < BENCH_SUITE_COUNT; i++) {
137719ea8026Sopenharmony_ci            bench_define_suite(&bench_suites[i]);
137819ea8026Sopenharmony_ci
137919ea8026Sopenharmony_ci            for (size_t j = 0; j < bench_suites[i].case_count; j++) {
138019ea8026Sopenharmony_ci                // does neither suite nor case name match?
138119ea8026Sopenharmony_ci                if (bench_ids[t].name && !(
138219ea8026Sopenharmony_ci                        strcmp(bench_ids[t].name,
138319ea8026Sopenharmony_ci                            bench_suites[i].name) == 0
138419ea8026Sopenharmony_ci                        || strcmp(bench_ids[t].name,
138519ea8026Sopenharmony_ci                            bench_suites[i].cases[j].name) == 0)) {
138619ea8026Sopenharmony_ci                    continue;
138719ea8026Sopenharmony_ci                }
138819ea8026Sopenharmony_ci
138919ea8026Sopenharmony_ci                case_forperm(
139019ea8026Sopenharmony_ci                        &bench_suites[i],
139119ea8026Sopenharmony_ci                        &bench_suites[i].cases[j],
139219ea8026Sopenharmony_ci                        bench_ids[t].defines,
139319ea8026Sopenharmony_ci                        bench_ids[t].define_count,
139419ea8026Sopenharmony_ci                        perm_run,
139519ea8026Sopenharmony_ci                        NULL);
139619ea8026Sopenharmony_ci            }
139719ea8026Sopenharmony_ci        }
139819ea8026Sopenharmony_ci    }
139919ea8026Sopenharmony_ci}
140019ea8026Sopenharmony_ci
140119ea8026Sopenharmony_ci
140219ea8026Sopenharmony_ci
140319ea8026Sopenharmony_ci// option handling
140419ea8026Sopenharmony_cienum opt_flags {
140519ea8026Sopenharmony_ci    OPT_HELP                     = 'h',
140619ea8026Sopenharmony_ci    OPT_SUMMARY                  = 'Y',
140719ea8026Sopenharmony_ci    OPT_LIST_SUITES              = 'l',
140819ea8026Sopenharmony_ci    OPT_LIST_CASES               = 'L',
140919ea8026Sopenharmony_ci    OPT_LIST_SUITE_PATHS         = 1,
141019ea8026Sopenharmony_ci    OPT_LIST_CASE_PATHS          = 2,
141119ea8026Sopenharmony_ci    OPT_LIST_DEFINES             = 3,
141219ea8026Sopenharmony_ci    OPT_LIST_PERMUTATION_DEFINES = 4,
141319ea8026Sopenharmony_ci    OPT_LIST_IMPLICIT_DEFINES    = 5,
141419ea8026Sopenharmony_ci    OPT_LIST_GEOMETRIES          = 6,
141519ea8026Sopenharmony_ci    OPT_DEFINE                   = 'D',
141619ea8026Sopenharmony_ci    OPT_GEOMETRY                 = 'G',
141719ea8026Sopenharmony_ci    OPT_STEP                     = 's',
141819ea8026Sopenharmony_ci    OPT_DISK                     = 'd',
141919ea8026Sopenharmony_ci    OPT_TRACE                    = 't',
142019ea8026Sopenharmony_ci    OPT_TRACE_BACKTRACE          = 7,
142119ea8026Sopenharmony_ci    OPT_TRACE_PERIOD             = 8,
142219ea8026Sopenharmony_ci    OPT_TRACE_FREQ               = 9,
142319ea8026Sopenharmony_ci    OPT_READ_SLEEP               = 10,
142419ea8026Sopenharmony_ci    OPT_PROG_SLEEP               = 11,
142519ea8026Sopenharmony_ci    OPT_ERASE_SLEEP              = 12,
142619ea8026Sopenharmony_ci};
142719ea8026Sopenharmony_ci
142819ea8026Sopenharmony_ciconst char *short_opts = "hYlLD:G:s:d:t:";
142919ea8026Sopenharmony_ci
143019ea8026Sopenharmony_ciconst struct option long_opts[] = {
143119ea8026Sopenharmony_ci    {"help",             no_argument,       NULL, OPT_HELP},
143219ea8026Sopenharmony_ci    {"summary",          no_argument,       NULL, OPT_SUMMARY},
143319ea8026Sopenharmony_ci    {"list-suites",      no_argument,       NULL, OPT_LIST_SUITES},
143419ea8026Sopenharmony_ci    {"list-cases",       no_argument,       NULL, OPT_LIST_CASES},
143519ea8026Sopenharmony_ci    {"list-suite-paths", no_argument,       NULL, OPT_LIST_SUITE_PATHS},
143619ea8026Sopenharmony_ci    {"list-case-paths",  no_argument,       NULL, OPT_LIST_CASE_PATHS},
143719ea8026Sopenharmony_ci    {"list-defines",     no_argument,       NULL, OPT_LIST_DEFINES},
143819ea8026Sopenharmony_ci    {"list-permutation-defines",
143919ea8026Sopenharmony_ci                         no_argument,       NULL, OPT_LIST_PERMUTATION_DEFINES},
144019ea8026Sopenharmony_ci    {"list-implicit-defines",
144119ea8026Sopenharmony_ci                         no_argument,       NULL, OPT_LIST_IMPLICIT_DEFINES},
144219ea8026Sopenharmony_ci    {"list-geometries",  no_argument,       NULL, OPT_LIST_GEOMETRIES},
144319ea8026Sopenharmony_ci    {"define",           required_argument, NULL, OPT_DEFINE},
144419ea8026Sopenharmony_ci    {"geometry",         required_argument, NULL, OPT_GEOMETRY},
144519ea8026Sopenharmony_ci    {"step",             required_argument, NULL, OPT_STEP},
144619ea8026Sopenharmony_ci    {"disk",             required_argument, NULL, OPT_DISK},
144719ea8026Sopenharmony_ci    {"trace",            required_argument, NULL, OPT_TRACE},
144819ea8026Sopenharmony_ci    {"trace-backtrace",  no_argument,       NULL, OPT_TRACE_BACKTRACE},
144919ea8026Sopenharmony_ci    {"trace-period",     required_argument, NULL, OPT_TRACE_PERIOD},
145019ea8026Sopenharmony_ci    {"trace-freq",       required_argument, NULL, OPT_TRACE_FREQ},
145119ea8026Sopenharmony_ci    {"read-sleep",       required_argument, NULL, OPT_READ_SLEEP},
145219ea8026Sopenharmony_ci    {"prog-sleep",       required_argument, NULL, OPT_PROG_SLEEP},
145319ea8026Sopenharmony_ci    {"erase-sleep",      required_argument, NULL, OPT_ERASE_SLEEP},
145419ea8026Sopenharmony_ci    {NULL, 0, NULL, 0},
145519ea8026Sopenharmony_ci};
145619ea8026Sopenharmony_ci
145719ea8026Sopenharmony_ciconst char *const help_text[] = {
145819ea8026Sopenharmony_ci    "Show this help message.",
145919ea8026Sopenharmony_ci    "Show quick summary.",
146019ea8026Sopenharmony_ci    "List bench suites.",
146119ea8026Sopenharmony_ci    "List bench cases.",
146219ea8026Sopenharmony_ci    "List the path for each bench suite.",
146319ea8026Sopenharmony_ci    "List the path and line number for each bench case.",
146419ea8026Sopenharmony_ci    "List all defines in this bench-runner.",
146519ea8026Sopenharmony_ci    "List explicit defines in this bench-runner.",
146619ea8026Sopenharmony_ci    "List implicit defines in this bench-runner.",
146719ea8026Sopenharmony_ci    "List the available disk geometries.",
146819ea8026Sopenharmony_ci    "Override a bench define.",
146919ea8026Sopenharmony_ci    "Comma-separated list of disk geometries to bench.",
147019ea8026Sopenharmony_ci    "Comma-separated range of bench permutations to run (start,stop,step).",
147119ea8026Sopenharmony_ci    "Direct block device operations to this file.",
147219ea8026Sopenharmony_ci    "Direct trace output to this file.",
147319ea8026Sopenharmony_ci    "Include a backtrace with every trace statement.",
147419ea8026Sopenharmony_ci    "Sample trace output at this period in cycles.",
147519ea8026Sopenharmony_ci    "Sample trace output at this frequency in hz.",
147619ea8026Sopenharmony_ci    "Artificial read delay in seconds.",
147719ea8026Sopenharmony_ci    "Artificial prog delay in seconds.",
147819ea8026Sopenharmony_ci    "Artificial erase delay in seconds.",
147919ea8026Sopenharmony_ci};
148019ea8026Sopenharmony_ci
148119ea8026Sopenharmony_ciint main(int argc, char **argv) {
148219ea8026Sopenharmony_ci    void (*op)(void) = run;
148319ea8026Sopenharmony_ci
148419ea8026Sopenharmony_ci    size_t bench_override_capacity = 0;
148519ea8026Sopenharmony_ci    size_t bench_geometry_capacity = 0;
148619ea8026Sopenharmony_ci    size_t bench_id_capacity = 0;
148719ea8026Sopenharmony_ci
148819ea8026Sopenharmony_ci    // parse options
148919ea8026Sopenharmony_ci    while (true) {
149019ea8026Sopenharmony_ci        int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
149119ea8026Sopenharmony_ci        switch (c) {
149219ea8026Sopenharmony_ci            // generate help message
149319ea8026Sopenharmony_ci            case OPT_HELP: {
149419ea8026Sopenharmony_ci                printf("usage: %s [options] [bench_id]\n", argv[0]);
149519ea8026Sopenharmony_ci                printf("\n");
149619ea8026Sopenharmony_ci
149719ea8026Sopenharmony_ci                printf("options:\n");
149819ea8026Sopenharmony_ci                size_t i = 0;
149919ea8026Sopenharmony_ci                while (long_opts[i].name) {
150019ea8026Sopenharmony_ci                    size_t indent;
150119ea8026Sopenharmony_ci                    if (long_opts[i].has_arg == no_argument) {
150219ea8026Sopenharmony_ci                        if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
150319ea8026Sopenharmony_ci                            indent = printf("  -%c, --%s ",
150419ea8026Sopenharmony_ci                                    long_opts[i].val,
150519ea8026Sopenharmony_ci                                    long_opts[i].name);
150619ea8026Sopenharmony_ci                        } else {
150719ea8026Sopenharmony_ci                            indent = printf("  --%s ",
150819ea8026Sopenharmony_ci                                    long_opts[i].name);
150919ea8026Sopenharmony_ci                        }
151019ea8026Sopenharmony_ci                    } else {
151119ea8026Sopenharmony_ci                        if (long_opts[i].val >= '0' && long_opts[i].val < 'z') {
151219ea8026Sopenharmony_ci                            indent = printf("  -%c %s, --%s %s ",
151319ea8026Sopenharmony_ci                                    long_opts[i].val,
151419ea8026Sopenharmony_ci                                    long_opts[i].name,
151519ea8026Sopenharmony_ci                                    long_opts[i].name,
151619ea8026Sopenharmony_ci                                    long_opts[i].name);
151719ea8026Sopenharmony_ci                        } else {
151819ea8026Sopenharmony_ci                            indent = printf("  --%s %s ",
151919ea8026Sopenharmony_ci                                    long_opts[i].name,
152019ea8026Sopenharmony_ci                                    long_opts[i].name);
152119ea8026Sopenharmony_ci                        }
152219ea8026Sopenharmony_ci                    }
152319ea8026Sopenharmony_ci
152419ea8026Sopenharmony_ci                    // a quick, hacky, byte-level method for text wrapping
152519ea8026Sopenharmony_ci                    size_t len = strlen(help_text[i]);
152619ea8026Sopenharmony_ci                    size_t j = 0;
152719ea8026Sopenharmony_ci                    if (indent < 24) {
152819ea8026Sopenharmony_ci                        printf("%*s %.80s\n",
152919ea8026Sopenharmony_ci                                (int)(24-1-indent),
153019ea8026Sopenharmony_ci                                "",
153119ea8026Sopenharmony_ci                                &help_text[i][j]);
153219ea8026Sopenharmony_ci                        j += 80;
153319ea8026Sopenharmony_ci                    } else {
153419ea8026Sopenharmony_ci                        printf("\n");
153519ea8026Sopenharmony_ci                    }
153619ea8026Sopenharmony_ci
153719ea8026Sopenharmony_ci                    while (j < len) {
153819ea8026Sopenharmony_ci                        printf("%24s%.80s\n", "", &help_text[i][j]);
153919ea8026Sopenharmony_ci                        j += 80;
154019ea8026Sopenharmony_ci                    }
154119ea8026Sopenharmony_ci
154219ea8026Sopenharmony_ci                    i += 1;
154319ea8026Sopenharmony_ci                }
154419ea8026Sopenharmony_ci
154519ea8026Sopenharmony_ci                printf("\n");
154619ea8026Sopenharmony_ci                exit(0);
154719ea8026Sopenharmony_ci            }
154819ea8026Sopenharmony_ci            // summary/list flags
154919ea8026Sopenharmony_ci            case OPT_SUMMARY:
155019ea8026Sopenharmony_ci                op = summary;
155119ea8026Sopenharmony_ci                break;
155219ea8026Sopenharmony_ci            case OPT_LIST_SUITES:
155319ea8026Sopenharmony_ci                op = list_suites;
155419ea8026Sopenharmony_ci                break;
155519ea8026Sopenharmony_ci            case OPT_LIST_CASES:
155619ea8026Sopenharmony_ci                op = list_cases;
155719ea8026Sopenharmony_ci                break;
155819ea8026Sopenharmony_ci            case OPT_LIST_SUITE_PATHS:
155919ea8026Sopenharmony_ci                op = list_suite_paths;
156019ea8026Sopenharmony_ci                break;
156119ea8026Sopenharmony_ci            case OPT_LIST_CASE_PATHS:
156219ea8026Sopenharmony_ci                op = list_case_paths;
156319ea8026Sopenharmony_ci                break;
156419ea8026Sopenharmony_ci            case OPT_LIST_DEFINES:
156519ea8026Sopenharmony_ci                op = list_defines;
156619ea8026Sopenharmony_ci                break;
156719ea8026Sopenharmony_ci            case OPT_LIST_PERMUTATION_DEFINES:
156819ea8026Sopenharmony_ci                op = list_permutation_defines;
156919ea8026Sopenharmony_ci                break;
157019ea8026Sopenharmony_ci            case OPT_LIST_IMPLICIT_DEFINES:
157119ea8026Sopenharmony_ci                op = list_implicit_defines;
157219ea8026Sopenharmony_ci                break;
157319ea8026Sopenharmony_ci            case OPT_LIST_GEOMETRIES:
157419ea8026Sopenharmony_ci                op = list_geometries;
157519ea8026Sopenharmony_ci                break;
157619ea8026Sopenharmony_ci            // configuration
157719ea8026Sopenharmony_ci            case OPT_DEFINE: {
157819ea8026Sopenharmony_ci                // allocate space
157919ea8026Sopenharmony_ci                bench_override_t *override = mappend(
158019ea8026Sopenharmony_ci                        (void**)&bench_overrides,
158119ea8026Sopenharmony_ci                        sizeof(bench_override_t),
158219ea8026Sopenharmony_ci                        &bench_override_count,
158319ea8026Sopenharmony_ci                        &bench_override_capacity);
158419ea8026Sopenharmony_ci
158519ea8026Sopenharmony_ci                // parse into string key/intmax_t value, cannibalizing the
158619ea8026Sopenharmony_ci                // arg in the process
158719ea8026Sopenharmony_ci                char *sep = strchr(optarg, '=');
158819ea8026Sopenharmony_ci                char *parsed = NULL;
158919ea8026Sopenharmony_ci                if (!sep) {
159019ea8026Sopenharmony_ci                    goto invalid_define;
159119ea8026Sopenharmony_ci                }
159219ea8026Sopenharmony_ci                *sep = '\0';
159319ea8026Sopenharmony_ci                override->name = optarg;
159419ea8026Sopenharmony_ci                optarg = sep+1;
159519ea8026Sopenharmony_ci
159619ea8026Sopenharmony_ci                // parse comma-separated permutations
159719ea8026Sopenharmony_ci                {
159819ea8026Sopenharmony_ci                    override->defines = NULL;
159919ea8026Sopenharmony_ci                    override->permutations = 0;
160019ea8026Sopenharmony_ci                    size_t override_capacity = 0;
160119ea8026Sopenharmony_ci                    while (true) {
160219ea8026Sopenharmony_ci                        optarg += strspn(optarg, " ");
160319ea8026Sopenharmony_ci
160419ea8026Sopenharmony_ci                        if (strncmp(optarg, "range", strlen("range")) == 0) {
160519ea8026Sopenharmony_ci                            // range of values
160619ea8026Sopenharmony_ci                            optarg += strlen("range");
160719ea8026Sopenharmony_ci                            optarg += strspn(optarg, " ");
160819ea8026Sopenharmony_ci                            if (*optarg != '(') {
160919ea8026Sopenharmony_ci                                goto invalid_define;
161019ea8026Sopenharmony_ci                            }
161119ea8026Sopenharmony_ci                            optarg += 1;
161219ea8026Sopenharmony_ci
161319ea8026Sopenharmony_ci                            intmax_t start = strtoumax(optarg, &parsed, 0);
161419ea8026Sopenharmony_ci                            intmax_t stop = -1;
161519ea8026Sopenharmony_ci                            intmax_t step = 1;
161619ea8026Sopenharmony_ci                            // allow empty string for start=0
161719ea8026Sopenharmony_ci                            if (parsed == optarg) {
161819ea8026Sopenharmony_ci                                start = 0;
161919ea8026Sopenharmony_ci                            }
162019ea8026Sopenharmony_ci                            optarg = parsed + strspn(parsed, " ");
162119ea8026Sopenharmony_ci
162219ea8026Sopenharmony_ci                            if (*optarg != ',' && *optarg != ')') {
162319ea8026Sopenharmony_ci                                goto invalid_define;
162419ea8026Sopenharmony_ci                            }
162519ea8026Sopenharmony_ci
162619ea8026Sopenharmony_ci                            if (*optarg == ',') {
162719ea8026Sopenharmony_ci                                optarg += 1;
162819ea8026Sopenharmony_ci                                stop = strtoumax(optarg, &parsed, 0);
162919ea8026Sopenharmony_ci                                // allow empty string for stop=end
163019ea8026Sopenharmony_ci                                if (parsed == optarg) {
163119ea8026Sopenharmony_ci                                    stop = -1;
163219ea8026Sopenharmony_ci                                }
163319ea8026Sopenharmony_ci                                optarg = parsed + strspn(parsed, " ");
163419ea8026Sopenharmony_ci
163519ea8026Sopenharmony_ci                                if (*optarg != ',' && *optarg != ')') {
163619ea8026Sopenharmony_ci                                    goto invalid_define;
163719ea8026Sopenharmony_ci                                }
163819ea8026Sopenharmony_ci
163919ea8026Sopenharmony_ci                                if (*optarg == ',') {
164019ea8026Sopenharmony_ci                                    optarg += 1;
164119ea8026Sopenharmony_ci                                    step = strtoumax(optarg, &parsed, 0);
164219ea8026Sopenharmony_ci                                    // allow empty string for stop=1
164319ea8026Sopenharmony_ci                                    if (parsed == optarg) {
164419ea8026Sopenharmony_ci                                        step = 1;
164519ea8026Sopenharmony_ci                                    }
164619ea8026Sopenharmony_ci                                    optarg = parsed + strspn(parsed, " ");
164719ea8026Sopenharmony_ci
164819ea8026Sopenharmony_ci                                    if (*optarg != ')') {
164919ea8026Sopenharmony_ci                                        goto invalid_define;
165019ea8026Sopenharmony_ci                                    }
165119ea8026Sopenharmony_ci                                }
165219ea8026Sopenharmony_ci                            } else {
165319ea8026Sopenharmony_ci                                // single value = stop only
165419ea8026Sopenharmony_ci                                stop = start;
165519ea8026Sopenharmony_ci                                start = 0;
165619ea8026Sopenharmony_ci                            }
165719ea8026Sopenharmony_ci
165819ea8026Sopenharmony_ci                            if (*optarg != ')') {
165919ea8026Sopenharmony_ci                                goto invalid_define;
166019ea8026Sopenharmony_ci                            }
166119ea8026Sopenharmony_ci                            optarg += 1;
166219ea8026Sopenharmony_ci
166319ea8026Sopenharmony_ci                            // calculate the range of values
166419ea8026Sopenharmony_ci                            assert(step != 0);
166519ea8026Sopenharmony_ci                            for (intmax_t i = start;
166619ea8026Sopenharmony_ci                                    (step < 0)
166719ea8026Sopenharmony_ci                                        ? i > stop
166819ea8026Sopenharmony_ci                                        : (uintmax_t)i < (uintmax_t)stop;
166919ea8026Sopenharmony_ci                                    i += step) {
167019ea8026Sopenharmony_ci                                *(intmax_t*)mappend(
167119ea8026Sopenharmony_ci                                        (void**)&override->defines,
167219ea8026Sopenharmony_ci                                        sizeof(intmax_t),
167319ea8026Sopenharmony_ci                                        &override->permutations,
167419ea8026Sopenharmony_ci                                        &override_capacity) = i;
167519ea8026Sopenharmony_ci                            }
167619ea8026Sopenharmony_ci                        } else if (*optarg != '\0') {
167719ea8026Sopenharmony_ci                            // single value
167819ea8026Sopenharmony_ci                            intmax_t define = strtoimax(optarg, &parsed, 0);
167919ea8026Sopenharmony_ci                            if (parsed == optarg) {
168019ea8026Sopenharmony_ci                                goto invalid_define;
168119ea8026Sopenharmony_ci                            }
168219ea8026Sopenharmony_ci                            optarg = parsed + strspn(parsed, " ");
168319ea8026Sopenharmony_ci                            *(intmax_t*)mappend(
168419ea8026Sopenharmony_ci                                    (void**)&override->defines,
168519ea8026Sopenharmony_ci                                    sizeof(intmax_t),
168619ea8026Sopenharmony_ci                                    &override->permutations,
168719ea8026Sopenharmony_ci                                    &override_capacity) = define;
168819ea8026Sopenharmony_ci                        } else {
168919ea8026Sopenharmony_ci                            break;
169019ea8026Sopenharmony_ci                        }
169119ea8026Sopenharmony_ci
169219ea8026Sopenharmony_ci                        if (*optarg == ',') {
169319ea8026Sopenharmony_ci                            optarg += 1;
169419ea8026Sopenharmony_ci                        }
169519ea8026Sopenharmony_ci                    }
169619ea8026Sopenharmony_ci                }
169719ea8026Sopenharmony_ci                assert(override->permutations > 0);
169819ea8026Sopenharmony_ci                break;
169919ea8026Sopenharmony_ci
170019ea8026Sopenharmony_ciinvalid_define:
170119ea8026Sopenharmony_ci                fprintf(stderr, "error: invalid define: %s\n", optarg);
170219ea8026Sopenharmony_ci                exit(-1);
170319ea8026Sopenharmony_ci            }
170419ea8026Sopenharmony_ci            case OPT_GEOMETRY: {
170519ea8026Sopenharmony_ci                // reset our geometry scenarios
170619ea8026Sopenharmony_ci                if (bench_geometry_capacity > 0) {
170719ea8026Sopenharmony_ci                    free((bench_geometry_t*)bench_geometries);
170819ea8026Sopenharmony_ci                }
170919ea8026Sopenharmony_ci                bench_geometries = NULL;
171019ea8026Sopenharmony_ci                bench_geometry_count = 0;
171119ea8026Sopenharmony_ci                bench_geometry_capacity = 0;
171219ea8026Sopenharmony_ci
171319ea8026Sopenharmony_ci                // parse the comma separated list of disk geometries
171419ea8026Sopenharmony_ci                while (*optarg) {
171519ea8026Sopenharmony_ci                    // allocate space
171619ea8026Sopenharmony_ci                    bench_geometry_t *geometry = mappend(
171719ea8026Sopenharmony_ci                            (void**)&bench_geometries,
171819ea8026Sopenharmony_ci                            sizeof(bench_geometry_t),
171919ea8026Sopenharmony_ci                            &bench_geometry_count,
172019ea8026Sopenharmony_ci                            &bench_geometry_capacity);
172119ea8026Sopenharmony_ci
172219ea8026Sopenharmony_ci                    // parse the disk geometry
172319ea8026Sopenharmony_ci                    optarg += strspn(optarg, " ");
172419ea8026Sopenharmony_ci
172519ea8026Sopenharmony_ci                    // named disk geometry
172619ea8026Sopenharmony_ci                    size_t len = strcspn(optarg, " ,");
172719ea8026Sopenharmony_ci                    for (size_t i = 0; builtin_geometries[i].name; i++) {
172819ea8026Sopenharmony_ci                        if (len == strlen(builtin_geometries[i].name)
172919ea8026Sopenharmony_ci                                && memcmp(optarg,
173019ea8026Sopenharmony_ci                                    builtin_geometries[i].name,
173119ea8026Sopenharmony_ci                                    len) == 0) {
173219ea8026Sopenharmony_ci                            *geometry = builtin_geometries[i];
173319ea8026Sopenharmony_ci                            optarg += len;
173419ea8026Sopenharmony_ci                            goto geometry_next;
173519ea8026Sopenharmony_ci                        }
173619ea8026Sopenharmony_ci                    }
173719ea8026Sopenharmony_ci
173819ea8026Sopenharmony_ci                    // comma-separated read/prog/erase/count
173919ea8026Sopenharmony_ci                    if (*optarg == '{') {
174019ea8026Sopenharmony_ci                        lfs_size_t sizes[4];
174119ea8026Sopenharmony_ci                        size_t count = 0;
174219ea8026Sopenharmony_ci
174319ea8026Sopenharmony_ci                        char *s = optarg + 1;
174419ea8026Sopenharmony_ci                        while (count < 4) {
174519ea8026Sopenharmony_ci                            char *parsed = NULL;
174619ea8026Sopenharmony_ci                            sizes[count] = strtoumax(s, &parsed, 0);
174719ea8026Sopenharmony_ci                            count += 1;
174819ea8026Sopenharmony_ci
174919ea8026Sopenharmony_ci                            s = parsed + strspn(parsed, " ");
175019ea8026Sopenharmony_ci                            if (*s == ',') {
175119ea8026Sopenharmony_ci                                s += 1;
175219ea8026Sopenharmony_ci                                continue;
175319ea8026Sopenharmony_ci                            } else if (*s == '}') {
175419ea8026Sopenharmony_ci                                s += 1;
175519ea8026Sopenharmony_ci                                break;
175619ea8026Sopenharmony_ci                            } else {
175719ea8026Sopenharmony_ci                                goto geometry_unknown;
175819ea8026Sopenharmony_ci                            }
175919ea8026Sopenharmony_ci                        }
176019ea8026Sopenharmony_ci
176119ea8026Sopenharmony_ci                        // allow implicit r=p and p=e for common geometries
176219ea8026Sopenharmony_ci                        memset(geometry, 0, sizeof(bench_geometry_t));
176319ea8026Sopenharmony_ci                        if (count >= 3) {
176419ea8026Sopenharmony_ci                            geometry->defines[READ_SIZE_i]
176519ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
176619ea8026Sopenharmony_ci                            geometry->defines[PROG_SIZE_i]
176719ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[1]);
176819ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
176919ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[2]);
177019ea8026Sopenharmony_ci                        } else if (count >= 2) {
177119ea8026Sopenharmony_ci                            geometry->defines[PROG_SIZE_i]
177219ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
177319ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
177419ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[1]);
177519ea8026Sopenharmony_ci                        } else {
177619ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
177719ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
177819ea8026Sopenharmony_ci                        }
177919ea8026Sopenharmony_ci                        if (count >= 4) {
178019ea8026Sopenharmony_ci                            geometry->defines[ERASE_COUNT_i]
178119ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[3]);
178219ea8026Sopenharmony_ci                        }
178319ea8026Sopenharmony_ci                        optarg = s;
178419ea8026Sopenharmony_ci                        goto geometry_next;
178519ea8026Sopenharmony_ci                    }
178619ea8026Sopenharmony_ci
178719ea8026Sopenharmony_ci                    // leb16-encoded read/prog/erase/count
178819ea8026Sopenharmony_ci                    if (*optarg == ':') {
178919ea8026Sopenharmony_ci                        lfs_size_t sizes[4];
179019ea8026Sopenharmony_ci                        size_t count = 0;
179119ea8026Sopenharmony_ci
179219ea8026Sopenharmony_ci                        char *s = optarg + 1;
179319ea8026Sopenharmony_ci                        while (true) {
179419ea8026Sopenharmony_ci                            char *parsed = NULL;
179519ea8026Sopenharmony_ci                            uintmax_t x = leb16_parse(s, &parsed);
179619ea8026Sopenharmony_ci                            if (parsed == s || count >= 4) {
179719ea8026Sopenharmony_ci                                break;
179819ea8026Sopenharmony_ci                            }
179919ea8026Sopenharmony_ci
180019ea8026Sopenharmony_ci                            sizes[count] = x;
180119ea8026Sopenharmony_ci                            count += 1;
180219ea8026Sopenharmony_ci                            s = parsed;
180319ea8026Sopenharmony_ci                        }
180419ea8026Sopenharmony_ci
180519ea8026Sopenharmony_ci                        // allow implicit r=p and p=e for common geometries
180619ea8026Sopenharmony_ci                        memset(geometry, 0, sizeof(bench_geometry_t));
180719ea8026Sopenharmony_ci                        if (count >= 3) {
180819ea8026Sopenharmony_ci                            geometry->defines[READ_SIZE_i]
180919ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
181019ea8026Sopenharmony_ci                            geometry->defines[PROG_SIZE_i]
181119ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[1]);
181219ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
181319ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[2]);
181419ea8026Sopenharmony_ci                        } else if (count >= 2) {
181519ea8026Sopenharmony_ci                            geometry->defines[PROG_SIZE_i]
181619ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
181719ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
181819ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[1]);
181919ea8026Sopenharmony_ci                        } else {
182019ea8026Sopenharmony_ci                            geometry->defines[ERASE_SIZE_i]
182119ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[0]);
182219ea8026Sopenharmony_ci                        }
182319ea8026Sopenharmony_ci                        if (count >= 4) {
182419ea8026Sopenharmony_ci                            geometry->defines[ERASE_COUNT_i]
182519ea8026Sopenharmony_ci                                    = BENCH_LIT(sizes[3]);
182619ea8026Sopenharmony_ci                        }
182719ea8026Sopenharmony_ci                        optarg = s;
182819ea8026Sopenharmony_ci                        goto geometry_next;
182919ea8026Sopenharmony_ci                    }
183019ea8026Sopenharmony_ci
183119ea8026Sopenharmony_cigeometry_unknown:
183219ea8026Sopenharmony_ci                    // unknown scenario?
183319ea8026Sopenharmony_ci                    fprintf(stderr, "error: unknown disk geometry: %s\n",
183419ea8026Sopenharmony_ci                            optarg);
183519ea8026Sopenharmony_ci                    exit(-1);
183619ea8026Sopenharmony_ci
183719ea8026Sopenharmony_cigeometry_next:
183819ea8026Sopenharmony_ci                    optarg += strspn(optarg, " ");
183919ea8026Sopenharmony_ci                    if (*optarg == ',') {
184019ea8026Sopenharmony_ci                        optarg += 1;
184119ea8026Sopenharmony_ci                    } else if (*optarg == '\0') {
184219ea8026Sopenharmony_ci                        break;
184319ea8026Sopenharmony_ci                    } else {
184419ea8026Sopenharmony_ci                        goto geometry_unknown;
184519ea8026Sopenharmony_ci                    }
184619ea8026Sopenharmony_ci                }
184719ea8026Sopenharmony_ci                break;
184819ea8026Sopenharmony_ci            }
184919ea8026Sopenharmony_ci            case OPT_STEP: {
185019ea8026Sopenharmony_ci                char *parsed = NULL;
185119ea8026Sopenharmony_ci                bench_step_start = strtoumax(optarg, &parsed, 0);
185219ea8026Sopenharmony_ci                bench_step_stop = -1;
185319ea8026Sopenharmony_ci                bench_step_step = 1;
185419ea8026Sopenharmony_ci                // allow empty string for start=0
185519ea8026Sopenharmony_ci                if (parsed == optarg) {
185619ea8026Sopenharmony_ci                    bench_step_start = 0;
185719ea8026Sopenharmony_ci                }
185819ea8026Sopenharmony_ci                optarg = parsed + strspn(parsed, " ");
185919ea8026Sopenharmony_ci
186019ea8026Sopenharmony_ci                if (*optarg != ',' && *optarg != '\0') {
186119ea8026Sopenharmony_ci                    goto step_unknown;
186219ea8026Sopenharmony_ci                }
186319ea8026Sopenharmony_ci
186419ea8026Sopenharmony_ci                if (*optarg == ',') {
186519ea8026Sopenharmony_ci                    optarg += 1;
186619ea8026Sopenharmony_ci                    bench_step_stop = strtoumax(optarg, &parsed, 0);
186719ea8026Sopenharmony_ci                    // allow empty string for stop=end
186819ea8026Sopenharmony_ci                    if (parsed == optarg) {
186919ea8026Sopenharmony_ci                        bench_step_stop = -1;
187019ea8026Sopenharmony_ci                    }
187119ea8026Sopenharmony_ci                    optarg = parsed + strspn(parsed, " ");
187219ea8026Sopenharmony_ci
187319ea8026Sopenharmony_ci                    if (*optarg != ',' && *optarg != '\0') {
187419ea8026Sopenharmony_ci                        goto step_unknown;
187519ea8026Sopenharmony_ci                    }
187619ea8026Sopenharmony_ci
187719ea8026Sopenharmony_ci                    if (*optarg == ',') {
187819ea8026Sopenharmony_ci                        optarg += 1;
187919ea8026Sopenharmony_ci                        bench_step_step = strtoumax(optarg, &parsed, 0);
188019ea8026Sopenharmony_ci                        // allow empty string for stop=1
188119ea8026Sopenharmony_ci                        if (parsed == optarg) {
188219ea8026Sopenharmony_ci                            bench_step_step = 1;
188319ea8026Sopenharmony_ci                        }
188419ea8026Sopenharmony_ci                        optarg = parsed + strspn(parsed, " ");
188519ea8026Sopenharmony_ci
188619ea8026Sopenharmony_ci                        if (*optarg != '\0') {
188719ea8026Sopenharmony_ci                            goto step_unknown;
188819ea8026Sopenharmony_ci                        }
188919ea8026Sopenharmony_ci                    }
189019ea8026Sopenharmony_ci                } else {
189119ea8026Sopenharmony_ci                    // single value = stop only
189219ea8026Sopenharmony_ci                    bench_step_stop = bench_step_start;
189319ea8026Sopenharmony_ci                    bench_step_start = 0;
189419ea8026Sopenharmony_ci                }
189519ea8026Sopenharmony_ci
189619ea8026Sopenharmony_ci                break;
189719ea8026Sopenharmony_cistep_unknown:
189819ea8026Sopenharmony_ci                fprintf(stderr, "error: invalid step: %s\n", optarg);
189919ea8026Sopenharmony_ci                exit(-1);
190019ea8026Sopenharmony_ci            }
190119ea8026Sopenharmony_ci            case OPT_DISK:
190219ea8026Sopenharmony_ci                bench_disk_path = optarg;
190319ea8026Sopenharmony_ci                break;
190419ea8026Sopenharmony_ci            case OPT_TRACE:
190519ea8026Sopenharmony_ci                bench_trace_path = optarg;
190619ea8026Sopenharmony_ci                break;
190719ea8026Sopenharmony_ci            case OPT_TRACE_BACKTRACE:
190819ea8026Sopenharmony_ci                bench_trace_backtrace = true;
190919ea8026Sopenharmony_ci                break;
191019ea8026Sopenharmony_ci            case OPT_TRACE_PERIOD: {
191119ea8026Sopenharmony_ci                char *parsed = NULL;
191219ea8026Sopenharmony_ci                bench_trace_period = strtoumax(optarg, &parsed, 0);
191319ea8026Sopenharmony_ci                if (parsed == optarg) {
191419ea8026Sopenharmony_ci                    fprintf(stderr, "error: invalid trace-period: %s\n", optarg);
191519ea8026Sopenharmony_ci                    exit(-1);
191619ea8026Sopenharmony_ci                }
191719ea8026Sopenharmony_ci                break;
191819ea8026Sopenharmony_ci            }
191919ea8026Sopenharmony_ci            case OPT_TRACE_FREQ: {
192019ea8026Sopenharmony_ci                char *parsed = NULL;
192119ea8026Sopenharmony_ci                bench_trace_freq = strtoumax(optarg, &parsed, 0);
192219ea8026Sopenharmony_ci                if (parsed == optarg) {
192319ea8026Sopenharmony_ci                    fprintf(stderr, "error: invalid trace-freq: %s\n", optarg);
192419ea8026Sopenharmony_ci                    exit(-1);
192519ea8026Sopenharmony_ci                }
192619ea8026Sopenharmony_ci                break;
192719ea8026Sopenharmony_ci            }
192819ea8026Sopenharmony_ci            case OPT_READ_SLEEP: {
192919ea8026Sopenharmony_ci                char *parsed = NULL;
193019ea8026Sopenharmony_ci                double read_sleep = strtod(optarg, &parsed);
193119ea8026Sopenharmony_ci                if (parsed == optarg) {
193219ea8026Sopenharmony_ci                    fprintf(stderr, "error: invalid read-sleep: %s\n", optarg);
193319ea8026Sopenharmony_ci                    exit(-1);
193419ea8026Sopenharmony_ci                }
193519ea8026Sopenharmony_ci                bench_read_sleep = read_sleep*1.0e9;
193619ea8026Sopenharmony_ci                break;
193719ea8026Sopenharmony_ci            }
193819ea8026Sopenharmony_ci            case OPT_PROG_SLEEP: {
193919ea8026Sopenharmony_ci                char *parsed = NULL;
194019ea8026Sopenharmony_ci                double prog_sleep = strtod(optarg, &parsed);
194119ea8026Sopenharmony_ci                if (parsed == optarg) {
194219ea8026Sopenharmony_ci                    fprintf(stderr, "error: invalid prog-sleep: %s\n", optarg);
194319ea8026Sopenharmony_ci                    exit(-1);
194419ea8026Sopenharmony_ci                }
194519ea8026Sopenharmony_ci                bench_prog_sleep = prog_sleep*1.0e9;
194619ea8026Sopenharmony_ci                break;
194719ea8026Sopenharmony_ci            }
194819ea8026Sopenharmony_ci            case OPT_ERASE_SLEEP: {
194919ea8026Sopenharmony_ci                char *parsed = NULL;
195019ea8026Sopenharmony_ci                double erase_sleep = strtod(optarg, &parsed);
195119ea8026Sopenharmony_ci                if (parsed == optarg) {
195219ea8026Sopenharmony_ci                    fprintf(stderr, "error: invalid erase-sleep: %s\n", optarg);
195319ea8026Sopenharmony_ci                    exit(-1);
195419ea8026Sopenharmony_ci                }
195519ea8026Sopenharmony_ci                bench_erase_sleep = erase_sleep*1.0e9;
195619ea8026Sopenharmony_ci                break;
195719ea8026Sopenharmony_ci            }
195819ea8026Sopenharmony_ci            // done parsing
195919ea8026Sopenharmony_ci            case -1:
196019ea8026Sopenharmony_ci                goto getopt_done;
196119ea8026Sopenharmony_ci            // unknown arg, getopt prints a message for us
196219ea8026Sopenharmony_ci            default:
196319ea8026Sopenharmony_ci                exit(-1);
196419ea8026Sopenharmony_ci        }
196519ea8026Sopenharmony_ci    }
196619ea8026Sopenharmony_cigetopt_done: ;
196719ea8026Sopenharmony_ci
196819ea8026Sopenharmony_ci    if (argc > optind) {
196919ea8026Sopenharmony_ci        // reset our bench identifier list
197019ea8026Sopenharmony_ci        bench_ids = NULL;
197119ea8026Sopenharmony_ci        bench_id_count = 0;
197219ea8026Sopenharmony_ci        bench_id_capacity = 0;
197319ea8026Sopenharmony_ci    }
197419ea8026Sopenharmony_ci
197519ea8026Sopenharmony_ci    // parse bench identifier, if any, cannibalizing the arg in the process
197619ea8026Sopenharmony_ci    for (; argc > optind; optind++) {
197719ea8026Sopenharmony_ci        bench_define_t *defines = NULL;
197819ea8026Sopenharmony_ci        size_t define_count = 0;
197919ea8026Sopenharmony_ci
198019ea8026Sopenharmony_ci        // parse name, can be suite or case
198119ea8026Sopenharmony_ci        char *name = argv[optind];
198219ea8026Sopenharmony_ci        char *defines_ = strchr(name, ':');
198319ea8026Sopenharmony_ci        if (defines_) {
198419ea8026Sopenharmony_ci            *defines_ = '\0';
198519ea8026Sopenharmony_ci            defines_ += 1;
198619ea8026Sopenharmony_ci        }
198719ea8026Sopenharmony_ci
198819ea8026Sopenharmony_ci        // remove optional path and .toml suffix
198919ea8026Sopenharmony_ci        char *slash = strrchr(name, '/');
199019ea8026Sopenharmony_ci        if (slash) {
199119ea8026Sopenharmony_ci            name = slash+1;
199219ea8026Sopenharmony_ci        }
199319ea8026Sopenharmony_ci
199419ea8026Sopenharmony_ci        size_t name_len = strlen(name);
199519ea8026Sopenharmony_ci        if (name_len > 5 && strcmp(&name[name_len-5], ".toml") == 0) {
199619ea8026Sopenharmony_ci            name[name_len-5] = '\0';
199719ea8026Sopenharmony_ci        }
199819ea8026Sopenharmony_ci
199919ea8026Sopenharmony_ci        if (defines_) {
200019ea8026Sopenharmony_ci            // parse defines
200119ea8026Sopenharmony_ci            while (true) {
200219ea8026Sopenharmony_ci                char *parsed;
200319ea8026Sopenharmony_ci                size_t d = leb16_parse(defines_, &parsed);
200419ea8026Sopenharmony_ci                intmax_t v = leb16_parse(parsed, &parsed);
200519ea8026Sopenharmony_ci                if (parsed == defines_) {
200619ea8026Sopenharmony_ci                    break;
200719ea8026Sopenharmony_ci                }
200819ea8026Sopenharmony_ci                defines_ = parsed;
200919ea8026Sopenharmony_ci
201019ea8026Sopenharmony_ci                if (d >= define_count) {
201119ea8026Sopenharmony_ci                    // align to power of two to avoid any superlinear growth
201219ea8026Sopenharmony_ci                    size_t ncount = 1 << lfs_npw2(d+1);
201319ea8026Sopenharmony_ci                    defines = realloc(defines,
201419ea8026Sopenharmony_ci                            ncount*sizeof(bench_define_t));
201519ea8026Sopenharmony_ci                    memset(defines+define_count, 0,
201619ea8026Sopenharmony_ci                            (ncount-define_count)*sizeof(bench_define_t));
201719ea8026Sopenharmony_ci                    define_count = ncount;
201819ea8026Sopenharmony_ci                }
201919ea8026Sopenharmony_ci                defines[d] = BENCH_LIT(v);
202019ea8026Sopenharmony_ci            }
202119ea8026Sopenharmony_ci        }
202219ea8026Sopenharmony_ci
202319ea8026Sopenharmony_ci        // append to identifier list
202419ea8026Sopenharmony_ci        *(bench_id_t*)mappend(
202519ea8026Sopenharmony_ci                (void**)&bench_ids,
202619ea8026Sopenharmony_ci                sizeof(bench_id_t),
202719ea8026Sopenharmony_ci                &bench_id_count,
202819ea8026Sopenharmony_ci                &bench_id_capacity) = (bench_id_t){
202919ea8026Sopenharmony_ci            .name = name,
203019ea8026Sopenharmony_ci            .defines = defines,
203119ea8026Sopenharmony_ci            .define_count = define_count,
203219ea8026Sopenharmony_ci        };
203319ea8026Sopenharmony_ci    }
203419ea8026Sopenharmony_ci
203519ea8026Sopenharmony_ci    // do the thing
203619ea8026Sopenharmony_ci    op();
203719ea8026Sopenharmony_ci
203819ea8026Sopenharmony_ci    // cleanup (need to be done for valgrind benching)
203919ea8026Sopenharmony_ci    bench_define_cleanup();
204019ea8026Sopenharmony_ci    if (bench_overrides) {
204119ea8026Sopenharmony_ci        for (size_t i = 0; i < bench_override_count; i++) {
204219ea8026Sopenharmony_ci            free((void*)bench_overrides[i].defines);
204319ea8026Sopenharmony_ci        }
204419ea8026Sopenharmony_ci        free((void*)bench_overrides);
204519ea8026Sopenharmony_ci    }
204619ea8026Sopenharmony_ci    if (bench_geometry_capacity) {
204719ea8026Sopenharmony_ci        free((void*)bench_geometries);
204819ea8026Sopenharmony_ci    }
204919ea8026Sopenharmony_ci    if (bench_id_capacity) {
205019ea8026Sopenharmony_ci        for (size_t i = 0; i < bench_id_count; i++) {
205119ea8026Sopenharmony_ci            free((void*)bench_ids[i].defines);
205219ea8026Sopenharmony_ci        }
205319ea8026Sopenharmony_ci        free((void*)bench_ids);
205419ea8026Sopenharmony_ci    }
205519ea8026Sopenharmony_ci}
2056