檢查快速入門導覽課程

本快速入門導覽課程會引導您完成使用元件檢查的基本知識。您將瞭解如何使用語言特定程式庫將 Inspect 整合至元件,並使用 ffx inspect 查看資料。

如需檢查概念的詳細逐步操作說明,請參閱「檢查程式碼研究室」。

專案設定

請參閱下列語言的快速入門指南:

C++

本節假設您正在編寫非同步元件,且元件的某些部分 (通常是 main.cc) 如下所示:

async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
// ...
loop.Run();

這會設定非同步迴圈、建立執行階段提供的 ComponentContext 包裝控點,然後在處理其他初始化工作後執行該迴圈。

將檢查程式庫依附元件新增至 BUILD.gn 檔案:

"//sdk/lib/inspect/component/cpp",
"//sdk/lib/sys/cpp",

新增下列項目:

#include <lib/inspect/component/cpp/component.h>

加入下列程式碼即可初始化檢查功能:

inspector_ = std::make_unique<inspect::ComponentInspector>(async_get_default_dispatcher(),
                                                           inspect::PublishOptions{});

您現在已使用「檢查」功能!將屬性附加至根節點,即可在檢查樹狀結構中建立屬性:

// Attach properties to the root node of the tree
inspect::Node& root_node = inspector_->root();
// Important: Hold references to properties and don't let them go out of scope.
auto total_requests = root_node.CreateUint("total_requests", 0);
auto bytes_processed = root_node.CreateUint("bytes_processed", 0);

如需您可使用的資料類型完整清單,請參閱支援的資料類型

健康狀態檢查

健康狀態檢查子系統可為元件健康狀態提供標準化檢查指標。您可以使用健康狀態節點回報元件的整體狀態:

inspector_->Health().StartingUp();

// ...

inspector_->Health().Ok();

測試

如要測試檢查程式碼,可以使用 //sdklib/inspect/testing/cpp/inspect.h

#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/testing/cpp/inspect.h>

#include <gtest/gtest.h>

using namespace inspect::testing;

這個程式庫包含一組完整的比對器,用於驗證檢查樹狀結構的內容。

// Validate the contents of the tree match
auto hierarchy_result = inspect::ReadFromVmo(inspector_.DuplicateVmo());
ASSERT_TRUE(hierarchy_result.is_ok());
EXPECT_THAT(hierarchy_result.take_value(),
            NodeMatches(AllOf(PropertyList(::testing::UnorderedElementsAre(
                UintIs("bytes_processed", 24), UintIs("total_requests", 2))))));

Rust

本節假設您正在編寫非同步元件,且元件的某些部分 (通常是 main.rs) 看起來與以下內容類似:

async fn main() -> Result<(), Error> {
  // ...
  let mut service_fs = ServiceFs::new();
  // ...
  service_fs.take_and_serve_directory_handle().unwrap();
  service_fs.collect::<()>().await;
  Ok(())
}

將檢查程式庫依附元件新增至 BUILD.gn 檔案:

"//src/lib/diagnostics/inspect/runtime/rust",
"//src/lib/diagnostics/inspect/rust",

加入下列程式碼即可初始化檢查功能:

// This creates the root of an Inspect tree
// The Inspector is a singleton that you can access from any scope
let inspector = fuchsia_inspect::component::inspector();
// This serves the Inspect tree, converting failures into fatal errors
let _inspect_server_task =
    inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default());

您現在已使用「檢查」功能!將屬性附加至根節點,即可在檢查樹狀結構中建立屬性:

// Attach properties to the root node of the tree
let root_node = inspector.root();
let total_requests = root_node.create_uint("total_requests", 0);
let bytes_processed = root_node.create_uint("bytes_processed", 0);

如需您可使用的資料類型完整清單,請參閱支援的資料類型

健康狀態檢查

健康狀態檢查子系統可為元件健康狀態提供標準化檢查指標。您可以使用健康狀態節點回報元件的整體狀態:

fuchsia_inspect::component::health().set_starting_up();

// ...

fuchsia_inspect::component::health().set_ok();

測試

如要測試檢查程式碼,可以使用 assert_data_tree 驗證檢查樹狀結構的內容:

// Get a reference to the root node of the Inspect tree
let inspector = fuchsia_inspect::component::inspector();

// ...

// Validate the contents of the tree match
diagnostics_assertions::assert_data_tree!(inspector, root: {
    total_requests: 2u64,
    bytes_processed: 24u64,
});

檢查程式庫

您已擁有 root_node,可以開始建立階層。本節說明一些可協助您上手的重要概念和模式。

  • 節點可以有任意數量的鍵/值組合,稱為 Properties
  • 值的索引鍵一律為 UTF-8 字串,值可以是下列支援的類型之一。
  • 一個節點可以有任意數量的子項,這些子項也是節點。

C++

上述程式碼可讓您存取名為「root」的單一節點。hello_world_property 是包含字串值的屬性 (也稱為 StringProperty)。

  • 值和節點是在父項節點下建立。

