更新 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. (選用) 視變更的內容而定,您可能必須適時更新validator測試,因為現有區塊的現有格式變更可能會導致這些測試中斷。

以下各節概述瞭如何整合各項變更,但實務來說,在按照上述方式分割設計前,請將下列幾點做為提示,以一致的方式設計整個系統。

更新 Rust 實作

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

設定

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

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

讀取者變更

Bitfield 最新消息

  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 功能。您可以在這裡將區塊分配並轉換為新的類型。如果您只修改現有區塊,那麼這可能就是您唯一需要變更的地方。

建立新的值類型

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

  1. 在建立的檔案中建立新類型。使用現有類型做為範例。類型一律可以存取內部 State。請使用此方法為新類型建立必要的方法,並呼叫在 State 中建立的方法。

  2. 將方法新增至 Node 以建立新類型。

  3. 確保您的類型在 VMO 中具有 RAII 語意。如果類型是值,系統可能會透過步驟 1 中現有類型複製的樣板來自動執行此操作。

最後,返回並更新讀取器變更內容中的測試以使用新的 API!

更新 C++ 實作

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

如上所述,本節應該在 Gerrit 中進行兩項變更。

設定

  1. 包含測試:
fx set core.x64 --with //zircon/system/ulib/inspect:tests
  1. 執行測試。
fx test inspect-cpp-unittest

讀取者變更

Bitfield 最新消息

本節說明如何為新類型定義位元欄位。

更新區塊定義

  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. 如果您需要支援 Add、Subtract 和 Set: 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 是插入的引數。

訂製

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

  2. 確保您的課程具有 internal::State 這個好友課程。注意:如需可複製的起點,請參閱 class Link

狀態動作最新資訊

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

  1. 更新 State 標頭

    1. 新增「建立」和「免費」方法。例如:MyFoo CreateMyFoo(<args>); void FreeMyFoo(MyFoo* property);,其中 args 通常包含名稱、父項和某些初始值。

    2. 為您需要支援類型的每項作業新增方法。舉例來說,如果您的類型可以設定,就要設定 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 讀取者
  3. Rust 寫入者
  4. Rust 驗證工具變更
  5. C++ 驗證工具異動
  6. 說明文件更新