你真的了解Systrace吗?
欢迎关 Android茶话会 在技术学习、个人成长的道路上,让我们一起前进!
一、什么是SysTrace?
在日常开发中有时候遇到棘手的性能问题就需要使用这个工具,Systrace 是 「Android 4.1」 中新增的性能数据采样和分析工具。它可帮助开发者收集 Android 关键子系统
(如 SurfaceFlinger/SystemServer/Kernel/Input/Display 等 Framework 部分关键模块、服务,View」系统等 的运行信息,从而帮助开发者更直观的「分析系统瓶颈,改进性能」。
二、如何使用SysTrace?
2.1 采集trace
首先我们需要了解Trace的采集主要涉及几部分:采集方式、自定义trace阶段、Release包抓取Trace和系统Trace类同异步调用的差异;
命令行采集:
- 设备要求**「Android 4.3 (API level 18)及以上」**
- 命令如下:python systrace.py [options][「categories」],示例如下:
python /Users/yangzhiyong/Library/Android/sdk/platform-tools/systrace/systrace.py -t 10 -o trace.html gfx input view sched freq wm am hwui workq res dalvik sync disk load perf hal rs idle mmc -a com.ss.android.lark.debug
# -o : 指示输出文件的路径和名字
# -t : 抓取时间(最新版本可以不用指定, 按 Enter 即可结束),单位为秒
# -b : 指定 buffer 大小 (一般情况下,默认的 Buffer 是够用的,如果你要抓很长的 Trae , 那么建议调大 Buffer )
# -a : 指定 app 包名 (如果要 Debug 自定义的 Trace 点, 记得要加这个)
查看支持的categories
- adb shell atrace --list_categories
- python systrace.py -l
// 粗体部分是常用的categories;
gfx - Graphics
input - Input
view - View System
webview - WebView
wm - Window Manager
am - Activity Manager
sm - Sync Manager
audio - Audio
video - Video
camera - Camera
hal - Hardware Modules
res - Resource Loading
dalvik - Dalvik VM
rs - RenderScript
bionic - Bionic C Library
power - Power Management
pm - Package Manager
ss - System Server
database - Database
network - Network
adb - ADB
vibrator - Vibrator
aidl - AIDL calls
pdx - PDX services
sched - CPU Scheduling
freq - CPU Frequency
idle - CPU Idle
disk - Disk I/O
sync - Synchronization
memreclaim - Kernel Memory Reclaim
binder_driver - Binder Kernel driver
binder_lock - Binder global lock trace
系统自带工具:
设备要求**「Android 9(API level 28)及其以上」**
开发者模式->系统跟踪
trace导出:
- 通知栏分享;
- adb pull /data/local/traces/ .
- systrace --from-file .ctrace .perfetto-trace 转换成html
网页版采集分析工具:💻
- 设备要求**「Android 10(API level 29)及其以上;」**
- Perfetto UI
❝
除此之外还有一些常用的技巧
❞
- 自定义TAG
具体使用可参考:developer.android.com/topic/perfo…
「定义」
- 在事件开始调用Trace.beginSection(event);
- 在事件结束调用Trace.endSection(),需成对调用;
「使用」 添加了自定义TAG后,需要-a指定包名参数,才可以采集到自定义的trace信息;
- Release包抓取trace
反射调用setAppTracingAllowed即可:
try {
Class threadClazz = Class.forName("android.os.Trace");
Method setAppTracingAllowed = threadClazz.getDeclaredMethod("setAppTracingAllowed", boolean.class);
setAppTracingAllowed.invoke(null, true);
} catch (Exception e) {
e.printStackTrace();
}
3. 同异步trace的差异
「同步」 | 「异步」 | 「区别」 |
---|---|---|
beginSection(@NonNull String sectionName) | beginAsyncSection(@NonNull String methodName, int cookie) | 异步调用需要传methodName及cookie,开始结束匹配更准确,且不用B-A-A-B类似嵌套,不过该接口为隐藏接口,需反射调用,可使用SystemTracer类; |
endSection() | endAsyncSection(@NonNull String methodName, int cookie) |
2.2 分析
拿到这些trace 我们如何分析也是一个重要的部分
2.2.1 Trace文件打开
首先是打开这个文件
- chrome://tracing:推荐:不过近期版本该工具对trace中线程、进程名解析不出来,不利于查看;
- ui.perfetto.dev/#!/: 推荐,不过新版分析工具不支持VSync的高亮;
- perfetto.bytedance.net/#!/viewer: 不推荐,仅支持单进程查看,对于涉及系统调用的分析不方便;
较早版本sysTrace生成的html文件浏览器即可打开,但最近版本已无法正常打开,需要通过chrome工具手动加载;
2.2.2 面板区域说明
image.png
- 用户屏幕交互
- CPU 使用率
- CPU各核心的运行情况
- 进程信息
- 进程变量/进程
- 选中区段的详细信息
- 鼠标操作选项,可通过1-4快速切换
- 进程过滤
- VSync高亮配置
2.2.3 常用快捷键
比较常用的快捷键是W S A D M V;
- W : 放大 Systrace,放大可以更好地看清局部细节
- S : 缩小 Systrace,缩小以查看整体
- A : 左移
- D : 右移
- M : 高亮选中当前鼠标点击的段(这个比较常用,可以快速标识出这个方法的左右边界和执行时间,方便上下查看)
- V : 高亮VSync的时机,方便分析掉帧的原因;
image.png
2.2.4 线程状态
a) 线程状态
「线程状态」 | 「Systrace中的显示」 | 「说明」 |
---|---|---|
绿色 : 运行中(Running) | 我们经常会查看 Running 状态的线程,查看其运行的时间,与竞品做对比,分析快或者慢的原因 | |
蓝色 : 可运行(Runnable) | Runnable 状态的线程状态持续时间越长,则表示 cpu 的调度越忙,没有及时处理到这个任务 | |
白色 : 休眠中(Sleep) | 一般是在等事件驱动 | |
橘色 : 不可中断的睡眠态 IO Block(Uninterruptible Sleep WakeKill) | 一般是标示 IO 操作慢,如果有大量的橘色不可中断的睡眠态出现,那么一般是由于进入了低内存状态 | |
紫色 : 不可中断的睡眠态(Uninterruptible Sleep) | 一般是陷入了内核态,有些情况下是正常的,有些情况下是不正常的,需要按照具体的情况去分析 |
b) 线程唤醒
一个任务在进入 Running 状态之前,会先进入 Runnable 状态进行等待,而 Systrace 会把这个状态也标示在 Systrace 上; stateWhenDescheduled定义:chromium.googlesource.com/external/tr…
c) 线程参数说明
- Wall Duration:函数执行的总耗时
- CPU Duration:在 CPU 上执行的耗时
- Self Time:自身执行的总耗时,不包括子方法
- CPU Self Time:自身在 CPU 上执行的耗时,不包括子方法
d) 函数调用虚实区别
如下图,我们看到标识setUpHWComposer调用的紫色条并不是实心的,实心的部分代表CPU Duration相对于Wall Duration的占比:
2.2.5 CPU信息
image.png
- C-State
为了在CPU空闲的时候降低功耗,CPU可以被命令进入low-power模式。每个CPU都有几种power模式,这些模式被统称为C-states或者C-modes; C-States从C0开始,C0是CPU的正常工作模式,CPU处于100%运行状态。C后的数越高,CPU睡眠得越深,CPU的功耗被降低得越多,同时需要更多的时间回到C0模式。
「C-State」 | 「描述」 |
---|---|
C-0 | RUN MODE,运行模式。 |
C-1 | STANDBY,就位模式,随时准备投入运行 |
C-2 | DORMANT,休眠状态,被唤醒投入运行时有一定的延迟 |
C-3 | SHUTDOWN,关闭状态,需要有较长的延迟才能进入运行状态,减少耗电 |
- Clock Frequency:CPU当前运行频率;
- Clock Frequency Limits:CPU最大最小频率,通过该参数可以判断CPU核心差异,如大中小核
2.2.6 常见系统进程、线程
进程
system_server
- AMS
- WMS
- SurfaceFlinger
线程
- UI Thread //主线程
- Render Thread //渲染线程
- Binder Thread //跨进程调用线程
2.2.7 常见问题
a) 「锁等待」
「monitor contention」 with owner 「caton_dump_stack (11056)」 waiters=0 blocking from 「boolean android.os.MessageQueue.enqueueMessage(android.os.Message, long)(MessageQueue.java:544)」
结合代码看,这段信息的意思是,「caton_dump_stack」线程(线程ID是11056)作为「owner」持有了主线程消息队列对象锁,「waiters」表示等待在该对象锁上的其他线程数(不包括当前线程),所以总的有1个线程等待对象锁释放,当前线程等待的位置是「enqueueMessage调用处」。
b) 「SurfaceFlinger绘制」
SurfaceFlinger主要是收集各个UI渲染层的数据合成发送给Hardware Composer;一般应用的渲染层包括状态栏、应用页面、导航栏,每个部分都是单独渲染生成Buffer的,基本步骤如下:
- 应用收到VSYNC-app信号后,在**「UI Thread」完成数据计算;准备好后,将数据发送到「RenderThread」**;
- 应用在**「RenderThread」完成数据渲染后,将数据填充到「SurfaceFlinger」**的对应页面BufferQueue中;
- 在**「SurfaceFlinger」收到VSYNC-sf信号后,「SurfaceFlinger」** 会遍历它的层列表的BufferQueue,以寻找新的缓冲区。如果找到新的缓冲区,它会获取该缓冲区;否则,它会继续使用以前获取的缓冲区。「SurfaceFlinger」 必须始终显示内容,因此它会保留一个缓冲区。如果在某个层上没有提交缓冲区,则该层会被忽略;
- **「SurfaceFlinger」**向 「HWC」 提供一个完整的层列表,并询问“您希望如何处理这些层?”
- 「HWC」 的响应方式是将每个层标记为叠加层或 GLES 合成;
- 「SurfaceFlinger」 会处理所有 GLES 合成,将输出缓冲区传送到 「HWC」,并让 「HWC」 处理其余部分;
image.png
c) 「掉帧」
上一部分描述了SurfaceFlinger合成每帧数据的过程,在上述过程中,如果**「UI Thread」计算不及时,或者「RenderThread」渲染不及时,或者「BufferQueue」**中可用Buffer不足导致在下一次VSYNC信号来临之前,没有准备好需要显示的帧的数据,就会出现丢帧,Systrace 报告列出了渲染界面帧的每个进程,并指明了沿时间轴渲染的每个帧。在 16.6 毫秒内渲染的必须保持每秒 60 帧稳定帧速率的帧会以绿色圆圈表示。渲染时间超过 16.6 毫秒的帧会以黄色或红色帧圆圈表示:
- 绿色:未丢帧;
- 棕色:轻微丢帧,丢1帧 ;
- 红色:严重丢帧,丢大于1帧;
通过Systrace右上角的View Options > Highlight VSync,我们可以高亮VSYNC信号到来的时刻,需要注意的是,高亮的VSYNC主要是VSYNC-app信号(可在SurfaceFlinger进程中查看),且灰白相间的位置是VSYNC信号到来的时刻。 如下图,显示了丢1帧的情况,第二个VSYNC到来时,主线程仍未完成显示帧数据的计算,所以出现丢帧的问题。
三、常见trace工具对比
其实我们分析trace不光只有系统这一种方法,下图做个简单的总结
「工具名」 | 「类型」 | 「原理」 | 「优缺点」 | 「使用场景」 | 「使用说明」 | ||||
---|---|---|---|---|---|---|---|---|---|
Traceview | instrument | 利用 Android Runtime 函数调用的 event 事件,将函数运行的耗时和调用关系写入 trace 文件中。 | 「优点:」 全函数调用分析;「缺点:」 工具本身带来的性能开销过大,有时无法反映真实的情况。比如一个函数本身的耗时是 1 秒,开启 Traceview 后可能会变成 5 秒,而且这些函数的耗时变化并不是成比例放大; | - 线下- 整个程序执行流程的耗时 | 已废弃,之前DDMS有相关工具入口 | ||||
Nanoscope | instrument | 直接修改 Android 虚拟机源码,在ArtMethod执行入口和执行结束位置增加埋点代码,将所有的信息先写到内存,等到 trace 结束后才统一生成结果文件。 | 「优点:」- 全函数调用分析;- 性能开销小;- 可以支持分析任意一个应用,可用于做竞品分析;「缺点:」- 需要自己刷 ROM,并且当前只支持 Nexus 6P,或者采用其提供的 x86 架构的模拟器;- 默认只支持主线程采集,其他线程需要代码手动设置; | - 线下- 整个程序执行流程的耗时 | github.com/uber/nanosc… | ||||
systrace | sample | 实际是其他工具的封装,Systrace使用atrace开启追踪,然后读取ftrace的缓存,并且把它重新转换成HTML格式。 | 「优点:」- 可以看到整个流程系统和应用程序的调用流程。包括系统关键线程的函数调用,例如渲染耗时、线程锁,GC 耗时等;- 性能损耗可以接受;「缺点:」- 不支持应用程序代码的耗时分析;需手动添加或者编译期插装; | - 线下- 分析系统调用 | developer.android.com/topic/perfo… | ||||
Simpleperf | sample | 利用 CPU 的性能监控单元(PMU)提供的硬件 perf 事件。 | 「优点:」- 支持Native分析;- 性能开销非常低;「缺点:」- Java分析对Android版本要求比较高; | - 线下- 分析 Native 代码的耗时 | android.googlesource.com/platform/sy… 在 Android M 和以前,Simpleperf 不支持 Java 代码分析。- 在 Android O 和以前,需要手动指定编译 OAT 文件。- 在 Android P 和以后,无需做任何事情,Simpleperf 就可以支持 Java 代码分析; | ||||
Profiler(CPU Profiler) | 混合 | - Sample Java Methods 的功能类似于 Traceview 的 sample 类型;- Trace Java Methods 的功能类似于 Traceview 的 instrument 类型;- Trace System Calls 的功能类似于 systrace;- Sample Native (API Level 26+) 的功能类似于 Simpleperf; | 「优点:」- 集成在IDE中,操作简单;「缺点:」- 性能开销大,应用明显卡顿;- 无法用于自动化测试等场景; | - 线下 | developer.android.com/studio/prof… |
「instrument」:获取一段时间内所有函数的调用过程,可以通过分析这段时间内的函数调用流程,再进一步分析待优化的点。
「sample」:有选择性或者采用抽样的方式观察某些函数调用过程,可以通过这些有限的信息推测出流程中的可疑点,然后再继续细化分析。
来源:juejin.cn/post/7238172236185354297