诊断和测试 Codelab

本文档包含一个使用诊断和测试进行调试的 Codelab。目前 适用于在 fuchsia.git 中编写测试的开发者。

前提条件

设置您的开发环境。

此 Codelab 假定您已完成使用入门并已满足以下条件:

  1. 一棵经过检修和建造的紫红树。
  2. 运行 Fuchsia 的设备或模拟器 (ffx emu)。
  3. 一个工作站,用于向 Fuchsia 设备或模拟器提供组件 (fx serve)。

如需构建并运行此 Codelab 中的示例,请添加以下参数 添加到您的 fx set 调用中:

fx set core.x64 \
--release \
--with //examples/diagnostics/workshop \
--with //examples/diagnostics/workshop:tests

简介

有一个示例组件,它提供名为 ProfileStore 的协议:

@discoverable
closed protocol ProfileStore {
    strict Open(resource struct {
        key string:KEY_LENGTH;
        channel server_end:Profile;
    });

    strict OpenReader(resource struct {
        key string:KEY_LENGTH;
        channel server_end:ProfileReader;
    });

    strict Delete(struct {
        key string:KEY_LENGTH;
    }) -> (struct {
        success bool;
    });

    strict CreateOrOpen(resource struct {
        key string:KEY_LENGTH;
        channel server_end:Profile;
    });
};

此协议允许创建、删除和检查用户个人资料,其中包含 姓名和余额该组件存在错误 - 无法删除个人资料。

此 Codelab 的代码位于 //examples/diagnostics/workshop

运行组件

除了提供 ProfileStore 的主要组件之外,还有许多组件 连接到 ProfileStore 并与之互动。所有组件均位于 fuchsia-pkg://fuchsia.com/profile_store_example 软件包。

  • #meta/profile_store.cm - 投放 ProfileStore
  • #meta/add_olive.cm - 连接到 ProfileStore 并添加名为“Olive”的配置文件
  • #meta/add_balance_olive.cm - 连接到 ProfileStore 并为“Olive”增加余额个人资料
  • #meta/withdraw_balance_olive.cm - 连接到 ProfileStore 并从 “橄榄绿”个人资料
  • #meta/add_jane.cm - 连接到 ProfileStore 并添加名为“Jane”的配置文件
  • #meta/delete_olive.cm - 连接到 ProfileStore 并删除“Olive”个人资料

capability 由 #meta/laboratory_server.cm 组件路由。

您可以使用 ffx component 命令与组件交互,并从 组件使用 ffx log。 首先,在 shell 中运行 ffx log --tags workshop。此 shell 将包含来自 组件。在其他 shell 中,运行玩具组件:

# setup server
ffx component create /core/ffx-laboratory:profile_store fuchsia-pkg://fuchsia.com/profile_store_example#meta/laboratory_server.cm

# setup first client
ffx component create /core/ffx-laboratory:profile_store/clients:add_olive fuchsia-pkg://fuchsia.com/profile_store_example#meta/add_olive.cm

# see the results of the previous two steps
ffx component show profile_store

# add a profile key and read it
ffx component start /core/ffx-laboratory:profile_store/clients:add_olive
ffx component create /core/ffx-laboratory:profile_store/clients:reader fuchsia-pkg://fuchsia.com/profile_store_example#meta/profile_reader.cm
ffx component start /core/ffx-laboratory:profile_store/clients:reader

# demonstrate persistence
ffx component stop /core/ffx-laboratory:profile_store/profile_store
ffx component start /core/ffx-laboratory:profile_store/clients:reader

# update balance
ffx component create /core/ffx-laboratory:profile_store/clients:add_balance_olive fuchsia-pkg://fuchsia.com/profile_store_example#meta/add_balance_olive.cm
ffx component start /core/ffx-laboratory:profile_store/clients:add_balance_olive
ffx component start /core/ffx-laboratory:profile_store/clients:reader

# add second profile
ffx component create /core/ffx-laboratory:profile_store/clients:add_jane fuchsia-pkg://fuchsia.com/profile_store_example#meta/add_jane.cm
ffx component start /core/ffx-laboratory:profile_store/clients:add_jane
ffx component start /core/ffx-laboratory:profile_store/clients:reader

# update balance
ffx component create /core/ffx-laboratory:profile_store/clients:withdraw_balance_olive fuchsia-pkg://fuchsia.com/profile_store_example#meta/withdraw_balance_olive.cm
ffx component start /core/ffx-laboratory:profile_store/clients:withdraw_balance_olive
ffx component start /core/ffx-laboratory:profile_store/clients:reader

# delete olive (this will not work as there is a bug in the server code)
ffx component create /core/ffx-laboratory:profile_store/clients:delete_olive fuchsia-pkg://fuchsia.com/profile_store_example#meta/delete_olive.cm
ffx component start /core/ffx-laboratory:profile_store/clients:delete_olive
ffx component start /core/ffx-laboratory:profile_store/clients:reader

