Features

Everything in the box.

rdbg ships as three binaries — a daemon, a CLI, and a GUI — that all share one wire protocol. Here's what each layer brings.

Core debug engine

Built directly on the Win32 Debugging API.

The daemon (rdbgd64.exe for Win64 targets, rdbgd32.exe for Win32 targets) launches your target under DEBUG_PROCESS and pumps the debug event loop in-process. No middleman; the wire protocol is identical between the two flavours.

Line-delimited JSON wire protocol

Every operation — launch, set_breakpoint, inspect_object, step_over — is one JSON object per line over TCP. Trivial to drive from any language.

M3 · docs/protocol.md · one client per daemon, sequential reconnects supported

Software breakpoints (0xCC patching)

Resolve source:line via the loaded MAP, write 0xCC, handle the hit with restore-byte / rewind-RIP / trap-flag single-step / re-patch. Re-arm survives stepping right after a hit.

M4 · unlimited slots · set_breakpoint / clear_breakpoint / list_breakpoints

Hardware breakpoints (DR0–DR3)

Up to four DR-slot HW breakpoints alongside SW BPs. hw: prefix on a BP spec routes to the next free debug register. Address forms accepted: source:line, address, or module!symbol (resolved via PDB). Code BPs today; data watchpoints (read/write watches via DR slots) planned.

M4.3 · max 4 concurrent · per-thread CONTEXT propagation

Annotated stack traces — MAP and PDB

Hand-rolled MAP-file parser plus a cross-process x64 RBP-chain walker. Every frame resolves to unit.pas:line symbol+offset for the launched exe, and to module!Symbol+offset for any DLL the target loaded — including kernel32, ntdll, user32, etc. PDBs are auto-fetched from Microsoft's public symbol server on first use and cached at %LOCALAPPDATA%\rdbg\symbols. Pure-Delphi MSF parser; no dbghelp.dll redistribution.

M2 + M17 · Rdbg.MapFile + Rdbg.StackWalk + Rdbg.Pdb · symsrv-shaped cache (interoperable with dbghelp / symchk / VS)

Single-step and step-over

Trap-flag single-stepping with line-aware step-over: loops the TF step until RIP's MAP resolution moves to a different source:line, so CALL instructions step through cleanly.

M9 / M12 · step_over · 50k-instruction safety cap

TObject inspection via VMT walking

Given a target VA pointing at a Delphi object, walks the VMT chain returning class name + instance size for every ancestor up to TObject. Self-calibrates for the RTL build.

M5 · inspect_object · auto-issued on every exception

Recursive RTTI property decoding

Walks the entire props list cross-process: ordinals (any width), Int64, all float flavours, every string flavour, tkClass (recurses), classref, pointers, interfaces, variants, dyn / static arrays.

M15 / M16 · visibility tags · is_default annotation · max-depth capped

Expression evaluator (path expressions)

One wire call: evaluate with expr:"Self.Caption" or "Form.Edit1.Text" or "0x<hex>.ComponentCount". Pascal-style path resolution — dotted member access, both field- and method-backed, chained any depth, with cross-process RPC for property getters carried through transparently. Same return shape as inspect_object's per-prop entries, so existing renderers work.

v1 · Rdbg.Expr parser · reuses BuildPropEntry + RTTI walker · AV diagnostics flow through

Local variable inspection (TD32/TDS reader)

inspect_locals returns parameter and stack-local information for the procedure containing the paused frame's RIP. Reads the EXE's .debug PE section — Borland's CodeView 4 derivative (magic FB09) — that Delphi emits when you build with -V ("Place debug information in EXE"). Decodes both stack-spilled and register-allocated parameters; resolves names through the global names table; demangles procedure signatures via a tight Itanium-ABI-style parser (Borland's variant). Auto-walks past kernel32!DbgBreakPoint at DebugBreak() to land on user code.

Chunk B · Rdbg.Td32 + Rdbg.Td32.LocalsLookup · version-gated on FB09; future format bumps surface as version_warning rather than silent miscompilation · --dump-tds diagnostic CLI for bug reports

Method-backed property values via cross-process RPC

