RFC-0079:偵測調試記錄資料遺失 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 修正偵測調試記錄資料遺失的機制。 |
問題 | |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2021-02-17 |
審查日期 (年-月-日) | 2021-03-25 |
摘要
本文提出更新機制,以便偵測來自核心的 debuglog 物件的遺失訊息。
背景和動機
核心偵錯記錄子系統是簡單的記錄設施,可讓使用者模式程式讀取及寫入記錄訊息。從邏輯上來說,這個系統會提供單一 FIFO 記錄緩衝區,可供多個寫入器或讀取器寫入或讀取。
偵錯記錄可能會發生資料遺失的情形。假設讀者可以跟上寫入率,所有讀者都會依照寫入順序,看到所有作者寫入的所有訊息。不過,如果讀取器速度緩慢且無法跟上,就會錯過訊息。
使用者模式程式可透過 zx_debuglog_write
或核心透過 printf
將訊息寫入偵錯記錄。您可以透過 zx_debuglog_read
閱讀訊息。此外,核心會提供一個專用執行緒,稱為 debuglog_dumper,用於讀取偵錯記錄中的訊息,並將訊息寫入偵錯序列埠。
偵錯記錄緩衝區的容量是固定的。達到該容量上限時,系統可能會捨棄最近未寫入的訊息,騰出空間存放新訊息。未「追上」的讀者,永遠不會看到已刪除的訊息。
知道記錄是否完整,可讓您推斷特定事件是否缺少,因此偵測遺漏的記錄訊息是記錄系統的重要功能。偵錯記錄讀取器必須能夠偵測記錄訊息何時遭到捨棄。
目前,偵錯記錄會提供機制,讓讀者偵測記錄資料的丟棄時間和字節數,也就是 zx_log_record_t
的 uint32_t rolled_out
欄位。
使用 zx_debuglog_read
讀取 zx_log_record_t
後,rolled_out
欄位會包含從該讀取器上次成功讀取後,從調試記錄中刪除的記錄訊息位元組數量。這個值包含已捨棄記錄標頭的位元組,以及已捨棄記錄主體的位元組。
rolled_out
機制是透過讓每個讀取器維護指向調試記錄緩衝區的指標,指向尚未讀取的下一個訊息,來實作。偵錯記錄會維護一個寫入指標,指向下一個訊息的寫入位置。如果讀取器發現寫入指標已傳遞讀取指標,就會知道自己遺漏了一或多則訊息。讀取器可透過減去指標值,判斷遺漏了多少個記錄資料位元組 (包括標頭)。
rolled_out
機制目前未使用。
提案
本 RFC 建議...
以記錄為導向的偵測功能取代以位元組為導向的資料遺失偵測功能。
為了更貼近偵錯記錄讀取器的預期,尤其是 debuglog_dumper,現有的以位元組為導向的
rolled_out
機制將會替換為每個記錄的序號,可用於偵測資料遺失 (序列中的空白)。debuglog_dumper 必須將讀取到的每則訊息寫入偵錯序列埠。由於序列埠可能不會特別快速,debuglog_dumper 通常無法跟上 debuglog,導致訊息遭到捨棄。發生這種情況時,我們會在序列埠上輸出訊息,指出資料遺失的情況,以及遺失了多少訊息,類似於 Linux 的
printk
輸出內容。使用 64 位元值,避免資料遺失而未偵測到的可能性。
由於
rolled_out
欄位的大小為 32 位元,且計數的單位為位元組而非記錄,因此如果在兩次呼叫zx_debuglog_read
之間寫入 4 GB 的記錄資料,就可能會導致值溢位,進而導致未偵測到的資料遺失。這在實際情況中不太可能發生,但我們還是希望能完全排除這種可能性。如果我們將 32 位元位元組序列欄位替換為 32 位元每筆記錄序列欄位,則建立溢位所需的資料量會增加到約 128 GB。使用 64 位元序列欄位,即使記錄率非常高,也能完全忽略溢位可能性。啟用日後的實作最佳化功能。
日後可能會進行一些最佳化,但這取決於是否允許多個偵錯記錄讀取器「共用」單一
zx_log_record_t
。在沒有緩慢讀取器的情況下,所有偵錯記錄讀取器都應以相同順序顯示完全相同的記錄資料。除了
rolled_out
之外,zx_log_record_t
的所有欄位都會在記錄寫入偵錯記錄時固定。rolled_out
的不同之處在於,它會在讀取記錄時,針對每個讀取器計算。如果我們有其他方法可用來偵測資料遺失,且不使用每個讀取器可能不同的欄位,我們就能在日後啟用一些潛在的最佳化方式,讓單一記錄可供所有讀取器共用。
設計
當偵錯記錄寫入偵錯記錄時,會為每個記錄指派以 0 開頭的 64 位元序號。
每個記錄的序號都會比前一個記錄多一個。移除 zx_log_record_t
的 rolled_out
欄位,並用記錄的序號 uint64_t sequence
取代。
zx_debuglog_read
的呼叫端可以偵測序列中的空白,並計算遺漏了多少訊息。
實作
zx_debuglog_read
和 zx_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 呼叫端以便使用。
步驟如下:
將 64 位元序號新增至偵錯記錄訊息 (
dlog_header_t
) 的私人內部表示法。變更偵錯記錄使用者,以便使用
zx_log_record_t
(或等同的語言),而非硬式編碼欄位偏移量。具體來說,請建立 Rust 等價型別,並更新 Rust 程式碼以便使用該型別。將
zx_debuglog_read
的void*
參數變更為zx_log_record_t*
。將
zx_log_record_t
的rolled_out
欄位變更為填入零的unused
欄位。將
zx_log_record_t
的unused
欄位替換為uint64_t sequence
,並確保不會建立隱含的結構體填充。無論語言為何,請對所有zx_log_record_t
等價類型執行相同的操作。更新
zx_debuglog_read
說明文件,說明呼叫端如何使用新的序號欄位來偵測資料遺失情形。
步驟 1、2 和 3 會各自取得 CL。步驟 4、5 和 6 會在單一 CL 中執行。
成效
管理序列計數器的執行階段成本
系統會在保持鎖定狀態下執行偵錯記錄作業,以便使用一般 uint64_t
值產生序列。預期不會對效能造成可測量的影響。
每個記錄序列值的大小影響
內核的私人記錄實作項目 zx_log_record_t
和 dlog_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
會回報遭到捨棄/抑制的訊息數量,而非遭到捨棄/抑制的記錄資料位元組。