| # zxdb_cli |
| |
| ## Problem Understanding |
| |
| Traditional debugging tools like `zxdb` are designed for interactive, human |
| workflows. Developers can manage continuous streams of logs and maintain |
| debugger state across a long session. |
| |
| However, automated agents and AI assistants operate differently. They work in |
| discrete turns, struggle with commands that run indefinitely, and can be |
| overwhelmed by the verbose output of the Debug Adapter Protocol (DAP). |
| |
| To enable these tools to effectively use `zxdb`, we need a bridge between the |
| chatty, connection-oriented DAP and a simpler request-response model that fits |
| their turn-based workflow. |
| |
| `fx debug cli` solves this by using a background daemon to manage the persistent |
| debugger connection. The CLI then exposes a clean, summarized interface to run |
| commands and get immediate feedback without getting stuck in interactive loops |
| or protocol noise. |
| |
| ## Usage and Behavior |
| |
| ### Fallback to Interactive Session |
| |
| If the entry-point script is run without the `cli` subcommand (or with arguments |
| that don't match it), it behaves as a thin wrapper around `ffx debug connect`. |
| It will pass all arguments directly to the standard interactive `zxdb` session, |
| maintaining the experience for human developers. |
| |
| ### Automated Package Server |
| |
| Both the interactive fallback and the background daemon automatically ensure |
| that the Fuchsia package server is running. This removes the need for developers |
| (or agents) to manually check and start the package server before beginning a |
| debugging session. |
| |
| ### Basic Workflow Examples |
| |
| The CLI allows running a full debugging session using a request-response model. |
| Below is a standard workflow: starting the daemon, attaching to a process, and |
| stopping the daemon. |
| |
| #### Standard CLI Usage |
| |
| 1. **Start the daemon**: |
| ```bash |
| fx debug cli start |
| ``` |
| Output: |
| ``` |
| Spawning daemon... |
| Daemon is ready. |
| ``` |
| |
| 2. **Attach to a process** (e.g., attaching to `cobalt.cm`): |
| ```bash |
| fx debug cli attach cobalt.cm |
| ``` |
| Output: |
| ``` |
| {"success": true, "message": "", "body": {"success": true}} |
| ``` |
| |
| 3. **Query threads**: |
| ```bash |
| fx debug cli threads |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"threads": [{"id": 1, "name": "main"}, {"id": 2, "name": "worker"}]}} |
| ``` |
| |
| 4. **Pause a thread**: |
| ```bash |
| fx debug cli pause 1 |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"success": true}} |
| ``` |
| |
| 5. **Resume a thread**: |
| ```bash |
| fx debug cli continue 1 |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"success": true}} |
| ``` |
| |
| 6. **Get stack trace**: |
| ```bash |
| fx debug cli stackTrace 1 |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"stackFrames": [{"id": 0, "name": "main", "line": 42}]}} |
| ``` |
| |
| 7. **Stop the daemon**: |
| ```bash |
| fx debug cli stop |
| ``` |
| Output: |
| ``` |
| {"success": true, "message": "Daemon stopping", "body": null} |
| ``` |
| |
| #### JSON CLI Usage (With JSON) |
| |
| For programmatic environments or agents, the same operations can be invoked via JSON payloads using the `--json` flag. |
| |
| 1. **Start the daemon**: |
| ```bash |
| fx debug cli --json '{"command": "start"}' |
| ``` |
| Output: |
| ``` |
| Spawning daemon... |
| Daemon is ready. |
| ``` |
| |
| 2. **Attach to a process**: |
| ```bash |
| fx debug cli --json '{"command": "attach", "filter": "cobalt.cm"}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "message": "", "body": {"success": true}} |
| ``` |
| |
| 3. **Query threads**: |
| ```bash |
| fx debug cli --json '{"command": "threads"}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"threads": [{"id": 1, "name": "main"}, {"id": 2, "name": "worker"}]}} |
| ``` |
| |
| 4. **Pause a thread**: |
| ```bash |
| fx debug cli --json '{"command": "pause", "thread_id": 1}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"success": true}} |
| ``` |
| |
| 5. **Resume a thread**: |
| ```bash |
| fx debug cli --json '{"command": "continue", "thread_id": 1}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"success": true}} |
| ``` |
| |
| 6. **Get stack trace**: |
| ```bash |
| fx debug cli --json '{"command": "stackTrace", "thread_id": 1}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "body": {"stackFrames": [{"id": 0, "name": "main", "line": 42}]}} |
| ``` |
| |
| 7. **Stop the daemon**: |
| ```bash |
| fx debug cli --json '{"command": "stop"}' |
| ``` |
| Output: |
| ``` |
| {"success": true, "message": "Daemon stopping", "body": null} |
| ``` |
| |
| |
| ## Technical Architecture |
| |
| The system is built around two primary components: a long-lived background |
| **Daemon** and a short-lived **CLI** wrapper (`fx debug cli`). |
| |
| * **The Daemon**: This process maintains the continuous connection to the |
| debugger. It starts the package server and `zxdb`, connects to the DAP |
| server, and listens on a Unix Domain Socket (UDS) for requests. It queues |
| critical events and maintains the aggregate state of the session. |
| * **The CLI (`fx debug cli`)**: This is a stateless tool designed for |
| automated agents. It connects to the daemon via UDS, sends a command, |
| receives the response, and exits. |
| |
| ### How They Fit Together |
| |
| ```mermaid |
| graph TD |
| subgraph Host Machine |
| AI["Automated Agent"] -->|Invokes| CLI["fx debug cli"] |
| CLI -->|"UDS (JSON Protocol)"| Daemon["Daemon Binary"] |
| Daemon -->|"DAP (TCP Port 15678)"| Zxdb["zxdb"] |
| Daemon -->|Manages| PkgServer["Package Server"] |
| end |
| subgraph Fuchsia Target |
| Zxdb -->|"Debug IPC"| DebugAgent["Debug Agent"] |
| DebugAgent -->|"syscalls"| TargetApp["Target Application"] |
| end |
| ``` |
| |
| ### Communication Protocol |
| |
| The protocol between the CLI and the Daemon uses a simplified, non-strict |
| version of the Debug Adapter Protocol (DAP), transmitted as newline-delimited |
| JSON over the Unix Domain Socket. |
| |
| #### Smart Proxy and Schemas |
| |
| To maximize efficiency for automated agents, the daemon acts as a smart proxy. |
| It strips away verbose protocol metadata and boils messages down to their |
| essential fields. This ensures that response payloads are compact and |
| token-efficient. |
| |
| Example `get-state` Response: |
| ```json |
| { |
| "processes": [ |
| { |
| "id": "p1", |
| "name": "target_app", |
| "threads": [ |
| { |
| "id": 1, |
| "name": "main" |
| } |
| ] |
| } |
| ] |
| } |
| ``` |
| |
| ## Ecosystem Fit |
| |
| This tool layers on top of Fuchsia's existing debug infrastructure. It does not |
| replace `zxdb`; instead, it leverages `zxdb`'s DAP support (found in |
| `src/lib/debug/dap/python`) to provide a machine-friendly interface. By bridging |
| the gap between interactive streams and request-response patterns, it enables |
| automated tools to use our standard debugging stack effectively. |