RFC-0171:改进了诊断路由

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

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

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

摘要

提案:在 cmc 和 CML 中提供实用程序,以简化树中所有位置的诊断协议(fuchsia.logger.LogSinkfuchsia.diagnostics.InspectSink)的路由,并减少缺少日志或 Inspect 数据的 DX 痛点。虽然本文档重点介绍了“检查”和“日志记录”,但也可以用于提高大多数组件可能希望使用的其他协议(例如 fuchsia.tracing.provider.Registry)的可用性。

设计初衷

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

RFC-0168 建议使用协议 fuchsia.inspect.InspectSink,以允许组件发布 Inspect,从而带来一些改进并减少技术债务。与 fuchsia.logger.LogSink 一样,我们希望所有(或至少大多数)组件都使用 Inspect 插桩。目前,每个组件都可以 expose /diagnostics to framework,这让每个组件都可以公开“检查”功能,并将其提供给归档程序。在改用协议后,我们必须确保所有组件都能继续公开 Inspect 数据,这些数据对开发者来说非常有用,可帮助他们调试组件在运行时执行的操作。

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

组件管理器会利用组件的 LogSink 输出归因于该组件的路由错误。这样可以提高 DX,因为开发者可以快速发现路由错误。不过,如果 LogSink 未正确路由,这些错误最终会记录在归因于组件管理器的全局 syslog 中,并且开发者在查看自己的组件日志时更容易错过这些错误。

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

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

利益相关方

教练:leannogasawara@google.com

Reviewers:

  • 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 文档的形式进行共享,其中包含对所列替代方案的深入讨论,以及利益相关方之间的会议和对话。

设计

为了让开发者更轻松地在当前组件功能路由系统下永不错过日志和“检查”功能,并遵循最小权限和分层隔离原则,同时继续能够公开“检查”功能,我们将开发以下内容:

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

cmc 必需的优惠和用途

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

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

此外,cmc 还会获得等效的命令行选项 --must-use-protocol,用于检查 UseDecl 的等效性。

每次调用 cmc 时,GN 和 Bazel 工具都会更新,以便在这些选项中传递 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 路由到某些子元素,可以自由选择以下任一方式:

  • 使用可选 capability 路由:将协议 from: "void" 路由到要为其关闭单个 offer 的子项/集合。
  • 手动从所需来源路由协议。

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

  • bootstrap:将启用此选项,以确保 LogSink 被路由到引导加载程序中的所有组件。此外,它还会将 void 中的优惠 Inspect/LogSink 添加到归档程序。
  • root:将启用此选项,以确保将 LogSink 从归档库路由到其所有同级兄弟。由于 bootstrap 是公开此功能的模块,因此我们将从 void 中将优惠 Inspect/LogSink 添加到 bootstrap

通过这样做,我们希望 Fuchsia 上的每位开发者都更不容易误漏日志或检查数据。

zarvox@ 为此部分构建了一个原型(以及关系链)。

允许向 CML 中的所有子项和集合提供 capability

为了改进路由到所有子项的 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 中的任何组件使用。

此功能将在 Component Manager 安全政策中受限,以确保只有根级归档管理器和嵌入到测试中的归档管理器才能使用此功能。

优点

  • 每个组件都可以在树中的任何位置使用 InspectSinkLogSink
  • 与当前的全球状态保持一致,其中每个组件都可以公开“检查”功能。
  • 改进了开发者体验,因为开发者无需花时间找出其组件未记录日志的原因,即可了解测试中缺少优惠。
  • 可以静态检查 capability 的点对点使用情况。
  • 除非明确使用 capability,否则所有组件的命名空间都以“nothing”开头。
  • 涵盖通过 fuchsia.component.Realm/CreateChild 创建的动态组件。

缺点

  • 不再提供明确的父级-子级产品,这意味着这不符合分层隔离的安全原则。
  • 如需在拓扑中替换或模拟 LogSink/InspectSink,需要调整环境,这需要更改安全政策。
  • 这不是在自食其果吗?第三方开发者不能将环境用于其协议的任意用途,为什么我们可以?

实现 LogSinkInspectSink 框架功能

允许使用这些协议 from: framework。归档管理器可以将这些功能公开给框架,也可以在归档管理器和组件管理器之间建立合同来提供这些功能。

优点

  • 与上一个方案相同,但详细说明了诊断协议 (InspectSinkLogSink) 会成为框架协议,即使未由组件管理器提供也是如此。
  • 无需进行功能归因,因为每个组件都会获得自己的一组独特的框架功能,因此归因会直接在框架中进行。

缺点

  • 与上一个替代方案相同。
  • 从框架中使用的第一个协议实例,该协议并非由组件管理器直接提供:自身。
  • 不清楚如何在不构建供 Test Manager 使用的额外机制的情况下在测试中提供隔离的日志。
  • 为设备端的所有组件建立单个日志目的地。

cmc 自动向所有子级提供 LogSink

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

优点

  • 明确的父级-子级功能,可帮助模拟、替换拓扑中的协议等。
  • CML 没有任何变化。

缺点

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

cmc 中提供选项,并在 CML 中提供语法糖,可使此操作更加灵活,并提供一个机制供其他人利用,而不仅仅用于诊断。

不执行任何操作,并照常路由

让开发者手动为其所有子项提供 LogSinkInspectSink

优点

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

缺点

  • 当前问题:很容易错过路由 LogSink,导致在调试测试时浪费时间。
  • 其他问题:很容易错过将 InspectSink 路由到某个组件(鉴于目前每个人都可以公开它),导致现场缺少诊断信息。这与 LogSink 存在的问题完全相同,因此现在我们在两种协议中都存在此问题,而不是仅在一种协议中存在。

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

其他建议

我们还讨论了其他想法,例如使用 capability bundle 路由包含这两种协议的 diagnostics bundle,或以网域或 capability 源的形式改进环境。鉴于我们计划审核路由 API,因此放弃了这些方案,改为使用现有机制和 API 的短期解决方案。

在先技术和参考文档

不适用