在本程式碼研究室中,您將學會使用 Inspect (Rust 和 C++) 發布程式的診斷資訊,並使用 Inspect 資訊偵錯程式。
在本程式碼研究室中,您將修改程式,輸出檢查資料。 您將學到:
如何加入檢查程式庫。
如何在元件中初始化檢查。
如何透過編寫及讀取檢查資料,解決實際錯誤。
如何解讀檢查資料,確認程式是否符合您的需求。
什麼是「檢查」?
透過 Inspect,Fuchsia 元件可以公開目前狀態的結構化階層資訊。
如要進一步瞭解檢查功能,請參閱「Fuchsia 元件檢查」說明文件。
檢查功能有哪些優點?
元件檢查支援多種用途,包括:
偵錯
查看執行中元件的檢查資料,找出問題。舉例來說,您可以瞭解元件目前是否已連結至依附元件。
監控系統健康狀態
檢查資料可深入瞭解整體系統狀態。舉例來說,您可以瞭解系統未連上網際網路的原因。
收集使用情況或效能統計資料
您可以同時讀取多個元件的檢查資料,瞭解系統效能。舉例來說,您可以查看元件的連入連線清單,以及元件的記憶體用量。
我可以在 Inspect 中儲存哪些資訊?
您可決定要在「檢查」中公開的資料結構和內容。例如:
- 開放的 Wi-Fi 連線數量。
- 程式放送的要求數量。
- 剖析器遇到的錯誤次數。
- 資料結構的內容。
API 參考資料
必備事項
- 具備 Rust 或 C++ 的基本知識。
- 可執行建構指令的 Fuchsia 來源樹狀結構。
原始碼
程式碼位於:
本程式碼研究室分為多個部分,每個部分都有自己的子目錄。本程式碼研究室的起點是第 1 部分,而每個部分的程式碼都包含前幾部分的解決方案。
在本程式碼研究室中,您可以繼續在「part_1」中新增解決方案,也可以根據現有解決方案跳過部分步驟。
必要條件
設定開發環境。
本程式碼研究室假設您已完成「入門」課程,並具備下列條件:
- 已簽出並建構的 Fuchsia 樹狀結構。
- 搭載 Fuchsia 的裝置或模擬器 (
ffx emu)。 - 工作站,用於將元件 (
fx serve) 提供給 Fuchsia 裝置或模擬器。- 如要瞭解如何執行元件,請參閱元件開發人員指南。
如要建構及執行本程式碼研究室中的範例,請在 fx set 呼叫中加入下列引數:
C++
fx set core.x64 \
--with //examples/diagnostics/inspect/codelab/cpp \
--with-test //examples/diagnostics/inspect/codelab/cpp:hermetic_tests荒漠油廠
fx set core.x64 \
--with //examples/diagnostics/inspect/codelab/rust第 1 部分:有錯誤的元件
// Implementation of a string reverser.
@discoverable
closed protocol Reverser {
// Returns the input string reversed character-by-character.
strict Reverse(struct {
input string:1024;
}) -> (struct {
response string:1024;
});
};
這個通訊協定只有一個方法,稱為 Reverse,可反轉傳遞給它的任何字串。系統提供通訊協定的實作項目,但有重大錯誤。這個錯誤會導致嘗試呼叫 Reverse 方法的用戶端無限期停止呼叫。修正這項錯誤的責任在你。
執行元件
有一個用戶端應用程式會啟動 Reverser 元件,並將其餘的指令列引數以字串形式傳送至 Reverse:
查看用量
視要執行的程式碼研究室部分而定,您會啟動
client_i元件,其中i是範圍在 [1, 5] 的數字。舉例來說,如要啟動與 Codelab 第 2 部分反向器通訊的用戶端:C++
ffx component run /core/ffx-laboratory:client_part_2 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_2.cm荒漠油廠
ffx component run /core/ffx-laboratory:client_part_2 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_2.cm執行第 1 部分的程式碼,並反轉「Hello」字串
如要只指定單一字串「Hello」,請修改 common.shard.cml 的
program.args區段,然後建構並執行下列項目:C++
ffx component run /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_1.cm如要查看指令輸出內容,請查看記錄:
ffx log --tag inspect_cpp_codelab我們從記錄中看到元件收到「Hello」做為輸入內容,但沒有看到正確的反向輸出內容。
荒漠油廠
ffx component run /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_1.cm如要查看指令輸出內容,請查看記錄:
ffx log --tag inspect_rust_codelab我們從記錄中看到元件收到「Hello」做為輸入內容,但未看到正確的反向輸出內容。
如記錄所示,反向器無法正常運作。
嘗試使用更多引數執行用戶端:
將字串「World」新增至 common.shard.cml 的
program.args區段:{ program: { args: [ "Hello", "World", ], }, }建構並執行下列項目:
C++
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_1.cm ```荒漠油廠
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_1.cm ```我們可以發現元件列印了第一個輸入內容,但沒有看到預期輸出內容,也沒有第二個輸入內容。
現在可以檢查程式碼,排解問題。
查看程式碼
現在您可以重現問題,請查看用戶端執行的作業:
C++
在用戶端主機中:
// Repeatedly send strings to be reversed to the other component.
for (int i = 1; i < argc; i++) {
FX_LOGS(INFO) << "Input: " << argv[i];
std::string output;
status = zx::make_result(reverser->Reverse(argv[i], &output));
if (status.is_error()) {
FX_LOGS(ERROR) << "Error: Failed to reverse string.";
return status.status_value();
}
FX_LOGS(INFO) << "Output: " << output;
}
荒漠油廠
在用戶端主機中:
for string in args.strings {
info!("Input: {}", string);
match reverser.reverse(&string).await {
Ok(output) => info!("Output: {}", output),
Err(e) => error!(error:? = e; "Failed to reverse string"),
}
}
在這個程式碼片段中,用戶端會呼叫 Reverse 方法,但似乎從未收到回應。似乎沒有錯誤訊息或輸出內容,元件只是停止運作。
請查看本程式碼研究室這部分的伺服器程式碼。有許多標準元件設定:
C++
在第 1 部分:
記錄初始化
fuchsia_logging::LogSettingsBuilder builder; builder.WithTags({"inspect_cpp_codelab", "part1"}).BuildAndInitialize();建立非同步執行器
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();提供公共服務
context->outgoing()->AddPublicService(Reverser::CreateDefaultHandler());
荒漠油廠
在第 1 部分:
記錄初始化
#[fuchsia::main(logging_tags = ["inspect_rust_codelab", "part1"])]ServiceFs 初始化
let mut fs = ServiceFs::new();ServiceFs 集合
let running_service_fs = fs.collect::<()>().map(Ok);提供公共服務
fs.dir("svc").add_fidl_service(move |stream| reverser_factory.spawn_new(stream)); fs.take_and_serve_directory_handle()?;
請參閱反向器定義:
C++
在 reverser.h 中:
class Reverser final : public fuchsia::examples::inspect::Reverser {
public:
// CODELAB: Create a new constructor for Reverser that takes an Inspect node.
// Implementation of Reverser.Reverse().
void Reverse(std::string input, ReverseCallback callback) override;
// Return a request handler for the Reverser protocol that binds incoming requests to new
// Reversers.
static fidl::InterfaceRequestHandler<fuchsia::examples::inspect::Reverser> CreateDefaultHandler();
};
這個類別會導入 Reverser 通訊協定。名為 CreateDefaultHandler 的輔助方法會建構 InterfaceRequestHandler,為傳入的要求建立新的 Reverser。
荒漠油廠
在 reverser.rs 中:
pub struct ReverserServerFactory {}
impl ReverserServerFactory {
// CODELAB: Create a new() constructor that takes an Inspect node.
pub fn new() -> Self {
Self {}
}
pub fn spawn_new(&self, stream: ReverserRequestStream) {
// CODELAB: Add stats about incoming connections.
ReverserServer::new().spawn(stream);
}
}
struct ReverserServer {}
impl ReverserServer {
// CODELAB: Create a new() constructor that takes an Inspect node.
fn new() -> Self {
Self {}
}
pub fn spawn(self, mut stream: ReverserRequestStream) {
fasync::Task::local(async move {
while let Some(request) = stream.try_next().await.expect("serve reverser") {
// CODELAB: Add stats about incoming requests.
let ReverserRequest::Reverse { input, responder: _ } = request;
let _result = input.chars().rev().collect::<String>();
// Yes, this is silly. Just for codelab purposes.
fasync::Timer::new(fasync::MonotonicInstant::after(
zx::MonotonicDuration::from_hours(10),
))
.await
}
})
.detach();
}
}
這個結構體適用於 Reverser 通訊協定。ReverserServerFactory (稍後會更清楚) 會在建立與 Reverser 的新連線時建構 ReverserServer。
新增檢查
現在您已瞭解程式碼結構,可以開始使用 Inspect 檢測程式碼,找出問題。
您可能曾透過列印或記錄來偵錯程式。雖然這通常很有效,但持續執行的非同步元件通常會隨著時間輸出大量內部狀態記錄。本程式碼研究室說明如何使用 Inspect 取得元件目前狀態的快照,不必再深入研究記錄。
包含 Inspect 依附元件:
C++
在
source_set("lib")下的public_deps中,BUILD.gn 內:source_set("lib") { sources = [ "reverser.cc", "reverser.h", ] public_deps = [ "//examples/diagnostics/inspect/codelab/fidl:fuchsia.examples.inspect_hlcpp", "//sdk/lib/inspect/component/cpp", ] }荒漠油廠
在
rustc_binary("bin")下的deps中,BUILD.gn 內:"//src/lib/diagnostics/inspect/runtime/rust", "//src/lib/diagnostics/inspect/rust", "//src/lib/fuchsia",初始化 Inspect:
C++
在 main.cc 中:
#include <lib/inspect/component/cpp/component.h> inspect::ComponentInspector inspector(loop.dispatcher(), {});荒漠油廠
在 main.rs 中:
use fuchsia_inspect::component; let _inspect_server_task = inspect_runtime::publish( component::inspector(), inspect_runtime::PublishOptions::default(), );您目前使用的是「檢查」。
新增簡單的「版本」屬性,顯示執行的版本:
C++
inspector.root().RecordString("version", "part2");這個程式碼片段會執行下列作業:
取得「檢查」階層的「根」節點。
元件的檢查階層包含節點樹狀結構,每個節點都含有任意數量的屬性。
在
RecordString中建立新資源。這會在根目錄上新增
StringProperty。這個StringProperty稱為「版本」,值為「part2」。我們將屬性設為「part1」。在根節點中記錄新屬性。
建立屬性的常見方式是透過節點上的
Create*方法。以這些方法建立的屬性生命週期會與傳回的物件繫結,而銷毀物件會導致屬性消失。程式庫提供便利方法Record*,可建立屬性並將屬性生命週期繫結至呼叫該方法的節點。因此,新屬性的存在時間與節點本身相同 (在本例中,與根節點相同,因此與元件的整個執行作業相同)。
荒漠油廠
component::inspector().root().record_string("version", "part2");這個程式碼片段會執行下列作業:
- 取得「檢查」階層的「根」節點。
元件的檢查階層包含節點樹狀結構,每個節點都含有任意數量的屬性。
- 使用
record_string建立新屬性。
這會在根目錄上新增
StringProperty。這個StringProperty稱為「版本」,值為「part2」。我們將屬性設為「part1」。- 並記錄在根節點中。
建立屬性的常見方式是透過節點上的
create_*方法。以這些方法建立的屬性生命週期會與傳回的物件繫結,而銷毀物件會導致屬性消失。程式庫提供便利方法record_*,可建立屬性並將屬性生命週期繫結至呼叫該方法的節點。因此,新屬性的存在時間與節點本身相同 (在本例中,與根節點相同,因此與元件的整個執行作業相同)。
讀取檢查資料
您已將「檢查」新增至元件,現在可以讀取內容:
重建及更新目標系統
fx build && fx ota執行用戶端:
C++
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_1.cmffx log --tag inspect_cpp_codelab荒漠油廠
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_1.cmffx log --tag inspect_rust_codelab使用
ffx inspect查看輸出內容:ffx inspect show這會傾印整個系統的所有檢查資料,資料量可能相當龐大。
由於
ffx inspect支援 glob 比對,請執行下列指令:C++
ffx inspect show 'core/ffx-laboratory:client_part_1/reverser' # or `ffx inspect show inspect_cpp_codelab` metadata: filename = fuchsia.inspect.Tree component_url = fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/part_1.cm timestamp = 4728864898476 payload: root: version = part1荒漠油廠
ffx inspect show 'core/ffx-laboratory:client_part_1/reverser' # or `ffx inspect show inspect_rust_codelab` metadata: filename = fuchsia.inspect.Tree component_url = fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/part_1.cm timestamp = 4728864898476 payload: root: version = part1您也可以將輸出內容顯示為 JSON:
C++
ffx --machine json-pretty inspect show 'core/ffx-laboratory:client_part_1/reverser'畫面會顯示類似以下的輸出:
[ { "data_source": "Inspect", "metadata": { "errors": null, "filename": "fuchsia.inspect.Tree", "component_url": "fuchsia-pkg://fuchsia.com/inspect_pp_codelab#meta/part_1.cm", "timestamp": 5031116776282 }, "moniker": "core/ffx-laboratory\\:client_part_5/reverser", "payload": { "root": { "version": "part1", }, "version": 1 } ]荒漠油廠
ffx --machine json-pretty inspect show 'core/ffx-laboratory:client_part_1/reverser'畫面會顯示類似以下的輸出:
[ { "data_source": "Inspect", "metadata": { "errors": null, "filename": "fuchsia.inspect.Tree", "component_url": "fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/part_1.cm", "timestamp": 5031116776282 }, "moniker": "core/ffx-laboratory\\:client_part_5/reverser", "payload": { "root": { "version": "part1", }, "version": 1 } ]
檢測程式碼以找出錯誤
您已初始化 Inspect,也瞭解如何讀取資料,現在可以開始檢測程式碼並找出錯誤。
先前的輸出內容顯示元件的實際執行方式,以及元件並未完全停止運作。否則 Inspect 讀取作業會停滯。
為每個連線新增資訊,觀察連線是否由元件處理。
在根節點中新增子項,以包含
reverser服務的統計資料:C++
context->outgoing()->AddPublicService( Reverser::CreateDefaultHandler(inspector.root().CreateChild("reverser_service")));荒漠油廠
let reverser_factory = ReverserServerFactory::new(component::inspector().root().create_child("reverser_service"));更新伺服器以接受這個節點:
C++
更新 reverser.h 和 reverser.cc 中的
CreateDefaultHandler定義:#include <lib/inspect/cpp/inspect.h> fidl::InterfaceRequestHandler<fuchsia::examples::inspect::Reverser> Reverser::CreateDefaultHandler( inspect::Node node) {荒漠油廠
更新
ReverserServerFactory::new,在 reverser.rs 中接受這個節點:use fuchsia_inspect as inspect; pub struct ReverserServerFactory { node: inspect::Node, // ... } impl ReverserServerFactory { pub fn new(node: inspect::Node) -> Self { // ... Self { node, // ... } } // ... }新增屬性,追蹤連線數量:
C++
fidl::InterfaceRequestHandler<fuchsia::examples::inspect::Reverser> Reverser::CreateDefaultHandler( inspect::Node node) { // ... // Return a handler for incoming FIDL connections to Reverser. // // The returned closure contains a binding set, which is used to bind incoming requests to a // particular implementation of a FIDL interface. This particular binding set is configured to // bind incoming requests to unique_ptr<Reverser>, which means the binding set itself takes // ownership of the created Reversers and frees them when the connection is closed. return [connection_count = node.CreateUint("connection_count", 0), node = std::move(node), // ...荒漠油廠
use { // ... fuchsia_inspect::NumericProperty, // ... }; pub struct ReverserServerFactory { node: inspect::Node, // ... connection_count: inspect::UintProperty, } impl ReverserServerFactory { pub fn new(node: inspect::Node) -> Self { // ... let connection_count = node.create_uint("connection_count", 0); Self { node, // ... connection_count, } } pub fn spawn_new(&self, stream: ReverserRequestStream) { self.connection_count.add(1);這個程式碼片段示範如何建立名為
connection_count的新UintProperty(內含 64 位元不帶正負號的整數),並將其設為 0。在處理常式 (針對每個連線執行) 中,屬性會遞增 1。重新建構及重新執行元件,然後執行
ffx inspect:C++
ffx --machine json-pretty inspect show inspect_cpp_codelab荒漠油廠
ffx --machine json-pretty inspect show inspect_rust_codelab現在您應該會看到:
... "payload": { "root": { "version": "part1", "reverser_service": { "connection_count": 1, } } }
上述輸出內容顯示用戶端已成功連線至服務,因此掛斷問題必定是由 Reverser 實作本身所造成。具體來說,瞭解下列事項會很有幫助:
如果連線仍處於開啟狀態,但用戶端已停止回應。
如果呼叫了
Reverse方法。
練習:為每個連線建立子節點,並在 Reverser 內記錄「request_count」。
提示:您可以利用公用程式函式產生專屬名稱:
C++
auto child = node.CreateChild(node.UniqueName("connection-"));荒漠油廠
let node = self.node.create_child(inspect::unique_name("connection"));系統會建立以「connection」開頭的專屬名稱。
C++
提示:建議您為 Reverser 建立建構函式,並採用 inspect::Node。本程式碼研究室的第 3 部分會說明這項模式的實用性。
荒漠油廠
提示:建立 ReverserServer 的建構函式會很有幫助,因為這會採用 inspect::Node,與我們對 ReverserServerFactory 執行的操作相同。
提示:您需要在 Reverser 上建立成員,才能保留
request_count資源。類型為inspect::UintProperty。後續追蹤:請求計數是否提供您所需的所有資訊?新增
response_count。進階:您能否在所有連線中加入所有要求的計數?Reverser 物件必須共用某些狀態。您可能會發現,將引數重構為 Reverser 中的獨立結構體很有幫助 (如需這個方法的解決方案,請參閱第 2 部分)。
完成本練習並執行 ffx inspect 後,您應該會看到類似下列內容:
...
"payload": {
"root": {
"version": "part1",
"reverser_service": {
"connection_count": 1,
"connection0": {
"request_count": 1,
}
}
}
}
上述輸出內容顯示連線仍處於開啟狀態,且已收到一項要求。
C++
如果您也新增了「response_count」,可能就會發現這個錯誤。
Reverse 方法會收到 callback,但從未以 output 的值呼叫。
荒漠油廠
如果您也新增了「response_count」,可能就會發現這個錯誤。
Reverse 方法會收到 responder,但從未以 result 的值呼叫。
傳送回覆:
C++
// At the end of Reverser::Reverse callback(std::move(output));荒漠油廠
responder.send(&result).expect("send reverse request response");再次執行用戶端:
C++
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_1.cm畫面會顯示類似以下的輸出:
Creating component instance: client_part_1 ffx log --tag inspect_cpp_codelab [00039.129068][39163][39165][inspect_cpp_codelab, client] INFO: Input: Hello [00039.194151][39163][39165][inspect_cpp_codelab, client] INFO: Output: olleH [00039.194170][39163][39165][inspect_cpp_codelab, client] INFO: Input: World [00039.194402][39163][39165][inspect_cpp_codelab, client] INFO: Output: dlroW [00039.194407][39163][39165][inspect_cpp_codelab, client] INFO: Done reversing! Please use `ffx component stop`荒漠油廠
ffx component run --recreate /core/ffx-laboratory:client_part_1 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_1.cm畫面會顯示類似以下的輸出:
Creating component instance: client_part_1 ffx log --tag inspect_rust_codelab [00039.129068][39163][39165][inspect_rust_codelab, client] INFO: Input: Hello [00039.194151][39163][39165][inspect_rust_codelab, client] INFO: Output: olleH [00039.194170][39163][39165][inspect_rust_codelab, client] INFO: Input: World [00039.194402][39163][39165][inspect_rust_codelab, client] INFO: Output: dlroW [00039.194407][39163][39165][inspect_rust_codelab, client] INFO: Done reversing! Please use `ffx component stop`
這個元件會持續執行,直到您執行 ffx component stop 為止。只要元件正在執行,您就可以執行 ffx inspect 並觀察輸出內容。
第 1 部分到此結束。您也可以選擇提交變更:
git commit -am "solution to part 1"第 2 部分:診斷元件間的問題
您收到錯誤報告。「FizzBuzz」團隊表示他們無法從您的元件接收資料。
除了提供 Reverser 通訊協定,這個元件也會連線至「FizzBuzz」服務並列印回應:
C++
fuchsia::examples::inspect::FizzBuzzPtr fizz_buzz;
context->svc()->Connect(fizz_buzz.NewRequest());
fizz_buzz->Execute(30, [](std::string result) { FX_LOGS(INFO) << "Got FizzBuzz: " << result; });
荒漠油廠
let fizzbuzz_fut = async move {
let fizzbuzz = client::connect_to_protocol::<FizzBuzzMarker>()
.context("failed to connect to fizzbuzz")?;
match fizzbuzz.execute(30u32).await {
Ok(result) => info!(result:%; "Got FizzBuzz"),
Err(_) => {}
};
Ok(())
};
如果查看記錄,會發現系統從未列印這項記錄。
C++
ffx log --tag inspect_cpp_codelab荒漠油廠
ffx log --tag inspect_rust_codelab請診斷並解決這個問題。
使用「檢查」診斷問題
執行元件,看看會發生什麼情況:
C++
ffx component run /core/ffx-laboratory:client_part_2 fuchsia-pkg://fuchsia.com/inspect_cpp_codelab#meta/client_part_2.cm荒漠油廠
ffx component run /core/ffx-laboratory:client_part_2 fuchsia-pkg://fuchsia.com/inspect_rust_codelab#meta/client_part_2.cm幸好 FizzBuzz 團隊使用 Inspect 檢測元件。
使用
ffx inspect讀取 FizzBuzz Inspect 資料,您應該會看到:"payload": { "root": { "fizzbuzz_service": { "closed_connection_count": 0, "incoming_connection_count": 0, "request_count": 0, ...這項輸出內容確認 FizzBuzz 未收到任何連線。
新增檢查功能以找出問題:
C++
// CODELAB: Instrument our connection to FizzBuzz using Inspect. Is there an error? fuchsia::examples::inspect::FizzBuzzPtr fizz_buzz; context->svc()->Connect(fizz_buzz.NewRequest()); fizz_buzz.set_error_handler([&](zx_status_t status) { // CODELAB: Add Inspect here to see if there is a response. }); fizz_buzz->Execute(30, [](std::string result) { // CODELAB: Add Inspect here to see if there was a response. FX_LOGS(INFO) << "Got FizzBuzz: " << result; });荒漠油廠
let fizzbuzz_fut = async move { let fizzbuzz = client::connect_to_protocol::<FizzBuzzMarker>() .context("failed to connect to fizzbuzz")?; match fizzbuzz.execute(30u32).await { Ok(result) => { // CODELAB: Add Inspect here to see if there is a response. info!(result:%; "Got FizzBuzz"); } Err(_) => { // CODELAB: Add Inspect here to see if there is an error } }; Ok(()) };
練習:在 FizzBuzz 連線中新增檢查功能,找出問題
提示:以上程式碼片段可做為起點,其中提供連線嘗試的錯誤處理常式。
C++
後續追蹤:你可以將狀態儲存在某處嗎?您可以使用 zx_status_get_string(status) 將其轉換為字串。
進階:inspector 有一個名為 Health() 的方法,可在特定位置播報整體健康狀態。由於我們的服務必須連線至 FizzBuzz 才能正常運作,請加入以下內容:
/*
"fuchsia.inspect.Health": {
"status": "STARTING_UP"
}
*/
inspector.Health().StartingUp();
/*
"fuchsia.inspect.Health": {
"status": "OK"
}
*/
inspector.Health().Ok();
/*
"fuchsia.inspect.Health": {
"status": "UNHEALTHY",
"message": "Something went wrong!"
}
*/
inspector.Health().Unhealthy("Something went wrong!");
荒漠油廠
進階:fuchsia_inspect::component 設有名為 health() 的函式,可傳回物件,在特殊位置 (檢查樹狀結構根項的節點子項) 中發布整體健康狀態。由於服務必須連線至 FizzBuzz 才能正常運作,請加入以下內容:
/*
"fuchsia.inspect.Health": {
"status": "STARTING_UP"
}
*/
fuchsia_inspect::component::health().set_starting_up();
/*
"fuchsia.inspect.Health": {
"status": "OK"
}
*/
fuchsia_inspect::component::health().set_ok();
/*
"fuchsia.inspect.Health": {
"status": "UNHEALTHY",
"message": "Something went wrong!"
}
*/
fuchsia_inspect::component::health().set_unhealthy("something went wrong!");
完成這項練習後,您應該會看到系統呼叫連線錯誤處理常式,並顯示「找不到」錯誤。檢查輸出內容後,發現 FizzBuzz 正在執行,因此可能有些設定錯誤。很抱歉,並非所有項目都使用檢查功能 (目前是這樣!),因此請查看記錄:
C++
ffx log --filter FizzBuzz畫面會顯示類似以下的輸出:
...
... No capability available at path /svc/fuchsia.examples.inspect.FizzBuzz
for component /core/ffx-laboratory:client_part_2/reverser, verify the
component has the proper `use` declaration. ...
荒漠油廠
ffx log --filter FizzBuzz畫面會顯示類似以下的輸出:
...
... No capability available at path /svc/fuchsia.examples.inspect.FizzBuzz
for component /core/ffx-laboratory:client_part_2/reverser, verify the
component has the proper `use` declaration. ...
沙箱錯誤是常見的陷阱,有時很難發現。
查看 part2 中繼資料,您會發現缺少服務:
C++
在 part_2/meta 中為 Fizzbuzz 新增 use 項目:
use: [
{ protocol: "fuchsia.examples.inspect.FizzBuzz" },
],
荒漠油廠
在 part_2/meta 中為 Fizzbuzz 新增 use 項目:
use: [
{ protocol: "fuchsia.examples.inspect.FizzBuzz" },
],
新增「fuchsia.examples.inspect.FizzBuzz」後,請重建並再次執行。現在,您應該會在記錄中看到 FizzBuzz,以及 OK 狀態:
C++
ffx log --tag inspect_cpp_codelab畫面會顯示類似以下的輸出:
[inspect_cpp_codelab, part2] INFO: main.cc(57): Got FizzBuzz: 1 2 Fizz
4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz
22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz
荒漠油廠
ffx log --tag inspect_rust_codelab畫面會顯示類似以下的輸出:
[inspect_rust_codelab, part2] INFO: main.rs(52): Got FizzBuzz: 1 2 Fizz
4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz
22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz
第 2 部分到此結束。
現在可以選擇提交解決方案:
git commit -am "solution for part 2"第 3 部分:Inspect 的單元測試
Fuchsia 上的所有程式碼都應經過測試,這也適用於檢查資料。
一般來說,您不需要測試檢查資料,但如果其他工具 (例如 Triage 或 Feedback) 依賴檢查資料,您就必須測試。
Reverser 具有基本單元測試。運作執行:
C++
單元測試位於 reverser_unittests.cc。
fx test inspect_cpp_codelab_unittests荒漠油廠
單元測試位於 reverser.rs > mod tests。
fx test inspect_rust_codelab_unittests單元測試可確保 Reverser 正常運作 (不會停止回應!),但不會檢查 Inspect 輸出內容是否符合預期。
將節點傳遞至建構函式是「依附元件插入」的一種形式,可讓您傳遞依附元件的測試版本,以檢查其狀態。
開啟 Reverser 的程式碼如下所示:
C++
binding_set_.AddBinding(std::make_unique<Reverser>(ReverserStats::CreateDefault()),
ptr.NewRequest());
// Alternatively
binding_set_.AddBinding(std::make_unique<Reverser>(inspect::Node()),
ptr.NewRequest());
荒漠油廠
let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<ReverserMarker>();
let reverser = ReverserServer::new(ReverserServerMetrics::default());
reverser.spawn(stream);
系統會將 Inspect Node 的預設版本傳遞至 Reverser。這樣一來,反向程式碼就能在測試中正常執行,但無法對檢查輸出內容進行斷言。
C++
練習:變更 OpenReverser,將 Reverser 的依附元件做為引數,並在建構 Reverser 時使用。
提示:在測試函式中建立
inspect::Inspector。您可以使用inspector.GetRoot()取得根目錄。提示:您需要在根層級建立子項,才能傳遞至
OpenReverser。
荒漠油廠
練習:變更 open_reverser,將 ReverserServerFactory 的依附元件做為引數,並在建構 Reverser 時使用。
提示:在測試函式中建立
fuchsia_inspect::Inspector。您可以使用inspector.root()取得根目錄。注意:請勿直接在測試中使用
component::inspector(),這會建立靜態檢查器,在所有測試中都會保持運作,可能導致不穩定或非預期的行為。對於單元測試,請一律優先使用新的fuchsia_inspect::Inspector提示:您需要在根層級建立子項,才能傳遞至
ReverserServerFactory::new。
後續步驟:建立多個反向連線,並分別測試。
完成這項練習後,單元測試會在「檢查」階層中設定實際值。
在「檢查」中新增程式碼,測試輸出內容:
C++
#include <lib/inspect/testing/cpp/inspect.h>
fpromise::result<inspect::Hierarchy> hierarchy =
RunPromise(inspect::ReadFromInspector(inspector));
ASSERT_TRUE(hierarchy.is_ok());
上述程式碼片段會讀取包含 Inspect 資料的基礎虛擬記憶體物件 (VMO),並將其剖析為可讀取的階層。
您現在可以讀取個別屬性和子項,如下所示:
auto* global_count =
hierarchy.value().node().get_property<inspect::UintPropertyValue>("request_count");
ASSERT_TRUE(global_count);
EXPECT_EQ(3u, global_count->value());
auto* connection_0 = hierarchy.value().GetByPath({"connection_0x0"});
ASSERT_TRUE(connection_0);
auto* requests_0 =
connection_0->node().get_property<inspect::UintPropertyValue>("request_count");
ASSERT_TRUE(requests_0);
EXPECT_EQ(2u, requests_0->value());
荒漠油廠
use diagnostics_assertions::assert_data_tree;
let inspector = inspect::Inspector::default();
assert_data_tree!(inspector, root: {
reverser_service: {
total_requests: 3u64,
connection_count: 2u64,
"connection0": {
request_count: 2u64,
response_count: 2u64,
},
"connection1": {
request_count: 1u64,
response_count: 1u64,
},
}
});
上述程式碼片段會從包含檢查資料的基礎虛擬記憶體物件 (VMO) 讀取快照,並剖析為可讀取的階層。
練習:為其餘檢查資料新增斷言。
第 3 部分到此結束。
您也可以選擇提交變更:
git commit -am "solution to part 3"第 4 部分:檢查的整合測試
整合測試 是 Fuchsia 軟體開發工作流程的重要環節。整合測試可讓您觀察實際元件在系統上執行時的行為。
執行整合測試
您可以按照下列步驟,執行程式碼研究室的整合測試:
C++
fx test inspect_cpp_codelab_integration_tests荒漠油廠
fx test inspect_rust_codelab_integration_tests這會針對本程式碼研究室的所有部分執行整合測試。
查看程式碼
請查看整合測試的設定方式:
查看整合測試的元件資訊清單:
C++
在 part_4/meta 中找出元件資訊清單 (cml)
荒漠油廠
在 part_4/meta 中找出元件資訊清單 (cml)
畫面上會顯示下列訊息:
{ ... use: [ { protocol: "fuchsia.diagnostics.ArchiveAccessor" }, ] }這個檔案會使用父項的
fuchsia.diagnostics.ArchiveAccessor通訊協定。所有測試都能使用這個通訊協定,讀取測試領域中所有元件的診斷資訊。查看整合測試本身。個別測試案例相當簡單:
C++
在 part4/tests/integration_test.cc 中找出整合測試。
TEST_F(IntegrationTestPart4, StartWithFizzBuzz) { auto ptr = ConnectToReverser({.include_fizzbuzz = true}); bool error = false; ptr.set_error_handler([&](zx_status_t unused) { error = true; }); bool done = false; std::string result; ptr->Reverse("hello", [&](std::string value) { result = std::move(value); done = true; }); RunLoopUntil([&] { return done || error; }); ASSERT_FALSE(error); EXPECT_EQ("olleh", result); // CODELAB: Check that the component was connected to FizzBuzz. }StartComponentAndConnect負責建立新的測試環境,並在其中啟動程式碼研究室元件。include_fizzbuzz_service選項會指示方法選擇性地加入 FizzBuzz。這項功能會測試您的檢查輸出內容是否符合預期,以防無法連線至 FizzBuzz (如第 2 部分所述)。荒漠油廠
在 part4/tests/integration_test.rs 中找出整合測試。
#[fuchsia::test] async fn start_with_fizzbuzz() -> Result<(), Error> { let test = IntegrationTest::start(4, TestOptions::default()).await?; let reverser = test.connect_to_reverser()?; let result = reverser.reverse("hello").await?; assert_eq!(result, "olleh"); // CODELAB: Check that the component was connected to FizzBuzz. Ok(()) }IntegrationTest::start負責建立新的測試環境,並在其中啟動程式碼研究室元件。include_fizzbuzz選項會指示方法選擇性啟動 FizzBuzz 元件。這項功能會測試您的檢查輸出內容是否符合預期,以防無法連線至 FizzBuzz (如第 2 部分所示)。在測試固件中新增下列方法,從 ArchiveAccessor 服務讀取資料:
C++
#include <rapidjson/document.h> #include <rapidjson/pointer.h> std::string GetInspectJson() { fuchsia::diagnostics::ArchiveAccessorPtr archive; auto svc = sys::ServiceDirectory::CreateFromNamespace(); svc->Connect(archive.NewRequest()); while (true) { ContentVector current_entries; fuchsia::diagnostics::BatchIteratorPtr iterator; fuchsia::diagnostics::StreamParameters stream_parameters; stream_parameters.set_data_type(fuchsia::diagnostics::DataType::INSPECT); stream_parameters.set_stream_mode(fuchsia::diagnostics::StreamMode::SNAPSHOT); stream_parameters.set_format(fuchsia::diagnostics::Format::JSON); { std::vector<fuchsia::diagnostics::SelectorArgument> args; args.emplace_back(); args[0].set_raw_selector(ReverserMonikerForSelectors() + ":root"); fuchsia::diagnostics::ClientSelectorConfiguration client_selector_config; client_selector_config.set_selectors(std::move(args)); stream_parameters.set_client_selector_configuration(std::move(client_selector_config)); } archive->StreamDiagnostics(std::move(stream_parameters), iterator.NewRequest()); bool done = false; iterator->GetNext([&](auto result) { if (result.is_response()) { current_entries = std::move(result.response().batch); } done = true; }); RunLoopUntil([&] { return done; }); // Should be at most one component. ZX_ASSERT(current_entries.size() <= 1); if (!current_entries.empty()) { std::string json; fsl::StringFromVmo(current_entries[0].json(), &json); // Ensure the component is either OK or UNHEALTHY. if (json.find("OK") != std::string::npos || json.find("UNHEALTHY") != std::string::npos) { return json; } } // Retry with delay until the data appears. usleep(150000); } return ""; }荒漠油廠
use anyhow::format_err; use diagnostics_assertions::{AnyProperty, assert_data_tree}; use diagnostics_reader::{ArchiveReader, DiagnosticsHierarchy}; async fn get_inspect_hierarchy(test: &IntegrationTest) -> Result<DiagnosticsHierarchy, Error> { let moniker = test.reverser_moniker_for_selectors(); ArchiveReader::inspect() .add_selector(format!("{}:root", moniker)) .snapshot() .await? .into_iter() .next() .and_then(|result| result.payload) .ok_or_else(|| format_err!("expected one inspect hierarchy")) }運動。在測試中使用傳回的資料,並將斷言新增至傳回的資料:
C++
rapidjson::Document document; document.Parse(GetInspectJson());對傳回的 JSON 資料新增判斷提示。
提示:列印 JSON 輸出內容可能有助於查看結構定義。
提示:您可以依路徑讀取值,如下所示:
提示:您可以將預期值做為 rapidjson::Value 傳遞,藉此
EXPECT_EQ:rapidjson::Value("OK")。
rapidjson::GetValueByPointerWithDefault( document, "/payload/root/fuchsia.inspect.Health/status", "")荒漠油廠
let hierarchy = get_inspect_hierarchy(&test).await?;在傳回的
DiagnosticsHierarchy上新增斷言。- 提示:列印 JSON 輸出內容可能有助於查看結構定義。
整合測試現在會確保檢查輸出內容正確無誤。
第 4 部分到此結束。
您可以選擇提交解決方案:
git commit -am "solution to part 4"第 5 部分:意見回饋選取器
本節仍在建構中。
待辦事項:撰寫意見回饋選取器,並將測試新增至整合測試。
TODO:意見回饋和其他管道的選取器