通过诊断调试进行调试

“诊断”提供了多个产品,可帮助组件作者同时调试其组件 开发和探索这个领域。

在本次研讨会中,我们将探索三项核心技术:

结构化日志记录

诊断提供结构化日志记录库,以允许组件写入日志。 为了帮助查找该错误,我们将向配置文件存储组件添加一些日志。

向组件添加日志记录的第一步是添加日志记录库 放在您的二进制依赖项中为此,请按如下所示更新 BUILD.gn

source_set("lib") {
  ...
  public_deps = [
    ...
    "//sdk/lib/syslog/cpp",
  ]
}

在我们调用其中一个日志记录宏时,日志记录便会初始化。不过,这些库 一些应在 main() 中调用的实用程序,例如配置标记(如果仅需要,这 可选)。

以后查询一组组件的日志时,会用到标记。为此,我们可以将 workshop 标记:

#include <lib/syslog/cpp/log_settings.h>
...
fuchsia_logging::SetTags({"workshop", "profile_store_server"});

现在该写入一些日志了。我们将使用 FX_SLOG 宏,它允许写入 结构化键和值。

例如,当我们收到 ProfileStore::Open 上的请求时,可以添加以下日志,但 配置文件不存在:

#include <lib/syslog/cpp/macros.h>
...
FX_SLOG(WARNING, "Profile doesn't exist", KV("key", key.c_str()));

请尝试添加该日志,进行构建 (fx build),重新启动组件 (ffx component start ...),然后 然后运行:ffx log --tags workshop

我们可以添加哪些其他日志来帮助识别该日志?请多加尝试!

可在此补丁中找到解决方案。

检查

检查允许组件公开自身的状态。与日志(即日志)不同 “Inspect”表示对组件当前状态的实时视图。

建议您先仔细阅读检查快速入门中的相关内容。如果您想 如果您想深入了解 Inspect,您也可以按照 Inspect Codelab 的说明操作。

首先,添加库依赖项:

source_set("lib") {
  ...
  public_deps = [
    ...
    "//sdk/lib/inspect/component/cpp",
  ]
}

接下来,在 main.cc 中初始化 Inspect:

#include <lib/inspect/component/cpp/component.h>
...
// Immediately following the line defining "startup".
auto inspector = std::make_unique<inspect::ComponentInspector>(async_get_default_dispatcher(), inspect::PublishOptions{});
inspector->Health().Ok();

现在,您可以在重启 profile_store 后查看“Inspect”(检查):

# Note that double \\ is necessary! ':' must be escaped in a "diagnostic selector."
ffx inspect show core/ffx-laboratory\\:profile_store/profile_store

您应该会看到该组件的状态是“正常”。检查 在与类层次结构集成时非常有用。任意值 已启用 inspect::Node 的 root 权限,包含更多节点!尝试修改 ProfileStore,以便能够编译以下内容:

  // In main.cc
  std::unique_ptr<ProfileStore> profile_store =
      std::make_unique<ProfileStore>(loop.dispatcher(), inspector->GetRoot().CreateChild("profiles"));

提示:在以下情况下,您需要更新 ProfileStoreTests 类: 更改 ProfileStore 的构造函数。您只需将 inspect::Node() 作为新参数。

现在,您已经配置了基本的 Inspect,接下来可以添加什么推断 有助于防止/找出此组件中的 bug? - 考虑为每个 Profile 添加一个 inspect::Node,然后创建 使用 CreateChild 对传递给 ProfileStore 的节点执行这些操作。 - 考虑使用inspect::LazyNode (node.CreateLazyNode(...)) 来动态创建层次结构。

可在此补丁中找到可能的解决方案: https://fuchsia-review.googlesource.com/c/fuchsia/+/682671

分类

借助分类功能,您可以编写规则以自动处理检查快照并发现潜在问题 或收集快照可能包含的统计信息。

除了通读 Triage Codelab 之外, 参阅分类配置指南

首先,请前往 examples/diagnostics/workshop/triage/profile_store.triage 创建一个新文件 内容如下:

{
  select: {
    profile_status: "INSPECT:core/ffx-laboratory\\:profile_store/profile_store:fuchsia.inspect.Health:status",
  },
  act: {
    profile_status_ok: {
      type: "Warning",
      trigger: "profile_status != 'OK'",
      print: "Profile store is not healthy.",
    }
  }
}

如果您按照上一部分中的“检查”快速入门进行了操作,请运行 ffx triage --config examples/diagnostics/workshop/triage/。如果 profile_store 正在运行且 报告状态正常,您不会看到任何失败情况!请尝试将通话更改为 Health().Ok()main.ccHealth().StartingUp(),并运行 ffx triage --config examples/diagnostics/workshop/triage/。这次您应该能看到警告。

请尝试编写一个分类配置,该配置可能有助于找出 字段。

