RFC-0222:引入了 Fuchsia 控制器

RFC-0222:介绍 Fuchsia 控制器
状态已接受
领域
  • 开发者
  • FIDL
  • 测试
说明

添加 Fuchsia 控制器以用于主机端脚本测试和测试 Fuchsia 设备

问题
Gerrit 更改
  • 761117
作者
审核人
提交日期(年-月-日)2022-11-17
审核日期(年-月-日)2023-06-13

总结

Future Fuchsia 体验工具 FFX 提供了一种从主机与 Fuchsia 设备通信的方法。它使用紫红色接口定义语言 (FIDL) 调用各种服务,以进行开发和测试。为了向该工具添加更多功能,FFX 支持子工具,这些子工具是用于扩展 FFX 命令行接口和功能的不同二进制文件。该工具完全用 Rust 编写。

如果用户希望在 Fuchsia 设备上访问 FIDL 协议,他们可以为 FFX 编写插件、使用 Fuchsia 脚本层SL4F,或者提交在系统上编写组件。

这些选项都不适用于轻量级实验、快速迭代或灵活性。FFX 插件需要使用 Rust 编写工具和进行编译。SL4F 不仅不受支持,还依赖于不安全的传输,并且与其进行交互需要在 Rust 中为您计划调用的每个 FIDL 接口编写 Facade。

这些方法都不支持用户遵守“自带运行时”的原则。

此 RFC 提出了一种通过 FFX 中的现有库访问设备的额外方法,该方法称为 Fuchsia Controller。其中包括:

  • 一个稳定的 ABI,可通过 FIDL 句柄模拟与 Fuchsia 设备进行交互,而无需更改 FFX 本身。
  • 利用稳定的 ABI(当前选择为 Python)为至少一种流行的脚本语言提供一流的支持。
  • 采用上述脚本语言的更高级别库,针对编写关键测试用例进行了优化。

设计初衷

目前没有受支持的机制来编写从主机设备到 Fuchsia 设备的交互脚本(通过 SDK、树内或其他方式)。

如果用户希望使用 FFX 与 Fuchsia 设备互动,则必须定义可编译到工具中的服务。如果某个服务在树外定义了,则用户将无法访问该服务。如果该服务已定义,用户必须编写并编译 FFX 插件。

Fuchsia 控制器可让用户以快速、高度迭代且易于自动化的方式与 Fuchsia 设备进行交互。支持稳定的 ABI 可以让用户使用自己的运行时(如果他们选择实现它),因为支持使用 C ABI 扩展几乎所有常用编程语言。

利益相关方

教员

待定

审核者

jeremymanson@google.com(工具) mgnb@google.com (FFX) ianloic@google.com (FIDL) chok@google.com (lacewing) jzgriffin@google.com (SL4F)

咨询人员

EngProd 团队 FFX 团队 FIDL 团队

社交

Fuchsia Controller 是工具、FFX、FIDL 和 EngProd 团队数月沟通的产品。其他团队包括组件框架和测试架构团队。作者还使用实现部分概述的技术编写和演示了概念验证和简要演示。

设计

概览

Fuchsia 控制器由三个部分组成:

  • FFX 客户端库。
  • 针对特定语言的扩展程序库。
  • 使用扩展库的更高级别的一级库。

这些都与 FFX 结合使用,以处理连接到 Fuchsia 设备。

Alt_text: Fuchsia 控制器的示意图。Fuchsia 控制器堆栈从较高级别的 Python 开始,然后流向主要 Python 绑定,然后进入 FFX 绑定。从这里,FIDL 传递到 FFX 守护程序监控的 Unix 域套接字。从这里,FFX 守护程序会使用与 Fuchsia 设备的隧道式 FIDL 连接来进行交互。此示例概述了常规交互,即获取遥控器服务公开的组件的代理

在继续之前,您需要了解一些必备知识。

FFX 的背景

FFX 是一种主机端工具,在撰写本文时,该工具使用“守护程序”,在本例中,该守护程序是在后台运行的自己分支的副本,可在发现 Fuchsia 设备的网络时通过 SSH 主动连接到此类设备。发现设备的方式有很多,但这些内容超出了本文档的讨论范围。

FFX 守护程序允许 FFX 工具的用户打开主机和 Fuchsia 设备之间的模拟 FIDL 频道。系统会先将消息从 FFX 客户端调用路由到 Daemon,然后再路由到 Fuchsia 设备。

《FFX》几乎完全用 Rust 编写。除此之外,用于向主机端模拟 FIDL 通道写入和从中读取数据的库也是用 Rust 编写的。

总而言之,FFX 允许用户使用与 FFX 实现紧密耦合的 Rust 编写的插件,通过 FIDL 与 Fuchsia 设备进行交互。

定义术语