eval_method_props:true hijacks the paused thread and calls each property getter on a 64 KB scratch stack so values come back live — TForm.Caption = 'MainWindow', TStrings.Count = 42, virtual getters dispatch through the VMT, managed-result strings/records decode out of a hidden out-param slot. Per-prop AV failures surface data_addr + op (read/write/execute) so an agent sees which pointer was bad without parsing EXCEPTION_RECORD.

M17 · RunRpcCall · bitness-clean (RCX/RDX on x64, EAX/EDX on x86) · eval_timeout_ms bounds runaway getters
rdbgui

A real debugger interface, not a wrapper.

VCL Win64 native. Three columns, tabbed source viewer, project tree, structured event list. Built with the same wire that everything else speaks.

Project-oriented launch

Open a .dproj, hit Go: connect → launch → install breakpoints → continue past loader BP, all in one click. Project sidecar persists target host:port and default BPs.

Three connection modes

Local spawns the daemon as a child. SSH tunnels through ssh -L and runs the daemon on the remote box. Attach connects to a daemon you started yourself.

Project validation

On Open Project, ValidateDproj checks the filesystem for missing .exe, missing .map next to the exe, non-Win64 platform, non-Debug config. Blocking issues gate Go.

Object explorer

An inspect_object response renders as a flat row table with Inspect-> buttons on every tkClass row. Multiple windows coexist, F5 refreshes, breadcrumb back to parent.

Source viewer with gutters

Owner-drawn line-number gutter + marker column: open circle for MAP-resolvable lines, filled red for set BPs. Click to toggle. Auto-navigates to the top frame on every break / step.

State-machine UI

Disconnected / connected / running / paused. Every menu and toolbar entry knows when it's relevant; only the verbs that make sense at any given moment are enabled.

rdbgc & agents

For when the GUI isn't there.

The CLI is a one-shot driver. The wire is the real interface — agents, scripts, and CI all live there.

Built-in CLI verbs

rdbgc ships with five canonical verbs that cover most workflows:

  • launch — spawn the target, stream events to stdout.
  • debug — one-shot "set BPs, print every hit".
  • inspect — auto inspect_object on every exception.
  • list — enumerate windows, threads, breakpoints.
  • send — cross-process input injection.

Agent-friendly

The wire's pause-loop mental model maps cleanly to autonomous tools. The daemon blocks on every BP hit; an agent reads the event, walks the stack, decides a next step, and sends continue.

Sequential reconnects work since v1.2, so an agent can drop the connection between probes without killing the target.

Read the agent guide →

v1.2 — input injection

Drive a target's UI cross-process.

Five new wire methods that let agents and tooling click through a Delphi VCL UI without a human in the loop.

v1.2

list_windows

Enumerate top-level + child HWNDs in the target process. Class name, caption, rect, owner.

v1.2

post_message / send_input

Fire WM_* into a target HWND, or synthesize raw key/mouse input through SendInput on the daemon side.

v1.2

send_keys / send_key_sequence

Type a string into the focused control, or replay an ordered sequence with optional dwell times.

v1.2

click_at

Synthesize a mouse click at a window-relative coordinate, optionally focusing the HWND first.

Soon

HWND-generation counter

Bump a counter on every major window event so clients know when to re-enumerate.

Soon

send_message (sync)

Synchronous-return wire method for messages whose return value matters (e.g. RM_GetObjectInstance).

Honest scope

What rdbg doesn't do (yet).

rdbg is opinionated about scope. These are explicit non-goals for v1.x — awareness, not regrets.

  • WoW64 cross-bitness debugrdbgd64 debugs Win64 targets, rdbgd32 debugs Win32 targets. Each daemon stays in its own bitness; debugging a Win32 target from a Win64 daemon is intentionally not supported.
  • Non-Windows targets — Linux/macOS need ptrace; that's a different project.
  • TLS on the wire — use an SSH tunnel for trusted-link transport.
  • Expression evaluation — rdbg reads memory and decodes values; no expression parser.
  • Concurrent multi-client daemon — one client at a time. Sequential reconnects work since v1.2.

Ready when you are.

Pick up the binaries, or follow the 5-minute quickstart from a console window.

Download Quickstart