RFC-0171:改进了诊断路由

RFC-0171:改进了诊断路由
状态已接受
领域
  • 诊断
  • 组件框架
说明

引入了 CML 和 CMC 实用程序,以改进诊断路由

问题
  • 93346
Gerrit 更改
  • 681567
作者
审核人
提交日期(年-月-日)2022-05-19
审核日期(年-月-日)2022-06-23

总结

我们在 cmc 和 CML 中提供实用程序,以简化诊断协议(fuchsia.logger.LogSinkfuchsia.diagnostics.InspectSink)在树中任意位置的路由,并减少因缺少日志或检查数据而导致的 DX 痛点。虽然本文档重点介绍检查和日志记录,但它也可以用于提高大多数组件可能希望具备的其他协议(如 fuchsia.tracing.provider.Registry)的可用性。

设计初衷

使用日志的 DX 痛点是组件需要在任何位置(在生产组件、测试、RealmBuilder 路由等中)路由 fuchsia.logger.LogSink。日志是大多数 Fuchsia 体验的核心部分,我们希望几乎每个组件和测试都会使用。

RFC-0168 提议使用 fuchsia.inspect.InspectSink 协议,该协议可让组件发布 Inspect ,从而带来一些改进并减少技术债务。与 fuchsia.logger.LogSink 一样,我们希望所有(或至少大多数)组件都使用 Inspect 插桩。目前,每个组件都可以 expose /diagnostics to framework,从而允许每个组件公开 Inspect 并使其可供 Archivist 使用。通过转为采用协议,我们必须确保所有组件都能够继续公开检查数据,这些数据对于开发者调试其组件在运行时的行为非常有用。

这不符合人体工程学,而且容易出错,因为我们需要更新所有 CML,以手动将此协议路由到当前正在编写 Inspect 的所有组件。LogSink 也有同样的问题,特别是在测试中,很容易忘记将 LogSink 路由到被测组件,从而导致日志缺失并浪费开发者时间。

组件管理器利用组件的 LogSink 来输出归因于该组件的路由错误。这有助于改进 DX,因为开发者可以快速发现路由错误。但是,如果 LogSink 没有正确路由,这些错误最终就会出现在归因于组件管理器的全局 syslog 中,而开发者在查看自己的组件日志时较容易将其忽略。

本文档试图通过在 cmc 和 CML 中引入实用程序来改善这种情况,以简化将这两个协议路由到每个组件的过程。

组件框架计划审核路由 API,并构思使模型更一致、更人性化的方法。这将需要几个季度的时间,因此最好采用使用现有基元的增量方法并对其进行扩展。

利益相关方

教员:leannogasawara@google.com

审核者

  • crjohns@google.com
  • geb@google.com
  • hjfreyer@google.com
  • shayba@google.com
  • zarvox@google.com

咨询人员

  • bryanhenry@google.com
  • cgonyeo@google.com
  • jmatt@google.com
  • thatguy@google.com

社交:此设计以 Google 文档、决策文档(其中对列出的替代方案进行了广泛讨论)以及利益相关方之间的会议和对话的形式进行了社会化。

设计

为了让开发者更轻松地在当前组件功能路由系统下不错过日志和 Inspect,与最小权限和分层隔离原则保持一致,并继续公开 Inspect,我们将开发以下内容:

  • cmc 必需的优惠检查工具。
  • 能够为 CML 中的所有子项和集合提供一项功能。
  • 用于诊断的新 CML 分片。
  • RealmBuilder 会自动为所有组件提供诊断功能。

cmc 项必需的优惠和用途

cmc 将获取一个命令行选项 --must-offer-protocol,其中包含协议名称列表,会针对该列表验证以下语句是否为 true:

对于清单中声明的每个子项和集合,对于必需协议列表中定义的每个协议,都存在一个从某个来源指向该子项和集合的 OfferDecl

此外,cmc 将获得等效的命令行选项 --must-use-protocol,该选项将检查等效项,但针对 UseDecl

GN 和 Bazel 工具将更新,以在每次调用 cmc 时在这些选项中传递 fuchsia.logger.LogSinkfuchsia.diagnostics.InspectSink

如果开发者希望完全停用此检查(无论如何为其清单调用 cmc),可以将以下内容添加到其 CML 文件中(这是 CML 中引入的新语法):

{
    disable: {
        must_use_protocol: [ "fuchsia.logger.LogSink", "fuchsia.diagnostics.InspectSink" ],
        must_offer_protocol: [ "fuchsia.logger.LogSink", "fuchsia.diagnostics.InspectSink" ],
    }
}

