全新下一代平臺級跟蹤工具:Perfetto使用指南

System Tracing

"系統跟蹤"就是記錄一段時間內的設備活動。系統跟蹤會生成跟蹤文件,該文件可用於生成系統報告。此報告可幫助開發者瞭解如何最有效地提升應用或遊戲的性能。

在Android平臺上,目前可以通過如下途徑生成系統跟蹤文件:

  • Traceur app: 一款用於將設備活動保存到跟蹤文件的Android工具。在搭載Android 10或更高版本的設備上,跟蹤文件會以Perfetto格式保存(如下所示)。在搭載較低版本Android系統的設備上,跟蹤文件會以Systrace格式保存。
  • Systrace: 平臺提供的舊版命令行工具,可記錄短時間內的設備活動,並保存在壓縮的文本文件中。該工具會生成一份報告,其中彙總了 Android 內核中的數據,例如 CPU 調度程序、磁盤活動和應用線程。

Perfetto是什麼

Perfetto是Android 10中引入的全新下一代平臺級跟蹤工具。

適用於Android、Linux和Chrome的更加通用和複雜的用於性能檢測和跟蹤分析的生產級開源項目。其核心是引入了一種全新的用戶空間到用戶空間的跟蹤協議,該協議可以直接將protobuf序列化到共享內存緩衝區。且既可用於平臺內部的內置數據源(例如ftrace、atrace、logcat),也可通過項目提供的SDK和庫暴露給C++應用程序。

同時該協議允許通過一個可擴展的基於protobuf的數據源配置機制對其進行動態配置。不同的數據源可以複用到用戶定義的緩衝區的不同子集上,也可以將任意長的跟蹤流導出到文件系統中。

它提供了用於記錄系統級和應用級活動的服務和庫、低開銷的native+java內存分析工具,可供SQL分析跟蹤文件的庫,以及一個基於Web用於將追蹤文件可視化方便分析的Perfetto UI[1]

Perfetto
Systrace

爲什麼使用它

對於習慣了使用systrace的開發者,起初使用perfetto可能會不習慣,雖然目前該工具並不能完全替代systrace,但這只是時間問題(目前該工具代碼更新非常頻繁),趨勢即是如此,而且也的確是很好用的.

相比systrace的優勢:

  • 其可記錄任意長度的跟蹤記錄並導出到文件系統中.
  • 更合理的可視化分析標記功能.
  • 內建SQLite數據庫,SQL查詢的支持,數據後期處理非常靈活.
  • 特定操作方便程度碾壓systrace(笑~)
  • 更強的拓展能力,某些特定數據的解析可以通過解析器版本更新支持 比如一開始不支持的syscall,現在支持了.

目前的缺點:

  • 個別操作還是沒systrace方便(比如目前爲止還不能多選點並自動計算時間差)
  • 暫時沒發現了(笑~)

如何使用它

抓取Perfetto跟蹤文件:

  • 你可以像atrace一樣使用它

但我覺得有點脫褲子放屁的嫌疑,基本不會去這樣用.

使用配置文件指導抓取(推薦)

配置文件是最靈活的方式,所有配置項都可按照你的喜好進行配置.

某些android 10的設備上可能要先啓動traced:

adb shell setprop persist.traced.enable 1

首先我們需要生成配置文件,這個操作建議在UI頁面生成,簡單的配置文件如下:

buffers: {
    size_kb: 522240
    fill_policy: DISCARD
}
data_sources: {
    config {
        name: "linux.process_stats"
        target_buffer: 1
        process_stats_config {
            scan_all_processes_on_start: true
        }
    }
}
data_sources: {
    config {
        name: "android.log"
        android_log_config {
            log_ids: LID_DEFAULT
            log_ids: LID_SYSTEM
        }
    }
}
data_sources: {
    config {
        name: "linux.sys_stats"
        sys_stats_config {
            stat_period_ms: 250
            stat_counters: STAT_CPU_TIMES
            stat_counters: STAT_FORK_COUNT
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "sched/sched_switch"
            ftrace_events: "power/suspend_resume"
            ftrace_events: "sched/sched_wakeup"
            ftrace_events: "sched/sched_wakeup_new"
            ftrace_events: "sched/sched_waking"
            ftrace_events: "power/cpu_frequency"
            ftrace_events: "power/cpu_idle"
            ftrace_events: "power/gpu_frequency"
            ftrace_events: "raw_syscalls/sys_enter"
            ftrace_events: "raw_syscalls/sys_exit"
            ftrace_events: "sched/sched_process_exit"
            ftrace_events: "sched/sched_process_free"
            ftrace_events: "task/task_newtask"
            ftrace_events: "task/task_rename"
            ftrace_events: "ftrace/print"
            atrace_categories: "gfx"
            atrace_categories: "input"
            atrace_categories: "view"
            atrace_categories: "wm"
            atrace_categories: "am"
            atrace_categories: "hal"
            atrace_categories: "res"
            atrace_categories: "dalvik"
            atrace_categories: "bionic"
            atrace_categories: "pm"
            atrace_categories: "ss"
            atrace_categories: "database"
            atrace_categories: "aidl"
            atrace_categories: "binder_driver"
            atrace_categories: "binder_lock"
            atrace_apps: "*"
        }
    }
}
duration_ms: 30000

這其中包含:

  • 首先是512M的Buffer配置,當Buffer滿了會自動結束抓取.
  • 數據源配置了gpu、logcat、以及ftrace、atrace.
  • 配置此次抓取最長持續時間爲30s. 接下來執行如下命令:
adb push perfetto.pbtxt /data/local/tmp/perfetto.pbtxt
adb shell 'cat /data/local/tmp/perfetto.pbtxt | perfetto --txt -c - -o /data/misc/perfetto-traces/trace'

#或者抓取長trace時候可以使用(配置文件也要修改爲長trace):
adb shell 'cat /data/local/tmp/perfetto.pbtxt | perfetto --txt -c - -o /data/misc/perfetto-traces/trace  --detach=perf_debug'
#結束抓取:
adb shell 'perfetto --attach=perf_debug --stop'

等待30s後導出/data/misc/perfetto-traces/trace文件即可.

使用Traceur app抓取

這種方式的優點在於使用方便,無需依賴PC只需要界面上點幾下即可完成抓取,但是目前應用並不是那麼完善,相對的靈活性也沒那麼高,無法抓取大部分ftrace event.

1.開啓開發者選項.

2.進入開發者選項頁面.

3.找到"系統跟蹤",然後選擇打開"顯示快捷圖塊"開關.

3.1(可選) 選擇你需要記錄的tag:

4.下拉狀態欄點擊"系統跟蹤"圖塊,然後即可開始抓取.

5.再次點擊接口停止抓取並導出文件.

adb pull /data/local/traces .
  • Perfetto UI抓取

很簡單,這裏就略過了

解析Trace

perfetto抓取的trace文件,只能通過項目內提供的Trace Processor來解析到內置的SQLITE中,針對不同大小的文件解析方法會有差異:

  • 通過官方提供的python api解析

這裏就跳過了,感興趣的建議去官網看下詳細的文檔.

小文件解析

PerfettoUI這個網站實際上通過WebAssembly技術運行了一個trace_processor,只是瀏覽器存在內存限制,所以通過WASM只能解析加載較小的文件,目前四大瀏覽器均已支持該技術:

WASM
PerfettoUI

所以當你嘗試加載超大的trace時,就會遇到如下類似的錯誤:

ERROR

這種情況下就不可以用瀏覽器直接解析了.

超大文件如何解析

WASM的這個內存限制來自於瀏覽器本身,所以加載超大文件的方法也很簡單了,不要使用WASM去運行trace_processor解析文件就好,這種情況下能解析多大的trace文件取決於你機器的內存大小:

官方已經提供了python腳本自動下載當前操作系統對應的trace_processor(目前僅支持Linux、Macos、Windows後續可能會支持,可以先使用WSL)

# Download prebuilts (Linux and Mac only)
curl -LO https://get.perfetto.dev/trace_processor
chmod +x ./trace_processor

# Start the interactive shell
./trace_processor trace.pftrace

# Start a local trace processor instance to replace wasm module in the UI
./trace_processor -D trace.pftrace 

######################################################################################################################################################################################################################################################### 100.0%
[494.172] processor_shell.cc:1130 Trace loaded: 1137.13 MB (29.6 MB/s)
Error stats for this trace:
                                    name                                      idx                                   source                                    value
---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
misplaced_end_event                      [NULL]                                   analysis                                                                    79854
task_state_invalid                       [NULL]                                   analysis                                                                   130057
[494.174] httpd.cc:136            [HTTP] Starting RPC server on 127.0.0.1:9001 and [::1]:9001

此時我們刷新打開perfettoUI會自動檢測到9001端口的RPC server:

檢測到RPC server

點擊"use loaded trace"即可打開9001端口上加載好的trace文件.

tips:如果本地機器內存較小,也可以掛到大內存的工作站上然後把9001端口轉發過去即可.

分析Trace

PerfettoUI

  • 如何查看線程喚醒端:

在systrace上,我們一般是通過查看Runnable狀態中的wakeup from,然後自己滑到對應的線程區域,如果線程之間喚醒關係比較長,那尋找起來未免太繁瑣.

Perfetto的操作方式不同,我們只需要點擊Runnable後的Running狀態,然後點擊下方的跳轉按鈕,:這會自動跳轉到線程調度區域中的對應軌道中,並且下方會顯示當前線程的喚醒端(♦) 以及此次喚醒的調度延遲時間:選中喚醒端線程對應的slice,然後同樣點擊跳轉按鈕:即可跳轉回對應的進程區域的軌道中:

  • 添加標記:

perfetto目前提供兩種標記類型,標記的方式分別爲: 點擊最上方的時間軌道即可添加時間點標記. 而通過按住鼠標左鍵選中一塊區域然後點擊"shift+m"即可添加常駐區域標記:通過選中已經添加的標記,我們可以選擇爲其添加標記名,或者更改其顏色,以及執行移除操作:而如果只是點擊"m"添加的是臨時區域標記,當你再次選中另外一塊區域添加臨時區域時,上一個臨時區域會自動移除.

指標子系統

當我第一眼看到SQL支持的時候我就在想,是否可以直接使用一段語句提取到我們常見場景的關鍵性能指標,幫助我們更方便的提取到我們需要的基本性能信息,果然這一點官方也早就想到了,這就是內置的"指標"系統. 項目內置的指標大概有如下這些:

指標名 說明
heap_profile_callsites 沒用過,暫時未知
android_cpu 統計出每個進程的CPU情況,包括以線程爲粒度的每個核心的佔用時間以及核心頻率情況.
java_heap_stats 沒用過,暫時未知
android_lmk 統計所有LMK事件指標,並會標記在UI中
android_mem 沒用過,暫時未知
android_ion ion內存指標
android_surfaceflinger 統計掉幀指標,並標記到UI中
android_package_list trace中的package列表
android_gpu GPU內存指標
android_mem_unagg 不知道怎麼分類的內存指標,暫時沒什麼用
display_metrics 重複幀性能指標,似乎僅限google自家機器使用
android_powrails 沒用過,暫時未知
trace_metadata 打印trace文件的一些基本信息
android_startup android應用冷啓動或熱啓動性能指標
android_thread_time_in_state 這個有BUG暫時不能使用

除了以上這些,你也可以在系統關鍵點自行打點,並且通過自定義"指標"的方式,設計僅針對自家設備的性能指標衡量文件.

執行指標:PerfettoUI可以執行內置指標

執行指標分析

如果你想執行自己的指標文件,那麼就需要使用如下命令:

./trace_processor --run-metrics <你的指標文件> <trace>

你將得到如下類似的指標分析結果:

output

Sql分析

Sql分析可以直接在PerfettoUI頁面執行:

Query

通過SQL分析其可以更高效的過濾trace文件,並且可以完成一些通過圖形界面無法很方便完成的統計,

數據庫完整的ER圖如下:

ER

一共55個table,9個view...關係比較複雜,看着比較嚇人而已,實際上我們沒必要全部記下來,因爲常用的就那幾個,並且其結構雖然複雜但是設計的卻很合理,下面先解釋一下常用的各個"名詞",sql表名也是同名的

slice:

slice

簡單講就是你通過: Trace.beginSection/ATRACE_BEGIN記錄的事件

counter:

counter

這個顧名思義...

sched:

sched

CPU調度信息就查詢這張表,但是這張表只包含Running的任務.其他狀態需要自己結合end_state和ts計算或者直接用下面的表.

thread_state:

thread_state

記錄線程的完整狀態,相當於擴展了的sched表.

tips:

  1. 當你查詢slice時發現缺少一些需要的上下文信息,此時請通過track_id去JOIN查詢對應的上下文: 例如你想知道你查詢的slice其屬於哪個線程,此時你就可以通過JOIN thread_track找到線程的utid,然後通過utid JOIN到thread表中找到這條線程的詳細信息.

  2. utid 對應 thread表,upid 對應 process表.

簡單案例

例如我們想查看某應用在bindApplication階段的核心分配情況,SQL只需要一句即可:

SELECT 
slice.name,TOTAL(sta.dur)/1e6 as cpu_dur,cpu from slice 
JOIN thread_track ON slice.track_id=thread_track.id
JOIN thread USING(utid)
JOIN thread_state sta ON (sta.utid=thread_track.utid AND (sta.ts >= slice.ts AND sta.ts+sta.dur <= slice.ts+slice.dur ))
WHERE slice.name="bindApplication" AND thread.name LIKE "%news" AND sta.state = "Running"
GROUP BY cpu
ORDER BY cpu_dur DESC

然後你就可以得到如下的輸出:

name cpu_dur cpu
bindApplication 40.128226 7
bindApplication 38.136771 5
bindApplication 28.789739 6
bindApplication 27.338964 4
bindApplication 2.463072 0
bindApplication 1.531979 2
bindApplication 0.612812 1
bindApplication 0.402761 3

工具靈活度反正是很夠了~具體怎麼活用就看各位大佬發動聰明的腦袋瓜了(認真)~

參考資料

[1]

Perfetto UI: https://ui.perfetto.dev


本文分享自微信公衆號 - 音視頻開發進階(glumes_blog)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章