更新 VMO 文件格式

本文档介绍了如何更新或扩展组件检查文件 格式

在扩展格式时,切勿破坏任何现有功能, 特别是“检查读者”和“验证”测试不过,将所有更改打包到一个文件中 审核内容可能会让人无所适从一般情况下,在一个链中应有 5-9 个更改或步骤 更改 VMO 格式:

  1. (可能不适用)通过更新 VMO 格式文档来选择类型编号
  2. 更新了 Rust 读取器。
  3. 更新 C++ 读取器
  4. 更新了 Rust writer。
  5. 更新了 C++ 写入器。
  6. (可能)更新验证器。
  7. 更新了文档。
  8. (未更改)(可选)发送功能通知。

选择编号类型

检查文件格式中查看类型表格,然后选择一个可用的类型编号。当前规范中总共有 256 种可能的类型。

为了保留新类型,请更新检查文件格式

实现

对一个读者或作者进行测试十分麻烦,要获得正确的 没有读取器和写入器的块级 API。

如需测试 reader 和 writer,请执行以下操作:

  1. 选择一种语言,使用单元设计并完全以该语言实现该功能 对 API 的实际使用情况进行建模。

  2. 将更改拆分为单独的读取方和写入方更改,将写入方堆叠在顶部 。

    此时,读取器中依赖于写入器 API 的测试可能会因读取器更改而失效。

  3. 将基准更新为该更改,然后使用较低级别的功能重写测试(保留测试的原始版本)。

    通常,您可以将对块代码的所有更改放入读取器更改, 为读取器 API 编写测试的麻烦。

  4. 测试会很难看。重新基于 Writer 更改,并移除修改后的测试,将其替换为使用高级 API 编写的原始测试。

  5. 请将这两项更改作为参考,并用第二语言复制它们。

  6. (可选)根据更改的内容,您可能需要更新 validator会不断地进行测试,因为现有 都有可能破坏它们

以下部分概述了如何综合运用各项更改,但实际操作时,可参考这些内容作为提示 在按上述方式拆分设计之前,统一设计整个系统。

更新 Rust 实现

本部分中的示例将创建一个名为 MyFoo 的新类型。

设置

  1. 添加测试:
fx set core.x64 --with //src/lib/diagnostics:tests
  1. 运行测试:

fx test inspect-format-tests fuchsia-inspect-tests

读者变更

位字段更新

  1. 如果定义了新的分块类型,请更新 BlockType 枚举

  2. 更新为 BlockType 定义的方法和函数。

  3. 如果要更改现有块中的字段或创建新的 BlockType,请更新 位字段布局

  4. 运行 inspect-format-tests 以验证更改是否已编译:

fx test inspect-format-tests
  1. 更新块定义,以添加用于读取和写入新数据的方法 字段。目前,在块库中添加写入功能是合适的; 没有它,几乎不可能编写读者测试。

  2. 编写用于执行新功能的块测试。

  3. 编写在块的前 8-16 个字节上做出断言的测试。

    通常,这意味着将预期内容写入包含十六进制值的 &[u8],并断言其与块用作容器的缓冲区等效。

更新读取器

您可以在 mod.rs 中找到读取器代码。此变更中的测试可能会有些棘手,因为高级 API 编写器不存在 。

更改作者

状态

主要变更将在 State 功能中进行。您可以在此处分配块并将其转换为新类型。 如果您只是要修改现有块,那么这可能是您唯一需要的位置 进行更改。

创建新的值类型

有一个 types 目录,您可以在其中为您的类型添加新文件。

  1. 在创建的文件中创建新类型。使用现有类型作为示例。类型始终 有权访问内部 State。使用它来创建必要的 方法,调用在 State 中创建的方法。

  2. Node 添加一个用于创建新类型的方法。

  3. 确保您的类型在 VMO 中具有 RAII 语义。如果您的类型是值,则这可能由从第 1 步中现有类型复制的样板自动完成。

最后,返回并从 Reader 更改更新测试,以使用新的 API!

更新 C++ 实现

本部分中的示例将创建一个名为 MyFoo 的新类型。

如上所述,本部分应在 Gerrit 中进行两项更改。

设置

  1. 添加测试:
fx set core.x64 --with //zircon/system/ulib/inspect:tests
  1. 运行测试。
fx test inspect-cpp-unittest

读者变更

位字段更新

本部分介绍了如何为新类型定义位字段。

更新屏蔽定义

  1. 更改 BlockType 以添加新类型。例如:kMyFoo = #;

  2. 如果您的类型需要新的头文件(通常如果它不是 VALUE),请使用结构体为您的类型定义头文件位字段。例如: struct MyFooBlockFields final : public BlockFields

  3. 如果您的类型需要新的载荷(需要使用块的第二个 8 个字节), 使用结构体为您的类型定义载荷位字段。例如: struct MyFooBlockPayload final

  4. 如果您的类型包含枚举(例如格式),请在 block.h 顶部定义一个新的枚举。例如:enum class MyFooBlockFormat : uint8_t