以下补丁中有一个可能的解决方案(基于 Inspect 解决方案构建): https://fuchsia-review.googlesource.com/c/fuchsia/+/684762

通过测试进行验证

本部分介绍了如何添加测试来验证修复情况。

此示例包含示例单元测试和一个 示例集成测试,其中包括一些 该错误会导致停用

请尝试编写新的测试,以帮助防止组件中出现 bug。您可以随意修改 也可以使用下面的流程从头开始创建新测试。

添加新的单元测试

单元测试的代码结构在很大程度上取决于所使用的运行时。本部分将介绍 说明设置用于验证 ProfileStore 行为的新单元测试的过程。 C++ 类。

examples/diagnostics/workshop/profile_store_unittest.cc 中创建一个新文件,其中包含以下内容:

#include "src/lib/testing/loop_fixture/test_loop_fixture.h"

#include <gtest/gtest.h>

#include "fuchsia/examples/diagnostics/cpp/fidl.h"
#include "lib/fidl/cpp/interface_ptr.h"
#include "profile_store.h"

class ProfileStoreTests : public gtest::TestLoopFixture {};

TEST_F(ProfileStoreTests, SampleTest) {
    ProfileStore store(dispatcher());
    fidl::InterfacePtr<fuchsia::examples::diagnostics::ProfileStore> store_ptr;
    store.AddBinding(store_ptr.NewRequest(dispatcher()));

    store_ptr->Delete("my_key", [&](bool successful) { EXPECT_FALSE(successful); });
    RunLoopUntilIdle();
}

这会设置一个最小的单元测试,该测试会创建一个 ProfileStore,并创建一个用于交互的客户端 并在异步测试循环下对其进行测试。 接下来,您将为该测试创建一个组件清单,该清单定义了如何运行测试。 在以下位置为该测试创建一个新的组件清单: examples/diagnostics/workshop/meta/profile_store_unittests.cm,其中包含以下内容:

{
    include: [
        // Needed for gtest runners
        "//src/sys/test_runners/gtest/default.shard.cml",
        // Needed so that logs are created
        "syslog/client.shard.cml",
    ],
    program: {
        binary: "bin/profile_store_unittests",
    },
    use: [
        {
            // ProfileStore uses /data to store profiles. We'll use the tmp
            // storage provided to the test.
            storage: "tmp",
            path: "/data",
        },
    ],
}

最后,将新的构建规则添加到 examples/diagnostics/workshop/BUILD.gn


# Builds the test binary.
executable("test_bin") {
  testonly = true
  output_name = "profile_store_unittests"

  sources = [
    "profile_store_unittest.cc",
  ]

  deps = [
    ":lib",
    "//src/lib/fxl/test:gtest_main",
    "//src/lib/testing/loop_fixture",
  ]
}

# Creates a test component and test package.
fuchsia_unittest_package("profile_store_unittests") {
  deps = [ ":test_bin" ]
  manifest = "meta/profile_store_unittests.cml"
}

# Update the existing group("tests") to include the new package as a dep
group("tests") {
  testonly = true
  deps = [
    # new dependency
    ":profile_store_unittests",

    ":profile_store_example_unittests",
    "example-integration:tests",
  ]
}

接下来,验证测试是否已构建并运行。

# Build is needed the first time so that fx test becomes aware of the new test.
# For subsequent test executions, fx build is automatically invoked.
fx build examples/diagnostics/workshop:tests

fx test profile_store_unittests

现在,您可以修改测试代码以验证行为。

添加新的集成测试

fx testgen 命令会自动生成集成测试样板设置,以供使用 RealmBuilder。要使用它,您需要找到已编译的组件清单 输出目录中包含 profile_store 组件的组件。

# find the manifest in output directory.
find $(fx get-build-dir) -name profile_store.cm

# generate integration tests.
fx testgen --cm-location find result --out-dir examples/diagnostics/workshop/tests -c

这应该会在 examples/diagnostics/workshop/tests 下生成几个文件。在运行 测试中,有几条构建规则需要更新:

  • 在新生成的 examples/diagnostics/workshop/tests/BUILD.gn 中, <ph type="x-smartling-placeholder">
      </ph>
    • {COMPONENT_FIDL_BUILD_TARGET} 替换为 ProfileStore fidl 的构建目标: //examples/diagnostics/workshop/fidl:fuchsia.examples.diagnostics
    • {COMPONENT_BUILD_TARGET} 替换为 ProfileStore 组件的构建目标: //examples/diagnostics/workshop:profile_store/
  • examples/diagnostics/workshop/BUILD.gn
    • 添加“tests”依赖于 group("tests") 定义。这样可以确保 GN 能够找到新测试。

接下来,验证测试是否已构建并运行。

# Build is needed the first time so that fx test becomes aware of the new test.
# For subsequent test executions, fx build is automatically invoked.
fx build examples/diagnostics/workshop:tests

fx test profile_store_test

测试运行后,您就可以修改样板以编写有用的测试了。