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

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

修正偵測調試記錄資料遺失的機制。

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

摘要

本文提出更新機制,以便偵測來自核心的 debuglog 物件的遺失訊息。

背景和動機

核心偵錯記錄子系統是簡單的記錄設施,可讓使用者模式程式讀取及寫入記錄訊息。從邏輯上來說,這個系統會提供單一 FIFO 記錄緩衝區,可供多個寫入器或讀取器寫入或讀取。

偵錯記錄可能會發生資料遺失的情形。假設讀者可以跟上寫入率,所有讀者都會依照寫入順序,看到所有作者寫入的所有訊息。不過,如果讀取器速度緩慢且無法跟上,就會錯過訊息。

使用者模式程式可透過 zx_debuglog_write 或核心透過 printf 將訊息寫入偵錯記錄。您可以透過 zx_debuglog_read 閱讀訊息。此外,核心會提供一個專用執行緒,稱為 debuglog_dumper,用於讀取偵錯記錄中的訊息,並將訊息寫入偵錯序列埠。

偵錯記錄緩衝區的容量是固定的。達到該容量上限時,系統可能會捨棄最近未寫入的訊息,騰出空間存放新訊息。未「追上」的讀者,永遠不會看到已刪除的訊息。

知道記錄是否完整,可讓您推斷特定事件是否缺少,因此偵測遺漏的記錄訊息是記錄系統的重要功能。偵錯記錄讀取器必須能夠偵測記錄訊息何時遭到捨棄。

目前,偵錯記錄會提供機制,讓讀者偵測記錄資料的丟棄時間和字節數,也就是 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 通常無法跟上 debuglog,導致訊息遭到捨棄。發生這種情況時,我們會在序列埠上輸出訊息,指出資料遺失的情況,以及遺失了多少訊息,類似於 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 的系統呼叫定義和說明文件並未明確指出會傳回 zx_log_record_t。而是指定 void*size_t,且呼叫端必須知道如何在結果上方投遞或「重疊」zx_log_record_t。轉換為 zx_log_record_t* 容易發生錯誤,因此 void* 系統呼叫參數會變更為 zx_log_record_t*,並更新呼叫端。

目前沒有 Rust 等同於 zx_log_record_t 的功能,且 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 可儲存 128KB 的標頭和訊息。訊息的取樣結果顯示,平均大小約為 100 位元組。假設這個平均大小,加上 32 位元組的標頭,FIFO 可儲存約 971 則訊息。使用每筆記錄序號後,數量會減少到約 917 筆。

安全性考量

這項提案不會影響系統安全性。Debuglog 讀取器是具有特殊權限的元件。在沒有資料遺失的情況下,偵錯記錄檔讀取器已可精確地合成記錄檔序列。

隱私權注意事項

不會影響隱私權。

測試

核心內單元測試會驗證基礎的調試記錄實作方式,而調試記錄核心測試會驗證在系統呼叫層可觀察到的行為。

說明文件

zx_log_record_t 的說明文件將會更新。

缺點、替代方案和未知事項

不執行任何動作

由於 rolled_out 尚未使用,因此目前只需投入少量工程資源即可實作這項提案。一旦下游程式碼開始使用 rolled_out,實作這項或類似的提案就會變得更加昂貴。

您可以將 UINT32_MAX 的語意記錄為「UINT32_MAX 或更多」,稍微緩解 32 位元迴轉的問題。或者,我們可以將 rolled_out 的類型變更為 uint64_t

錄製序列和位元組序列

如果空間足夠,我們可以在每個 zx_log_record_t 中同時放入記錄序列值和位元組序列值。這樣一來,Debuglog 讀取器就能評估資料遺失的記錄數量或遺失的位元組數。不過,這會進一步增加 dlog_header_t 的大小,而且我們不確定有使用者會丟棄位元組。

既有技術與參考資料

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