5682878f22
The seed use case end to end, orchestrating M3 + M4 (ARCHITECTURE §7.1). - core/diagnostic.py: start(game) runs a focused, game-tagged capture into a dedicated diagnostic log (window-scoped report, separate from the always-on crash log); finish() stops it and combines the capture summary (M3) with the health findings (M4). Game recorded as a log event so it survives crash+reboot. - CLI: rigdoctor diagnose start --game/--appid | status | finish. - recorder/record run gained an optional --game tag; reccontrol passes it through. - Tests for game recovery + the finish() combination. GUI/tray "Run Diagnostic" button and auto start/stop (D12 wrapper) come next. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
1.8 KiB
Python
76 lines
1.8 KiB
Python
"""Background-process control for the crash-capture recorder (shared by CLI + GUI).
|
|
|
|
Both front-ends start/stop/inspect the same `systemd`-style detached recorder via the
|
|
PID and status files, so behaviour is identical however you drive it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
|
|
from .. import config
|
|
|
|
|
|
def pid_alive(pid: int) -> bool:
|
|
try:
|
|
os.kill(pid, 0)
|
|
except OSError:
|
|
return False
|
|
return True
|
|
|
|
|
|
def running_pid() -> int | None:
|
|
try:
|
|
pid = int(config.PID_FILE.read_text().strip())
|
|
except (OSError, ValueError):
|
|
return None
|
|
return pid if pid_alive(pid) else None
|
|
|
|
|
|
def read_status() -> dict | None:
|
|
try:
|
|
return json.loads(config.STATUS_FILE.read_text())
|
|
except (OSError, ValueError):
|
|
return None
|
|
|
|
|
|
def start_background(
|
|
interval: float | None = None, out: str | None = None, game: str | None = None
|
|
) -> int | None:
|
|
"""Spawn a detached `record run`. Returns the child pid, or None if already running."""
|
|
if running_pid():
|
|
return None
|
|
config.STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
cmd = [sys.executable, "-m", "rigdoctor", "record", "run"]
|
|
if interval:
|
|
cmd += ["--interval", str(interval)]
|
|
if out:
|
|
cmd += ["--out", out]
|
|
if game:
|
|
cmd += ["--game", game]
|
|
out_fh = open(config.SPAWN_LOG, "a")
|
|
proc = subprocess.Popen(
|
|
cmd,
|
|
stdout=out_fh,
|
|
stderr=subprocess.STDOUT,
|
|
stdin=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
return proc.pid
|
|
|
|
|
|
def stop_background() -> bool:
|
|
"""Signal the running recorder to stop. Returns False if it wasn't running."""
|
|
pid = running_pid()
|
|
if not pid:
|
|
return False
|
|
try:
|
|
os.kill(pid, signal.SIGTERM)
|
|
except OSError:
|
|
return False
|
|
return True
|