Node 類別具有每個支援值類型的建立者方法。hello_world_property 是使用 CreateStringProperty 建立而成。您可以呼叫 root_node.CreateChild("child name"),在根節點下建立子項。請注意,名稱必須是 UTF-8 字串。

  • 值和節點有嚴格的擁有權語意。

hello_world_property 擁有屬性。刪除後 (超出範圍) 會刪除基礎屬性,且不再出現在元件的檢查輸出內容中。子節點也是如此。

如果您建立的值不需要修改,請使用 ValueList 來將其保持有效,直到不再需要使用為止。

  • 我們會盡可能進行調查。

由於空間限制,檢查程式庫可能無法滿足 Create 要求。您的程式碼不會出現這個錯誤:您會收到 Node/Property 物件,但這些方法不是免人工管理。

  • 模式:將子節點傳入子項物件。

建議您在類別建構函式中加入 inspect::Node 引數。父項物件 (應擁有自己的 inspect::Node) 可能會在建構時,將 CreateChild(...) 的結果傳遞至其子項:

class Child {
  public:
    Child(inspect::Node my_node) : my_node_(std::move(my_node)) {
      // Create a string that doesn't change, and emplace it in the ValueList
      my_node_.CreateString("version", "1.0", &values_);
      // Create metrics and properties on my_node_.
    }

  private:
    inspect::Node my_node_;
    inspect::StringProperty some_property_;
    inspect::ValueList values_;
    // ... more properties and metrics
};

class Parent {
  public:
    // ...

    void AddChild() {
      // Note: inspect::UniqueName returns a globally unique name with the specified prefix.
      children_.emplace_back(my_node_.CreateChild(inspect::UniqueName("child-")));
    }

  private:
    std::vector<Child> children_;
    inspect::Node my_node_;
};

Rust

Rust 程式庫提供兩種管理節點和屬性的方式:建立和記錄。

使用 create_* 方法時,屬性或節點物件的擁有權屬於呼叫端。捨棄傳回的物件後,該屬性就會移除。 例如:

{
    let property = root.create_int("name", 1);
}

在這個範例中,property 超出範圍,因此會呼叫屬性捨棄。讀者不會看到這個屬性。

使用 record_* 方法時,屬性的生命週期會與父項節點連結。刪除節點時,系統會刪除記錄的屬性。

{
    let node = root.create_child("name");
    {
      node.record_uint(2); // no return
    }
    // The uint property will still be visible to readers.
}

在本範例中,除非父項 node 超出範圍,否則讀者可以查看與 name 相關聯的 uint 屬性。

動態價值

本節說明檢查程式庫是否支援在讀取時延遲加載的節點。這些方法會接受回呼函式,而非值。讀取屬性值時,會叫用回呼函式。

C++

C++ 程式庫有兩個屬性建立者,可用於動態值:CreateLazyNodeCreateLazyValues

這兩種方法都會使用回呼傳回 inspect::Inspector 的承諾,唯一的差別在於動態值儲存在樹狀結構中。

root->CreateLazyNode(name, callback) 會使用指定的 name 建立 root 的子節點。callback 會在讀取時傳回 inspect::Inspector,其中的根節點會在讀取時複製到父項階層。以下範例顯示名為「lazy」的子項具有「version」字串屬性,且有一個稱為「lazy」的子項。

root->CreateLazyValues(name, callback) 的運作方式與 root->CreateLazyNode(name, callback) 類似,但承諾根節點上的所有屬性和子節點會直接加入為原始 root 的值。在本範例的第二個輸出內容中,系統不會顯示內部延遲節點,而且其值會分割為 root 上的屬性。

root->CreateLazy{Node,Values}("lazy", [] {
  Inspector a;
  a.GetRoot().CreateString("version", "1.0", &a);
  a.GetRoot().CreateLazy{Node,Values}("lazy", [] {
    Inspector b;
    b.GetRoot().RecordInt("value", 10);
    return fpromise::make_ok_promise(std::move(b));
  }, &a);

  return fpromise::make_ok_promise(std::move(a));
});

輸出 (CreateLazyNode):

root:
  lazy:
    version = "1.0"
    lazy:
      value = 10

輸出 (CreateLazyValues):

root:
  value = 10
  version = "1.0"

CreateLazy{Node,Values} 的傳回值是擁有傳遞回呼的 LazyNode。刪除 LazyNode 後,系統就一律不會呼叫回呼。如果您同時刪除 LazyNode 和執行回呼,系統會封鎖刪除作業,直到回呼傳回承諾為止。

如要動態公開 this 的屬性,只需編寫以下內容:

class Employee {
  public:
    Employee(inspect::Node node) : node_(std::move(node)) {
      calls_ = node_.CreateInt("calls", 0);

      // Create a lazy node that populates values on its parent
      // dynamically.
      // Note: The callback will never be called after the LazyNode is
      // destroyed, so it is safe to capture "this."
      lazy_ = node_.CreateLazyValues("lazy", [this] {
        // Create a new Inspector and put any data in it you want.
        inspect::Inspector inspector;

        // Keep track of the number of times this callback is executed.
        // This is safe because the callback is executed without locking
        // any state in the parent node.
        calls_.Add(1);

        // ERROR: You cannot modify the LazyNode from the callback. Doing
        // so may deadlock!
        // lazy_ = ...

        // The value is set to the result of calling a method on "this".
        inspector.GetRoot().RecordInt("performance_score",
                                      this->CalculatePerformance());

        // Callbacks return a fpromise::promise<Inspector>, so return a result
        // promise containing the value we created.
        // You can alternatively return a promise that is completed by
        // some asynchronous task.
        return fpromise::make_ok_promise(std::move(inspector));
      });
    }

