systrace 是分析 Android 裝置效能的主要工具。但它其實是其他工具的包裝函式。這是 atrace 周圍的主機端包裝函式,也是控制使用者空間追蹤並設定 ftrace 的裝置端可執行檔,以及 Linux 核心中的主要追蹤機制。systrace 會使用 atrace 啟用追蹤功能,然後讀取 ftrace 緩衝區,並將所有內容包裝在自給自足的 HTML 檢視器中。(雖然較新的核心支援 Linux 增強版 Berkeley 封包篩選器 (eBPF),但以下說明文件適用於 3.18 核心 (沒有 eFPF),因為 Pixel/Pixel XL 使用的是這類核心)。
systrace 由 Google Android 和 Google Chrome 團隊擁有,並以開放原始碼形式納入 Catapult 專案。除了 systrace,Catapult 還包含其他實用的公用程式。舉例來說,ftrace 的功能比 systrace 或 atrace 更豐富,可直接啟用,且包含一些對偵錯效能問題至關重要的進階功能。(這些功能需要取得 root 存取權,且通常需要新的核心)。
執行 systrace
在 Pixel/Pixel XL 上偵錯抖動時,請先執行下列指令:
./systrace.py sched freq idle am wm gfx view sync binder_driver irq workq input -b 96000
結合 GPU 和顯示管道活動所需的額外追蹤點,您就能從使用者輸入追蹤到螢幕上顯示的畫面。請將緩衝區大小設為較大,以免遺失事件 (如果沒有大型緩衝區,某些 CPU 在追蹤記錄的某個時間點之後就不會包含任何事件)。
查看 Systrace 時,請注意每個事件都是由 CPU 上的某些項目觸發。
由於 systrace 是建構在 ftrace 之上,而 ftrace 則在 CPU 上執行,因此 CPU 上的某些項目必須寫入 ftrace 緩衝區,以記錄硬體變更。也就是說,如果您想知道為何顯示邊界會變更狀態,可以查看在轉換的確切時間點上 CPU 正在執行的內容 (在日誌中觸發變更的 CPU 執行內容)。這個概念是使用 systrace 分析效能的基礎。
範例:工作影格
本例說明一般 UI 管道的 systrace。如要跟著範例操作,請下載追蹤記錄的 ZIP 檔案 (也包含本節所提及的其他追蹤記錄)、解壓縮檔案,然後在瀏覽器中開啟 systrace_tutorial.html
檔案。請注意,這個 systrace 檔案很大;除非您在日常工作中使用 systrace,否則這個追蹤記錄可能會比單一追蹤記錄中提供更多資訊。
針對 TouchLatency 等一致的週期性工作負載,UI 管道包含下列項目:
- SurfaceFlinger 中的 EventThread 會喚醒應用程式 UI 執行緒,表示現在是轉譯新影格的時機。
- 應用程式會使用 CPU 和 GPU 資源,在 UI 執行緒、RenderThread 和 hwuiTasks 中轉譯影格。這是 UI 使用的大部分容量。
- 應用程式會使用繫結器將算繪影格傳送至 SurfaceFlinger,然後 SurfaceFlinger 會進入休眠狀態。
- SurfaceFlinger 中的第二個 EventThread 會喚醒 SurfaceFlinger,觸發組合和顯示輸出。如果 SurfaceFlinger 判斷沒有工作要執行,就會再次進入休眠狀態。
- SurfaceFlinger 會使用硬體編譯器 (HWC)/硬體編譯器 2 (HWC2) 或 GL 處理合成作業。HWC/HWC2 合成速度較快且耗電量較低,但會受到晶片系統 (SoC) 的限制。這項作業通常需要約 4 到 6 毫秒,但由於 Android 應用程式一律會使用三重緩衝,因此可能會與步驟 2 重疊。(雖然應用程式一律會使用三重緩衝,但 SurfaceFlinger 中可能只有一個待處理的影格,因此看起來就像是雙重緩衝)。
- SurfaceFlinger 會使用供應商驅動程式將最終輸出內容調度至顯示器,然後進入休眠狀態,等待 EventThread 喚醒。
讓我們逐步查看從 15409 毫秒開始的影格:

