本文字数:8409字
预计阅读时间:22分钟
什么是Perfetto
?Perfetto
是Android 10
中引入的全新平台级跟踪工具。这是适用于Android
、Linux
和Chrome
的更加通用和复杂的开源跟踪项目。与Systrace
不同,它提供数据源超集,可让您以protobuf
编码的二进制流形式记录任意长度的跟踪记录。您可以在Perfetto
界面中打开这些跟踪记录。Perfetto
是一款比较强大的安卓性能分析工具(它还可以用于分析其他系统),其功能涵盖了对CPU
的追踪、电池耗电追踪、系统调用的追踪,内存分配与释放的追踪。除了性能数据记录还,它还有自带的分析工具,分析工具包括:通过自定义追踪功能来拼接命令行,用SQL
方式筛选数据,将数据转换成其他格式方便自定义数据处理,Web
形式的火焰图(Web
可离线),以及Web
上的ADB
功能(有bug
)。
Perfetto
的优缺点是什么?
优点:
1.功能强大;
2.效率高;
3.可靠性强。
缺点:
1.只针对Android 9
及以上机型;
2.学习门槛略高,至少需要知道些许ADB
、Python
、LLM
、pprof
、Perfetto
命令行的知识点,才能熟练运用该工具;
3.功能复杂且已嵌入安卓系统中修改难度大;
4.无法过滤so
,只能全部分析完了再筛选;
5. 暂时只能在Mac
上操作,否则无法解析函数堆栈信息(需要自己编译一个windows
的trace-to-text
程序);
6. 无对比功能。
Perfetto
的内存分析原理是什么?
Perfetto
:
源码:git clone
https://android.googlesource.com/platform/external/perfetto/
Perfetto
也是使用注入(hook
)的方式,将内存分配和释放的几个函数修改为自己的代码指令后,当内存分配和释放时进行统计。与LoliPerfiler
不同的是,它有得天独厚的优势,就是它本身就是安卓系统自带的程序,不需要用JDWP
方式启动某个程序,直接启动执行程序就可以了。
Perfetto-
系统分析,应用程序跟踪和跟踪分析。Perfetto
是用于性能检测和跟踪分析的生产级开源堆栈。它提供用于记录系统级和应用程序级跟踪的服务和库,本机+Java
堆分析,使用SQL
分析跟踪的库以及基于Web
的UI
以可视化和探索多GB
跟踪。
Linux
内核跟踪:捕获高频ftrace
数据:调度活动,任务切换延迟,CPU
频率等等。
用户空间分析器和额外的探针:本机堆分析,Java
堆分析,/ proc
状态文件的轮询器。
内置于Android
:自Android 9 Pie
以来该平台的一部分,也可在Linux
上运行。
raceconv
工具将Perfetto
跟踪转换为其他跟踪格式。
高效的跟踪点仪器:以高吞吐量,低开销的跟踪点记录C++
应用程序的活动。
结构化和可配置的事件:定义自定义protobuf
消息以表示强类型的应用程序特定信息,仅跟踪您需要的信息。
与系统范围的跟踪集成:在同一时间线上将应用程序的状态与系统范围内的分析数据相关联。
heapprofd
:Android
堆分析器。
此CI
用于AOSP
的TreeHugger
的顶部(而不是替代)。它提供早期测试信号,并涵盖TreeHugger
不支持的其他操作系统和较旧的Android
设备。
有四个主要部分:
前端:AppEngine
控制器:AppEngine BG
服务
工作者:Compute Engine + Docker
数据库:Firebase
实时数据库
它们通过Firebase
数据库耦合。DB
是整个CI
的真理之源。
交互式痕迹探索:使用Perfetto UI
记录,查看和处理跟踪数据。
支持流行的跟踪格式文件:TraceEvent JSON
,Android systrace
,ftrace
文本输出。
完全在您的浏览器中运行:不涉及服务器交互,即使您离线也可以工作。
痕量分析-跟踪处理器是一个C++
库(/ src / trace_processor
),它吸收以多种格式编码的跟踪,并公开一个SQL
接口,用于查询一致的表集中所包含的跟踪事件。它还具有其他功能,包括计算摘要度量,使用用户友好的描述注释跟踪以及从跟踪的内容派生新事件。
基于SQL
的跟踪模型:跟踪处理器提取跟踪并公开基于SQLite
的接口以通过外壳和UI
访问跟踪的内容。
大痕量分析:支持高达数十GB
的跟踪。
可互操作的:可以导入和导出流行的跟踪格式:Chromium JSON
跟踪格式,Android Systrace
,ftrace
,CSV
。
官方说明,CPU
分析:
https://perfetto.dev/docs/quickstart/android-tracing#perfetto-cmdline
官方说明,c++
内存分析:
https://perfetto.dev/docs/data-sources/native-heap-profiler
使用web
界面:
https://ui.perfetto.dev
分析命令行说明:
https://perfetto.dev/docs/reference/perfetto-cli
堆内存分析说明:
https://perfetto.dev/docs/reference/heap_profile-cli
使用系统工具获取跟踪记录
1)在Android 10
的手机上,开发者模式新增加了一个“系统跟踪”的功能,我们首先将开发者模式下的“系统跟踪”打开:
2)从“类别”选项中选择我们关注的信息类别:
3)设置完之后,下拉快捷选项多了个棒棒糖形状的图标:
这个时候杀掉我们需要调试的应用,然后点击开启棒棒糖,接着打开应用,等待应用完全打开之后,再点击一次棒棒糖,结束录制。
4)我们保存录制后生成的跟踪文件存储在设备的“/data/local/traces
”目录下,文件的后缀名为“.perfetto-trace
”,我们就可以在网站界面(https://ui.perfetto.dev)中打开这些跟踪记录,进行性能分析了。
使用Perfetto
网站获取perfetto-trace
文件
1)打开https://ui.perfetto.dev;
2)点击record new trace
;
3)在web
站点上选指令,可选择更详细的指令,包括cpu
综合数据、gpu
综合数据、函数调用堆栈、内存memory
分配情况;
4)开始录入,点击start recording
,网站自动生成脚本代码,默认是10秒dump
一次(或一次性导出);
5)点击stop
结束录制,在/data/misc
目录下,即可看到生成的.perfetto-trace
文件。
使用脚本语言
Android Q
及P
先设置:
adb shell setprop persist.traced.enable 1
Android Q
以上perfetto
默认运行,无需上述步骤。
常规选项:
下表列出了在perfetto
的两种模式下都可使用的选项:
两种方式,两种方式不能混用:
简单模式:
使用类似与systrace
。如果需要设定时间加-t
,默认跟踪10s。-o
输出文件的位置,使用/data/misc/perfetto-traces/
,否则perfetto
没有权限访问, 然后pull
出文件,在Perfetto UI
中打开使用。如下命令可直接使用,通常这种信息对大多数情况都够用了。
生成.perfetto-trace
文件:
adb shell perfetto gfx input view wm am ss sched freq idle binder_driver res rs -o /data/misc/perfetto-traces/trace -t 10s
导出.perfetto-trace
文件:
adb pull /data/misc/perfetto-traces/trace ~/trace
普通模式:
借助config
文件配置要调试的内容,命令行如下,其中config.pbtx为config file
,可以借助Perfetto UI
的recording setting
(Perfetto UI
)生成,或者在Perfetto UI
中的redording settings
中设置后好,在recording command
中copy
所有内容,在terminal
中直接copy
执行即可。
cat config.pbtx | adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/trace.perfetto-trace
官方网址:
https://ui.perfetto.dev/#!/
打开上述网址,点击Open trace file
,选择本地录制好的perfetto trace
或ftrace
等文件,即可以Timeline
方式展现各进程、线程的详细跟踪信息。
当trace
文件大于1G时,Open trace file
会出现内存溢出无法访问。
此时需要使用trace_processor
来辅助,该程序建议在Linux
环境下运行,Win10
系统可安装WSL
(Ubuntu20.04
),参考附录安装WSL
。
# 下载官方trace_processor
:
curl -LO https://get.perfetto.dev/trace_processor
chmod +x ./trace_processor
运行如下命令来加载perfetto trace
文件:
./trace_processor --full-sort -D xxx.pftrace
Windows
下也可以运行如下命令(不稳定,内存耗用大):
python3 trace_processor --full-sort -D xxx.pftrace
Chrome
浏览器打开https://ui.perfetto.dev/#!/
,会自动检测本地是否已经有trace_processor
生成的HTTP SERVER
(9001
端口),如下图提示,请选择:
“YES, use loaded trace
”,将自动解析trace_processor
已经加载的pftrace
文件。
a. 图例指标
slice
(片段,选中片段后会显示黑色边框),对应代码中Trace.beginSection/ATRACE_BEGIN
记录的事件。
counter
(计数器,离散的数值点) 代码中Trace.traceCounter/ATRACE_INT
记录的事件。
sched/freq
(CPU
调度、频率)
thread_state
(线程状态)
点击片段上方线程调度信息片段(Running
),可以看到线程当前运行在哪个CPU
上。
点击向右箭头图标,可以在CPU
调度中看到该运行片段,可以看到调度时延信息。被P(Process):system_server的 T(Thread):Binder_1754_18
线程唤醒,从就绪到运行延迟了363us
,再次点击,可以回到原片段,这个跳转比systrace
更加灵活方便。同样的,Binder
调用也可以如此在目标和原调用线程跳转来分析查看。
b. 添加标记
点击最上方的时间轨道即可添加时间点标记;通过按住鼠标左键选中一块区域或者点击某一片段,然后按下“shift+m
”即可添加常驻区域标记。选中已经添加的标记,底部出现的Current Selection TAB
里可以为其添加标记名,更改其颜色,以及执行移除操作。
按下“m
”添加的是临时区域标记,再次选中另外一块区域添加临时区域时,上一个临时区域会自动移除。
c. 锁竞争(lock contention
)
看到lock contention
片段,可以点击上边的monitor contention
来查看当前对象锁竞争发生的调用栈,如下详情中显示当前对象锁被Owner
(Binder:1754_16
)持有,其持锁当前运行在serviceDoneExecuting
(AMS.java 16426
行),且当前等待该对象锁的线程已经有2个了;当前线程执行被阻塞在getUidState
方法中(AMS.java 6614
行)。
在已经加载trace
的perfetto UI
界面,Search
框中输入 : ,
即可开启SQL
输入,我们就可以使用SQL
来查询并定位具体的trace
片段(slice
)。
输入SQL
语句,Enter
,得到查询结果,显示在底部表格中,点击表格中的每一行,可以跳转到具体的slice
中,根据trace
上下文可以进一步分析问题。
列举几个常用的SQL
查询:
1 |
列出所有doFrame片段,按耗时倒序排列,取前100条 |
select slice_id,track_id,ts,dur,dur/1e6,name from slice WHERE name like '%doFrame%' order by dur desc limit 100 |
2 |
1查询的基础上,指定process name为systemui,即systemui自身的绘帧信息 |
select slice_id,track_id,ts,dur,dur/1e6,slice.name from slice JOIN thread_track ON slice.track_id = thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name = 'com.android.systemui' and slice.name like '%doFrame%' order by dur desc limit 100 |
3 |
system_server中各OPF:关键字片段的耗时信息,包含各片段的真实running_time(每个slice可能有一段时间running,一段时间sleep,一段时间runnable,需要借助thread_state表来查询slice中各调度时间片的状态) |
select slice_id,track_id,thread.utid,slice.ts,slice.dur,(slice.dur/1e6) as dur_ms, (select total(case when thread_state.ts < slice.ts then MIN(slice.ts+slice.dur,thread_state.ts+thread_state.dur)-slice.ts when (thread_state.ts+thread_state.dur) > (slice.ts+slice.dur) then (slice.ts+slice.dur-MAX(thread_state.ts,slice.ts)) else thread_state.dur end) from thread_state where thread_state.utid=thread.utid and thread_state.state='Running' and thread_state.ts < (slice.ts+slice.dur) and (thread_state.ts+thread_state.dur) > slice.ts)/1e6 as total_running,slice.name from slice JOIN thread_track ON slice.track_id = thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name='system_server' and slice.name like 'OPF:%' order by slice.dur desc limit 400 |
4 |
system_server中的锁竞争情况(lockcontention),lock_depth表示当时参与此对象锁竞争的线程个数 |
select count(1) as lock_depth, s.slice_id,s.track_id,s.ts,s.dur,s.dur/1e6 as dur_ms,ctn.otid,s.name from slice s, (select slice_id,track_id,ts,dur,name,substr(name, 46, instr(name,')')-46) as otid from slice t WHERE name like 'Lock contention on a monitor lock %' order by dur) ctn JOIN thread_track ON s.track_id=thread_track.id JOIN thread USING(utid) JOIN process USING(upid) WHERE process.name = 'system_server' and s.name like 'Lock contention on a monitor lock %' and substr(s.name, 46, instr(s.name,')')-46) = ctn.otid and ctn.slice_id <> s.slice_id and ctn.ts >= s.ts and (ctn.ts+ctn.dur) <= (s.ts+s.dur) group by s.slice_id order by s.dur desc |
来一个简单例子,模拟卡顿现象,代码如下:
从log
立可以看出损耗的相关代码:
进入perfetto
,导入trace
文件,界面如下,找到对应的包名线程:
能看到onResume
方法阻塞了4s的样子,这样能在具体的类里尽快定位产生耗时的方法,其余的功能可以具体分析。
由于android
手机日益更新换代,现在折叠屏手机已经面市,搜狐视频要根据折叠屏幕尺寸不同变化适配不同的UI
展示,这次的需求。
是手机界面展示成这样:
折叠屏展开后展示成不同样子:
结果发现只要手机展开就会无响应,然后就crash
崩溃,log
里也没啥有用信息,实在不好定位问题,百思不得其解,最后想到用Perfetto
看看有没有什么有用信息,按照上述步骤得到trace
文件,导入界面如下,找到搜狐视频的包名线程,可以看到:
上千次执行RV onBindView
方法,在代码里找到对应位置,发现是2个xml
的UI
都在刷新,所以导致图片View
加载混乱,所以一直重复刷新,修改后切换界面就正常了,长叹一口气,问题圆满解决,鼓掌撒花~
https://blog.csdn.net/imqingyue/article/details/124194680?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-124194680-blog-110933087.pc_relevant_multi_platform_featuressortv2dupreplace&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-124194680-blog-110933087.pc_relevant_multi_platform_featuressortv2dupreplace&utm_relevant_index=2
https://blog.csdn.net/u011578734/article/details/110933087
https://developer.android.google.cn/studio/command-line/perfetto
https://blog.csdn.net/vviccc/article/details/124567746?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-124567746-blog-124194680.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-124567746-blog-124194680.pc_relevant_layerdownloadsortv1&utm_relevant_index=9