bench: fd-scan tool; BRINGUP session record - dry drills, homing witness, panic drill

This commit is contained in:
ScottW514
2026-08-15 08:52:51 -04:00
parent 31ac4bbcbd
commit e1207316ec
2 changed files with 75 additions and 25 deletions
+42 -25
View File
@@ -285,9 +285,31 @@ found and fixed: `auth.c` read `X-ForgeFIRM-Token`/`Host`/`Origin`/
`Sec-Fetch-Site` case-sensitively (a title-casing client was refused); `Sec-Fetch-Site` case-sensitively (a title-casing client was refused);
now `u_map_get_case`. Bench tooling for the session is committed now `u_map_get_case`. Bench tooling for the session is committed
(`scripts/bench/platform_drills.py`, `live_fire_drills.py` `ircut` / (`scripts/bench/platform_drills.py`, `live_fire_drills.py` `ircut` /
`expstop` / `ctrlstart`). Session rules, now standing: one live-laser run `expstop` / `ctrlstart`, `fdscan.sh`). Session rules, now standing: one
per turn with the operator's confirmation before the next; only live-laser run per turn with the operator's confirmation before the next;
observations, never inferences, in live-fire reporting. only observations, never inferences, in live-fire reporting.
**Dry drills the same session, all PASS on the board:** decay/microstep
readback per axis (every value reads back, out-of-range `3` refused
`EINVAL`); dead-man trip readback (closing the flock'd fd mid-run →
`closed while locked and driver is running! Emergency stop`, `pic`/`head`/
`thermal: making safe`; heater and TEC off, measure laser, UV LED and Z
driver off, pump/exhaust/intake/air-assist **unchanged**); three
`rmmod`/`modprobe` cycles with a thread reading state/position/faults/
hall_sensor throughout (6618 reads served, 14162 refused while unloaded,
no oops/BUG/WARNING); the LED sequence ran (bright/dark/pulse/restore);
the module's probe lines read `EPIT clock 66000000 Hz` and `SDMA channel
26 reserved for pulse playback (script at halfword 7680)` with no bank
warnings; forgectrl's helper children (`curl` during `/update/check`, the
snapshot path) never hold a pulse-device descriptor — only the controller
does; a `$H` gfcloud homing session completed in 56 s with 7 accelerometer
motion windows above the 500-count threshold at the ~100 Hz sampler
(anchor written, `H:1`); a kernel panic (`sysrq c`) mid-move stopped
motion instantly (operator-witnessed) and the board rebooted on `panic=10`
into a healthy state (liveness MOTION OK, controller running, latch
commanded locked). Observed once, cause not established: after the three
module reloads the first liveness probe read NO MOTION (p2p 343/241); the
ladder's rail-off/re-probe recovered it (p2p 3466/2163) — a module reload
resets the analog configuration, and the ladder exists for this.
**Phase 11 (licensing, legal, and documentation hygiene — the last **Phase 11 (licensing, legal, and documentation hygiene — the last
phase) is code-complete and host-verified, 2026-08-15.** Licensing: phase) is code-complete and host-verified, 2026-08-15.** Licensing:
@@ -2264,34 +2286,29 @@ accordingly ("Automatic — AP country, else World").
trip before calling the gate proven, and measure an actual flame trip before calling the gate proven, and measure an actual flame
signature (a tea light in the closed bed, machine idle) so the 15 signature (a tea light in the closed bed, machine idle) so the 15
is anchored on both sides. is anchored on both sides.
- **Kernel platform-hygiene batch (item 9), on the flashed image:** - ~~**Kernel platform-hygiene batch (item 9), on the flashed
panic mid-motion with motors locked and the laser latched (motion image**~~ — **DONE 2026-08-15** (panic mid-motion, decay/microstep
stops, safety lines read safe); decay mode set/readback per axis; readback, LED sequence + clean unload, probe lines, dead-man head
LED behavior and a clean module unload; the two new probe lines in readback, concurrent `cat` during `rmmod` — session record above).
dmesg with no bank warnings; a dead-man trip with the head Still needing a debug kernel build: load/unload under
registers read back (measure laser off, UV LED off, lens motor `CONFIG_DEBUG_MUTEXES` and a forced `-EPROBE_DEFER` unwind.
de-energized). Plus the Phase 8 trio: load/unload under - ~~**Dead-man collateral**~~ — **DONE 2026-08-15**: the trip leaves
`CONFIG_DEBUG_MUTEXES` (needs a debug kernel build), a forced pump and airflow running (readback drill); helper children never
`-EPROBE_DEFER` unwind, and a concurrent `cat` of a state attr hold the pulse device (fd-scan during `/update/check` + snapshot);
during `rmmod`. the armed kill on the *expected*-stop path failed first (5 s of
- **Dead-man collateral:** a kernel dead-man trip leaves the pump and continued fire), the defect is fixed on both sides, and the re-run
airflow running (heat sources only go off); kill forgectrl during passed. The literal "kill forgectrl mid-download" variant needs a
an update download (no pinned device, no EBUSY respawn storm). published `.fw` to download and was covered by the fd-scan instead.
~~Re-run the armed kill drill on the *expected*-stop path~~ —
**DONE 2026-08-15**: it failed first (5 s of continued fire), the
defect is fixed on both sides, and the re-run passed (see the
session record above).
- **Physical-evidence negatives:** force a head I²C error and confirm - **Physical-evidence negatives:** force a head I²C error and confirm
the witnesses report an error, not a plausible value; confirm a the witnesses report an error, not a plausible value; confirm a
failed head capture leaves the measure laser off. failed head capture leaves the measure laser off.
- **Cloud mode:** the C-1 cancel-with-a-rejected-settings-action - **Cloud mode:** the C-1 cancel-with-a-rejected-settings-action
drill (cancel must stop the cut); malformed-frame and DNS-blip drill (cancel must stop the cut); malformed-frame and DNS-blip
injections against a live session; the oversize/bad-header job injections against a live session; the oversize/bad-header job
rejected before the ring loads (tracked in `CLOUD.md`); re-verify rejected before the ring loads (tracked in `CLOUD.md`); ~~re-verify
the homing accelerometer motion-window counts against the the homing accelerometer motion-window counts~~ **DONE 2026-08-15**
characterized thresholds at the next live homing (the witness now (7 windows ≥ 500 at the ~100 Hz sampler on a real `$H`); confirm a
samples at ~100 Hz); confirm a real print header's fan duties real print header's fan duties round-trip to the engine (item 8).
round-trip to the engine (item 8).
- **Opportunistic:** `STATE_FAULT` recovery via `enable` without a - **Opportunistic:** `STATE_FAULT` recovery via `enable` without a
module reload the next time a DRV8825 fault line actually trips. module reload the next time a DRV8825 fault line actually trips.
- **Config-dependent, deliberately not gated:** an armed GRBL job - **Config-dependent, deliberately not gated:** an armed GRBL job
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
# Runs ON the board. While forgectrl spawns helper children (the update
# check's curl, the snapshot's media-ctl/v4l2-ctl), scan every child for a
# descriptor on the pulse device. Expected: none - only the controller
# inherits /dev/glowforge (F_SETFD cleared for that one spawn); every other
# child gets the O_CLOEXEC default. Usage: fdscan.sh <panel-token>
TOK="$1"
P=$(pidof forgectrl)
[ -n "$P" ] || { echo "forgectrl not running"; exit 1; }
CTRL=$(pidof grblHAL_glowforge)
echo "forgectrl pid $P, controller pid ${CTRL:-none}"
# Kick the helpers off in the background.
( curl -s -X POST -H "X-ForgeFIRM-Token: $TOK" http://127.0.0.1:8080/update/check >/tmp/upd.out 2>&1 ) &
( curl -s -o /tmp/snap.jpg http://127.0.0.1:8080/cam/snapshot ) &
hits=0; seen=0; names=""
i=0
while [ $i -lt 60 ]; do # ~6 s of scanning at 10 Hz
for c in $(pgrep -P "$P"); do
seen=$((seen+1))
n=$(cat /proc/$c/comm 2>/dev/null)
names="$names $n"
if ls -l /proc/$c/fd 2>/dev/null | grep -q glowforge; then
if [ "$c" != "$CTRL" ]; then hits=$((hits+1)); echo "HIT: child $c ($n) holds the pulse device"; ls -l /proc/$c/fd | grep glowforge; fi
fi
done
i=$((i+1)); usleep 100000 2>/dev/null || sleep 0.1
done
wait
echo "children observed: $(echo $names | tr ' ' '\n' | sort | uniq -c | tr '\n' ';')"
echo "controller holds pulse fd: $(ls -l /proc/$CTRL/fd 2>/dev/null | grep -c glowforge)"
echo "update/check reply: $(cat /tmp/upd.out | head -c 200)"
echo "snapshot bytes: $(wc -c < /tmp/snap.jpg 2>/dev/null)"
echo "non-controller children holding the pulse device: $hits ($([ $hits -eq 0 ] && echo PASS || echo FAIL))"