本指南說明如何使用 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,透過標準元件選取器或元件網址比對,檢查 Driver Inspect 資料。
必要條件
如果您不熟悉檢查元素的基本概念,請參閱下列指南:
在驅動程式庫中加入 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. 新增建構依附元件
C++
在驅動程式庫的 BUILD.gn 中新增 //sdk/lib/inspect/component/cpp:
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",
],
)
荒漠油廠
在驅動程式庫的 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. 初始化並發布檢查
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)回報標準元件健康狀態。
荒漠油廠
在實作 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 診斷指令查詢其檢查資料。
驅動程式代號
驅動程式元件的別名會反映裝置拓撲和驅動程式庫的執行位置:
- 開機驅動程式:
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"依路徑名稱查詢特定驅動程式庫程式: 由於集合子項名稱是以半形冒號分隔,因此元件路徑名稱中的半形冒號必須在選取器中逸出:
sh ffx inspect show "bootstrap/boot-drivers\:suspend"
使用 iquery 達成目標
透過序列或 ffx target ssh 連線至目標裝置時:
iquery show my_driver.cm
iquery show "bootstrap/*-drivers*:root"
測試驅動程式庫 Inspect
單元測試
在 C++ 驅動程式庫單元測試中,使用驅動程式單元測試程式庫,直接從驅動程式庫檢查器驗證檢查樹狀結構:
#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。請注意,在選取器字串中,分隔集合和子項路徑名稱半形冒號必須逸出 (\\:):
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,
}
});