  private:
    inspect::Node node_;
    inspect::IntProperty calls_;
    inspect::LazyNode lazy_;
};

Rust

請參閱 C++ 動態價值支援,如同 Rust 中應用的類似概念。

例子:

root.create_lazy_{child,values}("lazy", [] {
    async move {
        let inspector = Inspector::default();
        inspector.root().record_string("version", "1.0");
        inspector.root().record_lazy_{node,values}("lazy", || {
            let inspector = Inspector::default();
            inspector.root().record_int("value", 10);
            // `_value`'s drop is called when the function returns, so it will be removed.
            // For these situations `record_` is provided.
            let _value = inspector.root().create_int("gone", 2);
            Ok(inspector)
        });
        Ok(inspector)
    }
    .boxed()
});

Output (create_lazy_node):
root:
  lazy:
    version = "1.0"
    lazy:
      value = 10

Output (create_lazy_values):
root:
  value = 10
  version = "1.0"

字串參照

C++

您可以使用 inspect::StringReference 減少含有大量重複資料的檢查階層的記憶體用量。例如

using inspect::Inspector;

Inspector inspector;

for (int i = 0; i < 100; i++) {
  inspector.GetRoot().CreateChild("child", &inspector);
}

將會在檢查輸出內容中加入 100 個字串 "child" 副本。

或者,

using inspect::Inspector;
using inspect::StringReference;

namespace {
  const StringReference kChild("child");
}

Inspector inspector;
for (int i = 0; i < 100; i++) {
  inspector.GetRoot().CreateChild(kChild, &inspector)
}

只會產生一個參照 100 次的 "child" 副本。

這會為每個子節點減少 16 個位元組,對共用資料的費用則為 32 個位元組。淨結果可省下 1568 個位元組。

如果打算使用全域常數索引鍵,則建議使用此模式。

Rust

在 Rust 檢查中,系統會自動刪除重複的字串名稱。例如:

use fuchsia_inspect::Inspector;

let inspector = Inspector::default();
for _ in 0..100 {
  inspector.root().record_child("child");
}

只會產生 1 個 "child" 副本,這些副本會參照 100 次。

這會為每個子節點減少 16 個位元組,對共用資料的費用則為 32 個位元組。淨結果可省下 1568 個位元組。

查看檢查資料

您可以使用 ffx inspect 指令查看從元件匯出的「檢查」資料。

本節假設您擁有執行中 Fuchsia 系統的 SSH 存取權,且已開始執行元件。我們會使用 my_component.cm 做為元件資訊清單名稱的預留位置。

讀取「檢查」資料

下列指令會顯示系統中執行的所有元件檢查階層:

ffx inspect show

透過 ffx inspect list 的輸出內容,您可以將單一元件 (例如 my_component.cm) 指定為 ffx inspect show 的輸入:

ffx inspect show --manifest my_component.cm

指定上方的 --manifest 後,系統會針對在系統上執行的所有元件執行個體傳回資料。如果您知道元件的特定路徑名稱 (例如 core/my_component),可以改為傳遞該模擬器:

ffx inspect show core/my_component

您可以指定多個元件,例如 core/font_providercore/my_component

ffx inspect show core/font_provider core/my_component

您也可以指定節點和屬性值。如要查看所有可能的選取器清單,請使用 ffx inspect selectors

ffx inspect selectors core/my_component

接著,您可以指定選取器 (例如 core/my_component:root) 做為 ffx inspect show 的輸入項目:

ffx inspect show core/my_component:root

如果您不知道元件的路徑名稱,可以使用 --manifest 搭配所有相符元件音效 (使用 *) 的選取器:

ffx inspect show --manifest my_component.cm *:root

如果您已按照上述建議步驟操作,這將輸出以下內容:

root:
  hello = world

支援的資料類型

類型 說明 附註
IntProperty 包含帶正負號 64 位元整數的指標。 所有語言
UIntProperty 包含未帶正負號 64 位元整數的指標。 Dart 不支援這項功能
DoubleProperty 包含雙浮點數的指標。 所有語言
BoolProperty 包含雙浮點數的指標。 所有語言
{Int,Double,Uint}陣列 指標類型的陣列,包括各種直方圖的型別包裝函式。 支援與基礎指標類型相同的語言
StringArray 字串陣列。以 StringReference 表示。 Dart 不支援這項功能。
StringProperty 具有 UTF-8 字串值的屬性。 所有語言
ByteVectorProperty 具有任意位元組值的屬性。 所有語言
節點 指標、屬性等節點可能是巢狀結構的節點。 所有語言
LazyNode 以動態方式將完整的節點樹狀結構例項化。 C++、Rust