全新下一代平台级跟踪工具:Perfetto使用指南
共 14757字,需浏览 30分钟
·
2021-03-10 10:01
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