圖 1 是被一般影格包圍的一般影格,因此是瞭解 UI 管道運作方式的絕佳起點。TouchLatency 的 UI 執行緒列在不同時間點包含不同的顏色。這些長條代表執行緒的不同狀態:
- 灰色。睡覺。
- 藍色。可執行 (可執行,但排程器尚未選取要執行的項目)。
- 綠色。正在執行 (排程器認為正在執行)。
- 紅色。不可中斷的休眠 (通常會在核心的鎖定中休眠)。可能代表 I/O 負載。對偵錯效能問題非常實用。
- 橘色。因 I/O 負載而無法中斷休眠狀態。
如要查看不可中斷休眠狀態的原因 (可透過 sched_blocked_reason
追蹤點查看),請選取紅色的不可中斷休眠狀態片段。
在 EventThread 執行期間,TouchLatency 的 UI 執行緒會變成可執行。如要查看喚醒原因,請按一下藍色部分。

圖 2 顯示 TouchLatency UI 執行緒是由 tid 6843 喚醒,而 tid 6843 對應的是 EventThread。UI 執行緒會喚醒、轉譯影格,並將其排入佇列,供 SurfaceFlinger 使用。

如果追蹤記錄中已啟用 binder_driver
標記,您可以選取繫結器交易,查看該交易中涉及的所有程序相關資訊。

圖 4 顯示,在 15,423.65 毫秒時,SurfaceFlinger 中的 Binder:6832_1 會變成可執行,因為有 tid 9579 (即 TouchLatency 的 RenderThread)。您也可以在繫結器交易的兩側看到 queueBuffer。
在 SurfaceFlinger 端的 queueBuffer 期間,TouchLatency 的待處理影格數量會從 1 變成 2。

圖 5 顯示三重緩衝處理,其中有兩個已完成的框架,而應用程式即將開始轉譯第三個框架。這是因為我們已捨棄部分影格,因此應用程式會保留兩個待處理影格,而不是一個,以避免進一步捨棄影格。
隨後,SurfaceFlinger 的主執行緒會由第二個 EventThread 喚醒,以便輸出較舊的待處理影格至螢幕:

SurfaceFlinger 會先鎖定較舊的待處理緩衝區,導致待處理緩衝區計數從 2 減少為 1。

鎖定緩衝區後,SurfaceFlinger 會設定組合,並將最終影格提交至螢幕。(部分這些部分會在 mdss
追蹤點中啟用,因此可能不會包含在 SoC 中)。

接著,mdss_fb0
會喚醒 CPU 0。mdss_fb0
是顯示管道的核心執行緒,用於將轉譯的影格輸出至螢幕。我們可以看到 mdss_fb0
在追蹤記錄中為自己的一列 (向下捲動即可查看)。

mdss_fb0
會喚醒、短暫執行、進入不可中斷的睡眠狀態,然後再次喚醒。
範例:無法運作的框架
本範例說明用於偵錯 Pixel/Pixel XL 抖動的 systrace。如要跟著範例操作,請下載追蹤記錄的 ZIP 檔案 (包括本節所提及的其他追蹤記錄)、解壓縮檔案,然後在瀏覽器中開啟 systrace_tutorial.html
檔案。
開啟 systrace 後,您會看到類似以下的畫面:

如要查看卡頓情形,請檢查「SurfaceFlinger」下方的「FrameMissed」列。FrameMissed 是 HWC2 提供的品質改善功能,查看其他裝置的 systrace 時,如果裝置未使用 HWC2,則可能不會顯示 FrameMissed 列。無論是哪種情況,FrameMissed 都與 SurfaceFlinger 缺少其中一個非常規律的執行階段,以及應用程式 (com.prefabulated.touchlatency
) 在 vsync 時未變更的待處理緩衝區計數相關。

