更新 VMO 檔案格式

本文說明如何更新或擴充元件檢查檔案格式

擴充格式時,請務必不要破壞任何現有功能,尤其是檢查讀取器和驗證測試。不過,將所有變更打包成單一變更,審查起來可能會很吃力。一般來說,鏈結中應有 5 到 9 個變更或步驟,可改變 VMO 格式:

  1. (可能不適用) 更新 VMO 格式文件,選擇類型編號
  2. 更新 Rust 讀取器。
  3. 更新 C++ 讀取器。
  4. 更新 Rust 寫入器。
  5. 更新 C++ 寫入器。
  6. (可能) 更新驗證工具。
  7. 更新說明文件。
  8. (非變更)(選用) 傳送功能公告。

選擇型號

在「檢查檔案格式」中查看類型表格,然後選擇可用的類型編號。目前的規格共有 256 種可能的類型。

如要預訂新類型,請更新檢查檔案格式

實作

如果沒有讀取器或寫入器,測試讀取器或寫入器會很麻煩,而且如果沒有讀取器和寫入器,也很難取得適當的區塊層級 API。

如要測試讀取者和寫入者,請按照下列步驟操作:

  1. 選擇一種語言,並完全以該語言設計及實作這項功能,同時使用單元測試模擬 API 的實際使用情形。

  2. 將變更分成讀取器和寫入器變更,並將寫入器堆疊在讀取器上方。

    此時,讀者中依附於 Writer API 的測試可能已在讀者變更中損毀。

  3. 將變更重新納入基準,並使用較低層級的功能重新編寫測試 (保留原始測試版本)。

    通常您可以將所有變更放入讀取器變更中,但這樣會導致讀取器 API 的測試變得複雜。

  4. 測試結果會很難看。重新設定寫入器變更的基準,並移除修改後的測試,改用以高階 API 編寫的原始測試。

  5. 以這兩項變更做為參考,並以第二種語言複製這些變更。

  6. (選用) 視變更內容而定,您可能必須一邊進行變更,一邊更新驗證工具測試,因為現有區塊的現有格式變更可能會導致測試失敗。

下列各節將說明如何組合各項變更,但實際上,請先將這些內容視為設計整個系統的提示,再如上所述分割設計。

更新 Rust 實作項目

本節中的範例會建立名為 MyFoo 的新型別。

設定

  1. 包含測試:
fx set core.x64 --with //src/lib/diagnostics:tests
  1. 執行測試:

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

讀取者變更

位元欄位更新

  1. 如要定義新的 Block Type,請更新 BlockType 列舉

  2. 更新為 BlockType 定義的方法和函式。

  3. 如要變更現有區塊中的欄位或建立新的 BlockType,請更新位元欄位版面配置

  4. 執行 inspect-format-tests,確認變更已編譯:

fx test inspect-format-tests
  1. 更新區塊定義,加入讀取和寫入新欄位的方法。此時在區塊程式庫中加入寫入功能是適當的,因為沒有這項功能,幾乎不可能編寫讀取器測試。

  2. 編寫區塊測試,運用新功能。

  3. 編寫測試,對區塊的前 8 到 16 個位元組進行判斷。

    這通常是指將預期內容寫入 &[u8] (包含十六進位值),並判斷其是否等同於區塊用做容器的緩衝區。

更新讀者

您可以在 mod.rs 中找到讀取器程式碼。 這項變更中的測試可能很棘手,因為高階 API 撰寫器尚未存在。

作者變更

狀態

主要異動在於State功能。 您可以在這裡分配區塊,並轉換為新類型。 如果您只是要修改現有區塊,可能只需要在這裡進行變更。

建立新的值類型

您可以在型別目錄中新增型別檔案。

  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 是 Set 的引數型別。

  3. 如需支援陣列的數值運算: using MyFood = internal::ArrayProperty<T>,其中 T 是陣列中位置的引數型別。

  4. 如需支援插入直方圖: using MyFoo = internal::{Linear,Exponential}Histogram<T>,其中 T 是 Insert 的引數。

Bespoke

  1. 建立新的型別包裝函式。例如 class MyFoo final

  2. 確認您的類別已將 internal::State 設為友元類別。注意:如要取得可複製的起點,請參閱class Link這篇文章。

狀態動作更新

State 類別是所有類型所有作業的實際實作項目。本節說明如何實作完成包裝函式實作所需的作業。

  1. 更新 State 標題

    1. 新增「建立」和「免費」方法。舉例來說: 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_ 是否為空值。

導入測試

  1. 使用低階作業的測試更新狀態單元測試

  2. 使用高階讀取器實作的測試,更新讀取器單元測試

變更鏈結範例

  1. C++ 讀取器
  2. Rust Reader
  3. Rust 撰寫者
  4. Rust 驗證器變更
  5. C++ 驗證工具變更
  6. 說明文件更新