实现类型读取器

本部分介绍了如何使新类型可读。

根据您的类型更新检查层次结构

值(Node 的子项)

  1. 根据您的类型使用新数字更新 PropertyFormat 枚举。必须在 该特定枚举,无需与您选择的格式类型序数匹配。

  2. 创建新的值类型。例如: using MyFooValue = internal::Value<T, static_cast<size_t>(PropertyFormat::kMyFoo)>;

  3. 使用新值更新 PropertyValue 变体。注意:fit::internal::variant 中的索引必须与 PropertyFormat 的值一致。

不是值

  1. 您需要在层次结构文件中创建自己的内存表示对象。
  1. 更新实际读取器

  2. 更新 InnerScanBlocks 以分派您的类型。如果您要创建新的Property,可以 只需添加 BlockType 即可。

  3. 如果您需要自定义解析器,请实现 InnerParseMyFoo,它接受父级(如果需要)和指向已扫描分块的指针。

作者更改

类型封装容器声明

本部分介绍了如何为新类型声明 C++ RAII 样式的封装容器。

类型封装容器包含该类型拥有的块的索引。您将对 在这些块上执行操作,包括创建和删除, 状态操作更新

更新写入者类型定义

确定您是否可以重复使用现有封装容器,或者是否需要使用自定义类型:

重复使用

  1. 如果您需要支持添加、减法和设置,请使用 using MyFoo = internal::NumericProperty<T>, 其中 T 是这些操作的参数类型。

  2. 如果您需要支持 Set:using MyFoo = internal::Property<T>,其中 T 是其参数 设置为“设置”。

  3. 如果您需要支持对数组进行数字运算,请执行以下操作: using MyFood = internal::ArrayProperty<T>,其中 T 是 数组。

  4. 如果您需要支持向直方图插入数据,请使用 using MyFoo = internal::{Linear,Exponential}Histogram<T>,其中 T 是 Insert 的参数。

定制

  1. 创建一个新的类型封装容器。例如 class MyFoo final

  2. 确保您的类有 internal::State 为好友类。注意:请参阅 class Link 可复制的起点。

状态操作更新

State 类是所有类型的所有操作的实际实现。本部分介绍了如何实现完成封装容器实现所需的操作。

  1. 更新了 State 标头

    1. 添加了 Create 和 Free 方法。例如:MyFoo CreateMyFoo(<args>); void FreeMyFoo(MyFoo* property);,其中 args 通常包含名称、父级和一些初始值。

    2. 为需要支持的每种操作类型添加方法。例如,如果您的类型可以是 Set,则为 void SetMyFoo(MyFoo* property, T),其中 T 与您更新后的 types.h 中的类型相同。

  2. 更新 State

    1. 实现新类型的方法。不同类型的实现会有所不同。本部分简要介绍了每种方法必须执行的操作:

      • MyFoo CreateMyFoo(Args...) 负责分配多个块、设置其值,并将其返回为封装在 MyFoo 中的值。您可以使用私有构造函数 以根据其封装的 BlockIndex 对象创建 MyFoo。存在各种内部帮助程序 以简化此操作。如需查看示例,请参阅 CreateIntProperty

      • void FreeMyFoo(MyFoo* property) 负责释放 MyFoo。有时,释放块需要满足特定的排序要求或进行必要的更新。如需查看有关如何释放值的示例,请参阅 InnerFreeValue

      • 操作(例如 void SetMyFoo(MyFoo* property, T value))会更改分配给 MyFoo 的块的值,以实现操作。有关示例,请参阅 SetIntProperty

实现类型封装容器

本部分介绍了如何实现之前声明的封装容器方法。

  1. 更新写入者类型定义

    • 如果您使用的是现有模板化类型,则需要为新基本替换每个方法 类型 T。例如,如果您输入了 using MyFoo = internal::Property<T>,则会编写: template<> void internal::Property<T>::OPERATION(...) { ... }

    • 如果您创建了自己的类型,只需为声明的方法创建定义即可。您需要执行以下操作:

      • 让构造函数调用 state_->CreateMyFoo(...);

      • 让析构函数调用 state_->FreeMyFoo(...);

      • 让其他方法调用 State 上的相应实现。

      • 请让所有构造函数和方法在调用之前检查 state_ 是否不为 null。

实现测试

  1. 使用针对低级别操作的测试更新状态单元测试

  2. 使用高级别读取器实现的测试更新读取器单元测试

更改链示例

  1. C++ 读取器
  2. Rust Reader
  3. Rust 写入者
  4. Rust 验证器变更
  5. C++ 验证器变更
  6. 文档更新