如果开发者不希望将 LogSinkInspectSink 路由到其部分子级,则可以选择以下任一方法:

  • 使用可选功能路由:将协议 from: "void" 路由到要停用单个优惠的子项/集合。
  • 手动从所需来源路由协议。

生成这些功能的 bootstraproot 领域需要一些特殊处理:

  • bootstrap:将开启此选项,以确保将 LogSink 路由到引导加载程序中的所有组件。此外,还会将 voidInspect/LogSink 优惠添加到 Archivist。
  • root:将开启此选项,以确保我们将 LogSink 从 Archivist 路由到其所有同级。由于 bootstrap 是公开此功能的选项,因此我们将添加从 voidbootstrapInspect/LogSink 优惠。

这样做,我们希望 Fuchsia 的每位开发者都不太可能错误地错过日志或检查数据。

zarvox@ 为本部分构建了原型(和关系链)。

允许为 CML 中的所有子项和集合提供功能

为了改进路由到所有子级的 DX,我们将在 CML 中引入语法糖,以允许将功能路由到“所有子级和集合”。

此语法糖可以按如下方式使用:

offer: [
    {
        protocol: "fuchsia.logger.LogSink",
        from: "parent",
        to: "all",
    }
]

当编译包含该语法的 CML 文件时,会生成 N 个 OfferDecl,其中 N 是组件包含的集合和子级的总数。

目标为 allOfferDecl 将在 cmc 中进行控制,只能用于上一部分中所述的新可选参数中定义的协议。

CML 分片

将创建以下分片:

// syslog/use.shard.cml
{
    use: [
        { protocol: "fuchsia.logger.LogSink" },
    ],
}

// syslog/offer.shard.cml
offer: [
    {
        protocol: "fuchsia.logger.LogSink",
        from: "parent",
        to: "all"
    }
]

// inspect/use.shard.cml
{
    use: [
        { protocol: "fuchsia.diagnostics.InspectSink" },
    ],
}

// inspect/offer.shard.cml
offer: [
    {
        protocol: "fuchsia.diagnostics.InspectSink",
        from: "parent",
        to: "all"
    }
]

系统将更新以下现有分片:

  • syslog/client.shard.cml:包含 syslog/use.shard.cmlsyslog/offer.shard.cml
  • inspect/client.shard.cml:包含 inspect/use.shard.cmlinspect/offer.shard.cml

仅执行路由而不执行任何程序的逻辑组件可以使用 offer.shard.cml。需要使用这些协议但需要配置要路由到其子级的内容的组件可以使用 use.shard.cml。其余函数可以使用标准的便捷 client.shard.cml

如果某个组件没有子项或集合,但仍使用 client.shard.cml(因为它使用的是协议),那么分片中的 offer to all 语句将为空操作,因为它是语法糖,且仅扩展为 OfferDecl,如前所述。

为方便起见,我们将提供一个同时包含 client.shard.cml 文件的 diagnostics/client.shard.cml

进行了 RealmBuilder 项更新以支持 offer to all

为便于将诊断协议路由到被测所有组件,RealmBuilder 将接收一些更新,以允许将协议路由到所有子项和集合:

  • 自动为所有子级和集合提供 LogSink 和 InspectSink。在 Rust 中,可能如下所示:

    builder
        .add_route(
            Route::new()
                .capability(Capability::protocol_by_name("fuchsia.diagnostics.InspectSink"))
                .from(Ref::parent())
                .to(Ref::all()),
        )
        .await?;
    
  • 我们希望所有测试都这样做,但某些小众场景除外,因此 RealmBuilder 会自动将这些协议路由到所有组件。这似乎与 cmc 和 CML 中采用的方法不一致,但 RealmBuilder API 在某些方面已经有所偏离,以提供更便捷且更适合测试的工作流。鉴于我们预计 99% 的时间会将这些协议路由到测试组件,那么我们将教 RealmBuilder 自动执行此操作,并提供一种关闭方法:

    let builder = RealmBuilder::new().await?;
    …
    let instance = builder
        .route_logs_to_all(false)     // defaults to true
        .route_inspect_to_all(false)
        .build()
        .await;
    

实现

  1. 更新了 cmc 以支持 CML 中的新标志和 offer to all
  2. 添加一个包含 offer LogSink to allsyslog/offer.shard.cml
  3. 更新树中的 cmc 使用情况以使用新的标志,并更新可能缺少路由的现有 CML。GN 和 Bazel SDK 将更新,但对于一组所需的协议,默认为 [],直到 OOT CML 完成迁移,以获得一整套优惠为止。
  4. 更新树中的 cmc 使用情况以使用新的标志,并更新可能缺少路由的现有 CML(利用优惠分片)。
  5. 更新 GN 和 Bazel SDK,以要求使用诊断协议。
  6. 添加 syslog/client.shard.cml 中的 syslog/offer.shard.cml
  7. 滚动后,重构正在使用优惠分片但不再需要的 OOT 清单,因为它们已通过客户端分片包含。

