| <!-- |
| Copyright 2026 The Fuchsia Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| # Driver Debugging Framework |
| |
| Fuchsia provides a productionized driver debugging framework that allows developers to author, expose, and interactively execute driver-specific diagnostic and debug commands. |
| |
| ## Architecture |
| |
| The debugging framework consists of four primary components: |
| |
| 1. **`fuchsia.driver.debug.Debug` FIDL Protocol**: Defines the standardized interface for listing and executing debug commands across drivers. |
| 2. **Rust Helper Library (`sdk/lib/driver/debug/rust`)**: Simplifies the authoring of debug subcommands in Rust drivers with `argh` argument parsing, automatic `--help` generation, command discovery, and request dispatching. |
| 3. **`debug` CLI Utility (`src/devices/bin/driver_debug`)**: A host/target CLI tool packaged for interactive execution inside `component explore`. |
| 4. **Driver Integration (`driver/debug.shard.cml`)**: DFv2 drivers include `driver/debug.shard.cml` in their component manifest and export `fuchsia.driver.debug.Debug` via `ServiceFs` in their outgoing directory (`/out/svc/fuchsia.driver.debug.Debug`). |
| |
| --- |
| |
| ## FIDL Protocol (`fuchsia.driver.debug`) |
| |
| The `fuchsia.driver.debug` protocol exposes two methods: |
| |
| ```fidl |
| library fuchsia.driver.debug; |
| |
| using zx; |
| |
| const MAX_COMMAND_NAME_LENGTH uint32 = 256; |
| const MAX_ARG_LENGTH uint32 = 1024; |
| const MAX_ARG_COUNT uint32 = 128; |
| const MAX_DESCRIPTION_LENGTH uint32 = 1024; |
| const MAX_COMMAND_COUNT uint32 = 64; |
| |
| type CommandInfo = table { |
| 1: name string:MAX_COMMAND_NAME_LENGTH; |
| 2: description string:MAX_DESCRIPTION_LENGTH; |
| }; |
| |
| @discoverable |
| open protocol Debug { |
| flexible Execute(resource struct { |
| args vector<string:MAX_ARG_LENGTH>:MAX_ARG_COUNT; |
| stdout zx.Handle:SOCKET; |
| stderr zx.Handle:SOCKET; |
| }) -> (struct { |
| exit_code int32; |
| }) error zx.Status; |
| |
| flexible ListCommands() -> (struct { |
| commands vector<CommandInfo>:MAX_COMMAND_COUNT; |
| }) error zx.Status; |
| }; |
| ``` |
| |
| --- |
| |
| ## Implementing Debug Commands in a Rust Driver |
| |
| ### 1. Include the CML Shard in the Driver Manifest |
| |
| `driver/debug.shard.cml` is not included implicitly for all drivers. Any driver that implements the `fuchsia.driver.debug.Debug` protocol must explicitly include `"driver/debug.shard.cml"` in its `.cml` component manifest. |
| |
| This shard: |
| - Declares and exposes the `fuchsia.driver.debug.Debug` protocol capability from `self`. |
| - Configures the `fuchsia.dash.launcher-tool-urls` facet (`"fuchsia-pkg://fuchsia.com/driver_debug"`) so the `debug` CLI tool is automatically available in `ffx component explore`. |
| |
| ```json5 |
| { |
| include: [ |
| "driver/debug.shard.cml", |
| "driver_component/driver.shard.cml", |
| "inspect/client.shard.cml", |
| "syslog/client.shard.cml", |
| ], |
| program: { |
| runner: "driver", |
| binary: "driver/my_driver.so", |
| bind: "meta/bind/my_driver.bindbc", |
| }, |
| } |
| ``` |
| |
| ### 2. Define Subcommands with `argh` |
| |
| Define your subcommand structs and top-level subcommand enum with `#[derive(FromArgs)]` and `#[argh(subcommand)]`: |
| |
| ```rust |
| use argh::FromArgs; |
| |
| #[derive(FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "ping")] |
| /// Ping the driver to verify connectivity. |
| pub struct PingArgs { |
| #[argh(option, short = 'c', default = "1", description = "number of pings")] |
| pub count: u32, |
| } |
| |
| #[derive(FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "reset")] |
| /// Reset the hardware device state. |
| pub struct ResetArgs { |
| #[argh(switch, description = "perform a hard reset")] |
| pub hard: bool, |
| } |
| |
| #[derive(FromArgs, Debug, PartialEq)] |
| #[argh(subcommand)] |
| pub enum MyDriverCommands { |
| Ping(PingArgs), |
| Reset(ResetArgs), |
| } |
| ``` |
| |
| ### 3. Handle Debug Requests |
| |
| Use `driver_debug::next_command` in a `while let` loop to read parsed subcommands from a `DebugRequestStream`: |
| |
| ```rust |
| use fidl_fuchsia_driver_debug::DebugRequestStream; |
| |
| async fn handle_debug(mut stream: DebugRequestStream) -> Result<(), fidl::Error> { |
| while let Some((cmd, responder)) = driver_debug::next_command(&mut stream).await? { |
| let result: Result<String, anyhow::Error> = match cmd { |
| MyDriverCommands::Ping(args) => { |
| let mut out = String::new(); |
| for i in 1..=args.count { |
| out.push_str(&format!("ping {i}\n")); |
| } |
| Ok(out) |
| } |
| MyDriverCommands::Reset(args) => { |
| if args.hard { |
| // perform hardware reset |
| Ok("hard reset completed\n".to_string()) |
| } else { |
| Ok("soft reset completed\n".to_string()) |
| } |
| } |
| }; |
| responder.send(result)?; |
| } |
| Ok(()) |
| } |
| ``` |
| |
| `driver_debug::next_command` automatically: |
| - Responds to `ListCommands` requests using `argh::SubCommands` metadata (command names and doc comments). |
| - Handles `--help` flags and syntax/argument parsing errors on `Execute` requests, writing output to `stdout`/`stderr` and returning the appropriate exit code (`0` for help, `2` for syntax errors). |
| |
| Alternatively, you can use `driver_debug::serve(stream, handler)` with an async closure when you don't need custom stream loop control. |
| |
| ### 4. Serve the Protocol in the Driver |
| |
| In your driver's `start` method: |
| |
| ```rust |
| use fidl_fuchsia_driver_debug::DebugRequestStream; |
| use fuchsia_component::server::ServiceFs; |
| |
| let mut service_fs = ServiceFs::new(); |
| |
| service_fs.dir("svc").add_fidl_service(move |stream: DebugRequestStream| { |
| fuchsia_async::Scope::current().spawn(async move { |
| let _ = handle_debug(stream).await; |
| }); |
| }); |
| |
| context.serve_outgoing(&mut service_fs)?; |
| ``` |
| |
| --- |
| |
| ## Using the `debug` CLI in `component explore` |
| |
| When debugging a running system, use `ffx component explore` to drop into the driver component namespace: |
| |
| ```sh |
| $ ffx component explore /bootstrap/boot-drivers:my-driver |
| ``` |
| |
| ### List Available Commands |
| |
| ```sh |
| $ debug list-commands |
| COMMAND DESCRIPTION |
| ------- ----------- |
| ping Ping the driver to verify connectivity. |
| reset Reset the hardware device state. |
| ``` |
| |
| ### Execute Commands |
| |
| ```sh |
| $ debug ping -c 3 |
| ping 1 |
| ping 2 |
| ping 3 |
| ``` |
| |
| ```sh |
| $ debug reset --hard |
| hard reset completed |
| ``` |
| |
| ### Help and Diagnostics |
| |
| ```sh |
| $ debug --help |
| Usage: debug <command> [<args>] |
| |
| Driver debug commands. |
| |
| Options: |
| --help display usage information |
| |
| Commands: |
| ping Ping the driver to verify connectivity. |
| reset Reset the hardware device state. |
| ``` |
| |
| ### Return Codes |
| |
| - `0`: Success (or `--help` output printed to standard output). |
| - `1`: Driver internal execution error / handler failure (details in standard error). |
| - `2`: Syntax or argument parsing error (details in standard error). |