Port DoE trial-summary persistence (doe_region.c) onto the reshuffled branch
Local master had 9 unpushed commits building block-backed DoE persistence on Artemis's own disk (FABRIC-3.md SXXXV.3/.4/.12, designed and built 2026-09-17, never pushed) that this branch never inherited after the Tripod/kernel reshuffle diverged history. Ports the mechanism cleanly onto the current Hera/Artemis/Hestia shape: - doe_region.h/.c: growable, CRC-protected trial-summary ring in Artemis's top-of-device metadata fence, immediately past log_region.c's own ceiling (devblock_from_top 98-102). Same accessor/CRC discipline as log_region.c. - log_words.c: DOE-PERSIST-TRIAL and DOE-RESUME-COUNT kernel-only FORTH words. - multiuser-doe.4th: per-ISA tagging (MU-ISA-TAG!) and campaign resume support (MU-EXEC-CAMPAIGN now takes a start-idx). - per-isa-doe.4th (new): RUN-AMD64-CAMPAIGN/RUN-AARCH64-CAMPAIGN/ RUN-RISCV64-CAMPAIGN entry points, resuming from DOE-RESUME-COUNT. - extract_doe_region.py (new): host-side reader, CRC-verified, no filesystem assumptions -- reads the ring directly off a raw disk image. Deliberately not ported: the R/LaTeX report-generation pipeline (analyse_multiuser_doe.R, multiuser_doe_report.tex, build_multiuser_doe_report.sh) -- not needed to prove persistence works, and unscoped for this pass. Verified live on all three architectures: clean boot, word_count 530->532, zero UNKNOWN WORD. amd64 and riscv64 both live-exercised end-to-end (write via DOE-PERSIST-TRIAL, count via DOE-RESUME-COUNT, clean QEMU exit, then read back correctly with extract_doe_region.py against the raw disk image -- no host serial dependency, matching the real bare-metal requirement this campaign is rehearsing for). riscv64 finding (not a defect in this port, no fix applied): DOE-PERSIST-TRIAL failed 3/3 on a first attempt against a fresh disk image with "virtio_blk: vblk_io timed out". Root cause: disk/artemis.img is a sparse file (180KB allocated of a 1GB apparent size); the new DOE region (devblock_from_top 98-102) is genuinely virgin territory on a fresh image, and riscv64's slower TCG emulation missed the existing virtio_blk spin-bound budget (already documented as fragile, FABRIC-3.md 2026-09-09) on that first cold touch. Confirmed by isolation: a plain BLOCK/UPDATE at an already-warm LBN succeeded instantly, the pre-existing LOG-ERROR" (same meta-zone mechanism, an already-warmed offset range) also succeeded, and a reboot reusing the now-partially-warmed disk let DOE-PERSIST-TRIAL succeed immediately and verify correctly on read-back. Pre-existing environmental fragility, not a logic bug in the ported address computation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
6302dcb50e
commit
2d773ca923
+4
-2
@@ -470,7 +470,8 @@ LOADER_SRCS_BASE := \
|
|||||||
$(KERNEL_SRC)/heartbeat.c \
|
$(KERNEL_SRC)/heartbeat.c \
|
||||||
$(KERNEL_SRC)/homeblocks_sig.c \
|
$(KERNEL_SRC)/homeblocks_sig.c \
|
||||||
$(KERNEL_SRC)/artemis_sig.c \
|
$(KERNEL_SRC)/artemis_sig.c \
|
||||||
$(KERNEL_SRC)/log_region.c
|
$(KERNEL_SRC)/log_region.c \
|
||||||
|
$(KERNEL_SRC)/doe_region.c
|
||||||
|
|
||||||
LOADER_ASM := \
|
LOADER_ASM := \
|
||||||
$(KERNEL_SRC)/arch/$(ARCH)/boot.S \
|
$(KERNEL_SRC)/arch/$(ARCH)/boot.S \
|
||||||
@@ -528,7 +529,8 @@ KERNEL_SRCS_BASE := \
|
|||||||
$(KERNEL_SRC)/heartbeat.c \
|
$(KERNEL_SRC)/heartbeat.c \
|
||||||
$(KERNEL_SRC)/homeblocks_sig.c \
|
$(KERNEL_SRC)/homeblocks_sig.c \
|
||||||
$(KERNEL_SRC)/artemis_sig.c \
|
$(KERNEL_SRC)/artemis_sig.c \
|
||||||
$(KERNEL_SRC)/log_region.c
|
$(KERNEL_SRC)/log_region.c \
|
||||||
|
$(KERNEL_SRC)/doe_region.c
|
||||||
|
|
||||||
KERNEL_ASM := $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.S)
|
KERNEL_ASM := $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.S)
|
||||||
|
|
||||||
|
|||||||
+22
-19
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-22T22:29:26Z -->
|
<!-- Generated by mkcapsule --manifest 2026-09-23T14:57:37Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
|
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
|
||||||
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | yes |
|
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | yes |
|
||||||
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
|
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
|
||||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x22140588ee7c39e6` | yes |
|
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0xb4f6f0c2c582a4d4` | yes |
|
||||||
|
| `per-isa-doe.4th` | 5117, 5119 | `0x73f21c35b683e295` | yes |
|
||||||
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
|
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
|
||||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
|
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
|
||||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | yes |
|
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | yes |
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | yes |
|
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | yes |
|
||||||
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | yes |
|
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | yes |
|
||||||
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | yes |
|
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | yes |
|
||||||
| `zuse.4th` | 4016, 4017, 4018 | `0x490ded9be257a90b` | yes |
|
| `zuse.4th` | 4016, 4017, 4018 | `0xa14d5a7a88791cf6` | yes |
|
||||||
|
|
||||||
## Block Map (sorted by LBN)
|
## Block Map (sorted by LBN)
|
||||||
|
|
||||||
@@ -109,9 +110,9 @@
|
|||||||
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4016 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4016 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4017 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4017 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4018 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4018 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||||
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||||
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
||||||
@@ -332,18 +333,18 @@
|
|||||||
| 5001 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
| 5001 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||||
| 5002 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
| 5002 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||||
| 5043 | `workload-calib1.4th` | `0x3b9f2d17b554fabc` | ok |
|
| 5043 | `workload-calib1.4th` | `0x3b9f2d17b554fabc` | ok |
|
||||||
| 5044 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5044 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5045 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5045 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5046 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5046 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5047 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5047 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5048 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5048 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5049 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5049 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5050 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5050 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5051 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5051 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5052 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5052 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5053 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5053 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5054 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5054 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5055 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
| 5055 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
|
||||||
| 5056 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
|
| 5056 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
|
||||||
| 5057 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
|
| 5057 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
|
||||||
| 5058 | `workload-1-lite.4th` | `0x44a7a7e3176dcc8d` | ok |
|
| 5058 | `workload-1-lite.4th` | `0x44a7a7e3176dcc8d` | ok |
|
||||||
@@ -364,10 +365,12 @@
|
|||||||
| 5113 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
| 5113 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||||
| 5114 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
| 5114 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||||
| 5115 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
| 5115 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||||
|
| 5117 | `per-isa-doe.4th` | `0x73f21c35b683e295` | ok |
|
||||||
|
| 5119 | `per-isa-doe.4th` | `0x73f21c35b683e295` | ok |
|
||||||
|
|
||||||
## Conflicts
|
## Conflicts
|
||||||
|
|
||||||
None.
|
None.
|
||||||
|
|
||||||
---
|
---
|
||||||
*36 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
*37 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ CREATE MU-RUN-MATRIX MU-MAX-RUNS CELLS ALLOT
|
|||||||
: MU-MATRIX@ ( idx -- val ) CELLS MU-RUN-MATRIX + @ ;
|
: MU-MATRIX@ ( idx -- val ) CELLS MU-RUN-MATRIX + @ ;
|
||||||
VARIABLE MU-N-RUNS
|
VARIABLE MU-N-RUNS
|
||||||
: MU-INIT-MATRIX ( -- ) MU-N-RUNS @ 0 DO I I MU-MATRIX! LOOP ;
|
: MU-INIT-MATRIX ( -- ) MU-N-RUNS @ 0 DO I I MU-MATRIX! LOOP ;
|
||||||
|
CREATE MU-ISA-TAG-BUF 16 ALLOT VARIABLE MU-ISA-TAG-LEN
|
||||||
|
: MU-ISA-TAG! ( c-addr u -- )
|
||||||
|
16 MIN DUP MU-ISA-TAG-LEN !
|
||||||
|
MU-ISA-TAG-BUF SWAP CMOVE ;
|
||||||
|
|
||||||
Block 5050
|
Block 5050
|
||||||
VARIABLE MU-SW-I VARIABLE MU-SW-J
|
VARIABLE MU-SW-I VARIABLE MU-SW-J
|
||||||
@@ -105,12 +109,16 @@ VARIABLE MU-RUN-ID VARIABLE MU-CFG
|
|||||||
VARIABLE MU-REP VARIABLE MU-NW
|
VARIABLE MU-REP VARIABLE MU-NW
|
||||||
VARIABLE MU-MODE VARIABLE MU-NREPS
|
VARIABLE MU-MODE VARIABLE MU-NREPS
|
||||||
: MU-EMIT-TRIAL-MARKER ( -- )
|
: MU-EMIT-TRIAL-MARKER ( -- )
|
||||||
." MU-TRIAL run=" MU-RUN-ID @ .
|
." MU-TRIAL isa=" MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ TYPE
|
||||||
|
." run=" MU-RUN-ID @ .
|
||||||
." cfg=" MU-CFG @ .
|
." cfg=" MU-CFG @ .
|
||||||
." rep=" MU-REP @ .
|
." rep=" MU-REP @ .
|
||||||
." nw=" MU-NW @ .
|
." nw=" MU-NW @ .
|
||||||
." mode=" MU-MODE @ .
|
." mode=" MU-MODE @ .
|
||||||
." fail=" MU-FAIL-COUNT @ . CR ;
|
." fail=" MU-FAIL-COUNT @ . CR
|
||||||
|
MU-RUN-ID @ MU-CFG @ MU-REP @ MU-NW @
|
||||||
|
MU-MODE @ MU-FAIL-COUNT @
|
||||||
|
MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ DOE-PERSIST-TRIAL ;
|
||||||
|
|
||||||
Block 5052
|
Block 5052
|
||||||
: MU-BIRTH-ALL ( -- )
|
: MU-BIRTH-ALL ( -- )
|
||||||
@@ -153,14 +161,15 @@ Block 5054
|
|||||||
MU-RUN-ID @ 1+ MU-RUN-ID ! ;
|
MU-RUN-ID @ 1+ MU-RUN-ID ! ;
|
||||||
|
|
||||||
Block 5055
|
Block 5055
|
||||||
: MU-EXEC-CAMPAIGN ( seed n-reps -- )
|
: MU-EXEC-CAMPAIGN ( seed n-reps start-idx -- )
|
||||||
|
>R
|
||||||
DUP MU-NREPS !
|
DUP MU-NREPS !
|
||||||
MU-N-CFG * MU-N-RUNS !
|
MU-N-CFG * MU-N-RUNS !
|
||||||
SEED
|
SEED
|
||||||
MU-INIT-MATRIX MU-SHUFFLE
|
MU-INIT-MATRIX MU-SHUFFLE
|
||||||
HB-ON
|
HB-ON
|
||||||
0 MU-RUN-ID !
|
R@ MU-RUN-ID !
|
||||||
MU-N-RUNS @ 0 DO
|
MU-N-RUNS @ R> DO
|
||||||
I MU-CAMPAIGN-STEP
|
I MU-CAMPAIGN-STEP
|
||||||
LOOP
|
LOOP
|
||||||
HB-OFF
|
HB-OFF
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
Block 5117
|
||||||
|
( per-isa-doe.4th - per-ISA DoE driver. FABRIC-3 SXXXIV.4/ )
|
||||||
|
( SXXXV.5/.7: 6-cell factorial from multiuser-doe.4th's own )
|
||||||
|
( MU-EXEC-CAMPAIGN, unchanged. Adds: one fixed seed per ISA, )
|
||||||
|
( N=3 reps/cell=18 trials/ISA (ratified 2026-09-17, not the )
|
||||||
|
( N=2 SXXXV.5 recommended). Load multiuser-doe.4th FIRST. )
|
||||||
|
20260917 CONSTANT AMD64-SEED
|
||||||
|
20260918 CONSTANT AARCH64-SEED
|
||||||
|
20260919 CONSTANT RISCV64-SEED
|
||||||
|
18 CONSTANT PER-ISA-NREPS
|
||||||
|
|
||||||
|
Block 5119
|
||||||
|
( Per-ISA entry points, single word, no args -- run ONE per )
|
||||||
|
( boot (one-QEMU-at-a-time). Uses MU-ISA-TAG! (multiuser-doe. )
|
||||||
|
( 4th) -- redefining MU-EMIT-TRIAL-MARKER here never worked, )
|
||||||
|
( see FABRIC-3.md SXXXV.12 for why. )
|
||||||
|
: RUN-AMD64-CAMPAIGN ( -- )
|
||||||
|
S" amd64" MU-ISA-TAG!
|
||||||
|
AMD64-SEED PER-ISA-NREPS
|
||||||
|
S" amd64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
|
||||||
|
: RUN-AARCH64-CAMPAIGN ( -- )
|
||||||
|
S" aarch64" MU-ISA-TAG!
|
||||||
|
AARCH64-SEED PER-ISA-NREPS
|
||||||
|
S" aarch64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
|
||||||
|
: RUN-RISCV64-CAMPAIGN ( -- )
|
||||||
|
S" riscv64" MU-ISA-TAG!
|
||||||
|
RISCV64-SEED PER-ISA-NREPS
|
||||||
|
S" riscv64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
StarForth — Steady-State Virtual Machine Runtime
|
||||||
|
|
||||||
|
Copyright (c) 2023–2025 Robert A. James
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the StarForth project.
|
||||||
|
|
||||||
|
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
|
||||||
|
You may obtain a copy of the License at:
|
||||||
|
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||||
|
|
||||||
|
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
express or implied, including but not limited to the warranties of
|
||||||
|
merchantability, fitness for a particular purpose, and noninfringement.
|
||||||
|
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* doe_region.h - Growable per-trial DoE-campaign-summary ring on Artemis's
|
||||||
|
* own disk (FABRIC-3.md §XXXV.3/.4, built 2026-09-17)
|
||||||
|
*
|
||||||
|
* Records one summary per completed multiuser-doe.4th/per-isa-doe.4th trial
|
||||||
|
* so a campaign's pass/fail and top-line physics picture survives past the
|
||||||
|
* QEMU serial log -- §XXXV.1 identified that doe_log.c's per-tick rows
|
||||||
|
* (console_puts()-only) cannot survive real bare-metal boot at all, and
|
||||||
|
* raw per-tick mirroring to block storage was ruled out entirely on size
|
||||||
|
* grounds (§XXXV.2: 516 MB for 8 trials vs. a ~1 GiB device). This region
|
||||||
|
* persists trial SUMMARIES only (v1, §XXXV.4) -- exactly the fields
|
||||||
|
* MU-EMIT-TRIAL-MARKER already computes, plus three trial-end aggregate
|
||||||
|
* physics reads (vm_physics_fleet_heat_sum(), vm_physics_conserved(),
|
||||||
|
* sk_vm_switch_signal_switch_count()) -- not a raw tick mirror.
|
||||||
|
*
|
||||||
|
* Same fence/accessor discipline log_region.h already established: lives
|
||||||
|
* in Artemis's top-of-device system-metadata fence, reached via
|
||||||
|
* block_subsystem.h's blk_meta_zone_read()/write() (devblock_from_top
|
||||||
|
* addressing), same growable-ring control-header shape (magic/version/CRC,
|
||||||
|
* devblocks/head_slot/tail_slot/record_count). A SEPARATE region from
|
||||||
|
* log_region.c's own -- different shape (structured numeric fields, not
|
||||||
|
* free text), different growth ceiling, and isolation so a runaway DoE
|
||||||
|
* region can never crowd out real log persistence (§XXXV.3's own stated
|
||||||
|
* reasoning, reused verbatim here).
|
||||||
|
*
|
||||||
|
* Fixed devblock_from_top allocation, immediately past log_region.c's own
|
||||||
|
* ceiling (which occupies [65, 65+1+LOG_REGION_MAX_DEVBLOCKS-1] =
|
||||||
|
* [65, 97] at full growth):
|
||||||
|
* 98 -- this region's control header (DOE_REGION_DEVBLOCK_FROM_TOP_BASE)
|
||||||
|
* 99-102 -- this region's slot devblocks, growable up to DOE_REGION_MAX_DEVBLOCKS
|
||||||
|
*
|
||||||
|
* Slot granularity is small (DOE_SLOT_SIZE, 64 bytes) relative to
|
||||||
|
* log_slot_t's 1024 -- a trial summary is a handful of fixed numeric
|
||||||
|
* fields plus an 8-byte ISA tag, nothing free-text-sized. 64 slots/devblock
|
||||||
|
* x DOE_REGION_INITIAL_DEVBLOCKS(1) = 64 already exceeds one full 3-ISA
|
||||||
|
* campaign's 54 trials (§XXXV.4); the growth ceiling (4 devblocks = 256
|
||||||
|
* slots) is headroom for repeated campaigns, not a sizing risk.
|
||||||
|
*
|
||||||
|
* No priority-eviction logic (unlike log_region.c's LOG_REGION_PROTECTED_
|
||||||
|
* MAX_LEVEL, §XXXII.4) -- there is no level concept for a trial summary;
|
||||||
|
* plain FIFO eviction of the oldest record when the ring is full AND
|
||||||
|
* already at the growth ceiling, which in practice will not be reached by
|
||||||
|
* any campaign shape this project has run or ratified.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STARKERNEL_DOE_REGION_H
|
||||||
|
#define STARKERNEL_DOE_REGION_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "starkernel/log_region.h" /* LOG_REGION_DEVBLOCK_FROM_TOP_BASE, LOG_REGION_MAX_DEVBLOCKS -- fence math only */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Fence allocation
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
#define DOE_REGION_DEVBLOCK_FROM_TOP_BASE \
|
||||||
|
(LOG_REGION_DEVBLOCK_FROM_TOP_BASE + 1u + LOG_REGION_MAX_DEVBLOCKS) /* 98 */
|
||||||
|
#define DOE_REGION_INITIAL_DEVBLOCKS 1u /* slot devblocks at first use, excludes control header */
|
||||||
|
#define DOE_REGION_GROWTH_INCREMENT 1u
|
||||||
|
#define DOE_REGION_MAX_DEVBLOCKS 4u /* ceiling -- devblock_from_top stays within [99,102] */
|
||||||
|
|
||||||
|
#define DOE_SLOT_SIZE 64u
|
||||||
|
#define DOE_SLOTS_PER_DEVBLOCK (4096u / DOE_SLOT_SIZE) /* 64 */
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* doe_region_ctrl_t - ring control header, one devblock at
|
||||||
|
* DOE_REGION_DEVBLOCK_FROM_TOP_BASE. Same shape as log_region_ctrl_t.
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
#define DOE_REGION_MAGIC 0x44454F44ull /* 'DOED' */
|
||||||
|
#define DOE_REGION_VERSION_0 0
|
||||||
|
|
||||||
|
#define DOE_REGION_PACK(ver) \
|
||||||
|
(DOE_REGION_MAGIC | ((uint64_t)(ver) << 32))
|
||||||
|
#define DOE_REGION_GET_MAGIC(m) ((uint32_t)((m) & 0xFFFFFFFFull))
|
||||||
|
#define DOE_REGION_GET_VERSION(m) ((uint8_t)(((m) >> 32) & 0xFF))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t magic; /* DOE_REGION_PACK(...) */
|
||||||
|
uint32_t devblocks; /* current slot-area size, in devblocks (excludes this header) */
|
||||||
|
uint32_t head_slot; /* index of the oldest live record */
|
||||||
|
uint32_t tail_slot; /* index where the NEXT record will be written */
|
||||||
|
uint32_t record_count; /* live records, <= devblocks * DOE_SLOTS_PER_DEVBLOCK */
|
||||||
|
uint64_t hdr_crc; /* covers every field above this one */
|
||||||
|
uint8_t _pad[4096 - (8 + 4 + 4 + 4 + 4 + 8)];
|
||||||
|
} doe_region_ctrl_t;
|
||||||
|
|
||||||
|
typedef char doe_region_ctrl_size_check[(sizeof(doe_region_ctrl_t) == 4096) ? 1 : -1];
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* doe_trial_slot_t - one trial summary record, exactly DOE_SLOT_SIZE bytes.
|
||||||
|
* Field order: uint64_t pair first (natural 8-byte alignment at offsets
|
||||||
|
* 0/8), then uint32_t fields (4-byte aligned from offset 16 on), then the
|
||||||
|
* ISA tag, then an EXPLICIT alignment field (_align_pad) so slot_crc lands
|
||||||
|
* on an offset the compiler needs no implicit padding to reach -- same
|
||||||
|
* "every field lands on its natural alignment with zero compiler-inserted
|
||||||
|
* padding" discipline log_slot_t's own doc comment states. Leaving this to
|
||||||
|
* an implicit trailing _pad array (as an earlier draft did) put slot_crc
|
||||||
|
* right after an 8-byte char array at a non-8-aligned offset, so the
|
||||||
|
* compiler silently inserted its own padding there instead and the
|
||||||
|
* hand-computed trailing pad size no longer matched sizeof() -- caught by
|
||||||
|
* this file's own static size-check failing to compile.
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
#define DOE_SLOT_ISA_MAX 8u /* NUL-padded, e.g. "amd64", "aarch64", "riscv64" */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t fleet_k_q48; /* vm_physics_fleet_heat_sum() at trial end */
|
||||||
|
uint64_t switch_count_cumulative; /* sk_vm_switch_signal_switch_count() at trial end */
|
||||||
|
uint32_t run_id;
|
||||||
|
uint32_t cfg;
|
||||||
|
uint32_t rep;
|
||||||
|
uint32_t nw;
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t fail_count;
|
||||||
|
uint32_t fleet_conserved; /* vm_physics_conserved() at trial end, 0/1 */
|
||||||
|
char isa[DOE_SLOT_ISA_MAX];
|
||||||
|
uint32_t _align_pad; /* explicit -- brings the next field to offset 56 (8-aligned) */
|
||||||
|
uint64_t slot_crc; /* covers every field above this one (§XXXV.12) --
|
||||||
|
* a devblock holds DOE_SLOTS_PER_DEVBLOCK=64 slots via
|
||||||
|
* one read-modify-write, so a crash mid-write to ANY
|
||||||
|
* slot in a devblock can corrupt its siblings; this is
|
||||||
|
* how a reader tells a genuine record from corruption. */
|
||||||
|
} doe_trial_slot_t;
|
||||||
|
|
||||||
|
typedef char doe_trial_slot_size_check[(sizeof(doe_trial_slot_t) == DOE_SLOT_SIZE) ? 1 : -1];
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* API
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* doe_region_append - Write one trial-summary record to the ring, growing
|
||||||
|
* it (within DOE_REGION_MAX_DEVBLOCKS) or evicting the oldest record (ring
|
||||||
|
* full and already at the growth ceiling) as needed. Initializes the ring
|
||||||
|
* on first use (control header blank).
|
||||||
|
*
|
||||||
|
* @param run_id, cfg, rep, nw, mode, fail_count Same fields
|
||||||
|
* MU-EMIT-TRIAL-MARKER already prints (multiuser-doe.4th Block 5051).
|
||||||
|
* @param fleet_k_q48, fleet_conserved, switch_count_cumulative Trial-end
|
||||||
|
* aggregate physics reads (§XXXV.4).
|
||||||
|
* @param isa ISA tag, e.g. "amd64" (may be empty for the pre-per-isa-
|
||||||
|
* doe.4th single-ISA campaign shape).
|
||||||
|
* @param isa_len Length of isa (truncated to DOE_SLOT_ISA_MAX-1).
|
||||||
|
* @return 0 on success, -1 on any read/write failure (ring left however
|
||||||
|
* the failed operation left it -- blk_meta_zone_write() itself
|
||||||
|
* never partially writes a devblock).
|
||||||
|
*/
|
||||||
|
int doe_region_append(uint32_t run_id, uint32_t cfg, uint32_t rep, uint32_t nw,
|
||||||
|
uint32_t mode, uint32_t fail_count,
|
||||||
|
uint64_t fleet_k_q48, uint32_t fleet_conserved,
|
||||||
|
uint64_t switch_count_cumulative,
|
||||||
|
const char *isa, uint32_t isa_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* doe_region_count_for_isa - Count CRC-valid, live records in the ring
|
||||||
|
* whose isa tag matches the given string (§XXXV.12, campaign crash-resume).
|
||||||
|
* A record that fails its slot_crc check does not count -- the caller
|
||||||
|
* (DOE-RESUME-COUNT) treats a corrupted slot as "not actually done," so
|
||||||
|
* that trial safely re-runs rather than being silently skipped. Returns 0
|
||||||
|
* if the ring has never been initialized (matching doe_region_append()'s
|
||||||
|
* own "blank == not yet initialized" discipline), never negative.
|
||||||
|
*
|
||||||
|
* @param isa ISA tag to match, e.g. "amd64".
|
||||||
|
* @param isa_len Length of isa (truncated to DOE_SLOT_ISA_MAX-1).
|
||||||
|
* @return Count of matching, CRC-valid records (0 or more).
|
||||||
|
*/
|
||||||
|
uint32_t doe_region_count_for_isa(const char *isa, uint32_t isa_len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* STARKERNEL_DOE_REGION_H */
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable
+256
@@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
extract_doe_region.py -- spool the DOE-PERSIST-TRIAL binary ring off
|
||||||
|
disk/artemis.img into a CSV, for the multiuser/multitasking DoE campaign
|
||||||
|
report (FABRIC-3.md §XXXV.10).
|
||||||
|
|
||||||
|
Reads the SAME devblock_from_top-addressed ring doe_region.c writes
|
||||||
|
(include/starkernel/doe_region.h has the authoritative layout: control
|
||||||
|
header at devblock_from_top=98, slot devblocks growable from 99, 64-byte
|
||||||
|
doe_trial_slot_t records, 64 slots/devblock). Two access modes:
|
||||||
|
|
||||||
|
--img PATH Read the image file directly (default). No privilege
|
||||||
|
needed, works on a plain regular file. This is what
|
||||||
|
"reading the drive" reduces to when the image is
|
||||||
|
just a file, which is the normal QEMU-dev case.
|
||||||
|
|
||||||
|
--loop PATH Attach PATH via losetup first (needs sudo), read the
|
||||||
|
resulting /dev/loopN node, then detach it again on
|
||||||
|
exit -- the literal "mount the virtual drive on a
|
||||||
|
loop device" step for when this runs against a real
|
||||||
|
block device or a copy pulled off real hardware. The
|
||||||
|
ring is raw binary at a fixed byte offset, not a
|
||||||
|
filesystem -- there is nothing to `mount` in the
|
||||||
|
POSIX-filesystem sense, so this attaches the loop
|
||||||
|
device and reads it exactly like a regular file.
|
||||||
|
|
||||||
|
Either mode is read-only and never modifies the image. Safe to run
|
||||||
|
against a live campaign's artemis.img (the ring's control header CRC
|
||||||
|
means a torn read mid-write is simply rejected as "ring not yet
|
||||||
|
initialized" by the same discipline doe_region.c itself uses -- see
|
||||||
|
doe_region_ctrl_read()); worst case is missing the very latest record
|
||||||
|
until the next run of this script.
|
||||||
|
|
||||||
|
CRC verification (§XXXV.12): both the control header and each individual
|
||||||
|
trial slot carry a CRC-64/XZ checksum (same polynomial and algorithm as
|
||||||
|
src/block_subsystem.c's compute_crc64() -- reimplemented here in Python,
|
||||||
|
not guessed at; verified bit-for-bit against a live kernel-written
|
||||||
|
record). A bad header CRC is treated as "ring not yet initialized" (same
|
||||||
|
as a bad magic/version). A bad slot CRC means that record is skipped and
|
||||||
|
reported, not silently included as if it were real data -- since
|
||||||
|
DOE_SLOTS_PER_DEVBLOCK=64 slots share one devblock via a read-modify-
|
||||||
|
write, a crash mid-write to any slot can corrupt its siblings too.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import csv
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
DEVBLOCK_SIZE = 4096
|
||||||
|
|
||||||
|
# Mirrors include/starkernel/doe_region.h and include/starkernel/log_region.h
|
||||||
|
ARTEMIS_SIG_DEVBLOCK_FROM_TOP = 64
|
||||||
|
LOG_REGION_DEVBLOCK_FROM_TOP_BASE = ARTEMIS_SIG_DEVBLOCK_FROM_TOP + 1 # 65
|
||||||
|
LOG_REGION_MAX_DEVBLOCKS = 32
|
||||||
|
DOE_REGION_DEVBLOCK_FROM_TOP_BASE = (
|
||||||
|
LOG_REGION_DEVBLOCK_FROM_TOP_BASE + 1 + LOG_REGION_MAX_DEVBLOCKS
|
||||||
|
) # 98
|
||||||
|
|
||||||
|
DOE_SLOT_SIZE = 64
|
||||||
|
DOE_SLOTS_PER_DEVBLOCK = DEVBLOCK_SIZE // DOE_SLOT_SIZE # 64
|
||||||
|
DOE_REGION_MAGIC = 0x44454F44 # 'DOED'
|
||||||
|
DOE_REGION_VERSION_0 = 0
|
||||||
|
|
||||||
|
# doe_region_ctrl_t: magic(u64) devblocks(u32) head_slot(u32) tail_slot(u32)
|
||||||
|
# record_count(u32) hdr_crc(u64), little-endian, rest is padding to 4096.
|
||||||
|
CTRL_FMT = "<QIIIIQ"
|
||||||
|
CTRL_SIZE = struct.calcsize(CTRL_FMT)
|
||||||
|
CTRL_CRC_SPAN = struct.calcsize("<QIIII") # everything before hdr_crc
|
||||||
|
|
||||||
|
# doe_trial_slot_t: fleet_k_q48(u64) switch_count_cumulative(u64)
|
||||||
|
# run_id(u32) cfg(u32) rep(u32) nw(u32) mode(u32) fail_count(u32)
|
||||||
|
# fleet_conserved(u32) isa(8s) _align_pad(u32, explicit -- see
|
||||||
|
# doe_region.h's own comment on why: brings slot_crc to an 8-aligned
|
||||||
|
# offset without relying on implicit compiler padding) slot_crc(u64).
|
||||||
|
SLOT_FMT = "<QQIIIIIII8sIQ"
|
||||||
|
SLOT_SIZE = struct.calcsize(SLOT_FMT)
|
||||||
|
SLOT_CRC_SPAN = struct.calcsize("<QQIIIIIII8sI") # everything before slot_crc
|
||||||
|
assert SLOT_SIZE == DOE_SLOT_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
# CRC-64/XZ, same polynomial/algorithm as compute_crc64() in
|
||||||
|
# src/block_subsystem.c (crc64_init()'s poly=0x42F0E1EBA9EA3693, table-
|
||||||
|
# driven, init/final XOR 0xFFFFFFFFFFFFFFFF) -- reimplemented here so
|
||||||
|
# host-side verification genuinely matches the kernel side, not guessed.
|
||||||
|
_CRC64_POLY = 0x42F0E1EBA9EA3693
|
||||||
|
_CRC64_TABLE = None
|
||||||
|
|
||||||
|
|
||||||
|
def _crc64_table():
|
||||||
|
global _CRC64_TABLE
|
||||||
|
if _CRC64_TABLE is None:
|
||||||
|
table = []
|
||||||
|
for i in range(256):
|
||||||
|
crc = i
|
||||||
|
for _ in range(8):
|
||||||
|
crc = (crc >> 1) ^ _CRC64_POLY if (crc & 1) else (crc >> 1)
|
||||||
|
table.append(crc & 0xFFFFFFFFFFFFFFFF)
|
||||||
|
_CRC64_TABLE = table
|
||||||
|
return _CRC64_TABLE
|
||||||
|
|
||||||
|
|
||||||
|
def compute_crc64(data: bytes) -> int:
|
||||||
|
table = _crc64_table()
|
||||||
|
crc = 0xFFFFFFFFFFFFFFFF
|
||||||
|
for byte in data:
|
||||||
|
idx = (crc ^ byte) & 0xFF
|
||||||
|
crc = table[idx] ^ (crc >> 8)
|
||||||
|
return crc ^ 0xFFFFFFFFFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
def dft_to_offset(devblock_from_top: int, total_devblocks: int) -> int:
|
||||||
|
idx = total_devblocks - 1 - devblock_from_top
|
||||||
|
if idx < 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"devblock_from_top={devblock_from_top} exceeds total_devblocks={total_devblocks}"
|
||||||
|
)
|
||||||
|
return idx * DEVBLOCK_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def loop_attach(img_path: Path):
|
||||||
|
"""Attach img_path via losetup (read-only), yield the /dev/loopN path,
|
||||||
|
detach on exit. Requires sudo -- the literal 'mount on a loop device'
|
||||||
|
step; nothing here is a filesystem mount."""
|
||||||
|
result = subprocess.run(
|
||||||
|
["sudo", "losetup", "--find", "--show", "--read-only", str(img_path)],
|
||||||
|
capture_output=True, text=True, check=True,
|
||||||
|
)
|
||||||
|
loop_dev = result.stdout.strip()
|
||||||
|
print(f" attached {img_path} -> {loop_dev}", file=sys.stderr)
|
||||||
|
try:
|
||||||
|
yield loop_dev
|
||||||
|
finally:
|
||||||
|
subprocess.run(["sudo", "losetup", "--detach", loop_dev], check=False)
|
||||||
|
print(f" detached {loop_dev}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def read_ring(dev_path: str):
|
||||||
|
"""Returns a list of dicts, one per live trial record, in ring order
|
||||||
|
(oldest first). Empty list if the ring has never been written."""
|
||||||
|
size = Path(dev_path).stat().st_size if not dev_path.startswith("/dev/") else None
|
||||||
|
if size is None:
|
||||||
|
with open(dev_path, "rb") as f:
|
||||||
|
f.seek(0, 2)
|
||||||
|
size = f.tell()
|
||||||
|
|
||||||
|
total_devblocks = size // DEVBLOCK_SIZE
|
||||||
|
if total_devblocks <= DOE_REGION_DEVBLOCK_FROM_TOP_BASE:
|
||||||
|
raise ValueError(
|
||||||
|
f"image too small ({size} bytes, {total_devblocks} devblocks) "
|
||||||
|
f"for the DOE region fence (needs > {DOE_REGION_DEVBLOCK_FROM_TOP_BASE})"
|
||||||
|
)
|
||||||
|
|
||||||
|
ctrl_off = dft_to_offset(DOE_REGION_DEVBLOCK_FROM_TOP_BASE, total_devblocks)
|
||||||
|
with open(dev_path, "rb") as f:
|
||||||
|
f.seek(ctrl_off)
|
||||||
|
ctrl_raw = f.read(DEVBLOCK_SIZE)
|
||||||
|
|
||||||
|
magic, devblocks, head_slot, tail_slot, record_count, hdr_crc = struct.unpack_from(
|
||||||
|
CTRL_FMT, ctrl_raw, 0
|
||||||
|
)
|
||||||
|
magic_lo = magic & 0xFFFFFFFF
|
||||||
|
version = (magic >> 32) & 0xFF
|
||||||
|
if magic_lo != DOE_REGION_MAGIC or version != DOE_REGION_VERSION_0:
|
||||||
|
print(" DOE region not yet initialized (no trials persisted yet)", file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
want_hdr_crc = compute_crc64(ctrl_raw[:CTRL_CRC_SPAN])
|
||||||
|
if want_hdr_crc != hdr_crc:
|
||||||
|
print(" DOE region control header CRC mismatch -- treating as "
|
||||||
|
"'not yet initialized' (same discipline doe_region_ctrl_read() "
|
||||||
|
"uses on the kernel side)", file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
total_slots = devblocks * DOE_SLOTS_PER_DEVBLOCK
|
||||||
|
records = []
|
||||||
|
corrupt = 0
|
||||||
|
with open(dev_path, "rb") as f:
|
||||||
|
idx = head_slot
|
||||||
|
for _ in range(record_count):
|
||||||
|
slot_devblock_dft = DOE_REGION_DEVBLOCK_FROM_TOP_BASE + 1 + (idx // DOE_SLOTS_PER_DEVBLOCK)
|
||||||
|
slot_off_in_devblock = (idx % DOE_SLOTS_PER_DEVBLOCK) * DOE_SLOT_SIZE
|
||||||
|
blk_off = dft_to_offset(slot_devblock_dft, total_devblocks)
|
||||||
|
f.seek(blk_off + slot_off_in_devblock)
|
||||||
|
raw = f.read(SLOT_SIZE)
|
||||||
|
(fleet_k_q48, switch_count, run_id, cfg, rep, nw, mode,
|
||||||
|
fail_count, fleet_conserved, isa_raw, _align_pad, slot_crc) = struct.unpack(SLOT_FMT, raw)
|
||||||
|
|
||||||
|
want_slot_crc = compute_crc64(raw[:SLOT_CRC_SPAN])
|
||||||
|
if want_slot_crc != slot_crc:
|
||||||
|
corrupt += 1
|
||||||
|
print(f" slot {idx} CRC mismatch (devblock-sharing torn write?) -- skipped", file=sys.stderr)
|
||||||
|
idx = (idx + 1) % total_slots
|
||||||
|
continue
|
||||||
|
|
||||||
|
isa = isa_raw.split(b"\x00", 1)[0].decode("ascii", errors="replace")
|
||||||
|
records.append({
|
||||||
|
"isa": isa,
|
||||||
|
"run_id": run_id,
|
||||||
|
"cfg": cfg,
|
||||||
|
"rep": rep,
|
||||||
|
"nw": nw,
|
||||||
|
"mode": mode,
|
||||||
|
"fail_count": fail_count,
|
||||||
|
"fleet_k_q48": fleet_k_q48,
|
||||||
|
"fleet_conserved": fleet_conserved,
|
||||||
|
"switch_count_cumulative": switch_count,
|
||||||
|
})
|
||||||
|
idx = (idx + 1) % total_slots
|
||||||
|
|
||||||
|
print(f" ring: devblocks={devblocks} head={head_slot} tail={tail_slot} "
|
||||||
|
f"record_count={record_count} -> {len(records)} valid records read"
|
||||||
|
+ (f", {corrupt} corrupt slot(s) skipped" if corrupt else ""),
|
||||||
|
file=sys.stderr)
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument("--img", default="disk/artemis.img", help="Path to the disk image (default: disk/artemis.img)")
|
||||||
|
ap.add_argument("--loop", action="store_true", help="Attach --img via losetup first (needs sudo)")
|
||||||
|
ap.add_argument("-o", "--out", default=None, help="Output CSV path (default: stdout)")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
img_path = Path(args.img)
|
||||||
|
if not img_path.exists():
|
||||||
|
print(f"extract_doe_region: image not found: {img_path}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.loop:
|
||||||
|
with loop_attach(img_path) as loop_dev:
|
||||||
|
records = read_ring(loop_dev)
|
||||||
|
else:
|
||||||
|
records = read_ring(str(img_path))
|
||||||
|
|
||||||
|
fieldnames = ["isa", "run_id", "cfg", "rep", "nw", "mode", "fail_count",
|
||||||
|
"fleet_k_q48", "fleet_conserved", "switch_count_cumulative"]
|
||||||
|
|
||||||
|
out_f = open(args.out, "w", newline="") if args.out else sys.stdout
|
||||||
|
try:
|
||||||
|
writer = csv.DictWriter(out_f, fieldnames=fieldnames)
|
||||||
|
writer.writeheader()
|
||||||
|
for rec in records:
|
||||||
|
writer.writerow(rec)
|
||||||
|
finally:
|
||||||
|
if args.out:
|
||||||
|
out_f.close()
|
||||||
|
print(f" wrote {len(records)} rows -> {args.out}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
StarForth — Steady-State Virtual Machine Runtime
|
||||||
|
|
||||||
|
Copyright (c) 2023–2025 Robert A. James
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the StarForth project.
|
||||||
|
|
||||||
|
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
|
||||||
|
You may obtain a copy of the License at:
|
||||||
|
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||||
|
|
||||||
|
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
express or implied, including but not limited to the warranties of
|
||||||
|
merchantability, fitness for a particular purpose, and noninfringement.
|
||||||
|
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* doe_region.c - Growable per-trial DoE-campaign-summary ring on Artemis's
|
||||||
|
* disk. See starkernel/doe_region.h for the format/interface design.
|
||||||
|
*
|
||||||
|
* Mirrors log_region.c's own read/write/grow/evict shape exactly (same
|
||||||
|
* blk_meta_zone_read()/write() accessor, same control-header CRC
|
||||||
|
* discipline), minus the priority-eviction logic that doesn't apply here
|
||||||
|
* (no level concept for a trial summary -- plain FIFO eviction only).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "starkernel/doe_region.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "block_subsystem.h" /* blk_meta_zone_read/write, compute_crc64 */
|
||||||
|
|
||||||
|
static uint64_t doe_region_ctrl_compute_crc(const doe_region_ctrl_t *ctrl) {
|
||||||
|
size_t crc_span = offsetof(doe_region_ctrl_t, hdr_crc);
|
||||||
|
return compute_crc64((const uint8_t *)ctrl, crc_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* §XXXV.12: a devblock holds DOE_SLOTS_PER_DEVBLOCK slots via one
|
||||||
|
* read-modify-write, so a crash mid-write can corrupt siblings of the
|
||||||
|
* slot actually being written. This is how a reader (doe_region_
|
||||||
|
* count_for_isa(), and any future host-side extractor) tells a genuine
|
||||||
|
* record from that corruption -- same offsetof-span discipline as the
|
||||||
|
* control header's own CRC above. */
|
||||||
|
static uint64_t doe_region_slot_compute_crc(const doe_trial_slot_t *slot) {
|
||||||
|
size_t crc_span = offsetof(doe_trial_slot_t, slot_crc);
|
||||||
|
return compute_crc64((const uint8_t *)slot, crc_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads the control header; returns 0 and *out populated only if magic,
|
||||||
|
* version, and CRC all check out. Any other result (blank, foreign,
|
||||||
|
* corrupt, or a read failure) is treated identically by the caller: "not
|
||||||
|
* yet initialized," matching log_region_ctrl_read()'s own discipline. */
|
||||||
|
static int doe_region_ctrl_read(doe_region_ctrl_t *out) {
|
||||||
|
if (blk_meta_zone_read(DOE_REGION_DEVBLOCK_FROM_TOP_BASE, (uint8_t *)out) != 0) return -1;
|
||||||
|
if (DOE_REGION_GET_MAGIC(out->magic) != (uint32_t)(DOE_REGION_MAGIC & 0xFFFFFFFFull)) return -1;
|
||||||
|
if (DOE_REGION_GET_VERSION(out->magic) != DOE_REGION_VERSION_0) return -1;
|
||||||
|
uint64_t want_crc = doe_region_ctrl_compute_crc(out);
|
||||||
|
if (want_crc != out->hdr_crc) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int doe_region_ctrl_write(doe_region_ctrl_t *ctrl) {
|
||||||
|
ctrl->hdr_crc = doe_region_ctrl_compute_crc(ctrl);
|
||||||
|
return blk_meta_zone_write(DOE_REGION_DEVBLOCK_FROM_TOP_BASE, (const uint8_t *)ctrl) == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doe_region_ctrl_init_fresh(doe_region_ctrl_t *ctrl) {
|
||||||
|
memset(ctrl, 0, sizeof(*ctrl));
|
||||||
|
ctrl->magic = DOE_REGION_PACK(DOE_REGION_VERSION_0);
|
||||||
|
ctrl->devblocks = DOE_REGION_INITIAL_DEVBLOCKS;
|
||||||
|
ctrl->head_slot = 0;
|
||||||
|
ctrl->tail_slot = 0;
|
||||||
|
ctrl->record_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads/writes the devblock holding slot_index (DOE_SLOTS_PER_DEVBLOCK
|
||||||
|
* slots per devblock), one devblock_from_top past the control header for
|
||||||
|
* every DOE_SLOTS_PER_DEVBLOCK slots. */
|
||||||
|
static uint32_t doe_region_slot_devblock_from_top(uint32_t slot_index) {
|
||||||
|
return DOE_REGION_DEVBLOCK_FROM_TOP_BASE + 1u + (slot_index / DOE_SLOTS_PER_DEVBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int doe_region_write_slot(uint32_t slot_index, const doe_trial_slot_t *slot) {
|
||||||
|
uint8_t devblock_buf[4096];
|
||||||
|
uint32_t dft = doe_region_slot_devblock_from_top(slot_index);
|
||||||
|
uint32_t slot_in_devblock = slot_index % DOE_SLOTS_PER_DEVBLOCK;
|
||||||
|
|
||||||
|
/* Read-modify-write: blk_meta_zone_*() only operates at whole-devblock
|
||||||
|
* granularity, and a devblock holds DOE_SLOTS_PER_DEVBLOCK slots, so a
|
||||||
|
* single-slot write must preserve its siblings. A read failure on a
|
||||||
|
* never-yet-written devblock (blank fence content) is not fatal here --
|
||||||
|
* proceed with a zeroed buffer, matching log_region_write_slot()'s own
|
||||||
|
* discipline. */
|
||||||
|
if (blk_meta_zone_read(dft, devblock_buf) != 0) {
|
||||||
|
memset(devblock_buf, 0, sizeof(devblock_buf));
|
||||||
|
}
|
||||||
|
memcpy(devblock_buf + (size_t)slot_in_devblock * DOE_SLOT_SIZE, slot, sizeof(*slot));
|
||||||
|
return blk_meta_zone_write(dft, devblock_buf) == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 0 and *out populated only if the slot's own CRC checks out --
|
||||||
|
* a corrupted sibling slot (§XXXV.12's own devblock-sharing risk) reads
|
||||||
|
* back as a failure here, not as a plausible-looking garbage record. */
|
||||||
|
static int doe_region_read_slot(uint32_t slot_index, doe_trial_slot_t *out) {
|
||||||
|
uint8_t devblock_buf[4096];
|
||||||
|
uint32_t dft = doe_region_slot_devblock_from_top(slot_index);
|
||||||
|
uint32_t slot_in_devblock = slot_index % DOE_SLOTS_PER_DEVBLOCK;
|
||||||
|
|
||||||
|
if (blk_meta_zone_read(dft, devblock_buf) != 0) return -1;
|
||||||
|
memcpy(out, devblock_buf + (size_t)slot_in_devblock * DOE_SLOT_SIZE, sizeof(*out));
|
||||||
|
if (doe_region_slot_compute_crc(out) != out->slot_crc) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int doe_region_append(uint32_t run_id, uint32_t cfg, uint32_t rep, uint32_t nw,
|
||||||
|
uint32_t mode, uint32_t fail_count,
|
||||||
|
uint64_t fleet_k_q48, uint32_t fleet_conserved,
|
||||||
|
uint64_t switch_count_cumulative,
|
||||||
|
const char *isa, uint32_t isa_len) {
|
||||||
|
doe_region_ctrl_t ctrl;
|
||||||
|
if (doe_region_ctrl_read(&ctrl) != 0) {
|
||||||
|
doe_region_ctrl_init_fresh(&ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t total_slots = ctrl.devblocks * DOE_SLOTS_PER_DEVBLOCK;
|
||||||
|
if (ctrl.record_count >= total_slots) {
|
||||||
|
if (ctrl.devblocks < DOE_REGION_MAX_DEVBLOCKS) {
|
||||||
|
/* Grow: new slots appear at indices [old_total_slots ..
|
||||||
|
* new_total_slots-1], nothing already written moves -- safe
|
||||||
|
* at any point in the ring's lifecycle (see log_region.c's
|
||||||
|
* own identical reasoning). */
|
||||||
|
ctrl.devblocks += DOE_REGION_GROWTH_INCREMENT;
|
||||||
|
if (ctrl.devblocks > DOE_REGION_MAX_DEVBLOCKS) ctrl.devblocks = DOE_REGION_MAX_DEVBLOCKS;
|
||||||
|
total_slots = ctrl.devblocks * DOE_SLOTS_PER_DEVBLOCK;
|
||||||
|
}
|
||||||
|
if (ctrl.record_count >= total_slots) {
|
||||||
|
/* Still full (growth ceiling already reached) -- plain FIFO
|
||||||
|
* eviction, no priority concept for a trial summary. */
|
||||||
|
ctrl.head_slot = (ctrl.head_slot + 1u) % total_slots;
|
||||||
|
ctrl.record_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doe_trial_slot_t slot;
|
||||||
|
memset(&slot, 0, sizeof(slot));
|
||||||
|
slot.fleet_k_q48 = fleet_k_q48;
|
||||||
|
slot.switch_count_cumulative = switch_count_cumulative;
|
||||||
|
slot.run_id = run_id;
|
||||||
|
slot.cfg = cfg;
|
||||||
|
slot.rep = rep;
|
||||||
|
slot.nw = nw;
|
||||||
|
slot.mode = mode;
|
||||||
|
slot.fail_count = fail_count;
|
||||||
|
slot.fleet_conserved = fleet_conserved;
|
||||||
|
|
||||||
|
uint32_t isa_n = isa_len;
|
||||||
|
if (isa_n > DOE_SLOT_ISA_MAX) isa_n = DOE_SLOT_ISA_MAX;
|
||||||
|
if (isa && isa_n) memcpy(slot.isa, isa, isa_n);
|
||||||
|
|
||||||
|
slot.slot_crc = doe_region_slot_compute_crc(&slot);
|
||||||
|
|
||||||
|
if (doe_region_write_slot(ctrl.tail_slot, &slot) != 0) return -1;
|
||||||
|
|
||||||
|
ctrl.tail_slot = (ctrl.tail_slot + 1u) % total_slots;
|
||||||
|
if (ctrl.record_count < total_slots) ctrl.record_count++;
|
||||||
|
|
||||||
|
return doe_region_ctrl_write(&ctrl) == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t doe_region_count_for_isa(const char *isa, uint32_t isa_len) {
|
||||||
|
doe_region_ctrl_t ctrl;
|
||||||
|
if (doe_region_ctrl_read(&ctrl) != 0) return 0;
|
||||||
|
|
||||||
|
/* Build the full DOE_SLOT_ISA_MAX-byte zero-padded tag exactly as
|
||||||
|
* doe_region_append() stores it, so this compares the whole field
|
||||||
|
* (including trailing zero padding) -- not just a prefix, which would
|
||||||
|
* wrongly match e.g. "amd" against a stored "amd64". */
|
||||||
|
char want[DOE_SLOT_ISA_MAX];
|
||||||
|
memset(want, 0, sizeof(want));
|
||||||
|
uint32_t isa_n = isa_len;
|
||||||
|
if (isa_n > DOE_SLOT_ISA_MAX) isa_n = DOE_SLOT_ISA_MAX;
|
||||||
|
if (isa && isa_n) memcpy(want, isa, isa_n);
|
||||||
|
|
||||||
|
uint32_t total_slots = ctrl.devblocks * DOE_SLOTS_PER_DEVBLOCK;
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t idx = ctrl.head_slot;
|
||||||
|
for (uint32_t i = 0; i < ctrl.record_count; i++) {
|
||||||
|
doe_trial_slot_t slot;
|
||||||
|
if (doe_region_read_slot(idx, &slot) == 0) {
|
||||||
|
if (memcmp(slot.isa, want, DOE_SLOT_ISA_MAX) == 0) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx = (idx + 1u) % total_slots;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
#include "starkernel/log_attrib.h" /* vm_log_attributed_vm() */
|
#include "starkernel/log_attrib.h" /* vm_log_attributed_vm() */
|
||||||
#include "starkernel/capsule_birth.h" /* capsule_vm_registry_get, VMRegistryEntry */
|
#include "starkernel/capsule_birth.h" /* capsule_vm_registry_get, VMRegistryEntry */
|
||||||
#include "starkernel/console.h" /* console_println() -- (LOG-APPEND-RAW) diagnostics */
|
#include "starkernel/console.h" /* console_println() -- (LOG-APPEND-RAW) diagnostics */
|
||||||
|
#include "starkernel/doe_region.h" /* doe_region_append() -- DOE-PERSIST-TRIAL */
|
||||||
|
#include "starkernel/capsule_vm_physics.h" /* vm_physics_fleet_heat_sum(), vm_physics_conserved() */
|
||||||
|
#include "starkernel/capsule_vm_switch_signal.h" /* sk_vm_switch_signal_switch_count() */
|
||||||
#include <string.h> /* strlen */
|
#include <string.h> /* strlen */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -296,6 +299,92 @@ static void log_word_append_raw(VM *vm)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* ── DOE-PERSIST-TRIAL ( run cfg rep nw mode fail isa-addr isa-u -- ) ── */
|
||||||
|
/*
|
||||||
|
* Kernel-only. Writes one trial-summary record to Artemis's on-disk DoE
|
||||||
|
* ring (doe_region.c), FABRIC-3.md §XXXV.3/.4. Called from
|
||||||
|
* MU-EMIT-TRIAL-MARKER (multiuser-doe.4th / per-isa-doe.4th) right
|
||||||
|
* alongside the existing console print, so a campaign's data survives
|
||||||
|
* past the QEMU serial log without needing a live host tail.
|
||||||
|
*
|
||||||
|
* fleet_k_q48/fleet_conserved/switch_count_cumulative are NOT caller-
|
||||||
|
* supplied -- read directly here via vm_physics_fleet_heat_sum(),
|
||||||
|
* vm_physics_conserved(), sk_vm_switch_signal_switch_count(), same
|
||||||
|
* discipline (LOG-APPEND-RAW) uses for its own source attribution: a
|
||||||
|
* trial-end snapshot no FORTH caller could spoof or get stale by passing
|
||||||
|
* the wrong value.
|
||||||
|
*/
|
||||||
|
#ifdef __STARKERNEL__
|
||||||
|
static void log_word_doe_persist_trial(VM *vm)
|
||||||
|
{
|
||||||
|
if (vm->dsp < 7) {
|
||||||
|
console_println("DOE-PERSIST-TRIAL: stack underflow");
|
||||||
|
vm->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cell_t isa_u = vm_pop(vm);
|
||||||
|
cell_t isa_addr = vm_pop(vm);
|
||||||
|
cell_t fail = vm_pop(vm);
|
||||||
|
cell_t mode = vm_pop(vm);
|
||||||
|
cell_t nw = vm_pop(vm);
|
||||||
|
cell_t rep = vm_pop(vm);
|
||||||
|
cell_t cfg = vm_pop(vm);
|
||||||
|
cell_t run = vm_pop(vm);
|
||||||
|
|
||||||
|
if (isa_u < 0 || isa_addr < 0 || (isa_addr + isa_u) > (cell_t)VM_MEMORY_SIZE) {
|
||||||
|
console_println("DOE-PERSIST-TRIAL: isa string address out of range");
|
||||||
|
vm->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *isa = (const char *)&vm->memory[isa_addr];
|
||||||
|
|
||||||
|
uint64_t fleet_k_q48 = vm_physics_fleet_heat_sum();
|
||||||
|
unsigned fleet_conserved = vm_physics_conserved() ? 1u : 0u;
|
||||||
|
uint64_t switch_count = sk_vm_switch_signal_switch_count();
|
||||||
|
|
||||||
|
if (doe_region_append((uint32_t)run, (uint32_t)cfg, (uint32_t)rep, (uint32_t)nw,
|
||||||
|
(uint32_t)mode, (uint32_t)fail,
|
||||||
|
fleet_k_q48, fleet_conserved, switch_count,
|
||||||
|
isa, (uint32_t)isa_u) != 0) {
|
||||||
|
vm->error = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ── DOE-RESUME-COUNT ( isa-addr isa-u -- n ) ── */
|
||||||
|
/*
|
||||||
|
* Kernel-only. §XXXV.12 campaign crash-resume: counts CRC-valid, live
|
||||||
|
* records in Artemis's DoE ring matching the given ISA tag. Called by
|
||||||
|
* each RUN-*-CAMPAIGN entry word (per-isa-doe.4th) before MU-EXEC-
|
||||||
|
* CAMPAIGN, so a re-boot after a crash resumes at the first trial that
|
||||||
|
* doesn't already have a good persisted record, instead of restarting
|
||||||
|
* the whole campaign from trial 0.
|
||||||
|
*/
|
||||||
|
#ifdef __STARKERNEL__
|
||||||
|
static void log_word_doe_resume_count(VM *vm)
|
||||||
|
{
|
||||||
|
if (vm->dsp < 1) {
|
||||||
|
console_println("DOE-RESUME-COUNT: stack underflow");
|
||||||
|
vm->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cell_t isa_u = vm_pop(vm);
|
||||||
|
cell_t isa_addr = vm_pop(vm);
|
||||||
|
|
||||||
|
if (isa_u < 0 || isa_addr < 0 || (isa_addr + isa_u) > (cell_t)VM_MEMORY_SIZE) {
|
||||||
|
console_println("DOE-RESUME-COUNT: isa string address out of range");
|
||||||
|
vm->error = 1;
|
||||||
|
vm_push(vm, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *isa = (const char *)&vm->memory[isa_addr];
|
||||||
|
uint32_t n = doe_region_count_for_isa(isa, (uint32_t)isa_u);
|
||||||
|
vm_push(vm, (cell_t)n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ── Registration ────────────────────────────────────────────────────── */
|
/* ── Registration ────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,6 +397,7 @@ static void log_word_append_raw(VM *vm)
|
|||||||
* String literals: LOG-ERROR" LOG-WARN" LOG-INFO" LOG-TEST" LOG-DEBUG"
|
* String literals: LOG-ERROR" LOG-WARN" LOG-INFO" LOG-TEST" LOG-DEBUG"
|
||||||
* Stack string: LOG-ERROR-STR LOG-WARN-STR LOG-INFO-STR
|
* Stack string: LOG-ERROR-STR LOG-WARN-STR LOG-INFO-STR
|
||||||
* LOG-TEST-STR LOG-DEBUG-STR
|
* LOG-TEST-STR LOG-DEBUG-STR
|
||||||
|
* Kernel-only: (LOG-APPEND-RAW) DOE-PERSIST-TRIAL DOE-RESUME-COUNT
|
||||||
*/
|
*/
|
||||||
void register_log_words(VM *vm)
|
void register_log_words(VM *vm)
|
||||||
{
|
{
|
||||||
@@ -344,5 +434,7 @@ void register_log_words(VM *vm)
|
|||||||
|
|
||||||
#ifdef __STARKERNEL__
|
#ifdef __STARKERNEL__
|
||||||
register_word(vm, "(LOG-APPEND-RAW)", log_word_append_raw);
|
register_word(vm, "(LOG-APPEND-RAW)", log_word_append_raw);
|
||||||
|
register_word(vm, "DOE-PERSIST-TRIAL", log_word_doe_persist_trial);
|
||||||
|
register_word(vm, "DOE-RESUME-COUNT", log_word_doe_resume_count);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user