RFC-0084:將更多指標新增至 zx_info_task_runtime_t | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 為 zx_info_task_runtime_t 新增額外指標,以便排除媒體效能問題。 |
問題 | |
變更 | |
作者 | |
審查人員 | |
提交日期 (年/月) | 2021-03-09 |
審查日期 (年/月) | 2021-04-06 |
摘要
ZX_INFO_TASK_RUNTIME
主題可讓您擷取工作在 CPU 上執行,或已排入佇列執行的總時間。為了診斷排程瓶頸,建議您加入額外的精細執行階段資訊,特別是等待頁面錯誤所花費的時間,以及等待核心互斥鎖所花費的時間。
提振精神
即時任務設有期限。有時候,會錯過期限,因為任務會在核心中花費非預期的長時間遭到封鎖。如要對這類情況進行偵錯,建議您先瞭解工作遭到封鎖的原因。舉例來說,如果工作的期限為 10 毫秒,但花費 11 毫秒等待頁面錯誤,我們可以斷定是因為網頁錯誤速度太慢而錯過了期限。
具體而言,我們想改善 audio_core
等媒體子系統產生的診斷問題。使用 audio_core
時,每個混合工作都必須在 10 毫秒內完成。如果工作時間超過 10 毫秒,使用者會聽到有瑕疵的音訊。最近,由於頁面錯誤 (也就是需要重新分頁 audio_core
執行檔頁面) 和核心堆積互斥運算的爭用情況,導致缺少期限。快照無法用來診斷這些問題,而是我們必須記錄追蹤記錄,而這個程序有時會因為在本機重現問題,而受到干擾 (因為錯誤只會透過特定環境中的特定應用程式觸發)。這些是高優先順序的問題,可能會對媒體成效造成嚴重負面影響。
我們的目標是從核心匯出足夠的診斷資訊,方便我們從快照診斷這些問題,而不必深入瞭解執行追蹤記錄。本文件建議在 zx_info_task_runtime_t
中加入新的統計資料,以便更完整回答「這項工作為何無法執行?」的問題。
Zircon 執行緒期限的背景
Zircon 期限設定檔包含三個元件:期限、容量和期限。將執行緒指派到期限設定檔後,Zircon 會保證每個「週期」在每個「週期」開始的「期限」內,執行緒都會分配給「容量」。舉例來說:
- 經期 = 10 毫秒
- 容量 = 2 毫秒
- 期限 = 5 毫秒
該執行緒每 10 毫秒會在接下來 5 毫秒內分配 2 毫秒的 CPU。錯過期限的方式有兩種:
工作預定延誤。舉例來說,如果下一個週期從 T 時間開始,但工作並未安排至 T+4 毫秒,則工作無法排定期限 (時間不應晚於 T+3 毫秒)。核心可以偵測這類錯過的期限,但在實務上,除非排程器發生錯誤或超額訂閱,否則這種情況應該不會發生。
這項工作需要 2 毫秒以上的時間才能完成。核心無法得知這種情況何時發生,因為它無法解讀任務邊界。例如,如果排定於 T+1 毫秒執行所有工作,總共執行 1 毫秒,接著封鎖 9 毫秒,則封鎖 9 毫秒。
我們的目標是協助診斷第二類錯過的期限。就目前的工作而言,如果工作的執行時間過長,它可以查詢 ZX_INFO_TASK_RUNTIME
,瞭解在使用者空間的 CPU 上執行多少時間。如果 cpu_time
大於任務預期的執行階段,任務就會知道執行的時間過長。不過,如果 cpu_time
佔總工作時間的一小部分,代表任務在核心中花費的大部分時間都有可能遭到封鎖且無法執行。這個 RFC 的目標是協助您瞭解時間所花費的時間。
設計空間
我們的目標是回答「這項工作為何無法執行?」的問題。我們必須做出一些決定:
回答是否完整?具體來說,我們是否應該列舉工作遭到封鎖的所有原因,或只列舉幾個重要原因?
我們該回答這個問題的精細程度為何?舉例來說,是否應只回報使用者層級能夠理解的簡單事件,例如「在 zx_channel_read 上封鎖」),還是應該納入 Zircon 目前實作項目的較低層級事件?
如果記錄 N 統計資料,我們是否應該要求這些 N 統計資料不能重疊,還是應該允許以任意方式重疊?
最簡單的直接方法是列舉幾個關注的事件,並產生這些事件的統計資料。由於媒體子系統發現頁面錯誤和核心鎖定爭用問題,因此我們可能會產生「網頁錯誤時間」和「在核心鎖定上花費的時間」等統計資料。
此外,這兩個統計資料可能無法捕捉到其他問題。因此,完整的解決方案具有吸引力。其中一個想法是列舉出執行緒進入核心的所有方式。包括 N 個硬體中斷 (計時器和裝置中斷) 和 K 軟體中斷 (系統呼叫和錯誤)。然後針對每種中斷類型分別產生 N+K 統計資料。計時器會在執行緒進入核心時啟動,並在控制項返回使用者空間執行緒時停止。不過,使用者層級可以計算「Syscall X 花費的時間」,因此核心計算這項資訊是多餘的。
另一個做法是產生「在核心模式下在 CPU 上執行的時間」和「在 X 上運作的時間」,其中 X 是核心基本功能,例如「核心鎖定」或「通道」。這樣的想法可能會因核心原始元件的變動而發生暴動和流失風險。
回到剛剛的步驟,我們真正希望能從野外裝置擷取追蹤記錄。理想情況下,我們會持續將追蹤記錄記錄到環形緩衝區中,並在觸及 TRACE_ALERT
後上傳該緩衝區。
設計
您想要完整的解決方案,但設計及建構可能需要較長的時間。我們需要立即在野外診斷效能迴歸。因此,我們建議您新增兩個目標指標來解決立即的問題:等待頁面錯誤所花費的時間,以及等待核心鎖定所花費的時間。
關於以上設計空間問題:
但是,我們不會力求完整性。
精細程度可以任意決定 (我們會記錄任何必要項目)
統計資料可能會以任意方式重疊
核心異動
// This struct contains a partial breakdown of time spent by this task since
// creation. The breakdown is not complete and individual fields may overlap:
// there is no expectation that these fields should sum to an equivalent
// "wall time".
typedef struct zx_info_task_runtime {
// Existing fields
zx_duration_t cpu_time;
zx_duration_t queue_time;
// New fields below here
// The total amount of time this task and its children spent handling page faults.
zx_duration_t page_fault_time;
// The total amount of time this task and its children spent waiting on contended
// kernel locks.
zx_duration_t lock_contention_time;
} zx_info_task_runtime_t;
系統會針對每個執行緒計算這兩個欄位,然後加總所有程序和工作 (與目前針對 cpu_time
和 queue_time
的工作) 進行加總。請注意,媒體子系統不需要執行各項程序和每項工作的匯總作業,但會將這些資料納入這裡,以便與 zx_info_task_runtime_t
中的現有欄位保持一致。
頁面錯誤分為多種類型和多個核心鎖定,page_fault_time
代表處理各類型網頁錯誤所花費的總時間。透過涵蓋所有頁面錯誤,我們就不必說明所涵蓋的頁面錯誤子集,因為核心可能會新增或移除特定類型的頁面錯誤,因為實作會隨著時間變更,並支援新架構。
lock_contention_time
涵蓋所有邊界鎖定。不過,「競爭」這個字詞並非刻意設定,因此核心可能會隨時間改進實作方式,進而平衡評估爭用成本與報表競爭時間的好處。如需更多討論,請參閱下文的「導入作業」。
使用者空間如何診斷錯過的期限
就這些新欄位而言,使用者空間可以使用如下的程式碼來診斷未超過的期限:
for (;;) {
zx_object_get_info(current_thread, ZX_TASK_RUNTIME_INFO, &start_info, ...)
deadline_task()
if (current_time() > deadline) {
zx_object_get_info(current_thread, ZX_TASK_RUNTIME_INFO, &end_info, ...)
// ...
// report stats from (end_info - start_info)
// ...
}
}
實作
page_fault_time
會計算所有頁面錯誤處理常式所耗費的總時間。在目前的實作項目中,這包括 vmm_page_fault_handler
和 vmm_accessed_fault_handler
。
lock_contention_time
會計算 Mutex::AcquireContendedMutex
和 BrwLock::Block
的總時間。這個方法已可存取目前的 Thread
和 current_ticks()
。實作未涵蓋旋轉鎖定。雖然旋轉鎖定可以終止,但目前我們會忽略旋轉轉動鎖定,因為評估旋轉鎖定的爭用情形可能所費不貲。
為了盡可能降低負擔,我們會將這些時間長度記錄為刻點計數,並在 zx_object_get_info
系統呼叫期間轉譯為 zx_duration_t
。其他實作詳細資料將依 cpu_time
和 queue_time
使用的現有模式生效。原型實作可在 fxrev.dev/469818 取得。
效能
我們會執行 Zircon mutex 基準測試,確認沒有任何迴歸問題。我們會在原始硬體 (x86 和 ARM) 上執行這些基準測試。此外,為驗證虛擬化環境中沒有任何迴歸問題,我們會在 QEMU (x86 和 ARM) 上執行這些基準測試。
回溯相容性
系統會產生 zx_info_task_runtime_t
結構體的版本,類似於其他 zx_info_*
結構體所完成的版本 (例如,請參閱 fxrev.dev/406754)。
安全性考量
ZX_INFO_TASK_RUNTIME
是側邊管道,可洩露已檢查工作的相關資訊。舉例來說,page_fault_time
可能會用來測量任務的記憶體存取模式。為緩解這類外洩情形,ZX_INFO_TASK_RUNTIME
主題已經需要 ZX_RIGHT_INSPECT
。凡是擁有該權利的使用者,皆可假設擁有工作私人資料的存取權。
ZX_INFO_TASK_RUNTIME
也可能會外洩其他工作的間接資訊。舉例來說,如果某項工作知道自己的 page_fault_time
,或許就能推論「其他」工作的記憶體存取模式。同樣地,如果工作知道在爭用核心鎖定上等待了多少時間,可以推斷其他工作使用共用核心資源的方式。未來我們可能會使用低解析度計時器建構 zx_info_task_runtime_t
。這不一定能防止時間攻擊,但可能會限制其有效性。
另一個防禦措施是限制 zx_info_task_runtime_t
對特殊開發人員版本的存取權。不過,這種做法會大幅限制這項功能的實用性,因為在開發環境中通常無法順利重現效能錯誤。我們需要可在正式版本中啟用的解決方案。
如要完全避免這個側邊管道,我們需要將指標報表和指標檢查分為不同的功能。舉例來說,如果我們持續將追蹤記錄記錄到環形緩衝區中,並在完成 TRACE_ALERT
後將該緩衝區上傳至特定的管道或通訊埠,則觸發 TRACE_ALERT
的工作就不需要獲得追蹤記錄的存取權,而這樣會排除側邊管道。如先前所述,這類解決方案的設計及建構需要很長的時間,但我們可以立即處理。
隱私權注意事項
無。
說明文件
Zircon syscall 說明文件必須更新,加入新的 zx_info_task_runtime_t
欄位。
先前的圖片和參考資料
在 Linux 中,最密切相關的先驗技術是 getrusage
,會回報使用者和系統 CPU 時間和頁面錯誤、I/O 作業和結構定義切換次數。Windows 的 GetThreadTimes
會回報使用者和系統 CPU 作業時間。硬體效能計數器 (例如 x86 上的 RDPMC
) 會提供類似的資訊,並有類似的安全疑慮。
測試
我們將透過 audio_core
記錄新執行階段資訊,手動進行測試 (而我們在原型實作上已完成這個步驟,請參閱 fxrev.dev/469819)。
我們會更新 cpu_time
和 queue_time
的現有測試,以測試舊版 zx_info_task_runtime_t
(名為 zx_info_task_runtime_v1_t
)。此外,Zircon 的 abi_type_validator.h 將會更新,以驗證新舊 ABI。確保 ABI 回溯相容性。
要為這項功能新增整合測試並不容易,因為沒有 API 可強制核心對核心造成鎖定爭用或觸發頁面錯誤 (除了程序終止的區隔錯誤之外)。