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]。
爲什麼使用它
對於習慣了使用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只能解析加載較小的文件,目前四大瀏覽器均已支持該技術:
所以當你嘗試加載超大的trace時,就會遇到如下類似的錯誤:
這種情況下就不可以用瀏覽器直接解析了.
超大文件如何解析
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:
點擊"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>
你將得到如下類似的指標分析結果:
Sql分析
Sql分析可以直接在PerfettoUI頁面執行:
通過SQL分析其可以更高效的過濾trace文件,並且可以完成一些通過圖形界面無法很方便完成的統計,
數據庫完整的ER圖如下:
一共55個table,9個view...關係比較複雜,看着比較嚇人而已,實際上我們沒必要全部記下來,因爲常用的就那幾個,並且其結構雖然複雜但是設計的卻很合理,下面先解釋一下常用的各個"名詞",sql表名也是同名的
slice:
簡單講就是你通過: Trace.beginSection/ATRACE_BEGIN記錄的事件
counter:
這個顧名思義...
sched:
CPU調度信息就查詢這張表,但是這張表只包含Running的任務.其他狀態需要自己結合end_state和ts計算或者直接用下面的表.
thread_state:
記錄線程的完整狀態,相當於擴展了的sched表.
★tips:
”
當你查詢slice時發現缺少一些需要的上下文信息,此時請通過track_id去JOIN查詢對應的上下文: 例如你想知道你查詢的slice其屬於哪個線程,此時你就可以通過JOIN thread_track找到線程的utid,然後通過utid JOIN到thread表中找到這條線程的詳細信息.
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 |
工具靈活度反正是很夠了~具體怎麼活用就看各位大佬發動聰明的腦袋瓜了(認真)~
參考資料
Perfetto UI: https://ui.perfetto.dev
本文分享自微信公衆號 - 音視頻開發進階(glumes_blog)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。