blob: 4b6ff90b3f94c2128ef32cacdfa849a6d7d77e28 [file] [log] [blame] [view]
# Test and debug the driver
Fuchsia supports step-through debugging of components using the Fuchsia debugger
(`zxdb`). The debugger attaches to the host process where a component is running,
and allows the developer to set breakpoints and step through code execution.
The Test Runner Framework enables developers to write tests that exercise driver
components.
In this section, you'll use the Fuchsia debugger (`zxdb`) to inspect a running
driver and build a test component to exercise the driver's functionality.
Note: For complete details on `zxdb` and the Test Runner Framework, see
[The Fuchsia Debugger][fuchsia-debugger] and
[Testing with Components][component-testing].
## Connect the debugger
To connect the Fuchsia debugger to the driver component, you'll need to
determine the PID of the host process. Use the `ffx driver list-hosts` command
to discover the PID of the host process where the driver is loaded:
```posix-terminal
ffx driver list-hosts
```
The command outputs a list similar to the following. Locate the driver host
where the `qemu_edu` driver is listed:
```none {:.devsite-disable-click-to-copy}
Driver Host: 5053
fuchsia-boot:///#meta/block.core.cm
fuchsia-boot:///#meta/bus-pci.cm
fuchsia-boot:///#meta/cpu-trace.cm
fuchsia-boot:///#meta/fvm.cm
fuchsia-boot:///#meta/hid.cm
fuchsia-boot:///#meta/netdevice-migration.cm
fuchsia-boot:///#meta/network-device.cm
fuchsia-boot:///#meta/platform-bus-x86.cm
fuchsia-boot:///#meta/platform-bus.cm
fuchsia-boot:///#meta/ramdisk.cm
fuchsia-boot:///#meta/sysmem.cm
fuchsia-boot:///#meta/virtio_block.cm
fuchsia-boot:///#meta/virtio_ethernet.cm
fuchsia-pkg://fuchsia.com/virtual_audio#meta/virtual_audio_driver.cm
Driver Host: 7774
fuchsia-boot:///#meta/intel-rtc.cm
Driver Host: 7855
fuchsia-boot:///#meta/pc-ps2.cm
{{ '<strong>' }}Driver Host: 44887 {{ '</strong>' }}
{{ '<strong>' }} fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm {{ '</strong>' }}
```
Make a note of the PID for the `qemu_edu` driver host.
In the above example, the PID is 44887.
Start the Fuchsia debugger with `ffx debug connect`:
```posix-terminal
ffx debug connect
```
Once the debugger connects to the target device, attach to the `qemu_edu` driver
host from the `zxdb` prompt:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>attach <var label="driver host">HOST_PID</var>
</pre>
Replace `HOST_PID` with the PID of the driver host identified in the previous
step. For example:
```none {:.devsite-disable-click-to-copy}
[zxdb] attach 44887
```
Set a breakpoint in the driver's `ComputeFactorial` function:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>break QemuEduServer::ComputeFactorial
</pre>
The command prints output similar to the following to indicate where the
breakpoint is set:
```none {:.devsite-disable-click-to-copy}
[zxdb] break QemuEduServer::ComputeFactorial
Created Breakpoint 1 @ QemuEduServer::ComputeFactorial
47 void QemuEduServer::ComputeFactorial(ComputeFactorialRequestView request,
◉ 48 ComputeFactorialCompleter::Sync& completer) {
49 auto edu_device = device_.lock();
```
## Step through the driver function
In a separate terminal, run the `eductl` tool again:
```posix-terminal
bazel run //fuchsia-codelab/qemu_edu/tools:pkg.eductl_tool -- fact 12
```
In the `zxdb` terminal, verify that the debugger has hit the breakpoint in the driver's
`ComputeFactorial` function. For example:
```none {:.devsite-disable-click-to-copy}
🛑 thread 2 on bp 1 qemu_edu::QemuEduServer::ComputeFactorial(qemu_edu::QemuEduServer*, fidl::WireServer<fuchsia_examples_qemuedu::Device>::ComputeFactorialRequestView, fidl::Completer<fidl::internal::WireCompleterBase<fuchsia_examples_qemuedu::Device::ComputeFactorial> >::Sync&) • qemu_edu.cc:144
46 // Driver Service: Compute factorial on the edu device
47 void QemuEduServer::ComputeFactorial(ComputeFactorialRequestView request,
▶ 48 ComputeFactorialCompleter::Sync& completer) {
49 auto edu_device = device_.lock();
50 if (!edu_device) {
```
Use the `list` command at the `zxdb` prompt to show where execution is currently
paused:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>list
</pre>
The command prints output similar to the following:
```none {:.devsite-disable-click-to-copy}
46 // Driver Service: Compute factorial on the edu device
47 void QemuEduServer::ComputeFactorial(ComputeFactorialRequestView request,
▶ 48 ComputeFactorialCompleter::Sync& completer) {
49 auto edu_device = device_.lock();
50 if (!edu_device) {
51 FDF_LOG(ERROR, "Unable to access device resources.");
52 completer.ReplyError(ZX_ERR_BAD_STATE);
53 return;
54 }
55
56 uint32_t input = request->input;
57
58 edu_device->ComputeFactorial(input);
```
Step into the `ComputeFactorial` function using the `next` command:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>next
</pre>
Print the contents of the request passed into the function:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>print request
</pre>
The command prints output containing the factorial input value:
```none {:.devsite-disable-click-to-copy}
(*)0x747c1f2e98 ➔ {input = 12}
```
Exit the debugger session and disconnect:
<pre class="devsite-click-to-copy">
<span class="no-select">[zxdb] </span>exit
</pre>
## Create a new system test component
In this section, you'll create a new test component that exercises the exposed
functions of the `qemu_edu` driver.
Create a new project directory in your Bazel workspace for a new test component:
```posix-terminal
mkdir -p fuchsia-codelab/qemu_edu/tests
```
After you complete this section, the project should have the following directory
structure:
```none {:.devsite-disable-click-to-copy}
//fuchsia-codelab/qemu_edu/tests
|- BUILD.bazel
|- meta
| |- qemu_edu_system_test.cml
|- qemu_edu_system_test.cc
```
Create the `qemu_edu/tests/BUILD.bazel` file and add the following statement to
include the necessary build rules from the Fuchsia SDK:
`qemu_edu/tests/BUILD.bazel`:
```bazel
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/BUILD.bazel" region_tag="imports" adjust_indentation="auto" %}
```
Create a new `qemu_edu/tests/meta/qemu_edu_system_test.cml` component manifest
file to the project with the following contents:
`qemu_edu/tests/meta/qemu_edu_system_test.cml`:
```json5
{
include: [
"//sdk/lib/gtest/gtest.shard.cml",
"syslog/client.shard.cml",
],
program: {
runner: "gtest_runner",
test: "bin/qemu_edu_system_test",
},
use: [
{
service: "fuchsia.examples.qemuedu.DeviceService",
from: "parent",
},
],
}
```
The test component discovers and accesses the driver using the
`fuchsia.examples.qemuedu.DeviceService` service capability. This component
also includes the `elf_test_runner.shard.cml`, which enables it to run using
the Test Runner Framework.
Create a new `qemu_edu/tests/qemu_edu_system_test.cc` file with the following
contents to implement the tests:
`qemu_edu/tests/qemu_edu_system_test.cc`:
```cpp
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/qemu_edu_system_test.cc" region_tag="imports" adjust_indentation="auto" %}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/qemu_edu_system_test.cc" region_tag="main_body" adjust_indentation="auto" %}
```
Each test case connects to the driver service and exercises one of its exposed
functions.
Add the following new rules to the project's build configuration to build the
test component into a Fuchsia test package:
`qemu_edu/tests/BUILD.bazel`:
{% set build_bazel_snippet %}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/BUILD.bazel" region_tag="binary" adjust_indentation="auto" %}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/BUILD.bazel" region_tag="component" adjust_indentation="auto" %}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/qemu_edu/tests/BUILD.bazel" region_tag="package" adjust_indentation="auto" %}
{% endset %}
```bazel
{{ build_bazel_snippet|replace("//src/qemu_edu","//fuchsia-codelab/qemu_edu")|trim() }}
```
## Run the system test
Use the `bazel run` command to build and execute the test component target:
```posix-terminal
bazel run //fuchsia-codelab/qemu_edu/tests:pkg.component
```
The `bazel run` command performs the following steps:
1. Build the component and package.
1. Publish the package to a local package repository.
1. Register the package repository with the target device.
1. Use `ffx test run` to execute the component's test suite.
Verify that all the tests pass successfully:
```none {:.devsite-disable-click-to-copy}
Running test 'fuchsia-pkg://bazel.test.pkg.system.test.component/qemu_edu_system_test#meta/qemu_edu_system_test.cm'
[RUNNING] main
[stdout - main]
Running main() from gmock_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from QemuEduSystemTest
[ RUN ] QemuEduSystemTest.LivenessCheck
[ OK ] QemuEduSystemTest.LivenessCheck (4 ms)
[ RUN ] QemuEduSystemTest.ComputeFactorial
[ OK ] QemuEduSystemTest.ComputeFactorial (4 ms)
[----------] 2 tests from QemuEduSystemTest (9 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (9 ms total)
[ PASSED ] 2 tests.
[PASSED] main
```
## What's Next?
Congratulations! You've successfully debugged and added tests to your Fuchsia
driver.
Now that you have experienced the basics of developing drivers on Fuchsia, take
your knowledge to the next level and dive deeper with the:
<a class="button button-primary"
href="/docs/concepts/drivers">Driver concepts</a>
<!-- Reference links -->
[component-testing]: /docs/development/testing/components/README.md
[fuchsia-debugger]: /docs/development/debugger/README.md