diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
| commit | 002e3d7d13c414ea874abbee8d82803dd3a72fe5 (patch) | |
| tree | 6627c12fa07f6a1cf73e5aaeb7390e698dd3a896 /debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py | |
| parent | 2f249817df327f5dcccf0464ad517fa60d6071d3 (diff) | |
| download | open-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.tar.gz open-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.zip | |
debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py b/debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py new file mode 100644 index 0000000..085751c --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/plugins/rmharness_plugin.py @@ -0,0 +1,120 @@ +"""green-eye plugin: RM host-oracle harness commands. + +Registers REPL commands that run the read-only Open-IOV RM tools through the +``rmharness`` wrapper scripts and record each run into the active session: + + greeneye> rmtool gpu list + greeneye> rmtool mem stats --format=json + greeneye> rmtop --once + greeneye> rmq + +Every run is recorded as a ``rm_harness_run`` event with a captured +stdout+stderr blob (via ``ge.recorder.blob``); write/ownership operations are +refused and recorded as ``rm_harness_skip``. The plugin is stdlib-only and +never touches kernel modules, VFIO, mdevs or the live GPU state. +""" +from __future__ import annotations + +import hashlib +import os +import subprocess +import sys +import time + +_TOOL_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +_HARNESS = os.path.join(_TOOL_ROOT, "rmharness") +_WRAPPERS = { + "rmtool": os.path.join(_HARNESS, "rmtool.sh"), + "rmtop": os.path.join(_HARNESS, "rmtop.sh"), + "rmq": os.path.join(_HARNESS, "rmq.sh"), +} +#: arguments that would name a write/ownership operation on any tool. +_BLOCKED = frozenset({ + "alloc", "allocate", "map", "unmap", "pin", "free", "create", + "destroy", "submit", "bind", "unbind", "reset", "start", "stop", + "load", "unload", "reboot", "write", "poke", "set", +}) + + +def _blocked_arg(args): + for arg in args: + token = arg.split("=", 1)[0].lstrip("-").lower() + if token in _BLOCKED: + return arg + return None + + +def _record(ge, tool, args, proc, duration): + stdout = proc.stdout or "" + stderr = proc.stderr or "" + payload = (stdout + stderr).encode("utf-8", "replace") + blob = None + try: + blob = ge.recorder.blob( + f"rmharness/{tool}-{time.time_ns()}.txt", payload) + except Exception: + blob = None + fields = dict( + plugin="rmharness", + tool=tool, + argv=list(args), + exit=proc.returncode, + duration_s=round(duration, 6), + stdout_sha256=hashlib.sha256( + stdout.encode("utf-8", "replace")).hexdigest(), + stderr_sha256=hashlib.sha256( + stderr.encode("utf-8", "replace")).hexdigest(), + stdout_bytes=len(stdout.encode("utf-8", "replace")), + stderr_bytes=len(stderr.encode("utf-8", "replace")), + blob=blob, + readonly=True, + ) + ge.recorder.event("rm_harness_run", **fields) + + +def _run(ge, tool, args): + wrapper = _WRAPPERS[tool] + if not os.path.isfile(wrapper): + print(f" rmharness: wrapper missing: {wrapper}", file=sys.stderr) + ge.recorder.event("rm_harness_error", plugin="rmharness", + tool=tool, error="wrapper missing") + return 2 + bad = _blocked_arg(args) + if bad is not None: + print(f" rmharness: refused '{bad}': read-only policy " + f"(see docs/17-RM-HARNESS.md)", file=sys.stderr) + ge.recorder.event("rm_harness_skip", plugin="rmharness", tool=tool, + argv=list(args), reason=f"blocked argument {bad!r}") + return 2 + start = time.monotonic() + proc = subprocess.run([wrapper, *args], capture_output=True, text=True) + duration = time.monotonic() - start + if proc.stdout: + print(proc.stdout, end="") + if proc.stderr: + print(proc.stderr, end="", file=sys.stderr) + _record(ge, tool, args, proc, duration) + directory = getattr(ge.recorder, "dir", None) + if directory: + print(f" rmharness: recorded {tool} run -> {directory}") + return proc.returncode + + +def register(ge): + ge.log("rmharness plugin loaded; tools=" + ",".join(sorted(_WRAPPERS))) + + def cmd_rmtool(session, args): + return _run(ge, "rmtool", args) + + def cmd_rmtop(session, args): + return _run(ge, "rmtop", args) + + def cmd_rmq(session, args): + return _run(ge, "rmq", args) + + ge.add_command("rmtool", cmd_rmtool, + help="rmtool <args>: read-only RM oracle (gpu/engine/mem)") + ge.add_command("rmtop", cmd_rmtop, + help="rmtop <args>: dashboard; use --once for capture") + ge.add_command("rmq", cmd_rmq, + help="rmq: existing tools/rmq host oracle (read-only)") |
