blob: 2bc43c431466a32fea1ab1a71afbd302f78f0f85 [file] [view]
---
name: fuchsia-debugger
description: Use fx debug cli to debug test failures.
---
# Overview
`fx debug cli` is a machine-readable, agent-friendly interface to `zxdb`. It
uses the Debug Adapter Protocol (DAP) to enable turn-based interactions that
agents are already accustomed to and possess knowledge of. Unlike traditional
console-based debuggers, `fx debug cli` exposes a stateless command-line
frontend that issues request-response style commands to the backend, allowing
agents to send and receive structured JSON as input and output.
`fx debug cli` integrates with `fx test` to allow automatic pausing during test
failures, which allows Agents to inspect the state and diagnose the failure.
---
## Testing Workflow
This mode delegates the management of the debugging session to the testing
framework. The framework automatically starts the `zxdb-daemon` process and
connects to the active target.
### Step 1: Start the Test in the Background
Run `fx test` with the `--agent-debugging-mode` flag. Execute this command
asynchronously in the background so you can proceed with other work while
observing the output:
```bash
fx test <test_suite_name> --agent-debugging-mode
```
*Note: This automatically starts the `zxdb-daemon` background process, which
begins listening for messages sent via `fx debug cli`.*
### Step 2: Poll for Target Transitions
Begin polling the active daemon process for events. Start with `last_seen_seq:
0` and increment the sequence number as events are received:
```bash
fx debug cli --json '{"command": "wait-for-event", "last_seen_seq": 0, "timeout": 10}'
```
> [!NOTE]
> **Socket Startup Delay**: The background daemon may take 1-2 seconds to
> initialize and bind the listening socket after `fx test` begins. If the
> initial command fails with `Daemon socket not found`, wait briefly and retry.
#### Example `wait-for-event` Response (Test Failure / Breakpoint):
```json
{
"success": true,
"events": [
{
"seq": 1,
"event": "stopped",
"body": {
"threadId": 1,
"reason": "breakpoint"
}
}
]
}
```
> [!WARNING]
> **Thread ID Key Casing Mapping Pitfall**:
> Output events returned by `wait-for-event` use **camelCase** keys (e.g.
> `"threadId": 1`). However, subsequent request commands (such as
> `stackTrace`, `continue`, or `pause`) strictly require **snake_case**
> parameters (e.g. `"thread_id": 1`).
> Always map `"threadId"` to `"thread_id"` when programmatically invoking
> requests.
Simultaneously, monitor the output and status of the background `fx test` task.
### Step 3: Handle Execution Scenarios
Depending on the status of the test run, handle one of the following scenarios:
#### Scenario A: Test Failure (Debugger Suspended)
If `wait-for-event` returns a `"stopped"` event, it indicates a test failure or
breakpoint hit. Perform diagnostics:
1. **Query Session State:** Retrieve all threads and processes active in the
session to identify the failing thread:
```bash
fx debug cli --json '{"command": "get-state"}'
```
Example Response:
```json
{
"success": true,
"body": {
"threads": [
{ "id": 1, "name": "initial-thread" }
],
"processes": { "12345": "my_test_binary" }
}
}
```
*Note: In the `get-state` thread list representation, the key is `"id"`. Map
this value to the `"thread_id"` parameter in subsequent requests.*
2. **Retrieve Stack Trace:** Get the stack trace for the suspended `thread_id`
to pinpoint the failure location:
```bash
fx debug cli --json '{"command": "stackTrace", "thread_id": 1}'
```
Example Response:
```json
{
"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
}
}
```
3. **Inspect Local Variables:** Retrieve variables and arguments within a
specific stack frame:
```bash
fx debug cli --json '{"command": "variables", "thread_id": 1, "frame_index": 0}'
```
4. **Evaluate Dynamic Expressions / Commands:** Run dynamic expression
evaluation or debugger commands within a stack frame. The underlying
debugger handles `evaluate` requests in the `"repl"` context, executing the
expression directly as a zxdb console command line.
Note that `evaluate` requires the target thread to be stopped prior to
invocation. If the thread is running, the request immediately returns an
error (`success: false`). Ensure the thread is suspended via a breakpoint or
`pause` command before evaluating.
To evaluate an expression or print a variable, prefix the expression with
the debugger's print command:
```bash
fx debug cli --json '{"command": "evaluate", "thread_id": 1, "frame_index": 0, "expression": "print my_var"}'
```
Example Response:
```json
{
"success": true,
"body": {
"result": "42",
"type": "int"
}
}
```
You can also execute arbitrary zxdb console commands such as symbol
inspection:
```bash
fx debug cli --json '{"command": "evaluate", "thread_id": 1, "frame_index": 0, "expression": "sym-info my_var"}'
```
5. **Teardown Session:** Once diagnostics are complete, terminate the debugging
session. This detaches from targets and lets the background test runner exit
cleanly:
```bash
fx debug cli --json '{"command": "stop"}'
```
#### Scenario B: Clean Pass (All Tests Succeed)
If all tests pass cleanly, the background `fx test` task will complete
successfully, exiting with status code `0` and summarizing `FAILED: 0` in its
output.
* **Edge Case: Poll Connection Failures**: Since `fx test` automatically reaps
the background daemon process on a clean exit, any active, blocking
`wait-for-event` command will terminate abruptly with a connection error
(e.g., connection refused or closed socket).
* **Action**: If `wait-for-event` returns a connection failure, check the status
of the background `fx test` task. If it completed successfully (exit code 0),
treat this as a successful clean pass, print a success message, and exit. **No
manual `"stop"` command or teardown is required.**
#### Scenario C: Proactive Breakpoint Installation (Non-Failing Paths)
By default, the testing framework attaches to targets weakly (`attach --weak`).
In this state, symbols are not loaded upfront, and dynamic `break` requests will
remain pending and unresolved until a crash occurs.
To debug a passing path or install breakpoints before test execution begins:
1. Start the test with the `--breakpoint` option to force a normal attach with
immediate symbol loading (paths must be fully qualified from the workspace
root):
```bash
fx test <test_name> --agent-debugging-mode --breakpoint <source_file>:<line>
```
2. Poll for the breakpoint hit stopped event via `wait-for-event`.
3. Once suspended, perform diagnostics and resume execution using `continue`.
---
## JSON Request Reference
All commands are sent as serialized JSON payloads to `fx debug cli --json
'<payload>'`.
| Action | Command Input Payload |
|---|---|
| **Wait for Event** | `{"command": "wait-for-event", "last_seen_seq": <seq>, "timeout": <secs>}` |
| **Get State** | `{"command": "get-state"}` *(Returns active threads, processes, and breakpoints)* |
| **List Threads** | `{"command": "threads"}` |
| **Set/Delete Breakpoint** | `{"command": "break", "file": "<workspace_root_path>", "line": <line_num>, "delete": <optional_bool>}` *(To delete, specify matching file and line)* |
| **Attach Process** | `{"command": "attach", "filter": "<name_or_pid>"}` |
| **Detach Process** | `{"command": "detach", "pid": <pid>}` or `{"command": "detach", "all": true}` |
| **Get Stack Trace** | `{"command": "stackTrace", "thread_id": <thread_id>}` |
| **List Variables** | `{"command": "variables", "thread_id": <thread_id>, "frame_index": <frame_index>}` |
| **Evaluate Expression** | `{"command": "evaluate", "thread_id": <thread_id>, "frame_index": <idx> or "frame_id": <id>, "expression": "<expr>", "start": <start>, "count": <count>}` |
| **Continue Thread** | `{"command": "continue", "thread_id": <thread_id>}` |
| **Pause Thread** | `{"command": "pause", "thread_id": <thread_id>}` |
| **Stop Session** | `{"command": "stop"}` |
### Global Parameters
* **Event History Pruning (`ack_seq`)**: Any command payload can include
`"ack_seq": <seq>` (e.g., `{"command": "get-state", "ack_seq": 5}`). The
daemon will prune all event history up to the acknowledged sequence to
optimize memory.
### Performance & Blocking
* **Smart Blocking**: Commands like `pause`, `stackTrace`, and `wait-for-event`
are blocking operations and may take up to 10 seconds depending on the
target's execution state. Unlike those commands, `evaluate` is non-blocking
and fails immediately if the target thread is not already stopped.