RFC-0222:引入了 Fuchsia 控制器

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

为 Fuchsia 设备的宿主机端脚本编写和测试添加 Fuchsia 控制器

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

摘要

FFX它使用 Fuchsia 接口定义语言 (FIDL) 调用各种服务进行 开发和测试。为了向该工具添加更多功能,FFX 支持子工具,这些子工具是用于扩展 FFX 的命令行界面和功能的单独二进制文件。该工具完全使用 Rust 编写。

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

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

这些方法都不允许用户遵循“自带运行时”原则。

此 RFC 提出了一种通过 FFX 中的现有库访问设备的额外方法,称为 Fuchsia 控制器。它包含以下内容:

  • 稳定的 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 控制器是工具、FFX、FIDL 和 EngProd 团队经过数月对话的成果。其他团队包括组件框架团队和测试架构团队。作者还使用实现部分中概述的 概念验证和高级演示编写并演示了 技术。

设计

概览

Fuchsia 控制器由三部分组成:

  • FFX 客户端库。
  • 特定于语言的扩展库。
  • 使用扩展库的更高级别的一流库。

这些库都与 FFX 协同工作,以处理与 Fuchsia 设备的连接。

替代文本:Fuchsia 控制器的示意图。Fuchsia 控制器堆栈从更高级别的 Python 开始,然后馈送到主 Python 绑定,再馈送到 FFX 绑定。然后,FIDL 会传递到 FFX 守护进程监控的 Unix 网域套接字。从这里开始,FFX 守护程序使用隧道式 FIDL 连接与 Fuchsia 设备进行交互。此示例概述了一般互动,即获取 Remote Control Service 公开的组件的代理

在继续之前,需要介绍一些必要事项。

FFX 的背景

FFX 是一种宿主机端工具,(在撰写本文时)它使用“守护程序”,在这种情况下,守护程序是其自身在后台运行的分支副本,当在网络上发现 Fuchsia 设备时,它会通过 SSH 主动连接到这些设备。发现设备的方法有很多,但这些方法不在本文档的讨论范围内。

FFX 守护程序允许 FFX 工具的用户在宿主机和 Fuchsia 设备之间打开模拟的 FIDL 信道。消息从 FFX 客户端调用路由,通过守护程序,然后路由到 Fuchsia 设备。

FFX 几乎完全使用 Rust 编写。除此之外,用于写入和读取宿主机端模拟 FIDL 渠道的库也使用 Rust 编写。

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

术语定义

以下部分的一些定义:

  • “隔离”是指以某种方式使用 FFX,使其可能拥有一个单独的 FFX 守护进程实例,该实例自行运行,而不使用通用套接字位置或任何通用 FFX 配置值。FFX 守护进程的实例不会与 Fuchsia 设备通信隔离(除非明确调用以执行此操作)。
  • “守护进程协议”是指 FFX 守护进程处理的宿主机端 FIDL 协议。这些协议各不相同,从软件包服务到检查网络上的 Fuchsia 设备,再到 Fuchsia 设备端口转发。守护程序本身可以被视为类似于公开其中几个守护程序协议的组件。
  • 当提及渠道、句柄和套接字的“zircon”时,这些都是通过 Overnet 在宿主机端模拟的。不过,将来这些可能会在 Fuchsia 设备上用作实际的 zircon 对象。

FFX 客户端库

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

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

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

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

特定于语言的扩展

这涉及 FFX 客户端库之上的简单抽象,以共享库的形式实现,然后将在第一类脚本库中使用。

实现

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

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

此外,还将提供 Python 绑定,以实现符合人体工程学的 FIDL 用法。

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

将 Python 与 SDK 一起分发是一个单独的主题,超出了本文档的范围,并且可能会封装在自己的 RFC 中。

FFX 客户端库

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

ABI 的模式旨在使大多数函数使用输出形参为调用方返回结构,返回值可以是 void 或错误。

如果可能,将使用 zx_status_tzx_handle_t 等已建立的类型,以便与 Zircon 代码库中现有的 C 库保持一致。

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

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 值可以指向一个 Rust 对象,该对象包含有关现有 FFX 环境的信息(运行目录、SDK 的位置、配置值以及我们与之通信的 Fuchsia 设备)。

这类似于现有的 zx_channel_read,只是 省略了 option 参数。ABI 不会导出与 Zircon 系统调用相同的符号,而是会显式写入头文件中,以便清楚地了解支持和不支持哪些功能(因为此代码最初可能仅针对宿主机编写,而不是针对 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 控制器原型 here

更高级别的绑定

对于 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 服务是在树外定义的,这样还可以使用 SDK 访问该服务(首先生成 FIDL IR,然后将其导入 Fuchsia 控制器)。

安全注意事项

此 RFC 中的主要安全影响与 FFX 本身有关,这超出了本文档的讨论范围。

测试

FIDL 线格式

FIDL 团队有一套测试(GIDL 和 DynSuite),可以针对这些测试对编码和解码进行测试。这适用于以下情况:使用 fidl_codec(因为尚未针对这些套件进行测试),或者 Python 显式写入/读取线格式。

FFX 客户端库和 Python C 绑定

这些绑定足够轻量级,除了验证是否满足某些异常条件之外,无需进行太多单元测试。为了测试 FFX 客户端库的 ffx_error_str(),可以使用明确定义的错误,以防止依赖于 FFX 本身中的特定字符串。

这些代码段的大部分测试将采用集成测试和/或端到端测试。一个简单的示例(用于练习每个代码段)是使用 Echo 协议进行与非 Fuchsia 设备相关的测试。对于针对实际 Fuchsia 设备的测试,可以获取遥控器服务代理的实例并调用 IdentifyHost 函数。

文档

Fuchsia 控制器的文档将保留在 fuchsia.dev 网站上。

缺点、替代方案和未知事项

缺点

一流的 Python 支持

虽然 FFX 客户端库旨在支持其他语言的绑定,但 Python 不适合较大的生产项目。可以说,Python 允许快速编写代码并快速实现功能,但代价是维护和可读性较差。

替代方案

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

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

未知事项

FIDL IR

使用 SDK 将 FIDL IR 树外提供给用户很难正确实现,因为它必须生成。因此,可能需要为 Bazel 添加额外的 build 规则,以简化依赖项。

SDK 中的 FFX

上述使用 FFX 守护进程协议的示例在树外将无法正常运行,因为 FFX FIDL 协议尚未在 SDK 中实现。API 审核已部分完成,但仍需完成。很难估计这可能需要多长时间,因为 FFX 仍有广泛的 FIDL 界面需要涵盖,并且通常也是一个不断变化的目标。

FIDL 编解码器

在最终结果中可能根本不需要使用 fidl_codec。 使用它还需要向 Python 共享库添加更多界面,然后需要仔细逐步淘汰。

考虑的替代方案包括:

  • 生成的代码(例如,在 C++ 和 Rust 中就是这样处理的)。
  • 在 Python 中实现的 fidl_codec 类似物。

在先技术和参考文档