本文档包含一个使用诊断和测试进行调试的 Codelab。目前 适用于在 fuchsia.git 中编写测试的开发者。
前提条件
设置您的开发环境。
此 Codelab 假定您已完成使用入门并已满足以下条件:
- 一棵经过检修和建造的紫红树。
- 运行 Fuchsia 的设备或模拟器 (
ffx emu
)。 - 一个工作站,用于向 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.cc
到Health().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 能够找到新测试。
- 添加“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_test
测试运行后,您就可以修改样板以编写有用的测试了。