/** * ProbeBackend is the abstraction layer for debug probes. * Each probe type (J-Link, OpenOCD, Black Magic Probe, probe-rs) * implements this interface. The MCP server calls only these methods. */ export declare enum ProbeState { DISCONNECTED = "disconnected", PROBE_CONNECTED = "probe_connected", TARGET_ATTACHED = "target_attached", GDB_RUNNING = "gdb_running" } /** Structured error codes returned by probe operations */ export declare enum ProbeErrorCode { PROBE_NOT_FOUND = "PROBE_NOT_FOUND", TARGET_UNREACHABLE = "TARGET_UNREACHABLE", ATTACH_FAILED = "ATTACH_FAILED", ATTACH_UNDER_RESET_FAILED = "ATTACH_UNDER_RESET_FAILED", STATE_DESYNC = "STATE_DESYNC", DEVICE_NOT_CONFIGURED = "DEVICE_NOT_CONFIGURED", GDB_SERVER_FAILED = "GDB_SERVER_FAILED", RTT_NOT_AVAILABLE = "RTT_NOT_AVAILABLE", TIMEOUT = "TIMEOUT", PROBE_BUSY = "PROBE_BUSY" } export interface CommandResult { success: boolean; /** Raw output from the probe tool */ rawOutput: string; /** Cleaned output (boilerplate stripped) */ output: string; error?: string; /** Structured error code for programmatic handling */ errorCode?: ProbeErrorCode; /** What stage succeeded before failure */ lastSuccessfulStage?: string; /** Suggested recovery action */ suggestedAction?: string; } export interface MemoryDumpLine { address: string; hex: string; ascii: string; } export interface GDBServerInfo { running: boolean; gdbPort: number; /** Port for RTT telnet access (J-Link specific, -1 if not supported) */ rttTelnetPort: number; } export interface ProbeStatus { state: ProbeState; probeType: ProbeType; deviceConfigured: boolean; deviceName: string; gdbServer: GDBServerInfo; rttConnected: boolean; } export type ProbeType = "jlink" | "openocd" | "blackmagic" | "probe-rs"; /** * Minimal surface a backend needs to route CPU-control and read commands * through a running GDB session instead of spawning its own probe-CLI * process. Implemented by `GDBClient`; injected by the MCP server via * `ProbeBackend.setGdbBridge()`. * * Kept intentionally small so backends don't depend on the concrete * GDB client class. */ export interface GdbBridge { isConnected(): boolean; command(cmd: string, timeout?: number): Promise<{ success: boolean; output: string; error?: string; stopReason?: string; }>; /** * Stop a running target out-of-band. * * Separate from `command` because it cannot go through the command channel: * with a synchronous remote, GDB blocks while the target runs and stops * reading stdin entirely, so a halt typed as a command is never seen. * Optional so alternative bridges need not implement it. */ interrupt?(timeout?: number): Promise<{ success: boolean; output: string; error?: string; stopReason?: string; }>; } /** * Abstract base for all debug probe backends. * Implementations only need to override the abstract methods. * Shared utilities (register parsing, fault decoding, memory parsing) * are provided by the base class. */ export declare abstract class ProbeBackend { abstract readonly type: ProbeType; abstract readonly displayName: string; protected _state: ProbeState; private _rttConnected; private _lock; /** * Optional GDB session the backend can route commands through. Injected * by the MCP server after both objects are constructed. When present * and connected, backends should prefer this over spawning a competing * probe-CLI process, since the underlying probe can only serve one * session at a time. */ protected gdbBridge?: GdbBridge; get state(): ProbeState; get rttConnected(): boolean; set rttConnected(v: boolean); /** Transition state with validation */ protected setState(newState: ProbeState): void; getStatus(): ProbeStatus; /** * Acquire exclusive access to the probe. Prevents concurrent commands * from racing the same J-Link session. */ protected acquireLock(fn: () => Promise): Promise; /** * Preflight check: verify target is reachable by reading DHCSR. * Returns null if OK, or an error CommandResult if unreachable. * Subclasses can override for probe-specific preflight. */ preflight(): Promise; /** * Run a command with preflight validation and auto-recovery. * Wraps the command in a lock to prevent concurrent access. */ withPreflight(operation: string, fn: () => Promise, skipPreflight?: boolean): Promise; /** * Recovery sequence. Subclasses should override to implement * probe-specific recovery (restart server, reconnect under reset, etc.) * Returns true if recovery succeeded. */ recover(): Promise; /** Inject a GDB session for command routing. See {@link GdbBridge}. */ setGdbBridge(bridge: GdbBridge | undefined): void; abstract getDeviceInfo(): Promise; abstract halt(): Promise; abstract resume(): Promise; abstract reset(halt?: boolean): Promise; abstract step(): Promise; abstract readMemory(address: number, length: number): Promise; abstract writeMemory(address: number, value: number): Promise; abstract readAllRegisters(): Promise; abstract readRegister(name: string): Promise; abstract flash(filePath: string, baseAddress?: number): Promise; abstract erase(): Promise; abstract setBreakpoint(address: number): Promise; abstract clearBreakpoints(): Promise; abstract startGDBServer(): Promise<{ success: boolean; message: string; }>; abstract stopGDBServer(): { success: boolean; message: string; }; abstract isGDBServerRunning(): boolean; abstract getGDBServerStatus(): GDBServerInfo; abstract getGDBServerOutput(lines?: number): string[]; abstract executeRaw(commands: string[]): Promise; /** Whether a target device has been configured */ abstract isDeviceConfigured(): boolean; /** Get the currently configured device name */ abstract getDeviceName(): string; /** Set the target device at runtime (no restart needed) */ abstract setDevice(device: string): void; /** List connected probes / scan for devices. Returns human-readable text. */ abstract listDevices(): Promise; /** Whether this probe supports RTT */ supportsRTT(): boolean; /** RTT telnet port when GDB server is running (-1 if not supported) */ getRTTPort(): number; abstract dispose(): void; /** * Parse register dump text into structured key-value pairs. * * Handles two wire formats: * - J-Link Commander `regs`: `NAME = VALUE`, several per line. * - GDB `info registers` / `info all-registers`: whitespace columns, * `name 0xhex decimal`, one per line, lowercase names. * * Both normalize to uppercase names and `0x`-prefixed, 8-digit * zero-padded values, so downstream consumers (`formatRegistersCompact`, * `diagnose_crash`'s `!== "0x00000000"` checks) behave identically * regardless of which channel served the read. */ parseRegisters(raw: string): Record | null; /** Format registers as a compact, LLM-friendly summary */ formatRegistersCompact(regs: Record): string; /** * Parse hex dump lines from probe output. * * The hex column is matched as an explicit run of byte pairs rather than * "anything up to two spaces". J-Link separates the two 8-byte halves of a * 16-byte line with a *double* space, exactly like the column before the * ASCII field: * * E000ED28 = 00 00 00 00 00 00 00 00 01 00 00 00 74 28 06 20 ......t(. * └──────── 8 bytes ─────┘└┘└──────── 8 bytes ────┘└┘└ ascii * * A non-greedy `(.+?)\s{2,}` stops at the first of those separators and * silently drops the second half of every line, which left * `readFaultRegisters` short of its 16-byte minimum and reporting * CFSR/HFSR/MMFAR/BFAR as all-zero — i.e. "no faults detected" on a live * crash. */ parseMemoryDump(raw: string): MemoryDumpLine[]; /** * Disarm the Cortex-M debug hardware so the target is left bootable. * * A breakpoint comparator left armed in the Flash Patch and Breakpoint unit * re-triggers on every subsequent run. With no debugger attached the debug * event escalates to HardFault, and the CPU parks in its fault handler * permanently — inherited by every later session, and not cleared by a * probe-issued reset, which by design leaves the debug block alone so a * debugger keeps control across target resets. * * Zeroing the comparators is the part that matters. FP_CTRL.ENABLE is * re-enabled by the J-Link DLL on every attach, so disabling the unit is * not durable on its own — it is only safe while the comparators are clear. * * Best-effort: this runs during teardown, where failing to tidy up must not * prevent the caller from disconnecting. */ disarmDebugState(): Promise<{ ok: boolean; detail: string; }>; /** Read fault registers and decode them (ARM Cortex-M specific) */ readFaultRegisters(): Promise<{ result: CommandResult; decoded: string; raw: { cfsr: number; hfsr: number; dfsr: number; mmfar: number; bfar: number; }; }>; } export declare function parseLittleEndian32(bytes: string[], offset: number): number; export declare function decodeFaultRegisters(cfsr: number, hfsr: number, mmfar: number, bfar: number, dfsr?: number): string; //# sourceMappingURL=backend.d.ts.map