Fuchsia 元件檢查總覽

Fuchsia 中的元件可能會公開符合 Inspect API 的結構化資訊。本文說明元件檢查的概念、介面、介面的 C++ 語言實作,以及面向使用者工具,用於與公開資訊的元件互動。

不確定該從何開始嗎?

概念

元件可能會公開節點樹狀結構,每個節點都有一組屬性。

圖:節點樹

節點

節點是元件中匯出的實體,可能包含 0 個或多個子項。每個節點都有名稱,且節點的每個子項都必須有不重複的名稱。

圖:節點

屬性

節點可以有任意數量的屬性。屬性具有字串索引鍵和值,值可以是下列任一類型:

數值類型

  • UintProperty - 64 位元無正負號整數。
  • IntProperty:64 位元帶正負號整數。
  • DoubleProperty:64 位元浮點值。
  • BoolProperty - 布林值。

字串類型

  • StringProperty - UTF-8 字串。
  • ByteVectorProperty:位元組向量。

陣列類型

  • UintArray、IntArray、DoubleArray:對應數值類型的陣列。

直方圖類型

  • LinearUintHistogram、LinearIntHistogram、LinearDoubleHistogram

直方圖,其中包含儲存在陣列中的固定大小值區。

  • ExponentialUintHistogram、ExponentialIntHistogram、ExponentialDoubleHistogram

直方圖,其中包含儲存在陣列中的指數大小值區。

檢查檔案格式

Inspect 檔案格式是一種二進位格式,支援在執行階段有效插入、修改及刪除節點和屬性。讀取器會擷取內容的一致快照,不與寫入器通訊。

檔案系統介面

頂層節點會以 VmoFiles 的形式,在元件的傳出目錄中公開,並以 .inspect 結尾。元件通常會將主要或根樹狀結構公開為 out/diagnostics/root.inspect。

語言程式庫

C++

C++ Inspect 程式庫提供 Inspect 檔案格式的完整寫入和讀取支援。

寫入檢查資料的元件應避免讀取該資料。讀取時必須周遊整個緩衝區,因此成本很高。

Inspector 類別提供包裝函式,可建立新的緩衝區,並加入一個根節點。節點和屬性具有類型包裝函式,當超出範圍時,會自動從緩衝區刪除基礎資料。

inspect_component 程式庫提供簡單的 ComponentInspector 單例介面,可協助處理從元件公開單一階層的常見情況。

health 功能支援以健康狀態檢查工具已知的格式,公開結構化健康狀態資訊。

stats 功能支援公開執行階段 Inspect 分配指標和緩衝區使用率。

有界清單功能支援在固定容量的 FIFO 節點緩衝區中,記錄滾動事件記錄。

測試比對器程式庫提供 GMock 比對器,用於驗證測試中從 Inspect 階層讀出的資料。

閱讀輔助

讀取程式庫支援將檢查檔案剖析為階層。Hierarchy 包含 NodeValue 和 PropertyValues,分別是 Node 和 Property 的剖析版本。

Hierarchy 的 NodeValue 會由 node() 傳回,子項 Hierarchy 則會由 children() 以向量形式傳回。GetByPath 函式支援依路徑讀取特定子項階層。

特定 NodeValue 的屬性可透過 properties() 存取器取得。如要判斷屬性是否包含特定類型,請將對應的 PropertyValue 類型做為範本參數傳遞至 Contains<T>() 方法:

  // Returns true if the first property of the hierarchy's node is an INT value.
  if (hierarchy.node().properties()[0].Contains<IntPropertyValue>()) {
    // ...
  }

使用 Get<T>() 方法取得屬性:

  // Get the IntPropertyValue of the first property on the node.
  // Note: This causes a runtime exception if the property does not contain
  // the given type, crashing the program.
  hierarchy.node().properties()[0].Get<IntPropertyValue>();

您也可以根據不同的可能格式類型進行切換:

  const auto& property = hierarchy.node().properties()[0];
  switch (property.format()) {
    case FormatType::INT:
      const auto& value = property.Get<IntPropertyValue>();
      /* ... */
      break;
    /* ... */
  }

陣列類型可能經過特殊格式設定,可包含直方圖。GetBuckets() 方法支援從 {Int,Uint,Double}ArrayValue 類型傳回直方圖值區陣列。如果基礎陣列不是特殊格式的直方圖,則陣列會是空的。