对于以下部分,其中一些定义:

  • “隔离”是指以这种方式使用 FFX,使其可以具有独立运行的 FFX 守护程序实例,而不使用公共套接字位置或任何常见的 FFX 配置值。FFX 守护程序的实例不会与 Fuchsia 设备通信(除非明确调用此守护程序)。
  • “守护程序协议”是指 FFX 守护程序处理的主机端 FIDL 协议。其中包括软件包传送、检查网络上的 Fuchsia 设备以及 Fuchsia 设备端口转发。守护程序本身可以看作类似于公开多个此类守护程序协议的组件。
  • 当提到通道、句柄和套接字的“zircon”时,这些通过网络在主机端模拟。不过,它们将来有可能在 Fuchsia 设备上用作实际的锆石对象。

FFX 客户端库

FFX 客户端库通过 C ABI 公开函数,以支持以下各项:

  • 创建、读取、写入和关闭锆石通道。
  • 创建、读取、写入和关闭 zircon 套接字。
  • 信号 zircon 句柄(主要用于事件对)。
  • 将锆石通道连接到 Fuchsia 设备上的组件。
  • 将 zircon 频道连接到 FFX 守护程序协议。

此外,还将支持其他功能,例如:

  • 创建隔离的 FFX 守护程序实例。
  • 声明 FFX 配置键值对。

特定语言的扩展程序

这涉及在 FFX 客户端库之上进行简单的抽象化处理,以作为共享库来实现,该库随后将在一流的脚本库中使用。

实现

如前所述,这将涉及创建两个共享库。

  • 不限定语言的“FFX 客户端”库,将公开与 FFX 通信的函数以及 zircon 句柄。
  • 语言绑定(在本例中为 Python)共享库。

此外,还包含用于人体工程学 FIDL 的 Python 绑定。

实现不涉及尚未包含在 Fuchsia 源代码树中的任何第三方依赖项。我们预计可以使用 FFX 客户端库实现对其他语言的支持,我们可能会支持该库(并且可能需要额外的第三方依赖项工作),但这不在本 RFC 的范围内。

随 SDK 分发 Python 是一个单独的主题,不在本文档的讨论范围之内,并且可能会封装在其自己的 RFC 中。

FFX 客户端库

本部分中的 C 定义旨在放置在将随 Fuchsia SDK 一起提供的头文件中。由于 C 中缺少命名空间,因此这些函数名称会相当冗长。此外,函数名称也不一定是最终名称。

ABI 的模式旨在使大多数函数使用输出参数为调用方返回结构,并且返回值为 void 或 error。

如果可能,zx_status_tzx_handle_t 等既有类型将与现有的 C 库(如 Zircon 代码库中)保持一致。

用于读取频道的函数示例可能如下所示(并非最终实现或文档来源):

extern zx_status_t ffx_channel_read(void* ctx,
                                    zx_handle_t handle,
                                    void* buf,
                                    uint32_t buf_len,
                                    zx_handle_t* handles,
                                    uint32_t handles_len
                                    uint32_t* actual_bytes_count,
                                    uint32_t* actual_handles_count);

ctx 值可以指向一个包含现有 FFX 环境相关信息的 Rust 对象(运行目录、SDK 的位置、配置值以及我们要与之通信的 Fuchsia 设备)。

这与现有的 zx_channel_read 类似,只是省略了选项参数。系统不会导出与 Zircon 系统调用相同的符号,而是将 ABI 明确写入标头中,以便清楚地了解哪些功能受支持和不受支持(因为此代码最初只针对主机而不是 Fuchsia 设备编写,因此可能会有特殊注意事项)。

下面的示例展示了如何获取句柄,如以下函数所示,它可通过使用遥控器 FIDL 定义连接到 Fuchsia 设备上的大多数组件。

extern zx_status_t ffx_connect_proxy(void* ctx, const char* endpoint_path,
                                     zx_handle_t* out);

这会向相关协议返回一个 FIDL 通道。endpoint_path 应包含通过组件框架直接连接到设备上的特定 FIDL 协议所必需的信息(在编写时,其格式为 "$moniker:expose:$protocol")。

借助对套接字/通道读写以及事件信号的支持,您可以复制 FFX 和 Overnet 当前支持的所有 FIDL 功能(但在 Python 中)。

Python 语言绑定

Python 语言绑定将定义使用既定的 create*/close*/destroy* 调用的对象,这些调用是用 C++ 编写的,并针对 FFX 客户端库提供精简封装容器。它还会使用 ffx_error_str() 导出错误类型。

您可以在此处的 Fuchsia Controller 原型中找到此实现(略有不同)的示例。

更高级别的绑定

对于实际使用的 FIDL 协议,可以使用更高级别的库。 它将使用 importlib 连接到 FIDL 中间表示法 (IR),从分布式 .pyz(可在开发者的 PYTHONPATH 中找到)中或从手动提供的目录中导入任何可用的 IR。这些钩子会创建在运行时与 FIDL 进行交互的类,类似于使用 C++ 或 Rust 进行编译时绑定时发生的情况。

示例如下:

import fidl.fuchsia_developer_ffx
from fuchsia_controller_py import Context