性能

cmc 将执行一些额外的工作,但预计不会对编译时产生任何重大影响。

安全注意事项

此更改与组件框架安全属性保持一致,特别是最小权限原则和分层隔离原则。

隐私注意事项

不会对隐私保护产生影响。

测试

新的 cmc 功能将接受单元测试。

文档

cmc 将更新以包含新选项,CML 将更新以描述新的 offer to all 功能。

缺点、替代方案和未知情况

Environment 中使用 debug_capabilities

这是他们考虑的主要备选方案。在此替代方案下,我们需要扩展 fuchsia.sys2.Environment,使其具有像 debug_capabilities 一样的 diagnostics_capabilities,或者将 debug_capabilities 转换为 diagnostics_capabilities,或者仅针对诊断协议使用 debug_capabilities,使它们可供树 from: diagnosticsfrom: debug 中的任何组件使用。

此功能在组件管理器安全政策中受限制,以确保只有根 Archivist 和嵌入在测试中的 Archivist 才能使用它。

优点

  • 每个组件都可以从树中的任何位置使用 InspectSinkLogSink
  • 与世界的当前状态一致:每个组件都可以显示 Inspect。
  • 改进了 DX,因为开发者无需花时间找出组件未记录的原因,即可发现测试中缺少某个优惠。
  • 可以对相应 capability 的使用情况进行静态检查。
  • 所有组件在其命名空间中都以“nothing”开头,除非它们明确使用此功能。
  • 涵盖通过 fuchsia.component.Realm/CreateChild 创建的动态组件。

缺点

  • 没有更明确的父子关系,这意味着,这与分层隔离的安全原则不一致。
  • 如需替换或模拟拓扑中的 LogSink/InspectSink,需要调整需要更改安全政策的环境。
  • 我们不吃我们自己的 dogfood 版本 - 第三方开发者的协议不会为了达到任意目的而使用环境,为什么会这样呢?

实现 LogSinkInspectSink 框架功能

允许使用这些协议 from: framework。Archivist 可以向框架公开这些功能,也可以在 Archivist 和组件管理器之间达成合约来提供这些功能。

优点

  • 与之前的替代方案中的优点相同,并详细说明了诊断协议(InspectSinkLogSink)会成为框架协议,即使组件管理器未提供该协议。
  • 无需进行功能归因,因为归因会直接在框架中发生,因为每个组件都有自己独特的框架功能集。

缺点

  • 缺点与上一个替代方案相同。
  • 框架中使用的第一个协议实例,该协议不由组件管理器直接提供:其本身。
  • 不清楚如何在不构建供测试管理器使用的其他机制的情况下在测试中提供隔离日志。
  • 为设备级所有组件建立一个日志目标位置。

cmc自动为所有儿童提供LogSink

cmc 会为每个子项和集合自动执行此操作,而不是使用标志来请求用户将 OfferDecl 添加到其 CML。

优点

  • 明确的父级-子级产品,有助于模拟、替换拓扑中的协议等。
  • CML 中没有任何更改。

缺点

  • CMC 中对功能的特殊处理。
  • .cml 中声明的组件与通过 Realm/CreateChild 构建的组件的行为不一致。

cmc 中提供选项并在 CML 中提供语法糖可提高其灵活性,并提供一种其他人可以利用的机制,而不仅仅是诊断机制。

不进行任何操作,像往常一样选择路线

让开发者手动向所有子级提供 LogSinkInspectSink

优点

  • 明确的父级-子级产品,有助于模拟、替换拓扑中的协议等。
  • API 边界仍然是父级和子级之间的本地问题,而不是涉及其他方的问题。

缺点

  • 当前问题:在调试测试时,很容易错过路由 LogSink,导致时间损失。
  • 其他问题:很容易错过将 InspectSink 路由到某个组件(考虑到目前所有人都可以公开它)导致在该字段中缺少诊断。这与 LogSink 遇到的问题一样,因此我们现在使用两种协议(而非仅一种协议)。

鉴于这些协议的使用广泛,我们认为在 cmcCML 中添加额外选项有助于降低丢失路线的可能性。

其他建议

我们还讨论了其他想法,例如使用功能包来路由同时包含协议的 diagnostics 包,或者以网域或功能源的形式改进环境。考虑到路由 API 的评估计划,这些 API 被废弃,取而代之的是使用现有机制和 API 的短期解决方案。

早期技术和参考资料

不适用