圖 11 顯示在 15598.29&nbps;ms 時遺漏的畫面。SurfaceFlinger 會在 vsync 間隔時短暫喚醒,然後在未執行任何作業的情況下再次進入休眠狀態,這表示 SurfaceFlinger 判斷不必再嘗試將畫面傳送至螢幕。原因是什麼?
如要瞭解管道如何針對這個影格發生故障,請先查看上述正常運作的影格範例,瞭解 systrace 中顯示的一般 UI 管道。準備就緒後,請返回錯過的畫面,然後向後搜尋。請注意,SurfaceFlinger 會喚醒並立即進入休眠狀態。查看 TouchLatency 的待處理影格數量時,會發現有兩個影格 (這可提供很好的線索,協助您瞭解發生了什麼事)。

由於 SurfaceFlinger 中有影格,因此這不是應用程式問題。此外,SurfaceFlinger 會在正確的時間喚醒,因此並非 SurfaceFlinger 的問題。如果 SurfaceFlinger 和應用程式都正常運作,則可能是驅動程式問題。
由於已啟用 mdss
和 sync
追蹤點,我們可以取得有關柵欄的資訊 (在顯示器驅動程式和 SurfaceFlinger 之間共用),這些柵欄可控制何時將影格提交至顯示器。這些區域會列在 mdss_fb0_retire
下方,代表影格在螢幕上顯示的時間。這些圍欄會提供為 sync
追蹤類別的一部分。哪些柵欄對應至 SurfaceFlinger 中的特定事件,取決於 SOC 和驅動程式堆疊,因此請與 SOC 供應商合作,瞭解追蹤記錄中的柵欄類別意義。

圖 13 顯示的畫格顯示時間為 33 毫秒,而非預期的 16.7 毫秒。在該切片的一半處,該影格應已被新影格取代,但並未取代。查看先前的畫面,看看是否有任何內容。

圖 14 顯示每個影格為 14.482 毫秒。兩個影格破裂的片段為 33.6 毫秒,這與兩個影格大致相符 (我們以 60 Hz 的速度算繪,每個影格為 16.7 毫秒,兩者相近)。但 14.482 毫秒完全不接近 16.7 毫秒,這表示顯示管道有嚴重問題。
請仔細調查圍欄的確切終點,以便判斷控制圍欄的因素為何。

工作佇列包含 __vsync_retire_work_handler
,會在柵欄變更時執行。查看核心來源,您會發現這是顯示器驅動程式的一部分。這似乎是顯示管道的關鍵路徑,因此必須盡可能快速執行。這項工作可在 70 毫秒左右執行 (並非長時間的排程延遲),但它是工作佇列,可能無法準確排程。
檢查先前的影格,判斷是否有影響;有時抖動可能會隨著時間累積,最終導致錯過截止時間。

由於檢視器在選取時會將 kworker 的執行可行行程設為白色,因此無法在 kworker 上看到可執行行,但統計資料會說明問題所在:顯示管道關鍵路徑的部分排程器延遲時間為 2.3 毫秒,這表示情況不佳。在繼續之前,請將顯示管道關鍵路徑的這一部分,從工作佇列 (以 SCHED_OTHER
CFS 執行緒執行) 移至專用的 SCHED_FIFO
kthread,以修正延遲問題。這個函式需要工作佇列無法 (且不應) 提供的時間保證。
這是造成卡頓的原因嗎?很難下定結論。除了容易診斷的情況 (例如核心鎖定爭用導致顯示重要執行緒進入休眠狀態) 之外,追蹤記錄通常不會指出問題。這是否可能是造成影格遺失的原因?當然可以。柵欄時間應為 16.7 毫秒,但在導致影格遺漏的影格中,柵欄時間並未接近這個值。考量到顯示管道緊密的耦合程度,柵欄時間點周圍的抖動可能會導致影格遺漏。
在這個範例中,解決方案包括將 __vsync_retire_work_handler
從工作佇列轉換為專用 kthread。這項調整可明顯改善抖動,並減少彈跳球測試中的 Jank。後續的追蹤記錄顯示,柵欄時間點非常接近 16.7 毫秒。