RFC-0222:引入 Fuchsia 控制器 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 添加了 Fuchsia 控制器,用于对 Fuchsia 设备进行主机端脚本编写和测试 |
问题 |
|
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2022-11-17 |
审核日期(年-月-日) | 2023-06-13 |
摘要
Future Fuchsia Experience 工具 FFX 提供了一种从宿主机与 Fuchsia 设备通信的方法。它使用 Fuchsia 接口定义语言 (FIDL) 调用各种服务以进行开发和测试。为了向该工具添加更多功能,FFX 支持子工具,这些子工具是用于扩展 FFX 命令行界面和功能的单独二进制文件。这些工具完全使用 Rust 编写。
如果用户希望在 Fuchsia 设备上访问 FIDL 协议,可以为 FFX 编写插件、使用 Scripting Layer For 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 可扩展几乎所有常用编程语言。
利益相关方
教员:
待定
Reviewers:
jeremymanson@google.com(工具) mgnb@google.com(FFX) ianloic@google.com(FIDL) chok@google.com(lacewing) jzgriffin@google.com(SL4F)
咨询了:
EngProd 团队 FFX 团队 FIDL 团队
社交:
Fuchsia Controller 是 Tools、FFX、FIDL 和 EngProd 团队数月来沟通交流的成果。其他团队包括组件框架团队和测试架构团队。作者还使用实现部分中介绍的技术编写并演示了概念验证和高级演示。
设计
概览
Fuchsia 控制器由三个部分组成:
- FFX 客户端库。
- 特定语言的扩展库。
- 使用扩展库的更高级别的优等库。
所有这些都与 FFX 协同工作,以处理连接到 Fuchsia 设备的操作。
在继续之前,我们需要先介绍一些必要事项。
FFX 的背景
FFX 是一个主机端工具,在撰写本文时使用的是“守护程序”,在本例中,守护程序是指在后台运行的自身分叉副本,会在网络上发现 Fuchsia 设备时主动通过 SSH 连接到这些设备。有许多方法可以发现设备,但这些方法不在本文档的讨论范围内。
FFX 守护程序允许 FFX 工具的用户在主机和 Fuchsia 设备之间打开模拟的 FIDL 通道。消息会从 FFX 客户端调用路由到守护程序,然后再路由到 Fuchsia 设备。
FFX 几乎完全使用 Rust 编写。此外,用于向主机端模拟的 FIDL 通道写入和从中读取的库也采用 Rust 编写。
总而言之,FFX 允许用户使用与 FFX 实现紧密耦合的 Rust 编写的插件,通过 FIDL 与 Fuchsia 设备交互。
定义术语
以下部分中的部分定义:
- “隔离”是指以一种方式使用 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_t
和 zx_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
值可以指向一个 Rust 对象,其中包含有关现有 FFX 环境的信息(运行目录、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"
)。
通过支持套接字/通道读写以及事件信号,我们将能够在 Python 中复制 FFX 和 Overnet 目前支持的所有 FIDL 功能。
Python 语言绑定
Python 语言绑定将定义使用已建立的 create*/close*/destroy* 调用的对象,这些对象以 C++ 编写,并在 FFX 客户端库周围提供一个轻量封装容器。它还会使用 ffx_error_str()
导出错误类型。
如需查看此(略有不同)实现的示例,请点击此处查看 Fuchsia 控制器原型。
更高级别的绑定
对于 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 代码以与 Rust、Dart 或 C++ 等其他 FIDL 语言绑定一致的方式进行操作,还可以在运行时实验 FIDL 协议,从而实现更迭代式的开发。
如果在树之外定义了 FIDL 服务,还可以使用 SDK 访问该服务(方法是先生成 FIDL IR,然后将其导入 Fuchsia Controller)。
安全注意事项
本文档中的主要安全影响与 FFX 本身相关,而这超出了本文档的范围。
测试
FIDL 线格式
FIDL 团队提供了一组测试(GIDL 和 DynSuite),可用于测试编码和解码。这适用于使用 fidl_codec
的情况(因为它尚未针对这些套件进行测试),或者 Python 明确写入/读取线格格式的情况。
FFX 客户端库和 Python C 绑定
这些异常足够轻量,除了验证是否满足特定异常条件之外,单元测试不需要做太多工作。为了测试 FFX 客户端库的 ffx_error_str()
,您可以使用定义良好的错误来防止依赖于 FFX 本身中的特定字符串。
对这些代码段的大部分测试将在集成测试和/或端到端测试中进行。一个简单的示例是,针对非 Fuchsia 设备依赖测试使用 Echo
协议来调用每段代码。对于针对实际 Fuchsia 设备的测试,可以获取遥控器服务代理的实例并调用 IdentifyHost
函数。
文档
Fuchsia Controller 的文档将保留在 fuchsia.dev
网站上。
缺点、替代方案和未知情况
缺点
一流的 Python 支持
虽然 FFX 客户端库旨在支持其他语言的绑定,但 Python 不适用于较大的正式版项目。可以说,Python 可以快速编写代码并快速实现所需功能,但代价是易于维护性和可读性。
替代方案
为什么不使用 Golang、Java、Rust 或 Dart?
在与端到端测试团队、面向产品的开发者和连接团队的初步讨论中,他们强烈倾向于使用可提供互动性且易于使用的脚本语言。这表明 Python 是第一个具有绑定的语言。
未知
FIDL IR
使用 SDK 将 FIDL IR 提供给用户(非树内)时,很难正确完成,因为它必须生成。因此,您可能需要为 Bazel 添加额外的构建规则,以简化依赖项。
SDK 中的 FFX
由于 FFX FIDL 协议尚未纳入 SDK,因此使用 FFX 守护程序协议的上述示例无法在树外运行。API 审核已完成部分工作,但仍需完成。很难估计这可能需要多长时间,因为 FFX 仍有大量 FIDL 接口要涵盖,而且通常也是一个移动目标。
FIDL 编解码器
在最终结果中使用 fidl_codec
可能完全没有必要。使用它还需要向 Python 共享库添加更多接口,而这些接口之后需要谨慎地逐步淘汰。
考虑的替代方案包括:
- 生成的代码(例如,C++ 和 Rust 中就是通过这种方式处理的)。
- 使用 Python 实现的
fidl_codec
类似项。