Rust

Rust Inspect 程式庫提供 Inspect 檔案格式的完整寫入和讀取支援。

寫入檢查資料的元件應避免讀取該資料。讀取時必須周遊整個緩衝區,因此成本很高。

Inspector 類別提供包裝函式,可建立新的緩衝區,並加入一個根節點。節點和屬性具有類型包裝函式,當超出範圍時,會自動從緩衝區刪除基礎資料。

component 模組支援簡單的 inspector 函式,可處理從元件公開單一階層的常見用途。

health 模組支援以健康檢查工具已知的格式,公開結構化健康資訊。

stats 模組支援公開執行階段 Inspect 分配指標和緩衝區使用率。

contrib crate 提供 BoundedListNode、inspect_log! 和時間公用程式,用於記錄加上時間戳記的事件記錄。

測試模組支援 assert_data_tree! 巨集,可比對 Inspect 資料進行測試。

測試

驗證器架構說明 Inspect 語言程式庫的整合測試架構。

使用者空間工具

ffx inspect

如要檢查元件的檢查階層,可以使用 ffx inspect show 指令:

  • 列印所有檢查階層:

    ffx inspect show
  • 列印特定元件的檢查階層 (例如 core/font_provider):

    ffx inspect show core/font_provider

ffx inspect show 也會對輸入內容進行模糊比對。例如:

ffx inspect show archivist.cm

只要這是系統中唯一公開 Inspect 且網址結尾為 archivist.cm 的元件,就會列印 bootstrap/archivist 的所有 Inspect。

如果輸入內容不夠具體,無法識別個別元件,模糊比對就會失敗。在這種情況下,工具會列印與查詢相符的路徑名稱清單。

