RFC-0079:偵測偵錯記錄檔資料遺失

RFC-0079:偵測偵錯記錄資料遺失
狀態已接受
區域
  • 核心
  • 診斷資料
說明

修改偵測偵錯記錄資料遺失情形的機制。

問題
變更
作者
審查人員
提交日期 (年/月)2021-02-17
審查日期 (年/月)2021-03-25

摘要

本文件提議更新相關機制,用於偵測來自核心偵錯記錄檔物件的訊息遭到捨棄。

背景和動力

核心偵錯記錄子系統是簡單的記錄設施,可讓使用者模式程式讀取和寫入記錄訊息。根據邏輯,這個系統提供單一 FIFO 記錄緩衝區,該緩衝區可以寫入或讀取多位寫入者或讀取者。

偵錯記錄可能會有損傷。假設讀者可以跟上寫入率,所有讀取者都會看見所有寫入者按撰寫順序寫入的所有訊息。但如果讀者速度慢且無法跟上進度,則可能會錯過訊息。

訊息可透過使用者模式程式透過 zx_debuglog_write,或透過 printf 將訊息寫入偵錯記錄。訊息可透過 zx_debuglog_read 讀取。此外,核心還有一個特殊的執行緒,稱為 debuglog_dumper,可用於讀取 debuglog 中的訊息,並將訊息寫入偵錯序列埠。

偵錯記錄緩衝區的容量固定。達到上限時,系統可能會捨棄最近寫入的最舊訊息,以騰出空間處理新訊息。所有未「讀完」的讀者都不會看到已捨棄的訊息。

瞭解記錄是否已完成,可以合理推斷是否缺少特定事件,因此偵測捨棄的記錄訊息是記錄系統的重要功能。偵錯記錄讀取器必須能夠偵測記錄訊息何時捨棄。

目前,偵錯記錄提供的機制可讓讀取器偵測記錄檔資料何時捨棄的位元組和數量 (即 zx_log_record_tuint32_t rolled_out 欄位)。

使用 zx_debuglog_read 讀取 zx_log_record_t 後,rolled_out 欄位會包含自上次成功讀取後,從偵錯記錄中捨棄的記錄訊息位元組數。這個值包含已捨棄的記錄標頭位元組數以及遭捨棄記錄主體的位元組。

如要實作 rolled_out 機制,請讓每位讀取器維持指向偵錯記錄緩衝區的指標,並指向其尚未讀取的下一則訊息。偵錯記錄會維護一個寫入指標,指向下一則訊息的寫入位置。如果讀取器發現寫入指標已傳遞其讀取指標,就會知道它錯過了一或多則訊息。透過減少指標值,讀取器可以判斷遺漏的記錄資料位元組數 (包括標頭)。

目前未使用 rolled_out 機制。

提案

這份 RFC 建議...

  1. 使用記錄導向偵測功能,取代位元組導向資料遺失偵測功能。

    為了更符合偵錯記錄讀取器的期望 (尤其是 debuglog_dumper),系統會將現有的位元組導向 rolled_out 機制替換為記錄序號,可用於偵測資料遺失 (序列中的間隔)。

    debuglog_dumper 必須將其讀取的每則訊息寫入偵錯序列埠。由於序列通訊埠的速度可能不特別快,debuglog_dumper 通常無法跟上偵錯記錄檔同步,而這會導致訊息遺失。發生這種情況時,我們會輸出訊息至序列埠,指出已遺失資料以及遺失的訊息數量,這與 Linux 的 printk 輸出內容類似。

  2. 使用 64 位元值避免發生未偵測到的資料遺失情形。

    由於 rolled_out 欄位的大小為 32 位元,並且會計算位元組而非記錄,因此如果在向 zx_debuglog_read 發出的兩次呼叫之間寫入 4 GB 的記錄資料,可能會導致該值溢位,進而導致未偵測到的資料遺失。雖然實務上不太可能發生,但將這類可能性完全排除也很好。如果要將 32 位元的位元組序列欄位替換為每個記錄的 32 位元序列欄位,建立溢位時所需的資料量會成長到約 128 GB。透過使用 64 位元序列欄位,我們可以完全忽略溢位的可能,即使記錄率極高也一樣。

  3. 啟用日後的導入最佳化功能。

    未來,某些最佳化做法取決於允許多個偵錯記錄讀取器「共用」單一 zx_log_record_t

    如果沒有慢速讀取器,所有偵錯記錄讀取器都會以相同的順序看到完全相同的記錄資料。除了 rolled_out 以外,在記錄寫入偵錯記錄時,zx_log_record_t 的所有欄位都會修正。rolled_out 不同,在從偵錯記錄中讀取記錄時,系統會依每個讀取工具計算這個結果。如果我們能以其他方式偵測資料遺失,且不使用可能依每位讀取者不同的欄位,我們未來可能會啟用某些潛在的最佳化功能,讓所有讀取工具共用單筆記錄。

設計

偵錯記錄檔寫入偵錯記錄檔時,會針對每個記錄指派從 0 開始的 64 位元序號。

