Skip to content

Breakpoints never stop execution — async I/O race in process launch #1

@Luiz-Frias

Description

@Luiz-Frias

Summary

When using ferroscope v1.1.0 as an MCP server (via Claude Code), breakpoints are set and resolved correctly but the process never stops at them. debug_state always reports "running" after debug_continue / process launch.

Environment

  • ferroscope: v1.1.0 (crates.io) — binary reports "v2.0" at startup
  • LLDB: 21.1.8 (Homebrew LLVM, /opt/homebrew/opt/llvm/bin/lldb)
  • Platform: macOS 15 (Darwin 25.2.0), Apple Silicon (arm64)
  • Rust: nightly, edition 2024
  • MCP client: Claude Code (Anthropic CLI)

Reproduction

  1. Configure ferroscope as an MCP server in .mcp.json:

    { "mcpServers": { "ferroscope": { "command": "/path/to/ferroscope", "args": [] } } }
  2. Use MCP tools to load a binary and set breakpoints:

    debug_run(binary_path="/path/to/debug_binary")
    debug_break(location="main")        → Breakpoint 1: 2 locations.
    debug_continue()                    → Process launched, state: "running"
    debug_state()                       → state: "running", location: null
    debug_backtrace()                   → "Program must be stopped to show backtrace"
    
  3. All breakpoints remain at hit count = 0.

Root Cause Analysis

LLDB works correctly when invoked directly:

lldb --batch \
  -o "target create /path/to/binary" \
  -o "breakpoint set --name main" \
  -o "process launch" \
  -o "thread backtrace" \
  -o "quit"

Output confirms the breakpoint is hit:

Process 42451 stopped
* thread #1, stop reason = breakpoint 1.2
    frame #0: 0x... main

The issue appears to be in ferroscope's stdio pipe communication with LLDB. When process launch is sent:

  1. LLDB writes the launch confirmation ("Process N launched: ...")
  2. LLDB writes the stop notification ("Process N stopped, stop reason = breakpoint ...")
  3. Ferroscope's read loop captures message (1) but returns before message (2) arrives due to a timeout

This is supported by the [TIMEOUT - Command may still be processing] messages that appear on several commands (debug_run, debug_list_breakpoints).

Workaround

Use LLDB batch scripts directly:

lldb --batch --source scripts/debug_trace.lldb

This processes commands synchronously and correctly stops at all breakpoints.

Breakpoint Formats Tested

Format Result
--name main Resolved (2 locations) but never hit
--name ensure_ready Resolved (1 location) but never hit
--name build_dfrr_state Resolved (1 location) but never hit
--file kernel.rs --line 154 N/A (ferroscope only supports --name)

All three resolved breakpoints stop correctly when using LLDB directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions