systrace 是分析 Android 裝置效能的主要工具。然而,它實際上是其他工具的包裝。它是atrace的主機端包裝器、控制用戶空間追蹤並設定ftrace的裝置端可執行檔以及 Linux 核心中的主要追蹤機制。 systrace 使用 atrace 啟用跟踪,然後讀取 ftrace 緩衝區並將其全部包裝在獨立的 HTML 檢視器中。 (雖然較新的核心支援 Linux 增強型伯克利資料包過濾器 (eBPF),但以下文件適用於 3.18 核心(無 eFPF),因為 Pixel/Pixel XL 上使用的是該核心。)
systrace 由 Google Android 和 Google Chrome 團隊所有,並且作為Catapult 專案的一部分開源。除了 systrace 之外,Catapult 還包括其他有用的實用程式。例如,ftrace 具有比 systrace 或 atrace 直接啟用的功能更多的功能,並且包含一些對於偵錯效能問題至關重要的進階功能。 (這些功能需要 root 存取權限,並且通常需要新核心。)
運行系統追蹤
在 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 使用 Hardware Composer (HWC)/Hardware Composer 2 (HWC2) 或 GL 處理合成。 HWC/HWC2 組合速度更快、功耗更低,但有局限性,取決於系統單晶片 (SoC)。這通常需要約 4-6 毫秒,但可能與步驟 2 重疊,因為 Android 應用程式始終是三重緩衝的。 (雖然應用程式始終是三重緩衝的,但 SurfaceFlinger 中可能只有一個待處理幀在等待,這使其看起來與雙緩衝相同。)
- SurfaceFlinger 調度最終輸出以使用供應商驅動程式進行顯示,然後返回睡眠狀態,等待 EventThread 喚醒。
讓我們瀏覽一下從 15409 毫秒開始的幀:

圖 1 是一個被普通框架包圍的普通框架,因此它是了解 UI 管道如何運作的一個很好的起點。 TouchLatency 的 UI 執行緒行在不同時間包含不同的顏色。條形表示線程的不同狀態:
- 灰色的。睡眠。
- 藍色的。可運行(它可以運行,但調度程序尚未選擇它運行)。
- 綠色的。主動運行(調度程序認為它正在運行)。
- 紅色的。不間斷睡眠(通常在內核中的鎖上睡眠)。可指示 I/O 負載。對於調試效能問題非常有用。
- 橘子.由於 I/O 負荷而不間斷的睡眠。
若要查看不間斷睡眠的原因(可從sched_blocked_reason
追蹤點取得),請選擇紅色不間斷睡眠片段。
當 EventThread 執行時,TouchLatency 的 UI 執行緒變得可運作。要查看是什麼喚醒了它,請點擊藍色部分。

圖 2 顯示 TouchLatency UI 執行緒被 tid 6843 喚醒,該執行緒對應於 EventThread。 UI 執行緒喚醒,渲染幀,並將其排入佇列以供 SurfaceFlinger 使用。

如果在追蹤中啟用了binder_driver
標記,您可以選擇binder事務來查看該事務中涉及的所有進程的資訊。

圖 4 顯示,在 15,423.65 毫秒時,SurfaceFlinger 中的 Binder:6832_1 由於 tid 9579(TouchLatency 的 RenderThread)而變得可運作。還可以看到binder事務兩側的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
) 未更改的待處理緩衝區計數相關。

圖 11 顯示了 15598.29ms 處的遺失畫面。 SurfaceFlinger 在 vsync 間隔短暫喚醒,然後返回睡眠狀態而不執行任何工作,這意味著 SurfaceFlinger 確定不值得再次嘗試向顯示器發送幀。為什麼?
若要了解此框架的管道如何分解,請先查看上面的工作框架範例,看看正常的 UI 管道在 systrace 中如何顯示。準備好後,返回到錯過的幀並向後工作。請注意,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 us 左右(調度延遲不是很長),但它是一個工作隊列,可能無法準確調度。
檢查前一幀以確定其是否有貢獻;有時,抖動會隨著時間的推移而累積,最終導致錯過最後期限。

kworker 上的可操作行是不可見的,因為檢視器在選擇它時會將其變成白色,但統計數據說明了問題:顯示管道關鍵路徑的一部分的調度程序延遲為 2.3 毫秒,效果很差。在繼續之前,請將顯示管道關鍵路徑的這一部分從工作佇列(以SCHED_OTHER
CFS 執行緒執行)移至專用SCHED_FIFO
kthread 來修復延遲。此功能需要工作佇列無法(也不打算)提供的時序保證。
這是卡頓的原因嗎?很難下定論。除了易於診斷的情況(例如核心鎖定爭用導致顯示關鍵執行緒休眠)之外,追蹤通常不會指定問題。這種抖動是否是丟幀的原因?絕對地。柵欄時間應為 16.7 毫秒,但在導致丟幀的幀中,它們與該值根本不接近。考慮到顯示管道的耦合程度,柵欄時序周圍的抖動可能會導致丟幀。
在此範例中,解決方案涉及將__vsync_retire_work_handler
從工作佇列轉換為專用 kthread。這導致抖動顯著改善,並減少了彈跳球測試中的卡頓現象。隨後的追蹤顯示柵欄時間非常接近 16.7 毫秒。