Perfetto使用教程
Perfetto 是谷歌开发的用于性能检测和跟踪分析的生产级开源堆栈。它提供用于记录系统级和应用程序级跟踪的服务和库,包括本机 + Java堆分析,使用SQL分析跟踪数据的C++库以及基于Web的可视化UI,能够清晰地展示GB级别体量的跟踪数据。Perfetto 适用于Linux内核和Android的整个系统范围内的性能数据收集、可视化及数据分析。
开启设备追踪
Android 11 (R)及以上默认开启Perfetto,Android 9 (P)和10 (Q) 需要手动开启,其他设备暂不支持:
# Needed only on Android 9 (P) and 10 (Q) on non-Pixel phones.
adb shell setprop persist.traced.enable 1
启动抓取方式
通过脚本工具tools/record_android_trace
,使用命令行工具启动设备抓取
adb shell perfetto [ --time TIMESPEC ] [ --buffer SIZE ] [ --size SIZE ]
[ ATRACE_CAT | FTRACE_GROUP/FTRACE_NAME]...
通过抓取页面Perfetto UI
Perfetto UI抓取简介
Recording settings
三种录制设置,适用不同的使用场景
Stop when full
- in-memory buffer size
设置存储trace的最大buffer大小,存储在内存中,不会影响到IO。 -
Max duration
设置此次 trace 抓取的最大时长
该模式下Perfetto的停止受buffer大小和trace时长的控制,满足两个任意条件之一,即可停止trace。
优点:trace不会因为overwrite而导致丢失。
缺点:如果trace太多,会导致提前结束,无法录制到出现问题时候的trace。
Ring buffer
该模式只会收到Max duration的影响,时间到了就停止抓取trace,但是trace会有被overwrite的风险。
Long trace
用于长时间地抓取trace,但是由于需要定时将buffer中的trace写到文件里面去,会有IO的影响。
Flush on disk every
间隔多长时间将buffer中的trace写入到文件,这个数值不能太大也不能太小,太大容易丢trace,太小容易出现IO问题。trace的停止主要受控于Max duration,或者手动停止。
CPU
CPU相关的常见功能统计
- Coarse CPU usage counter
粗粒度的CPU相关使用统计 -
Scheduling details
详细查看CPU运行的每个task
-
CPU frequency and idle states
查看每颗CPU的运行频率和是否空闲
- Syscalls
记录每一个系统调用,对性能影响较大,需要userdebug版本。
GPU
记录GPU的主频和内存使用
-
GPU frequency
记录GPU的频率
-
GPU memory
GPU内存使用量及各个进程的占用,仅支持Android 12+
Power
Memory
Android apps & svcs
- Atrace userspace annotations
开启这个选项之后,选择合适的atrace tag就可以开启对应的trace了,这个tag就对应了System Tracing的界面的catergray。 -
Event log (logcat)
可以实时记录log,然后将log和trace信息一一对应。 选择合适的log类型,就可以记录相应的log。
Chrome
如果需要分析webview相关的性能问题,可以开启该选项的相关功能
Stack Samples
- Callstack sampling
定期记录进程的当前函数调用堆栈
Advanced settings
目前只有一个开启ftrace功能,用于分析内核性能问题,可以选择相应的tag进行记录
Recording command
配置汇总和执行,完成上述参数配置后,可以在改选项中获得所有的配置参数,拷贝命令,直接adb开启录制追踪。
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace \
<<EOF
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
buffers: {
size_kb: 2048
fill_policy: DISCARD
}
data_sources: {
config {
name: "android.packages_list"
target_buffer: 1
}
}
data_sources: {
config {
name: "android.log"
android_log_config {
log_ids: LID_KERNEL
log_ids: LID_DEFAULT
log_ids: LID_RADIO
log_ids: LID_SECURITY
log_ids: LID_STATS
log_ids: LID_SYSTEM
}
}
}
data_sources: {
config {
name: "linux.perf"
perf_event_config {
timebase {
frequency: 100
timestamp_clock: PERF_CLOCK_BOOTTIME
}
callstack_sampling {
}
}
}
}
data_sources: {
config {
name: "org.chromium.trace_event"
chrome_config {
trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
privacy_filtering_enabled: true
client_priority: USER_INITIATED
}
}
}
data_sources: {
config {
name: "track_event"
chrome_config {
trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
privacy_filtering_enabled: true
client_priority: USER_INITIATED
}
track_event_config {
disabled_categories: "*"
enabled_categories: "accessibility"
enabled_categories: "audio"
enabled_categories: "aogh"
enabled_categories: "android_webview.timeline"
enabled_categories: "android_webview"
enabled_categories: "disabled-by-default-audio-worklet"
enabled_categories: "disabled-by-default-animation-worklet"
enabled_categories: "disabled-by-default-blink.debug"
enabled_categories: "log"
enabled_categories: "toplevel"
enabled_categories: "toplevel.flow"
enabled_categories: "scheduler"
enabled_categories: "sequence_manager"
enabled_categories: "disabled-by-default-toplevel.flow"
enabled_categories: "disabled-by-default-ipc.flow"
enabled_categories: "mojom"
enabled_categories: "v8"
enabled_categories: "blink"
enabled_categories: "cc"
enabled_categories: "gpu"
enabled_categories: "viz"
enabled_categories: "ui"
enabled_categories: "views"
enabled_categories: "benchmark"
enabled_categories: "evdev"
enabled_categories: "input"
enabled_categories: "loading"
enabled_categories: "net"
enabled_categories: "netlog"
enabled_categories: "navigation"
enabled_categories: "browser"
enabled_categories: "__metadata"
timestamp_unit_multiplier: 1000
filter_debug_annotations: true
enable_thread_time_sampling: true
filter_dynamic_event_names: true
}
}
}
data_sources: {
config {
name: "org.chromium.trace_metadata"
chrome_config {
trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
privacy_filtering_enabled: true
client_priority: USER_INITIATED
}
}
}
data_sources: {
config {
name: "android.heapprofd"
target_buffer: 0
heapprofd_config {
sampling_interval_bytes: 4096
continuous_dump_config {
dump_phase_ms: 30000
dump_interval_ms: 10000
}
shmem_size_bytes: 8388608
block_client: true
}
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "clk/*"
ftrace_events: "ext4/*"
ftrace_events: "f2fs/*"
ftrace_events: "fastrpc/*"
ftrace_events: "i2c/*"
ftrace_events: "irq/*"
ftrace_events: "kmem/*"
ftrace_events: "memory_bus/*"
ftrace_events: "mmc/*"
ftrace_events: "oom/*"
ftrace_events: "power/*"
ftrace_events: "regulator/*"
ftrace_events: "sched/*"
ftrace_events: "sync/*"
ftrace_events: "task/*"
ftrace_events: "ftrace/print"
atrace_categories: "am"
atrace_categories: "adb"
atrace_categories: "aidl"
atrace_categories: "dalvik"
atrace_categories: "audio"
atrace_categories: "binder_lock"
atrace_categories: "binder_driver"
atrace_categories: "bionic"
atrace_categories: "camera"
atrace_categories: "database"
atrace_categories: "gfx"
atrace_categories: "hal"
atrace_categories: "input"
atrace_categories: "network"
atrace_categories: "nnapi"
atrace_categories: "pm"
atrace_categories: "power"
atrace_categories: "rs"
atrace_categories: "res"
atrace_categories: "rro"
atrace_categories: "sm"
atrace_categories: "ss"
atrace_categories: "vibrator"
atrace_categories: "video"
atrace_categories: "view"
atrace_categories: "webview"
atrace_apps: "*"
buffer_size_kb: 512
drain_period_ms: 100
}
}
}
duration_ms: 40000
EOF
trace 文件获取
使用 adb pull /data/misc/perfetto-traces/trace ~/trace.perfetto-trace
提取跟踪日志文件并在 Perfetto UI 中打开它。
trace文件过大加载问题
当trace文件大于1G时,Open trace file会出现oops 内存溢出无法访问。
可以使用Trace Processor来加载trace文件
# 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.perfetto-trace
# Start a local trace processor instance to replace wasm module in the UI
./trace_processor trace.perfetto-trace --httpd
执行./trace_processor trace.perfetto-trace --httpd
后,Chrome浏览器打开 https://ui.perfetto.dev/#!/ ,会自动检测本地是否已经有trace_processor生成的HTTP SERVER(9001端口),如下图提示,请选择“YES, use loaded trace”,将自动解析 trace_processor已经加载的pftrace文件。
分析方法简介
slice 片段
对应代码中 Trace.beginSection/ATRACE_BEGIN 记录的事件,选中后会显示黑色边框
counter
计数器,记录离散的数值点,对应代码中 Trace.beginCounter/ATRACE_INT 记录的事件
CPU调度和频率
线程状态
点击片段上方线程调度信息片段(Running),可以看到线程当前运行在哪个CPU上
点击箭头 ,可以在CPU调度中看到该运行片段以及调度时延信息。
被P: /system/bin/traced_probes [1488]T: traced_probes [1488]线程唤醒,从就绪到运行延迟了 774us 792ns,再次点击箭头,可以回到原片段,这个跳转比较灵活方便。
锁竞争(lock contention)
在lock contention 片段上,可以点击上边的monitor contention来查看当前对象锁竞争发生的调用栈,如下详情中显示当前对象锁被Owner (Binder:1754_16)持有,其持锁当前运行在serviceDoneExecuting (AMS.java 16426行),且当前等待该对象锁的线程已经有5个了;当前线程执行被阻塞在getUidState方法中(AMS.java 6614行)。
SQL查询
Perfetto支持通过sql语句查询数据,提供常见的格式和关键字段信息:
-
slice表,横向track上的一条条小片段
-
ts:片段起始时间戳(单位ns)
-
dur:片段持续时长(ns)
-
track_id属于哪个track(水平timeline)
-
name: 片段标注的名称,对应Trace中打印的方法名、标记等信息
-
thread_track表,utid标识线程tid,并不是真实的线程tid
-
thread表,表示各线程信息,其中utid和thread_track表的utid关联
-
process表,upid和thread表的upid关联,表示线程所属的父进程
-
sched_slice,线程调度片段
-
thread_state,各track上边的线程调度片段,标识线程运行状态
Slices
横向轴上的一小段时间片段,具有指定的名称
> SELECT ts, dur, name FROM slice
ts dur name
-------------------- -------------------- ---------------------------
261187017446933 358594 eglSwapBuffersWithDamageKHR
261187017518340 357 onMessageReceived
261187020825163 9948 queueBuffer
261187021345235 642 bufferLoad
261187121345235 153 query
...
Counters
根据时间记录的离散数值点
···
SELECT ts, value FROM counter
ts value
261187012149954 1454.000000
261187012399172 4232.000000
261187012447402 14304.000000
261187012535839 15490.000000
261187012590890 17490.000000
261187012590890 16590.000000
...
···
Scheduler slices
CPU运行的详细task
> SELECT ts, dur, cpu, utid FROM sched
ts dur cpu utid
-------------------- -------------------- -------------------- --------------------
261187012170489 267188 0 390
261187012170995 247153 1 767
261187012418183 12812 2 2790
261187012421099 220000 6 683
261187012430995 72396 7 2791
...