Rust · reverse debugging
Bloodhound is a portable time-travel debugger built on a small self-contained stack VM. Most debuggers only move forward, so overshooting the moment a bug appears means restarting. Bloodhound records every instruction it runs, so you can step back, jump to any earlier step, and reconstruct the exact machine state forward execution had there. The stack, memory, locals, call frames, and program counter all rewind together. Pick a sample, set a breakpoint, and scrub the timeline in both directions.
sum_loop selected. Click the source line sum += i to set a breakpoint (its gutter turns red).global[0] climb 1, 3, 6, 10, 15 as the loop runs.0 and press Toggle watch. Continue once more and read the reported old to new change.factorial and use Continue to a breakpoint inside recurse, then read the deep call stack.The debugger's claims are backed by a machine-checkable oracle in tests/gate.rs. It builds a ground-truth forward trace that snapshots the full VM state at every step, then checks each feature against it. Reverse execution is proven to be an exact inverse, not an approximation.
# tests/gate.rs, checked against the ground-truth forward trace time-travel reversibility forward K then back K is byte-identical, goto(N) rebuilds step N breakpoint correctness stops at exactly the breakpoint addresses, nowhere else watchpoint correctness fires on exactly the steps the watched cell changes step-over / step-out same-frame next line, and return of the current frame conditional breaks + watchescompared to an independent Rust predicate, both directions # more random programs, longer run: BLOODHOUND_FUZZ_OPS=400 cargo test
This page reaches the same result the Rust engine does. It runs the program once and deep-clones the state after every step, so every motion command is index math over that history; the Rust engine reaches the identical state with a per-instruction undo journal. Conditional-breakpoint expressions are total and strictly read-only, so evaluating a condition can never disturb the machine.
The primitives that matter
Every one is a real command you can drive above or from the REPL, each small enough to read in the source.
back moves one instruction backward in time and restores the exact earlier state. The Rust engine does it with a per-instruction undo journal.
goto <step> jumps to an absolute step index, forward or back, reconstructing the state forward execution had at that point.
break <line> and breaki <addr>, each optionally if <expr>. Reverse-continue with rc stops at the previous breakpoint.
watch a global, memory cell, or local. It fires on exactly the steps the watched location changes, matching a reference diff of the trace.
s, n, and out work over source lines: descend into calls, run a call to completion, or return from the current frame.
Conditions read pc, depth, top, globals[e], and memory[e]. Evaluation is total and strictly read-only, so a condition can never disturb the machine.
Reverse debuggers exist. Bloodhound trades their reach over native processes for something small, deterministic, and identical on every platform.
Records and replays a native process, powerful for real programs but bound to Linux and specific hardware and heavyweight to set up.
Adds reverse execution to native targets, tied to gdb and platform-specific process control.
Captures full traces of Windows processes, a large tool built for a single operating system.
Debugs its own tiny stack VM instead of a native process, so it is one dependency-free Rust crate that behaves identically everywhere and whose reverse execution is checked by a machine oracle. Small enough to read in one sitting, safe enough for an agent to drive.
A REPL over a handful of built-in samples, an assembly loader, and the correctness gate.
cargo run loads a sample and drops into the debugger. Type help for every command and src to see the program.
cargo run -- list shows the built-in programs; cargo run -- file prog.asm loads your own assembly.
cargo run -- demo runs the scripted demonstration and exits.
cargo test runs the oracle in tests/gate.rs and the stress suite; the fuzz and stress scale knobs are environment variables.
# load the factorial sample into the REPL cargo run # run the scripted demonstration and exit cargo run -- demo # load a built-in sample by name, or list them cargo run -- sum_loop cargo run -- list # load an assembly file cargo run -- file prog.asm # the unit tests and the correctness gate cargo test # inside the REPL: c / rc / s / n / back / goto <step> / break / watch