本指南介绍了如何使用 Inspect 检测 Fuchsia 驱动程序框架 (DFv2) 驱动程序,以及如何从驱动程序组件查询 Inspect 数据。
概览
在 Fuchsia 的驱动程序框架 (DFv2) 中,驱动程序是组件。驱动程序直接参与 Fuchsia 的组件诊断架构,并使用标准 fuchsia.inspect.InspectSink 功能发布检查数据,就像任何其他 Fuchsia 组件一样。
DFv2 中检查功能的主要特征:
- 标准功能路由:驱动程序组件清单使用标准
inspect/client.shard.cml分片请求fuchsia.inspect.InspectSink。 - 归因于组件的检查:检查层次结构归因于每个驱动程序组件的组件别名(例如,在
bootstrap/boot-drivers或bootstrap/base-drivers下)。 - 标准工具:使用
ffx inspect或iquery通过标准组件选择器或组件网址匹配来检查驱动程序检查数据。
前提条件
如果您不熟悉 Inspect 的基础知识,请查看以下指南:
在驱动程序中包含检查
1. 更新组件清单 (.cml)
在驱动程序的 .cml 文件中添加 Inspect 客户端分片:
{
include: [
"inspect/client.shard.cml",
"syslog/client.shard.cml",
],
program: {
runner: "driver",
binary: "driver/my_driver.so",
bind: "meta/bind/my_driver.bindbc",
},
}
inspect/client.shard.cml 分片将 fuchsia.inspect.InspectSink 协议从父诊断目录路由到驱动程序的传入命名空间。
2. 添加 build 依赖项
C++
将 //sdk/lib/inspect/component/cpp 添加到驱动程序的 BUILD.gn 中:
fuchsia_cc_driver("my_driver") {
deps = [
"//sdk/lib/driver/component/cpp",
"//sdk/lib/inspect/component/cpp",
]
}
如果使用 Bazel 进行构建,请在 BUILD.bazel 中添加 @fuchsia_sdk//pkg/inspect_component_cpp:
fuchsia_cc_driver(
name = "my_driver",
deps = [
"@fuchsia_sdk//pkg/driver_component_cpp",
"@fuchsia_sdk//pkg/inspect_component_cpp",
],
)
Rust
在驱动程序的 BUILD.gn 中,取决于 //sdk/lib/driver/component/rust、//src/lib/diagnostics/inspect/rust 和 //src/lib/fuchsia-async:
fuchsia_rust_driver("my_driver") {
deps = [
"//sdk/lib/driver/component/rust",
"//src/lib/diagnostics/inspect/rust",
"//src/lib/fuchsia-async",
]
}
3. 初始化并发布 Inspect
C++
在 C++ 中,继承自 fdf::DriverBase2 的驱动程序可以使用 context.CreateInspector(this) 发布检查树:
#include <lib/driver/component/cpp/driver_base2.h>
#include <lib/driver/component/cpp/driver_export2.h>
#include <lib/inspect/component/cpp/component.h>
class MyDriver : public fdf::DriverBase2 {
public:
MyDriver() : fdf::DriverBase2("my_driver") {}
zx::result<> Start(fdf::DriverContext context) override {
// 1. Create and publish the ComponentInspector.
// Must be called before context.take_incoming() if transferring ownership.
component_inspector_ = context.CreateInspector(this);
// 2. Build the Inspect hierarchy from the root node.
hardware_node_ = component_inspector_->root().CreateChild("hardware_status");
revision_prop_ = hardware_node_.CreateUint("revision_id", 0x10);
requests_count_ = hardware_node_.CreateUint("requests_count", 0);
// 3. For values that do not change, use Record* helpers to avoid storing handles:
hardware_node_.RecordString("serial_number", "ABC-1234");
// 4. (Optional) Report component health:
component_inspector_->Health().Ok();
return zx::ok();
}
void HandleDeviceRequest() {
requests_count_.Add(1);
}
// Accessor for unit tests:
const inspect::Inspector& inspector() const {
return component_inspector_->inspector();
}
private:
std::optional<inspect::ComponentInspector> component_inspector_;
inspect::Node hardware_node_;
inspect::UintProperty revision_prop_;
inspect::UintProperty requests_count_;
};
FUCHSIA_DRIVER_EXPORT2(MyDriver);
要点:
context.CreateInspector(this):自动连接到传入命名空间中的fuchsia.inspect.InspectSink,并发布检查树,并将树名称设置为驱动程序的name()。在通过context.take_incoming()获取命名空间之前,调用context.CreateInspector(this)。- 节点和属性生命周期 (RAII):检查属性是 RAII 对象。如果属性会随时间变化(例如
requests_count_),请将属性对象存储为类成员或存储在inspect::ValueList中。如果您不保持媒体资源的有效性,系统会将其从“检查 VMO”中移除。对于不可变的值,请使用Record*辅助方法(例如RecordString、RecordUint),这些方法可将值直接提交到 VMO,而无需对象句柄。 - 组件健康状况:使用
component_inspector_->Health().Ok()、.Starting()或.Unhealthy(reason)报告标准组件健康状况。
Rust
在实现 fdf_component::Driver 的 Rust 驱动程序中:
use fdf_component::{Driver, DriverContext, DriverError};
use fuchsia_async::Scope;
use fuchsia_inspect::{Inspector, NumericProperty, UintProperty};
pub struct MyDriver {
// Keep the scope alive for the lifetime of the driver so that
// the Inspect publishing task continues running.
_inspect_scope: Scope,
inspector: Inspector,
requests_count: UintProperty,
}
impl Driver for MyDriver {
const NAME: &str = "my_driver";
async fn start(mut context: DriverContext) -> Result<Self, DriverError> {
let inspector = Inspector::default();
// Build hierarchy
let hardware = inspector.root().create_child("hardware_status");
hardware.record_string("serial_number", "ABC-1234");
let requests_count = hardware.create_uint("requests_count", 0);
inspector.root().record(hardware);
// Publish Inspect via InspectSink.
let inspect_scope = Scope::new_with_name("my_driver_inspect");
context.publish_inspect(&inspector, inspect_scope.to_handle())?;
Ok(Self {
_inspect_scope: inspect_scope,
inspector,
requests_count,
})
}
}
impl MyDriver {
pub fn handle_device_request(&self) {
self.requests_count.add(1);
}
}
要点:
- 保留
Scope:context.publish_inspect在提供的ScopeHandle上生成后台任务。将Scope存储在驱动程序结构中,以便在start()完成时不会取消任务。 - 树名称:Rust 的
context.publish_inspect使用默认树名称 (None) 发布。与通过CreateInspector发布的 C++ 驱动程序不同,除非使用自定义发布选项,否则在查询时不要传递--name树过滤条件。
查询驱动程序检查数据
由于驱动程序是组件,因此您可以使用标准的 Fuchsia 诊断命令查询其 Inspect 数据。
驱动程序别名
驱动程序组件 moniker 反映了设备拓扑和驱动程序运行的集合:
- 启动驱动程序:
bootstrap/boot-drivers:<device_node_path>(例如bootstrap/boot-drivers:dev.sys.platform.00_00_2d) - 封装的驱动程序(基本):
bootstrap/base-drivers:<device_node_path> - 打包的驱动程序(完整/通用):
bootstrap/full-drivers:<device_node_path>
使用 ffx inspect
在宿主开发工作站上:
按驱动程序组件网址 / 组件名称查询:
sh ffx inspect show my_driver.cm查询所有驱动程序组件:
sh ffx inspect show "bootstrap/*-drivers*:root"按 C++ 驱动程序树名称过滤:
sh ffx inspect show --name my_driver "bootstrap/*-drivers*:root"按 moniker 查询特定驱动程序: 由于集合子名称以英文冒号分隔,因此必须在选择器中转义组件 moniker 中的英文冒号:
sh ffx inspect show "bootstrap/boot-drivers\:suspend"
使用 iquery 达到目标
通过串行或 ffx target ssh 连接到目标设备时:
iquery show my_driver.cm
iquery show "bootstrap/*-drivers*:root"
测试驱动程序 Inspect
单元测试
在使用驱动程序单元测试库的 C++ 驱动程序单元测试中,直接从驱动程序的检查器验证 Inspect 树:
#include <lib/driver/testing/cpp/driver_test.h>
#include <lib/fpromise/single_threaded_executor.h>
#include <lib/inspect/cpp/reader.h>
#include <lib/inspect/testing/cpp/inspect.h>
#include <gtest/gtest.h>
TEST_F(MyDriverTestFixture, InspectMetrics) {
// Run driver methods that modify inspect state...
dut_.RunInDriverContext([&](MyDriver& driver) {
auto hierarchy = fpromise::run_single_threaded(
inspect::ReadFromInspector(driver.inspector()))
.take_value();
const auto* hw_node = hierarchy.GetByPath({"hardware_status"});
ASSERT_NE(hw_node, nullptr);
const auto* prop = hw_node->node().get_property<inspect::UintPropertyValue>("requests_count");
ASSERT_NE(prop, nullptr);
EXPECT_EQ(prop->value(), 1u);
});
}
使用 DriverTestRealm 进行集成测试
在 DriverTestRealm 内运行的集成测试中,使用 diagnostics_reader::ArchiveReader 查询 Inspect。请注意,在选择器字符串中,分隔集合和子 moniker 的英文冒号必须进行转义 (\\:):
use diagnostics_assertions::assert_data_tree;
use diagnostics_reader::ArchiveReader;
let moniker = format!("realm_builder\\:{}/driver_test_realm/boot-drivers\\:dev.sys.my_node", instance.root.child_name());
let hierarchy = ArchiveReader::inspect()
.add_selector(format!("{}:[name=my_driver]root", moniker))
.snapshot()
.await?
.into_iter()
.next()
.and_then(|result| result.payload)
.expect("driver inspect hierarchy not found");
assert_data_tree!(hierarchy, root: contains {
hardware_status: contains {
requests_count: 0u64,
}
});