# 45 — GSP RPC capture ingestion and replay prep (in development) > **Status: in development (2026-10-06, capture-ingestion wave).** This page > documents `harness/rm_capture.py`, a stdlib-only harness that turns the > **supervised real-client GSP RPC capture** (the tag contract produced by the > send-path instrumentation runbook) into structured, replayable artifacts: > a decoded capture document, a per-record comparison report, and a replay > **stimulus** document for the simulator's B4/B5 CSB/message-queue seam. > > Development-only: the tool reads files and writes reports. It never touches > a GPU, kernel module, mdev, guest or network, and it never runs or schedules > the capture itself. The capture is a supervised, human-run physical action > (RUNBOOK P1/P7); the tool only ingests its log. > > **See also:** `06-ACCURACY.md` (fidelity levels, M1–M6), > `17-RM-HARNESS.md` (read-only RM oracle policy), > `36-GSP-DEVICE-MODEL.md` (the S1 device model and boot ladder), > `38-GSP-EM-API.md` (TLV boundary), > `44-GMMU-ORACLE-AND-WORKFLOW.md` (the same offline compare discipline for > GMMU/FBIF registers). ## 1. What this is and why The open GR question is *what the real host client actually sends* while it creates and commits a GR context (the golden-image sequence and its promote controls). The authored capture runbook obtains that sequence from an instrumented reference kernel driver; this harness consumes the resulting log and prepares it for two workflows: 1. **Compare** — the real client's captured sequence vs. the open-iov host ladder's replay (or two driver/firmware versions), per record, with explicit unmatched lists. 2. **Replay** — an ordered stimulus document that the simulator's next rung (B4/B5: host-traffic doorbell + first CSB/TRANSCOM poll served + one scripted RM message end-to-end) can feed into the modelled firmware, so the server side of the captured sequence can be observed off-hardware and deterministically. The simulator consumes captured truth; it does not replace the capture. Known limitation up front: the capture runbook's hex helper currently caps a payload dump at 192 bytes. Payloads larger than that (for example a 560-byte `PROMOTE_CTX` control) decode as **`truncated`** and are **not replayable** until the helper is extended to emit chunked blocks. The decoder flags this per record and in the summary; it never pads a payload to look complete. ## 2. The capture log contract Lines are matched anywhere in the line, so dmesg/NVRM/timestamp prefixes are fine. Tags (exact grammar: `python3 harness/rm_capture.py --print-capture-hints`): ``` GE-GOLDEN-TRIGGER gspclient=<0|1> mig=<0|1> GE-GOLDEN-BEGIN GE-GOLDEN-END status=0x... GE-SEND fn=0x... len=0x... ret=0x... GE-CTRL cmd=0x... hClient=0x... hObject=0x... size=0x... paramp=0x... ret=0x... GE-ALLOC cls=0x... hClient=0x... hParent=0x... hObject=0x... size=0x... paramp=0x... ret=0x... GE-CTRL-DONE cmd=0x... rpc_status=0x... params_status=0x... GE- : # attaches to the newest record of that tag ``` * Hex blocks attach to the most recent record **of their own tag**; an orphan block is reported as a warning, never guessed onto another record. * A `GE-SEND` payload's named id is derived when no named control line was captured: function `76` (`GSP_RM_CONTROL`) carries the control id at payload offset 8; function `103` (`GSP_RM_ALLOC`) carries the class id at offset 12. * `ret=` is the caller return address: recorded as-is, capture-specific. ## 3. Schemas * `greeneye-rm-rpc-capture/1` — `{format, source, sha256, records[], summary, warnings, limits}`. Each record: `{index, kind, line, ts?, fn?, length?, size?, cmd?, hclass?, hclient?, hobject?, hparent?, caller?, caller_raw?, marker?, gspclient?, mig?, status?, rpc_status?, params_status?, params_hex?, params_bytes, params_size_expected?, truncated?, anomalies?}`. * `greeneye-rm-rpc-compare/1` — inputs + `summary` (matched / agree / shape-agree / partial / mismatch / A-only / B-only / agreement % / coverage % / mean byte agreement %), `rows[]` (per aligned record, with byte agreement and first-difference offset), `a_only[]`, `b_only[]`, `fidelity`, `limits`, `warnings`. * `greeneye-rm-rpc-stimulus/1` — `markers[]` (golden slice boundaries) and `messages[]` (`fn`/`cmd`/`hclass`/`wire_id`, `params_hex`, sizes, truncation flag). Handles, physical addresses and caller addresses are **omitted** on purpose: a replay must allocate/re-map them. ## 4. Commands ```bash python3 harness/rm_capture.py --print-capture-hints # prints only python3 harness/rm_capture.py decode --log capture.log --out dumps/rm-capture python3 harness/rm_capture.py compare --a real.json --b replay.json --out dumps/rm-compare python3 harness/rm_capture.py stimulus --capture real.json --out dumps/rm-stimulus.json python3 harness/rm_capture.py demo --out /tmp/rm-capture-demo # synthetic, measured ``` Inputs to `compare`/`stimulus` may be decoded JSON documents or raw logs; the loader auto-detects. A non-capture JSON format is a clean error. ## 5. Simulator linkage (B4/B5) — not wired yet The stimulus document is the intended input for the simulator's host-traffic rung: once the doorbell path and the first CSB/TRANSCOM poll are served and a scripted message can be delivered end-to-end (handoff-simulator §17.5), the captured golden-image sequence is the natural first real message set. Until then this tool stays offline: `stimulus` states the pending wiring in its `limits`, and nothing in `sim/` imports it yet. Ordering honesty: the capture is **send-side only**; server replies were not captured by the probes. A replay therefore reproduces the stimulus, not the reply sequence. ## 6. Admission record (draft — pending maintainer filing) Per the project admission rule (`docs/eng-ops.md` §2): all five fields. This record is a draft, like the simulator program's `gpgpu-sim/ADMISSION.md` records; a maintainer files it. | Field | Value | |---|---| | Scenario | RE/porting loop: ingest a supervised real-client GSP RPC capture into the workflow — decode the golden-image sequence, compare it with the open-iov ladder's replay or another version, and prepare a replay stimulus for the simulator's message-loop rung. No hardware access. | | Measurable outcome | Decode/compare/stimulus wall time vs. manual baseline; record and parameter integrity (no silent loss; truncation flagged); deterministic outputs (two runs byte-identical apart from timing). | | Baseline | Manual decode-by-eye of one golden sequence from a capture log: grep tags, count records, walk each parameter blob against the RPC structs, transcribe a replay table. Conservative estimate **1800 s** (not measured on a human). | | Evaluation threshold | Tool path ≤ **5%** of the baseline (≤90 s); demo `ok`; deterministic reports; new tests plus the full suite green. | | Failure signal | Silent record/parameter loss; truncation unflagged; non-deterministic reports; demo above threshold; or any physical action executed/scheduled by the tool → disable the feature and record it as a failed experiment. | Measured proof (2026-10-06, synthetic demo on this host): 15/14 records, 13 agree + 1 shape-agree (flipped parameter byte) + 1 A-only (dropped alloc), **wall ≈ 0.02 s** vs the 90 s threshold; artifacts under the `demo --out` directory; 27 new tests, full suite 444 green (twice). ## 7. Limits and deferrals * No hardware, module, mdev, guest or network action; the tool never runs the capture. * The 192-byte helper cap: extend the runbook helper to chunk before any large payload (promote entries) is considered replayable. * Handles/addresses are capture-specific and explicitly omitted from stimuli. * Send-side only; no reply/event ordering. * B4/B5 stimulus wiring, a captured-sequence vs. simulated-firmware accuracy loop, and symbol/`addr2line` mapping of captured caller addresses are deferred (the `gmmu_debug.py` static↔dynamic join is the pattern to reuse). * L0/L1 fidelity only; no timing claim.