Agent-friendly GDB CLI with an in-process bridge.
pry runs a Python socket server inside GDB and exposes every debugging capability as a clean JSON-over-Unix-socket RPC call. Agents drive GDB by invoking pry <command> shell commands — no GDB/MI parsing, no expect scripts, no fragile screen scraping.
Agent (Claude Code, OpenAI, etc.)
│ shell exec
▼
pry CLI
│ JSON over Unix socket
▼
GDB bridge plugin (runs inside GDB)
│ gdb Python API
▼
GDB
The bridge runs in-process using gdb.post_event() for thread-safe access to the full GDB Python API — frames, breakpoints, inferiors, memory, registers, and more. A reader-writer lock allows concurrent inspection commands while serializing mutations.
Requires Python >= 3.10, uv, and GDB with Python support.
uv tool install -e .Then install the GDB plugin:
pry plugin installThis symlinks the bridge into ~/.gdb/pry_agent_bridge/ and prints a source line to add to your ~/.gdbinit.
Install the bundled Claude Code/Codex skills:
pry skill installThat symlinks the bundled skills into ~/.claude/skills/ by default. If ~/.codex/ exists, it also installs them into ~/.codex/skills/. Use --mode copy if you want standalone copies instead. Restart your agent to pick up a new or renamed skill.
Launch a headless GDB session and start debugging:
# Start GDB with your binary (headless, bridge auto-starts)
pry launch ./mybinary
# Set a breakpoint and run
pry break set main
pry run
# Inspect state
pry backtrace
pry locals
pry registers
pry print "some_variable"
pry disasm
# Step through code
pry next
pry step
pry continue
# Done
pry killEvery command accepts --format [text|json|ndjson], --out <path>, and --instance <pid>.
| Command | Description |
|---|---|
pry launch [binary] [-- gdb-args...] |
Spawn headless GDB with bridge (--instance-id NAME, --gdb-binary) |
pry kill |
Terminate GDB session |
pry logs |
Show a session's captured inferior stdout/stderr and GDB output |
pry doctor |
Bridge health check and version info |
pry plugin install |
Install GDB bridge plugin |
pry skill install |
Install bundled agent skills |
| Command | Description |
|---|---|
pry load <path> |
Load a binary into GDB |
pry attach <pid> |
Attach to a running process |
pry connect <host:port> |
Connect to a remote GDB target (QEMU, gdbserver) (--connect-timeout) |
pry disconnect |
Disconnect from the remote target |
pry inferior list |
List inferiors |
All execution commands block until the inferior stops or exits, returning structured stop info (reason, frame, thread). Use --timeout N to auto-interrupt after N seconds. Use --background to return immediately while the inferior keeps running.
| Command | Description |
|---|---|
pry run [args...] |
Start program (--timeout, --background) |
pry continue |
Continue from stop (--timeout, --background) |
pry step [count] |
Source-level step into |
pry next [count] |
Source-level step over |
pry stepi |
Instruction-level step into |
pry nexti |
Instruction-level step over |
pry finish |
Run until function returns (--timeout, --background) |
pry until <location> |
Run until location (--timeout, --background) |
pry jump <location> |
Resume execution at a location (GDB jump) |
pry interrupt |
Interrupt running inferior (always works, even during background exec) |
pry status |
Show inferior execution state (running/stopped) |
pry wait |
Wait for running inferior to stop (--timeout) |
pry threads |
List threads with selected frame info (--pc, --function) |
When --timeout fires, the bridge auto-interrupts the inferior and returns the stop info with timeout_interrupt: true. The bridge remains responsive for subsequent commands.
| Command | Description |
|---|---|
pry break set <loc> |
Set breakpoint (--condition, --temporary, --hardware, --rebase, --image-base) |
pry break list |
List all breakpoints |
pry break delete <num> |
Delete breakpoint |
pry break enable/disable <num> |
Toggle breakpoint |
pry watch set <expr> |
Set watchpoint (--type write|read|access) |
pry watch list/delete/enable/disable |
Manage watchpoints |
For PIE binaries, use --rebase MODULE to set breakpoints by static offset:
# Binary Ninja says function is at 0x1234 — pry resolves the runtime address automatically
pry break set *0x1234 --rebase myprogram
# If your tool uses a non-zero image base (e.g., Binary Ninja's default 0x400000)
pry break set *0x40656e --rebase myprogram --image-base 0x400000| Command | Description |
|---|---|
pry trace |
Trace memory accesses within a code range |
pry trace --watch 0x7fffffffd5d4 --range 0x404610-0x405e30
pry trace --watch 0x7fffffffd5d4 --watch-size 4 --range 0x404610-0x405e30 --type access --timeout 60Uses hardware watchpoints gated by range boundary breakpoints for native-speed tracing. Reports every instruction within the range that touches the watched memory.
| Command | Description |
|---|---|
pry backtrace |
Stack backtrace (--full, --limit N) |
pry frame info |
Current frame info |
pry frame select <level> |
Select stack frame |
pry locals |
Local variables |
pry args |
Function arguments |
pry print <expr> |
Evaluate expression |
pry registers |
CPU registers (--all) |
pry memory read <addr> <len> |
Read memory (--display hex|string|bytes|pretty, --plain) |
pry memory write <addr> <hex> |
Write memory |
pry mappings |
List process memory mappings (structured vmmap) |
pry examine <addr> |
Examine memory GDB-style (x/NFU) |
pry call <expr> |
Call an inferior function and return its value |
pry display add/list/remove |
Auto-display expressions on each stop |
| Command | Description |
|---|---|
pry disasm [location] |
Disassemble (--count N) |
pry functions |
List functions (--query, --limit, --offset) |
pry symbols |
List symbols (--query, --limit, --offset) |
pry symbols |
List symbols (--query, --limit, --offset) |
pry types show <name> |
Type info (size, fields, declaration) |
pry source list [location] |
Source listing (--count N) |
pry info files |
ELF section info |
pry kbase |
Find the kernel virtual text base (KASLR); pwndbg then IDT fallback |
| Command | Description |
|---|---|
pry load <path> |
Load binary |
pry attach <pid> |
Attach to process |
pry inferior list |
List inferiors |
pry threads |
List threads with frame PCs/functions |
# Run any raw GDB command — works with pwndbg, custom scripts, user-defined commands
pry gdb 'info proc mappings'
pry gdb 'vmmap' # e.g. a pwndbg command, if pwndbg is loaded
# Run arbitrary Python inside GDB
pry py exec --code 'result["value"] = gdb.parse_and_eval("argc").string()'
pry py exec --script trace.py --timeout 120pry gdb forwards a raw command line to GDB, so anything GDB (or a loaded extension like pwndbg) understands is available without a dedicated pry subcommand. pry py exec runs arbitrary Python inside GDB with the gdb module and a result dict in scope. Use --timeout to prevent long-running scripts from hanging the bridge.
pry supports multiple concurrent GDB sessions. Each bridge registers itself under ~/.cache/pry/instances/ (override with $PRY_CACHE_DIR) as <gdb-pid>.json (metadata), <pid>.sock, and <pid>.log. Stale registrations are purged automatically as instances are discovered.
With a single session, commands auto-connect. With multiple, target one with --instance:
pry launch ./server
pry launch ./client
pry --instance 12345 break set handle_request # by GDB pidYou can also assign a stable name at launch and target it by name — handy when pids change across restarts:
pry launch ./server --instance-id web
pry --instance web break set handle_request # by name--instance accepts a pid or a name; if a name and a pid are spelled the same, the pid wins.
When output exceeds 10,000 tokens (measured with the o200k_base tokenizer), pry automatically spills under the pry cache (~/.cache/pry/spills/ by default, or $PRY_CACHE_DIR/spills) and prints an artifact envelope to stderr with the path, byte count, token count, and SHA-256. This prevents blowing agent context windows. Use --out <path> to always write to a file.
Newline-delimited JSON over a Unix stream socket.
Request:
{"id": "uuid", "op": "backtrace", "params": {"full": true}}Response:
{"ok": true, "result": [...], "error": null}# Install in editable mode
uv tool install -e .
# Build test fixtures
make -C tests/fixtures
# Run tests
pytestMIT