每筆記錄的序號只會比上述記錄的數字高一個。移除 zx_log_record_trolled_out 欄位,並替換為記錄的序號 uint64_t sequence

接著,zx_debuglog_read 的呼叫端可以偵測序列的間隙,並計算捨棄的訊息數量。

實作

樹狀結構外不會使用 zx_debuglog_readzx_log_record_t。雖然不需要完整的 Fuchsia 大型規模變更 (LSC) 程序,但系統會提出 FYI LSC 錯誤,並分階段完成實作。

rolled_out 欄位並未使用,但內含的結構體 zx_log_record_t 已用於 fuchsia.git 中的幾個位置。需要小心避免現有的程式碼毀損。「zx_log_record_t」未在樹狀結構外使用。

zx_debuglog_read 的 Syscall 定義和說明文件實際上並未指出它會傳回 zx_log_record_t。相反地,它會指定 void*size_t,而呼叫端必須知道要投放或「疊加」在結果上方的 zx_log_record_t。投放到 zx_log_record_t* 容易出錯,因此 void* syscall 參數會變更為 zx_log_record_t*,且呼叫端將更新。

目前沒有與 zx_log_record_t 和 Rust 呼叫端相等的 Rust 功能,會使用硬式編碼的偏移來存取欄位,因此變更其欄位的大小或偏移,可能會導致這些呼叫端無法顯示通知,而且不會顯示相關通知。在實作過程中,我們會建立相當於 zx_log_record_t 的 Rust,並更新 Rust 呼叫端來使用。

步驟如下:

  1. 將 64 位元序號新增至偵錯記錄訊息的私人內部表示法 (dlog_header_t)。

  2. 將偵錯記錄取用者變更為使用 zx_log_record_t (或對應的語言),而不是使用硬式編碼的欄位偏移。具體而言,請建立 Rust 對等類型,並更新 Rust 程式碼來使用該類型,

  3. zx_debuglog_readvoid* 參數變更為 zx_log_record_t*

  4. zx_log_record_trolled_out 欄位變更為零填滿的 unused 欄位。

  5. zx_log_record_tunused 欄位替換為 uint64_t sequence,並確保不會建立隱含結構的邊框間距。不管語言為何,所有 zx_log_record_t 對等類型都執行相同操作。

  6. 更新 zx_debuglog_read 說明文件,說明呼叫端如何使用新的序列欄位偵測資料遺失。

步驟 1、2 和 3 會各有專屬的 CL。步驟 4、5 和 6 會出現在單一 CL 中。

效能

序列計數器的執行階段成本管理

偵錯記錄作業會在保留鎖定時執行,因此我們可以使用一般的 uint64_t 值產生序列。目前沒有可評估的效能影響。

每個記錄序列值的大小影響

zx_log_record_tdlog_header_t (核心的私人記錄實作) 大小將會增加。zx_log_record_t 的 32 位元 rolled_out 欄位將替換為 64 位元記錄序列欄位,帶來 4 個位元組的淨增益。

dlog_header_t 大小異動更有趣,因為這是將記錄記錄儲存在 FIFO 的原生格式。dlog_header_t 的大小為 32 個位元組,且沒有 rolled_out 欄位,因此淨收益為完整的 8 個位元組。偵錯記錄物件中的 FIFO 空間受到限制,因此將每筆記錄檔記錄增加 8 位元組後,將能減少可在 FIFO 中儲存的記錄數量上限,並縮減訊息大小上限 (從 224 個位元組至 216 個位元組)。

FIFO 可以儲存 128 KB 的標頭和郵件。訊息取樣表示平均值約約 100 位元組。假設這個平均大小包含 32 位元組的標頭,FIFO 可以儲存大約 971 則訊息。使用每個記錄序號時,該數字會縮減至約 917。

安全性考量

提案不會改變系統安全性。偵錯記錄讀取器是具有特殊權限的元件。缺少資料遺失時,偵錯記錄檔讀取器已經能夠以完全準確的方式合成記錄檔記錄序列。

隱私權注意事項

不影響隱私權。

測試

核心單元測試會驗證基礎偵錯記錄檔實作,偵錯記錄檔核心測試則會驗證系統呼叫層中可觀察的行為。

說明文件

zx_log_record_t的說明文件將會更新。

缺點、替代方案和未知

不執行任何動作

由於尚未使用 rolled_out,因此目前需要投入少量的工程工作。一旦下游程式碼使用 rolled_out,實作此或類似的提案時就會增加。

UINT32_MAX 的語意記錄為「UINT32_MAX」以上,可以緩解 32 位元環繞的問題。或者,我們可以將 rolled_out 的類型變更為 uint64_t

記錄序列和位元組序列

如果空間可用,我們可以在每個 zx_log_record_t 中都放置記錄序列值和位元組序列值。如此一來,偵錯記錄讀取器就能測量記錄數或遺失的位元組數資料遺失。但是,這麼做會進一步增加 dlog_header_t 的大小,而且並不明確知道我們有捨棄位元組的用途。

先前的圖片和參考資料

Linux 的 printk 會回報捨棄/隱藏的訊息數量,而非捨棄/隱藏的記錄資料位元組數。