Fuchsia 支持使用 Fuchsia 调试程序 (zxdb
) 对组件进行逐行调试。调试程序会附加到运行组件的宿主进程,并允许开发者设置断点并逐行执行代码。借助测试运行程序框架,开发者可以编写用于测试驱动程序组件的测试。
在本部分中,您将使用 Fuchsia 调试程序 (zxdb
) 检查正在运行的驱动程序,并构建一个测试组件来测试驱动程序的功能。
连接调试程序
如需将 Fuchsia 调试程序连接到驱动程序组件,您需要确定主机进程的 PID。使用 ffx driver list-hosts
命令查找加载驱动程序的主机进程的 PID:
ffx driver list-hosts
该命令会输出类似于以下内容的列表。找到列出了 qemu_edu
驱动程序的驱动程序主机:
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
Driver Host: 44887
fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
记下 qemu_edu
驱动程序主机的 PID。在上面的示例中,PID 为 44887。
使用 ffx debug connect
启动 Fuchsia 调试程序:
ffx debug connect
调试程序连接到目标设备后,从 zxdb
提示中附加到 qemu_edu
驱动程序主机:
[zxdb] attach HOST_PID
将 HOST_PID
替换为在上一步中识别的驱动程序主机的 PID。例如:
[zxdb] attach 44887
在驱动程序的 ComputeFactorial
函数中设置断点:
[zxdb] break QemuEduServer::ComputeFactorial
该命令会输出类似于以下内容的输出,以指明断点的位置:
[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();
逐步调试驱动程序函数
在另一个终端中,再次运行 eductl
工具:
bazel run //fuchsia-codelab/qemu_edu/tools:pkg.eductl_tool -- fact 12
在 zxdb
终端中,验证调试程序是否已到达驱动程序的 ComputeFactorial
函数中的断点。例如:
🛑 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) {
在 zxdb
提示符中使用 list
命令,以显示当前执行暂停的位置:
[zxdb] list
该命令会输出类似于以下内容的输出:
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);
使用 next
命令进入 ComputeFactorial
函数:
[zxdb] next
输出传递给函数的请求的内容:
[zxdb] print request
该命令会输出包含阶乘输入值的输出:
(*)0x747c1f2e98 ➔ {input = 12}
退出调试程序会话并断开连接:
[zxdb] exit
创建新的系统测试组件
在本部分中,您将创建一个新的测试组件,用于调用 qemu_edu
驱动程序公开的函数。
在 Bazel 工作区中为新的测试组件创建一个新的项目目录:
mkdir -p fuchsia-codelab/qemu_edu/tests
完成本部分后,项目应具有以下目录结构:
//fuchsia-codelab/qemu_edu/tests
|- BUILD.bazel
|- meta
| |- qemu_edu_system_test.cml
|- qemu_edu_system_test.cc
创建 qemu_edu/tests/BUILD.bazel
文件并添加以下语句,以包含 Fuchsia SDK 中的必要构建规则:
qemu_edu/tests/BUILD.bazel
:
load(
"@rules_fuchsia//fuchsia:defs.bzl",
"fuchsia_cc_test",
"fuchsia_component_manifest",
"fuchsia_select",
"fuchsia_test_component",
"fuchsia_test_package",
)
在项目中创建一个新的 qemu_edu/tests/meta/qemu_edu_system_test.cml
组件清单文件,并添加以下内容:
qemu_edu/tests/meta/qemu_edu_system_test.cml
:
{
include: [
"syslog/client.shard.cml",
"sys/testing/elf_test_runner.shard.cml",
],
program: {
binary: 'bin/qemu_edu_system_test',
},
use: [
{
directory: "dev-topological",
rights: [ "r*" ],
path: "/dev",
},
],
// Required to enable access to devfs
facets: {
"fuchsia.test": { type: "devices" },
},
}
与 eductl
类似,测试组件使用 dev
目录功能发现和访问驱动程序。此组件还包含 elf_test_runner.shard.cml
,可让其使用测试运行程序框架运行。
创建一个包含以下内容的新 qemu_edu/tests/qemu_edu_system_test.cc
文件,以实现测试:
qemu_edu/tests/qemu_edu_system_test.cc
:
#include <dirent.h>
#include <fcntl.h>
#include <fidl/examples.qemuedu/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <sys/types.h>
#include <filesystem>
#include <gtest/gtest.h>
namespace {
constexpr char kDevfsRootPath[] = "/dev/sys/platform/";
constexpr char kEduDevicePath[] = "qemu-edu";
class QemuEduSystemTest : public testing::Test {
public:
void SetUp() {
auto device_path = SearchDevicePath();
ASSERT_TRUE(device_path.has_value());
zx::result endpoints = fidl::CreateEndpoints<examples_qemuedu::Device>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
ASSERT_EQ(fdio_service_connect(device_path.value().c_str(),
endpoints->server.TakeChannel().release()),
ZX_OK);
device_ = fidl::WireSyncClient(std::move(endpoints->client));
}
// Search for the device file entry in devfs
std::optional<std::string> SearchDevicePath() {
for (auto const& dir_entry : std::filesystem::recursive_directory_iterator(kDevfsRootPath)) {
if (dir_entry.path().string().find(kEduDevicePath) != std::string::npos) {
return {dir_entry.path()};
}
}
return {};
}
fidl::WireSyncClient<examples_qemuedu::Device>& device() { return device_; }
private:
fidl::WireSyncClient<examples_qemuedu::Device> device_;
};
TEST_F(QemuEduSystemTest, LivenessCheck) {
fidl::WireResult result = device()->LivenessCheck();
ASSERT_EQ(result.status(), ZX_OK);
ASSERT_TRUE(result->value()->result);
}
TEST_F(QemuEduSystemTest, ComputeFactorial) {
std::array<uint32_t, 11> kExpected = {
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800,
};
for (uint32_t i = 0; i < kExpected.size(); i++) {
fidl::WireResult result = device()->ComputeFactorial(i);
ASSERT_EQ(result.status(), ZX_OK);
EXPECT_EQ(result->value()->output, kExpected[i]);
}
}
} // namespace
每个测试用例都会打开设备驱动程序并调用其公开的某个函数。
将以下新规则添加到项目的 build 配置中,以将测试组件构建到 Fuchsia 测试软件包中:
qemu_edu/tests/BUILD.bazel
:
fuchsia_cc_test(
name = "qemu_edu_system_test",
size = "small",
srcs = [
"qemu_edu_system_test.cc",
],
deps = ["@com_google_googletest//:gtest_main"] + fuchsia_select({
"@platforms//os:fuchsia": [
"//fuchsia-codelab/qemu_edu/fidl:examples.qemuedu_cc",
"@fuchsia_sdk//pkg/fdio",
],
}),
)
fuchsia_component_manifest(
name = "manifest",
src = "meta/qemu_edu_system_test.cml",
includes = [
"@fuchsia_sdk//pkg/sys/testing:elf_test_runner",
"@fuchsia_sdk//pkg/syslog:client",
],
)
fuchsia_test_component(
name = "component",
manifest = "manifest",
deps = [
":qemu_edu_system_test",
],
)
fuchsia_test_package(
name = "pkg",
package_name = "qemu_edu_system_test",
test_components = [
":component",
],
visibility = ["//visibility:public"],
)
运行系统测试
使用 bazel run
命令构建和执行测试组件目标:
bazel run //fuchsia-codelab/qemu_edu/tests:pkg.component
bazel run
命令会执行以下步骤:
- 构建组件并打包。
- 将软件包发布到本地软件包代码库。
- 向目标设备注册软件包仓库。
- 使用
ffx test run
执行组件的测试套件。
验证所有测试是否都成功通过:
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
后续操作
恭喜!您已成功调试 Fuchsia 驱动程序并向其添加了测试。
现在,您已经了解了在 Fuchsia 上开发驱动程序的基础知识,接下来可以通过以下资源进一步提升您的知识水平并深入了解相关内容: