检查快速入门

本快速入门指南介绍了使用 组件检查。您将了解如何将 Inspect 集成到 使用特定语言的库替换您的组件,并使用 ffx inspect

有关 Inspect 概念的详细演示,请参阅 检查 Codelab

项目设置

请参阅下文,获取所选语言版本的快速入门指南:

C++

本部分假定您要编写异步组件,并且 组件的某些部分(通常为 main.cc)如下所示:

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

这会设置异步循环,并创建一个 ComponentContext 封装句柄 然后根据运行时提供的一些其他命令运行该循环, 初始化工作。

将 Inspect 库依赖项添加到 BUILD.gn 文件中

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

添加以下包含的内容

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

添加以下代码以初始化 Inspect

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

您现在可以使用 Inspect 了!在 Inspect 树中创建属性,方法是将 添加到根节点:

// 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))))));

储油站

本部分假定您要编写异步组件,并且 组件的一部分(通常为 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(())
}

将 Inspect 库依赖项添加到 BUILD.gn 文件中

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

添加以下代码以初始化 Inspect

// 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());

您现在可以使用 Inspect 了!在 Inspect 树中创建属性,方法是将 添加到根节点:

// 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();

测试

如需测试 Inspect 代码,您可以使用 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 以保持它们存在,直到不再需要它们。

  • 我们将尽最大努力进行检查。

由于空间限制,Inspect 库可能无法满足 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 库提供两种管理节点和属性的方法:创建 和录制。

使用 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.
}

在此示例中,读者可以看到与 name 关联的 uint 属性 直到父级 node 超出范围。

动态价值

本部分介绍了 Inspect 库对 在读取时延迟膨胀。这些方法接受回调函数,而不是 值。回调函数会在读取属性值时调用。

C++

C++ 库有两个针对动态值的属性创建者: CreateLazyNodeCreateLazyValues

这两种方法都接受一个回调,该回调返回针对 inspect::Inspector,唯一的区别在于动态价值的 存储在树中。

root->CreateLazyNode(name, callback) 会创建 具有指定 namerootcallback 会针对 inspect::Inspector,其根节点已拼接到父级层次结构中 读取。以下示例展示了一个名为“lazy”的子节点存在 字符串属性“version”还有一个称为 “懒洋洋的”。

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_;
};

储油站

请参阅 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);
}

将在检查中包含字符串 "child" 的 100 个副本 输出。

或者,

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 Inspect 中,系统会自动删除重复的字符串名称。例如,

use fuchsia_inspect::Inspector;

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

将仅生成被引用 100 次的 "child" 的 1 个副本。

这样会为每个子节点节省 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

使用 JSON pretty 打印机获取完整商家信息。例如:

use diagnostics_assertions::JsonGetter;
...
    #[fuchsia::test]
    fn my_test() {
        let inspect = fuchsia_inspect::component::inspector();
        ...
        print!("{}", inspect.get_pretty_json());
    }

支持的数据类型

类型 说明 备注
IntProperty 包含有符号 64 位整数的指标。 所有语言
UIntProperty 包含无符号 64 位整数的指标。 在 Dart 中不支持
DoubleProperty 包含双浮点数的指标。 所有语言
BoolProperty 包含双浮点数的指标。 所有语言
{Int,Double,Uint} 数组 指标类型的数组,包括用于各种直方图的类型化封装容器。 支持与基本指标类型相同的语言
StringArray 字符串数组。表示为 StringReference 在 Dart 中不受支持。
StringProperty 具有 UTF-8 字符串值的属性。 所有语言
ByteVectorProperty 具有任意字节值的属性。 所有语言
节点 一个节点,指标、属性和更多节点可以嵌套在其下。 所有语言
LazyNode 动态地实例化完整的节点树。 C++、Rust