上篇文章中簡單介紹了 systrace 的使用,以及如何簡單地分析 systrace 生成的 trace.html 文件了,但是如何更深刻地理解 trace.html 文件呢?
一. 概述
在上篇文章中已經介紹過了,systrace 本質上是對其他工具的封裝,包括 PC 端的 atrace 和設備端的 ftrace,ftrace 是 Linux 內核中的主要跟蹤機制。systrace 使用 atrace 開啟追蹤,然后讀取 ftrace 的緩存,并且把它重新轉換成HTML格式。
有一個概念是使用 systrace 分析性能的基礎概念,十分重要,需要大家理解:因為 systrace 是基于 ftrace 的,ftrace 是運行在 CPU 之上的,ftrace 緩沖區是用于記錄硬件變化情況的,所以 CPU 上的變化情況也會寫入 ftrace 緩沖區中。這也就意味著,如果你想知道顯示柵欄發生變化的原因,則可以查看對應時間點上 CPU 上運行了哪些線程,發生了哪些活動,這些線程的活動就是顯示柵欄發生變化的原因。
二. 示例:工作幀
這個示例介紹了一個正常 UI 管道(UI pipeline)的 systrace 工作流程,請先下載好 trace 文件,點這里下載。
對于一個持續的、周期性的工作負載 workload(比如:TouchLatency app),它的 UI 管道包括以下幾步
SurfaceFlinger 中的 EventThread 喚醒 app 的 UI Thread,表明是時候渲染新幀了
App 進程通過使用 CPU、GPU 資源,在 UI Thread、RenderThread 和 hwuiTask 中完成了一幀的渲染,然后通過 binder 將渲染好的幀發送到 SurfaceFlinger 中,將該幀壓入到幀緩沖隊列 queueBuffer 中。App 中的線程在執行完自己的工作以后便返回到休眠狀態。UI 渲染的大部分工作都在這一步中
-
SurfaceFlinger 中的第二個 EventThread 將喚醒 SurfaceFlinger 觸發構圖,并將最終幀提交到顯示輸出部分
SurfaceFlinger 通過 HWC/HWC2 或 GL 處理構圖,HW/HW2 處理構圖的速度更快且功率更低,但是存在一些取決于 SOC 的限制
這一步通常需要約 4~6ms 的時間,但是這一步可以和第二步同時進行,因為在 Android 應用始終會進行三重緩沖(雖然 Android 應用中始終是三重緩沖的,但是在 SurfaceFlinger 中可能只存在一個待處理幀,所以看起來和雙重緩沖很像)
如果 SurfaceFlinger 確定沒有任何任務需要執行,則會返回休眠狀態
SurfaceFlinger 通過供應商驅動程序將最終輸出部分調度到顯示部分,然后返回休眠狀態,等待下一次 EventThread 的喚醒
下面將結合 systrace_tutorial.html 文件具體分析一下上面幾個步驟
2.1 EventThread 喚醒 App 的 UI Thread
如下圖所示,在 15409.744 ms 處,可以看到在 Kernel CPU0 上運行了一個 EventThread 線程,從底部的描述信息可知 tid:6843,與此同時,在 com.prefabulated.touchlatency 應用中的 UI Thread 有一小段藍色部分。
在 systrace 中線程是具有顏色的,不同的顏色代表此時線程處于不同的狀態,各個顏色及狀態說明如下:
- 灰色:Sleeping,處于休眠狀態
- 藍色:Runnable,線程可以運行,但是調度器 scheduler 尚未調度讓它運行
- 綠色:Actively running,調度器 scheduler 認為它目前正處于運行狀態
- 紅色:Uninterruptible sleep,不可中斷的休眠狀態,通常處于內核中休眠鎖定狀態(generally sleeping on a lock in the kernel),一般是正在進行 I/O 操作,紅色狀態時通常對于性能調試非常有用
- 橙色:Uninterruptible sleep due to I/O load,由于 I/O load 而不可中斷的休眠狀態
如下圖所示,如果選中 com.prefabulated.touchlatency 應用中 UI Thread 最開始的一小段藍色狀態,可以看到此時 UI Thread State 是 Runnable 的(可運行狀態,但是未被 調度器 scheduler 調度到 CPU 上執行),在下面的描述信息中可以看到 "wakeup from tid:6843",正是由上圖中所示的 EventThread 喚醒的
從上面兩幅圖可以看出,在 EventThread 執行時 com.prefabulated.touchlatency 應用中的 UI Thread 變成了 Runnable 的狀態了,正是第一步 SurfaceFlinger 中的 EventThread 喚醒 app 應用中的 UI Thread,使其處于 Runnable 的狀態,表明是時候開始渲染新幀了。
2.2 App 進程開始渲染新的一幀并通過 Binder 發送數據到 SurfaceFlinger 中
第二步主要是在應用進程中,通過 CPU 和 GPU 資源,在 UI Thread、RenderThread 和 hwuiTask1 中渲染幀,最后通過 binder 將渲染幀數據發送到 SurfaceFlinger 中。
如下圖所示,com.prefabulated.touchlatency 進程中 UI Thread、RenderThread 和 hwuiTask1 中渲染幀,最后調用了 binder,這一步是幀渲染中執行時間很長的一步
我們選中上圖小紅框中的 binder_transaction,可以在下面的描述信息中看到如下所示的信息,Destination Process 6832
Calling PID 9579
、Calling tgid 9564
,表明此時 RenderThread 正在和 ProcessId 是 6832 進程進行 binder 通信,如下圖所示
我們看一下 SurfaceFlinger 進程中,此時正在運行的 Binder:6832_1,如下圖所示
我們選中 Binder:6832_1 中的 binder replay,可以在底部看到 Destination Process: 9564,Destination Thread:9579,Calling PID:6836,Calling tgid:6832,顧名思義,binder replay 正在響應剛才 RenderThread 的發出的 binder_transaction,Destination Process 是 9564,也正是 com.prefabulated.touchlatency 的 processId
在 SurfaceFlinger 進程中,通過 binder 接收到的 frame 幀數據被壓入到 queueBuffer 中,從下圖中可以看到,在 binder_reply 執行前后 com.prefabulated.touchlatency 中緩沖的幀由 1 個變成了 2 個。
下圖表示了三重緩沖,在緩沖隊列中存在兩個已緩沖的幀,應用程序將很快開始渲染第三幀
在 com.prefabulated.touchlatency 進程通過 CPU 和 GPU 資源在 UI Thread、RenderThread 和 hwuiTask1 中計算好渲染幀以后,通過 binder 將渲染幀發送到 SurfaceFlinger 進程中,然后就返回了休眠狀態,如下圖所示的三個紅色的框,表示的是灰色的,就是指此時線程正在休眠狀態,直到下一時刻被喚醒,重新渲染下一幀
2.3 SurfaceFlinger 鎖定緩沖區中的幀觸發構圖并提交最終幀到顯示輸出
接下來將開始第三步的工作,SurfaceFlinger 被第二個 EventThread 喚醒,并從緩沖區中鎖定較早的一幀,觸發構圖,并將最終幀提交到顯示輸出部分。
如下圖所示,在 15430.345 ms 處執行第二個 EventThread,喚醒 SurfaceFlinger,使其處于可運行的狀態(Runnable,藍色的),從底部的描述信息中可以看到其 tid:6845
我們再選中 SurfaceFlinger 中可運行的部分(Runnable,藍色的),如下圖所示,可以看到描述信息 "wakeup from tid: 6845",說明 SurfaceFlinger 此時確實是被上圖中所示的 EventThread 喚醒的
之后 SurfaceFlinger 便鎖定緩沖區中較早的一幀并觸發構圖,然后將最終幀提交給顯示輸出部分。
如下圖所示,在 15413.169 ms 處,通過 acquireBuffer 方法鎖定緩沖區中較早的一幀,queueBuffer 中緩沖幀的數量也由兩個,變成了一個
如下圖所示,在鎖定緩沖區中較早的一幀以后,便通過 doComposition 方法觸發了構圖,并且在 15433.787 ms 處通過 ATOMIC_COMMIT 操作將最終幀提交到顯示輸出
2.4 驅動程序將最終輸出部分調度到顯示部分
緊接著 mdss_fb0
線程被喚醒,如下圖所示,mdss_fb0 線程是顯示管道的內核線程,用于將渲染過的幀輸出到顯示部分
如下圖所示,可以看到 mdss_fb0 的執行情況,mdss_fb0 的具體執行信息需要查看相關的驅動程序文檔了。
三. 總結
上述內容是自己對 Understanding Systrace 部分內容的翻譯(其實已經有官方的翻譯 了解 Systrace,但是我覺得翻譯的并不好,層次感也不夠強 ),經過自己消化理解輸出的,也算是自己的學習。
通過本文有兩點收獲
- 分析 systrace 生成的 trace.html 更加得心應手,比如對一些快捷鍵的使用更熟練、更深刻的理解了底部的文字描述信息
- 對 Android 中每一幀 Frame 的渲染流程更加熟悉,對幀 Frame 的渲染流程有了直觀 & 完整的理解
希望本文也可以幫助到更多的朋友。