def main(ctx: Context):
  h = ctx.connect_daemon_protocol(fidl.fuchsia_developer_ffx.Echo.Marker)
  proxy = fidl.fuchsia_developer_ffx.Echo.Client(h)
  print(proxy.echo_string(value = 'foobar'))

if __name__ == '__main__':
  ctx = Context(None)
  main(ctx)

库钩子会捕获从 fidl. 导入的内容,以在现有的 FIDL IR 命名空间中搜索与 fuchsia.developer.ffx 匹配的内容,从而根据其中定义的结构体和协议生成类。

FIDL 有线格式编码/解码

首次传递时,实际的编码和解码很可能会使用 fidl_codec 库完成,之后便可完全用 Python 实现。

fidl_codec 包含用于基于 FIDL IR 定义从 FIDL 传输格式中读取数据以及向其中写入数据的代码,以及可用于根据 FIDL 消息构建对象的值访问者等实用程序。它会在 fidlcat 工具中使用。您可以使用访问者模式将 FIDL 结构体编码为来自 IR 生成的 Python 的传输格式,从而创建一个可随后写入 FIDL 句柄的缓冲区。

性能

FFX 客户端库 ABI

在跨越 FFX 客户端 ABI 边界(即 Unix 网域套接字读取/写入和与 Fuchsia 设备之间往来)时,会出现大多数性能开销。

除此之外,FFX 客户端库会将 FIDL 读取操作正好复制两次,一次是跨越 Rust ABI 边界,然后再一次将数据从 C++ 代码加载到 Python 对象中。日后如果此问题会得到解决,可以着手解决。

平均用例将涉及以较低的开销使用 FIDL,即使用结构体调用简单函数。在编写时,主机不支持更复杂的 FIDL(例如 VMO),因此不应成为影响性能的因素。

工效学设计

FFX 客户端 ABI

FFX 客户端二进制文件以尽可能小的配置文件实现,以防止在其他语言中实现绑定时,出现过多的样板代码。

更高级别的 Python 绑定

较高级别的 Python 绑定将使用 FIDL IR 在运行时创建类绑定。这样,您不仅可以使用 Python 代码的行为方式与其他 FIDL 语言绑定(如 Rust、Dart 或 C++),还可以在运行时对 FIDL 协议进行实验,从而实现更多的迭代开发。

如果 FIDL 服务是在 FIDL 树外部定义的,这也使得系统能够通过 SDK 访问该服务(首先生成 FIDL IR,然后将其导入 Fuchsia Controller)。

安全注意事项

此 RFC 中涉及的主要安全隐患与 FFX 本身相关,但这不在本文档的讨论范围内。

测试

FIDL 有线格式

FIDL 团队有一套测试(GIDL 和 DynSuite),可用于测试编码和解码。这适用于正在使用 fidl_codec(因为尚未针对这些套件进行测试)或 Python 明确对传输格式执行读写的情况。

FFX 客户端库和 Python C 绑定

这些测试非常轻量,因此除了验证是否符合特定异常条件之外,无需执行太多单元测试。为了测试 FFX 客户端库的 ffx_error_str(),您可以使用明确定义的错误来防止依赖于 FFX 本身内的特定字符串。

这些代码段的大部分测试将进行集成测试和/或端到端测试。一个执行每段代码的简单示例是使用 Echo 协议进行非依赖于紫红色设备的测试。如需针对实际 Fuchsia 设备进行测试,您可以获取遥控器服务代理的实例,并调用 IdentifyHost 函数。

文档

Fuchsia Controller 的相关文档会保留在 fuchsia.dev 网站上。

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

缺点

一流的 Python 支持

虽然 FFX 客户端库旨在支持其他语言的绑定,但 Python 不适用于较大的生产化项目。毫无疑问,Python 支持快速编写代码和以快速运行,但代价是易于维护和可读性。

替代选项

为什么不使用 Golang、Java、Rust 或 Dart?

在我们与端到端测试团队、面向产品的开发者和连接团队的初步讨论中,强烈偏好一种能够提供互动性和易用性的脚本语言。这指向 Python,是第一种具有绑定的语言。

未知

FIDL IR

使用 SDK 在树外将 FIDL IR 提供给用户会比较困难,因为必须生成 IR。因此,可能需要为 Bazel 添加额外的构建规则,以简化依赖项。

SDK 中的 FFX

以上使用 FFX 守护程序协议的示例在树之外将无法运行,因为 FFX FIDL 协议尚未纳入 SDK。API 审核已部分完成,但仍需要完成。很难衡量此过程可能需要多长时间,因为 FFX 仍然具有较宽的 FIDL Surface 要覆盖,并且通常也是一个移动目标。

FIDL 编解码器

可能根本不需要在最终结果中使用 fidl_codec。如果使用此方式,还需要向 Python 共享库添加更多 surface,之后需要小心淘汰。

可以考虑采用以下替代方案:

  • 生成的代码(例如在 C++ 和 Rust 中如何处理代码)。
  • 在 Python 中实现的 fidl_codec 模拟。

早期技术和参考资料