Tutorial: Debug tests using agent debugging mode

This tutorial explains the concepts and workflow of agent debugging mode in fx test and how to manage symbol loading and breakpoint resolution when integrating with automated scripts or IDE debuggers.

Overview

Agent debugging mode is designed for headless automation, IDE integration (like VS Code), and scripts. It runs zxdb as a background server exposing a Debug Adapter Protocol (DAP) endpoint on a Unix domain socket at /tmp/fx-debug-daemon.sock.

To start a test suite in this mode:

fx test <TEST_SUITE_NAME> --agent-debugging-mode

Default behavior: weak attachment

When you run fx test --agent-debugging-mode without any breakpoint arguments, the testing framework attaches zxdb to the target process "weakly" (using the attach --weak command).

What is weak attachment?

Weak attachment optimizes startup performance by preventing zxdb from proactively querying modules and loading symbol indexes for the process.

  • Deferred symbol loading: zxdb does not load symbols upon the initial attach.
  • Resolution limitations: because the symbol index is missing, any file:line or symbolic breakpoints registered dynamically through the DAP UDS interface after the test starts will remain "pending" and cannot be verified or resolved.
  • Autoresolution on crash: an exception or external event (such as a Rust panic or a C++ assertion failure) triggers symbol loading. Once stopped, symbols load automatically and pending breakpoints resolve.

Solution: proactive breakpoint installation

If you need to debug a passing path, or if you want to ensure your breakpoints are active and verified before execution starts, you must specify them on the fx test command line:

fx test <TEST_SUITE_NAME> --agent-debugging-mode --breakpoint <SOURCE_FILE>:<LINE>

How it works

  1. The presence of the --breakpoint option tells the fxtest framework to attach zxdb normally (omitting the --weak flag).
  2. Because the attachment is normal, zxdb immediately loads symbols for the target process.
  3. zxdb installs and resolves the specified breakpoints upfront during the initialization handshake.
  4. When the test execution hits the breakpoint, the debugger suspends the process and sends a "stopped" event with "reason": "breakpoint" to the DAP client.

Interactive debugging using fx debug cli

Once the execution stops at a breakpoint or exception, you can interact with the debugger from a second terminal using the fx debug cli wrapper tool.

To connect and retrieve the current debugger state:

fx debug cli --json '{"command": "get-state"}'

The tool returns a JSON response listing active processes and threads:

{
  "success": true,
  "body": {
    "threads": [
      { "id": 1, "name": "initial-thread" }
    ],
    "processes": { "12345": "my_test_binary" }
  }
}

To retrieve a stack trace for a specific thread, pass the thread_id:

fx debug cli --json '{"command": "stackTrace", "thread_id": 1}'

The tool returns the stack frame details:

{
  "success": true,
  "body": {
    "stackFrames": [
      {
        "id": 0,
        "name": "my_test_function",
        "source": {
          "name": "main_test.cc",
          "path": "/src/main_test.cc"
        },
        "line": 42,
        "column": 1
      }
    ],
    "totalFrames": 1
  }
}

Once diagnostic inspection is complete, resume the execution of the thread:

fx debug cli --json '{"command": "continue", "thread_id": 1}'

Use fx debug cli help to get a list of all commands you can run.

See also