Add authenticated uploads and rename fossmark to fossbench
Uploads can now be attributed to a fossbench.net profile: set FOSSBENCH_TOKEN and the client sends it as a Bearer token, which the server auto-approves and links to the account. Anonymous, pending-review upload stays the default when no token is set. New --upload/--noupload flags skip the interactive prompt for scripted runs, and the client reports HTTP 401/422 distinctly from other failures. The token is never printed or logged. Also renames the project and its internal identifiers (FM_/fm_ macros and symbols, source filenames, binary output names) from fossmark to fossbench, matching the actual product name. The "fossmark_version" field in the upload payload is left as-is, since it's the server API's fixed contract field, not this client's own name.
This commit is contained in:
+51
-51
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* test_kernels.c - correctness checks for the fossmark assembly kernels
|
||||
* test_kernels.c - correctness checks for the fossbench assembly kernels
|
||||
*
|
||||
* The benchmark's own best-of-N run guards against non-determinism, but a
|
||||
* kernel can be perfectly deterministic and still wrong. This file is the
|
||||
@@ -13,7 +13,7 @@
|
||||
* matter how many copies run at once; a hidden global or a reentrancy bug would
|
||||
* survive a single-threaded run but fail here.
|
||||
*
|
||||
* Build: cc -O2 -pthread test_kernels.c fossmark.S -o test_kernels -lm
|
||||
* Build: cc -O2 -pthread test_kernels.c fossbench.S -o test_kernels -lm
|
||||
* Exit status is 0 iff every check passes.
|
||||
*/
|
||||
|
||||
@@ -26,36 +26,36 @@
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern uint64_t fm_int_math(uint64_t iters);
|
||||
extern uint64_t fm_fp_math(uint64_t iters);
|
||||
extern uint64_t fm_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fm_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
|
||||
extern uint64_t fm_chacha20(uint8_t *buf, uint64_t len,
|
||||
extern uint64_t fb_int_math(uint64_t iters);
|
||||
extern uint64_t fb_fp_math(uint64_t iters);
|
||||
extern uint64_t fb_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fb_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
|
||||
extern uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
|
||||
const uint8_t key[32], uint64_t rounds);
|
||||
extern uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fm_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fm_chase(void **ptrs, uint64_t steps);
|
||||
extern uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fb_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fb_chase(void **ptrs, uint64_t steps);
|
||||
|
||||
static int failures = 0;
|
||||
static int checks = 0;
|
||||
|
||||
/*
|
||||
* Concurrency plumbing. Each check runs on every core at once; the counters and
|
||||
* stdout are shared, so ok()/note() serialise on this lock. `fm_primary` is set
|
||||
* stdout are shared, so ok()/note() serialise on this lock. `fb_primary` is set
|
||||
* on exactly one thread per check (the one running on the main thread): it owns
|
||||
* the human-readable output so the "[ ok ]" lines and diagnostics appear once,
|
||||
* not once per core. Every thread still evaluates every assertion, so a failure
|
||||
* on any core - even a silent secondary - is reported and counted.
|
||||
*/
|
||||
static pthread_mutex_t io_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static __thread int fm_primary = 1;
|
||||
static long fm_ncores = 1;
|
||||
static __thread int fb_primary = 1;
|
||||
static long fb_ncores = 1;
|
||||
|
||||
static void ok(const char *what, int cond)
|
||||
{
|
||||
pthread_mutex_lock(&io_lock);
|
||||
if (fm_primary) {
|
||||
if (fb_primary) {
|
||||
checks++;
|
||||
if (cond) {
|
||||
printf(" [ ok ] %s\n", what);
|
||||
@@ -76,7 +76,7 @@ static void note(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!fm_primary)
|
||||
if (!fb_primary)
|
||||
return;
|
||||
pthread_mutex_lock(&io_lock);
|
||||
va_start(ap, fmt);
|
||||
@@ -86,19 +86,19 @@ static void note(const char *fmt, ...)
|
||||
}
|
||||
|
||||
/* Run `check` on every core simultaneously. The main thread is the primary;
|
||||
* fm_ncores-1 workers run the same check as silent secondaries. */
|
||||
static void *fm_worker(void *arg)
|
||||
* fb_ncores-1 workers run the same check as silent secondaries. */
|
||||
static void *fb_worker(void *arg)
|
||||
{
|
||||
void (*check)(void) = *(void (**)(void))arg;
|
||||
|
||||
fm_primary = 0;
|
||||
fb_primary = 0;
|
||||
check();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void parallel(void (*check)(void))
|
||||
{
|
||||
long extra = fm_ncores - 1;
|
||||
long extra = fb_ncores - 1;
|
||||
pthread_t *th = NULL;
|
||||
long i, spawned = 0;
|
||||
|
||||
@@ -107,7 +107,7 @@ static void parallel(void (*check)(void))
|
||||
if (th) {
|
||||
for (i = 0; i < extra; i++)
|
||||
if (pthread_create(&th[spawned], NULL,
|
||||
fm_worker, &check) == 0)
|
||||
fb_worker, &check) == 0)
|
||||
spawned++;
|
||||
}
|
||||
}
|
||||
@@ -186,39 +186,39 @@ static void check_int(void)
|
||||
{
|
||||
/* determinism and non-triviality: the checksum must be stable and
|
||||
* must actually change with the iteration count */
|
||||
uint64_t a = fm_int_math(1000);
|
||||
uint64_t b = fm_int_math(1000);
|
||||
uint64_t c = fm_int_math(2000);
|
||||
uint64_t a = fb_int_math(1000);
|
||||
uint64_t b = fb_int_math(1000);
|
||||
uint64_t c = fb_int_math(2000);
|
||||
|
||||
ok("int_math is deterministic", a == b);
|
||||
ok("int_math depends on iters", a != c);
|
||||
ok("int_math(0) is zero", fm_int_math(0) == 0);
|
||||
ok("int_math(0) is zero", fb_int_math(0) == 0);
|
||||
}
|
||||
|
||||
static void check_fp(void)
|
||||
{
|
||||
uint64_t a = fm_fp_math(1000);
|
||||
uint64_t b = fm_fp_math(1000);
|
||||
uint64_t a = fb_fp_math(1000);
|
||||
uint64_t b = fb_fp_math(1000);
|
||||
double da;
|
||||
|
||||
memcpy(&da, &a, sizeof da);
|
||||
ok("fp_math is deterministic", a == b);
|
||||
ok("fp_math result is finite", isfinite(da));
|
||||
ok("fp_math(0) is zero", fm_fp_math(0) == 0);
|
||||
ok("fp_math(0) is zero", fb_fp_math(0) == 0);
|
||||
}
|
||||
|
||||
static void check_primes(void)
|
||||
{
|
||||
enum { LIM = 1000000 };
|
||||
uint8_t *sieve = malloc(LIM);
|
||||
uint64_t got = fm_primes(LIM, sieve);
|
||||
uint64_t got = fb_primes(LIM, sieve);
|
||||
uint64_t ref = ref_prime_count(LIM);
|
||||
|
||||
note(" primes < %d: got %llu, expected %llu\n",
|
||||
LIM, (unsigned long long)got, (unsigned long long)ref);
|
||||
ok("primes matches reference sieve", got == ref);
|
||||
ok("primes < 10 == 4", fm_primes(10, sieve) == 4); /* 2,3,5,7 */
|
||||
ok("primes < 2 == 0", fm_primes(2, sieve) == 0);
|
||||
ok("primes < 10 == 4", fb_primes(10, sieve) == 4); /* 2,3,5,7 */
|
||||
ok("primes < 2 == 0", fb_primes(2, sieve) == 0);
|
||||
free(sieve);
|
||||
}
|
||||
|
||||
@@ -228,11 +228,11 @@ static void check_simd(void)
|
||||
uint64_t a, b;
|
||||
|
||||
memset(buf, 0xA5, 256);
|
||||
a = fm_simd(500, buf);
|
||||
a = fb_simd(500, buf);
|
||||
memset(buf, 0xA5, 256);
|
||||
b = fm_simd(500, buf);
|
||||
b = fb_simd(500, buf);
|
||||
ok("simd is deterministic", a == b);
|
||||
ok("simd(0) is zero", fm_simd(0, buf) == 0);
|
||||
ok("simd(0) is zero", fb_simd(0, buf) == 0);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
@@ -255,18 +255,18 @@ static void check_compress(void)
|
||||
src[i] = (uint8_t)(z ^ (z >> 31));
|
||||
}
|
||||
}
|
||||
incompressible = fm_compress(src, N, ht);
|
||||
incompressible = fb_compress(src, N, ht);
|
||||
|
||||
/* all-zero data is maximally compressible: it must shrink hugely */
|
||||
memset(src, 0, N);
|
||||
compressible = fm_compress(src, N, ht);
|
||||
compressible = fb_compress(src, N, ht);
|
||||
|
||||
note(" 64KiB random -> %llu bytes, 64KiB zeros -> %llu bytes\n",
|
||||
(unsigned long long)incompressible,
|
||||
(unsigned long long)compressible);
|
||||
ok("compress expands random data", incompressible >= N);
|
||||
ok("compress shrinks constant data", compressible < N / 10);
|
||||
ok("compress is deterministic", fm_compress(src, N, ht) == compressible);
|
||||
ok("compress is deterministic", fb_compress(src, N, ht) == compressible);
|
||||
free(src);
|
||||
free(ht);
|
||||
}
|
||||
@@ -308,7 +308,7 @@ static void check_crypto(void)
|
||||
for (i = 0; i < 32; i++)
|
||||
key[i] = (uint8_t)(i * 5 + 1);
|
||||
memset(buf, 0, sizeof buf); /* zeros -> raw keystream */
|
||||
fm_chacha20(buf, sizeof buf, key, 1);
|
||||
fb_chacha20(buf, sizeof buf, key, 1);
|
||||
|
||||
ref_chacha_block(ref0, key, 0, zero_nonce);
|
||||
ref_chacha_block(ref1, key, 1, zero_nonce);
|
||||
@@ -335,9 +335,9 @@ static void check_crypto(void)
|
||||
for (i = 0; i < 32; i++)
|
||||
k2[i] = (uint8_t)(i * 3);
|
||||
memcpy(work, plain, 128);
|
||||
fm_chacha20(work, 128, k2, 1);
|
||||
fb_chacha20(work, 128, k2, 1);
|
||||
ok("chacha20 actually changes data", memcmp(work, plain, 128) != 0);
|
||||
fm_chacha20(work, 128, k2, 1);
|
||||
fb_chacha20(work, 128, k2, 1);
|
||||
ok("chacha20 round-trips (XOR is involutive)",
|
||||
memcmp(work, plain, 128) == 0);
|
||||
}
|
||||
@@ -353,7 +353,7 @@ static void check_physics(void)
|
||||
bodies[0] = -1.0; bodies[3] = 1.0; /* body 0 at x=-1, mass 1 */
|
||||
bodies[8] = 1.0; bodies[11] = 1.0; /* body 1 at x=+1, mass 1 */
|
||||
|
||||
fm_physics(bodies, 2, 200);
|
||||
fb_physics(bodies, 2, 200);
|
||||
|
||||
/* velocities must be equal and opposite (Newton's third law) */
|
||||
total_p = bodies[4] + bodies[12]; /* vx0 + vx1 */
|
||||
@@ -393,7 +393,7 @@ static void check_sort(void)
|
||||
}
|
||||
memcpy(b, a, N * sizeof(uint32_t));
|
||||
|
||||
s = fm_sort(a, N);
|
||||
s = fb_sort(a, N);
|
||||
ok("sort produces sorted output", is_sorted(a, N));
|
||||
|
||||
/* multiset is preserved: sort the reference with the C library and
|
||||
@@ -404,12 +404,12 @@ static void check_sort(void)
|
||||
|
||||
/* already-sorted input stays sorted and gives the same checksum */
|
||||
{
|
||||
uint64_t s2 = fm_sort(a, N);
|
||||
uint64_t s2 = fb_sort(a, N);
|
||||
ok("sort is idempotent on sorted data",
|
||||
is_sorted(a, N) && s2 == s);
|
||||
}
|
||||
|
||||
ok("sort of empty array is zero", fm_sort(a, 0) == 0);
|
||||
ok("sort of empty array is zero", fb_sort(a, 0) == 0);
|
||||
free(a);
|
||||
free(b);
|
||||
}
|
||||
@@ -425,25 +425,25 @@ static void check_chase(void)
|
||||
nodes[2] = &nodes[3];
|
||||
nodes[3] = &nodes[0];
|
||||
|
||||
/* 4 hops from &nodes[0] returns to &nodes[0]; fm_chase returns the
|
||||
/* 4 hops from &nodes[0] returns to &nodes[0]; fb_chase returns the
|
||||
* final pointer minus the starting pointer, so a full loop gives 0 */
|
||||
ok("chase completes a full cycle", fm_chase(nodes, 4) == 0);
|
||||
ok("chase(0) is zero", fm_chase(nodes, 0) == 0);
|
||||
ok("chase completes a full cycle", fb_chase(nodes, 4) == 0);
|
||||
ok("chase(0) is zero", fb_chase(nodes, 0) == 0);
|
||||
/* one hop lands on &nodes[1], i.e. one pointer-width past the start */
|
||||
ok("chase single hop offset",
|
||||
fm_chase(nodes, 1) == (uint64_t)((char *)&nodes[1] - (char *)&nodes[0]));
|
||||
fb_chase(nodes, 1) == (uint64_t)((char *)&nodes[1] - (char *)&nodes[0]));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
long n = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
fm_ncores = n > 0 ? n : 1;
|
||||
fb_ncores = n > 0 ? n : 1;
|
||||
|
||||
printf("\nfossmark kernel correctness tests\n");
|
||||
printf("\nfossbench kernel correctness tests\n");
|
||||
printf("=================================\n");
|
||||
printf("running each check on %ld core%s in parallel\n\n",
|
||||
fm_ncores, fm_ncores == 1 ? "" : "s");
|
||||
fb_ncores, fb_ncores == 1 ? "" : "s");
|
||||
|
||||
printf("Integer Math:\n"); parallel(check_int);
|
||||
printf("Floating Point Math:\n"); parallel(check_fp);
|
||||
|
||||
Reference in New Issue
Block a user