一般來說,這個指令接受下列項目:

  • 元件路徑名稱,例如 core/network/netstack。如果只傳遞路徑名稱,則不一定要逸出集合標記。

  • 模糊搜尋參數,對應至網址片段、資訊清單片段或路徑名稱片段。例如,bootstrap/archivist 的 ffx inspect show archivist.cm,資訊清單為 archivist.cm。

  • 元件選取器,例如 core/network/*。

  • 完整的診斷選取器,例如 core/network/netstack:root/path/to/*:property。在完整選取器案例中,必須執行逸出才能建立有效的選取器。

假設您有元件 bootstrap/driver-hosts:driver-host-337。如要避免在需要特定屬性時盡可能逸出,可以採用以下符合人體工學的做法:

ffx inspect driver-host-337 --data root/stats:my_stat

ffx 目標快照

ffx target snapshot 會產生 ZIP 封存檔,內含系統的診斷資訊,包括檢查:

ffx target snapshot

最佳做法

避免以巢狀方式嵌入其他元件的指標

設計元件診斷時,請將每個元件的 Inspect 階層嚴格限制在該元件產生的資料範圍內。請避免從用戶端或下層元件轉送或巢狀結構化檢查指標,並將其置於中介元件的檢查樹狀結構下。

在外部元件下轉送或巢狀 Inspect 指標,會造成工具、偵錯和隱私權法規遵循方面的問題:

  • 開發人員體驗和工具歸因:檢查資料會依元件路徑名稱歸因。ffx inspect show 和自動快照分類查詢等診斷工具,會使用元件路徑名稱 (例如 core/network/netstack) 檢查階層。如果將多個元件的指標巢狀嵌入單一中介服務,查詢產生器元件的路徑名稱不會傳回任何資料,而查詢中介服務則會傳回無法管理的匯總階層。

  • 隱私權審查和許可清單強制執行:Fuchsia 診斷管道和快照匯出工具會使用以路徑名稱為基礎的選取器,強制執行隱私權控制項。如果將多個產生器的指標放在外部路徑名稱下,會導致資料來源不明確、無法遮蓋精細資料,以及難以驗證隱私權審查。

  • 生命週期和故障隔離:代表其他元件保留指標的中介元件,可以獨立於實際指標產生器之外,繼續存在或終止。如果中介程式當機,就會產生過時的診斷資料或導致資料遺失。

每個元件都應直接在自己的路徑名稱下,發布專屬的 Inspect 樹狀結構。

調整 Inspect VMO 的大小,避免發生無訊息分配失敗

檢查階層會使用夥伴分配的檢查 VMO 格式,儲存在對應的 VMO 中。根據預設,C++ 和 Rust 程式庫分配的最大 VMO 大小為 256 KiB (262,144 位元組)。當節點和屬性寫入階層時,記憶體會以 4 KiB 頁面動態提交,最多可達此上限。

無聲分配失敗

如果 Inspect 樹狀結構超過設定的 VMO 容量上限,或記憶體片段導致無法分配要求大小的連續區塊,Inspect 程式庫會按照設計自動失敗:

  • 建立新的子節點或屬性會失敗,並傳回無作業物件 (或在多範圍字串或位元組酬載在後續範圍用盡空間時,儲存截斷的前置字元)。

  • 附加至有界清單 (例如 BoundedListNode) 時,如果容量已滿,系統仍會先移除最舊的項目,再嘗試分配新項目。如果記錄屬性時空間不足,系統可能會捨棄新項目,或留下空白或部分填入的項目節點。

  • 現有數值屬性、布林值屬性、數值陣列和直方圖的原地變異仍會正常運作。

  • 如果無法分配初始字串區塊或其第一個溢位範圍,更新字串屬性或字串陣列槽會保留先前的值;如果後續溢位範圍失敗,則會儲存截斷的前置字元。更新位元組屬性時,系統會先釋出舊的範圍。如果第一個範圍無法分配,則會留下空值;如果後續範圍失敗,則會留下截斷的前置字元。

  • 元件不會當機或發生緊急狀況。系統不會記錄節點和屬性建立失敗的情況;只有在 StringProperty::set 無法分配初始字串區塊或第一個溢位範圍,或 BytesProperty::set 無法分配第一個範圍時,Rust 程式庫才會記錄錯誤。

不過,如果分配失敗時未顯示任何訊息,開發和疑難排解期間可能不會注意到指標遺失或歷來事件記錄遭到截斷。

使用 fuchsia.inspect.Stats 偵測分配失敗

您可以在階層根層級提供 fuchsia.inspect.Stats 延遲節點,監控 Inspect 記憶體用量,並偵測捨棄的分配:

使用 ffx inspect show 查詢元件時,fuchsia.inspect.Stats 節點會顯示即時分配指標:

ffx inspect show <moniker>:root/fuchsia.inspect.Stats

這個指令會輸出類似以下的指標:

fuchsia.inspect.Stats:
  allocated_blocks = 142
  current_size = 36864
  deallocated_blocks = 28
  failed_allocations = 0
  maximum_size = 262144
  peak_bytes_requested = 34560
  total_dynamic_children = 1
  utilization_per_ten_k = 1406

如果 failed_allocations 的值不為零,表示 Inspect 已用盡 VMO 容量,並捨棄資料。peak_bytes_requested 會回報並行要求的位元組數最高值。失敗的要求仍會計入配額,且不會釋出,因此重複失敗的分配作業會使配額值超過實際需求高峰。您可以參考這個值,瞭解 Inspector VMO 需要多大,才能避免分配失敗。

規模調整建議和設定

設計元件診斷時:

  • 預估緩衝區需求:如果元件使用有界集合 (例如 BoundedListNode) 記錄歷來事件記錄,請計算預期的記憶體用量 (項目數量 × 平均項目大小),或檢查 fuchsia.inspect.Stats 中的 peak_bytes_requested,並確保 VMO 大小有足夠的空間。

  • 設定自訂 VMO 容量:如果 256 KiB 不足,請使用明確大小初始化檢查器:

    • C++:使用已設定 maximum_size 的 inspect::InspectSettings 結構體建構 inspect::Inspector。如要透過 inspect::ComponentInspector 發布,請在 inspect::PublishOptions 的 inspector 欄位中傳遞該 Inspector。

    • Rust:在任何其他使用 component 單例模式 (inspector()、health() 或 serve_inspect_stats()) 之前,請先呼叫 fuchsia_inspect::component::init_inspector_with_size(max_size),或透過 Inspector::new(InspectorConfig::default().size(max_size)) 設定 InspectorConfig。

  • 針對大型或動態資料集使用延遲節點:將大型集合 (例如轉送表或詳細快取項目) 卸載至延遲節點,以便視需求計算,而非永久占用 VMO 空間。

如需設定 VMO 大小和提供統計資料的具體程式碼範例,請參閱快速入門導覽課程指南中的「管理 VMO 大小和容量」。