Files
scriptc/tests/library-mode/traps/probe.c
Chris Tate e1f93475cb Deliver runtime-detected library traps structured, with codes, entry symbols, and profile overlays
- The library trap funnel (scr_library.c) now assembles every detected trap into the ratified 0x01 text 0x1F code 0x1F symbol [0x1F remediation] form: the baseline human line rides unchanged as field 0, and 0x01-led messages (facade-authored throws, the compile-time SC4012 contract trap) still pass verbatim.
- Trap kinds classify over the runtime's own message conventions into a new SC4013-SC4019 runtime code family (escaped exception, range, type, syntax, OOM, internal invariant, residual), documented beside SC4012 in the diagnostics registry; no arithmetic kind exists because no runtime site can fire one.
- Every generated entry prologue (init, reset, collect, each export, both emissions) records its external symbol in a SCR_LIB-only static slot the funnel reads, so the structured message names the trapping entry as the host linked it; profile teachings/remediations for the family land as a program-TU overlay table both backends emit identically.
- K5/K7/init-trap fixtures now pin the structured layout with the spec's parse rule (default text, code, entry symbol, fields=3 on a no-teachings profile), and new K12 tests pin the SC4014 teaching/remediation overlay plus the undeclared-code default; the verbatim and text-leads-printable pins stay.
- The default executable lane is byte-identical (signature-stripped cmp over a stash/rebuild pair); all library suites and the diagnostics suite are green on both emissions.
2026-07-23 19:57:38 -05:00

112 lines
3.6 KiB
C

/* K5/K6/K7 probe, mode-selected by argv[1]:
* trap — the runtime's range trap arrives as ONE structured
* trap-teaching message (this profile declares no
* teachings, so field 0 is the runtime's own line, the
* code is the detected-trap kind's, the symbol is the
* export the host called, and NO remediation field
* exists) plus a fault address, exactly once; the process
* survives (the sink longjmps to a host frame BELOW the
* entry — the conforming pattern; returning from the sink
* would abort)
* throw — an escaped exception reaches the sink structured the
* same way, its text the baseline "Uncaught ..." line
* poisoned — after a trap, any further entry aborts deterministically
* preregister — a trap before sink registration aborts
*/
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern void kp_init(void);
extern void kp_set_panic_sink(void (*fn)(void *, const uint8_t *, size_t, uint64_t), void *ctx);
extern double kp_boom(double i);
extern double kp_fail(const uint8_t *p, size_t len);
extern double kp_ok(double x);
static jmp_buf trap_jmp;
static int sink_calls = 0;
/* The spec's parse rule: baseline vs structured off msg[0]; structured
* messages split after the 0x01 marker on 0x1F into text/code/symbol/
* remediation. The trailing text_printable pins that the human text leads
* the buffer with a printable byte (plain-text degradation). */
static void show(const uint8_t *msg, size_t len) {
if (len == 0 || msg[0] != 0x01) { /* baseline: the whole buffer is the text */
printf("baseline printable=%d text=%.*s", len > 0 && msg[0] >= 0x20, (int)len, (const char *)msg);
return;
}
static const char *names[4] = {"text", "code", "symbol", "remediation"};
const uint8_t *p = msg + 1, *end = msg + len;
int fields = 0;
for (;;) {
const uint8_t *sep = memchr(p, 0x1f, (size_t)(end - p));
const uint8_t *stop = sep != NULL ? sep : end;
if (fields < 4) printf("%s=[%.*s]\n", names[fields], (int)(stop - p), (const char *)p);
fields++;
if (sep == NULL) break;
p = sep + 1;
}
printf("fields=%d text_printable=%d\n", fields, len > 1 && msg[1] >= 0x20);
}
static void sink(void *ctx, const uint8_t *msg, size_t len, uint64_t addr) {
(void)ctx;
sink_calls++;
printf("sink[%d]:\n", sink_calls);
show(msg, len);
printf("addr: %s\n", addr != 0 ? "nonzero" : "zero");
longjmp(trap_jmp, 1);
}
int main(int argc, char **argv) {
const char *mode = argc > 1 ? argv[1] : "trap";
if (strcmp(mode, "preregister") == 0) {
/* No sink registered: the trap must abort (K6). Nothing after the
* call may print. */
kp_init();
kp_boom(50);
printf("UNREACHABLE\n");
return 0;
}
kp_set_panic_sink(sink, NULL);
kp_init();
if (strcmp(mode, "trap") == 0) {
if (setjmp(trap_jmp) == 0) {
kp_boom(9);
printf("UNREACHABLE\n");
} else {
printf("survived, sink_calls=%d\n", sink_calls);
}
return 0;
}
if (strcmp(mode, "throw") == 0) {
if (setjmp(trap_jmp) == 0) {
kp_fail((const uint8_t *)"kaput", 5);
printf("UNREACHABLE\n");
} else {
printf("survived, sink_calls=%d\n", sink_calls);
}
return 0;
}
if (strcmp(mode, "poisoned") == 0) {
if (setjmp(trap_jmp) == 0) {
kp_boom(9);
} else {
printf("poisoned now\n");
fflush(stdout);
kp_ok(1); /* must abort — the library is poisoned */
printf("UNREACHABLE\n");
}
return 0;
}
fprintf(stderr, "unknown mode %s\n", mode);
return 2;
}