刘俊凯 发表于 2024-8-10 05:00:15

Android Perfetto Trace性能分析

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com
本人用 Perfetto 快 2 年了,公司一些同事和网友都来咨询过使用姿势,本人只能把自己一个略显粗糙点的笔记发给同事,有点不敷细节和不敷深度,于是趁着明朗假期,我把履历总结成一篇博客,团结 Perfetto 官网资料取长补短尽量做到深入浅出。
Perfetto 支持多个平台,包罗 Linux、Android 和 Chrome,并提供了用于记载系统级和应用级运动的服务和库、低开销的针对 Native 和 Java 的内存分析工具、可供 SQL 分析跟踪文件的 C++库和 Python 库(Python 基于 C++库),以及基于 Web 可视化方便分析 Trace 文件的 Perfetto UI。


[*]快速开始,Android 抓 Trace:

[*]https://perfetto.dev/docs/quickstart/android-tracing

[*]可视化工具,Perfetto UI:

[*]地点:https://ui.perfetto.dev/
[*]教程:https://perfetto.dev/docs/visualization/perfetto-ui

[*]开源:

[*]地点:https://github.com/google/perfetto
[*]贡献:https://perfetto.dev/docs/contributing/getting-started

聪明的同学从上面的信息中可以了解到很多信息了,下面咱们就展开细节聊一聊。
1、生成 Perfetto 抓 Trace 的设置

做 App 性能分析,一般关注这几类信息:

[*]Memory。
[*]CPU。
[*]GPU。
[*]Power。
[*]系统层消耗。
我们在抓 Trace 的时候可以指定应用、内存、CPU、GPU、电源和系统事件等,使用 Perfetto 时可以使用 PerfettoUI 来生成设置:https://ui.perfetto.dev/#!/record。
第一步,选择目的 Android 设备系统版本。打开 Perfetto UI,选择要抓 Trace 的目的设备,有 2 种方式,如图一所示:


[*]方式一,选择目的系统版本。
[*]方式二,选择 ADB 连接的设备。
   https://i-blog.csdnimg.cn/blog_migrate/ba783733af4459b178678b74f70637e5.png
(图一)第二步,调整 Trace 文件记载设置。主要是为了控制 Trace 文件大小大概 Trace 记载时长,如果文件太大则加载迟钝操纵也不太顺畅,如图二所示:
   https://i-blog.csdnimg.cn/blog_migrate/4b45b8a62e144e021d336dcf08ae7696.gif
(图二)第三步,设置要分析的探针信息。
网络 CPU 相干信息,需要点击在 Probes 下的 CPU,然后根据需要打开对应开关并做设置,如图三所示:
   https://i-blog.csdnimg.cn/blog_migrate/bbe4a6080fc2f4ad4fff279c8b9ecbf6.png
(图三)网络 GPU 相干信息,需要点击在 Probes 下的 GPU,然后根据需要打开对应开关,如图四所示:
   https://i-blog.csdnimg.cn/blog_migrate/0b5dc4cecc19bfadc244ee1fc72ef858.png
(图四)网络电源信息和内存信息同上,不再赘述。
这里需要强调网络 atrace 的设置,选中 Probes 下的 Android apps & avcs,如图五所示:
   https://i-blog.csdnimg.cn/blog_migrate/22f088e242d3fd3f418fe0990d95ea5c.gif
(图五)按照以下操纵步骤和留意项:

[*]打开 atrace 开关。
[*]默认是抓取所有 app trace,可以关闭Record events from Android apps and services,在下面添加目的 App 包名。
[*]在 Categories 下选择你关注的 trace 类型。
第四步,选择【Recording command】保存设置,一共有 3 种方式。
方式一,保存在本地,打开【Recording command】的页面后,点击按钮复制设置内容,如图六所示:
   https://i-blog.csdnimg.cn/blog_migrate/64d7ff29c7938e82ae9fc2ffe7df02a4.png
(图六)方式二,在 Perfetto UI 长期化保存,如图七所示:
   https://i-blog.csdnimg.cn/blog_migrate/fd8ce013321ce87016c7ea005826702e.png
(图七)方式三,生成远端链接分享给别人下载,如图八所示:
   https://i-blog.csdnimg.cn/blog_migrate/9ea4cfc14626898fda71f7f1962bbdd3.png
(图八)我们可以多实验下设置会更加认识和了解细节,保存后可以根据文档手动增删改一些设置(见下文),比如我们主要关注 CPU 信息时,做好对应设置生成的设置如下:
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.gpu.memory"
    }
}
data_sources: {
    config {
      name: "linux.process_stats"
      target_buffer: 1
      process_stats_config {
            scan_all_processes_on_start: true
      }
    }
}
data_sources: {
    config {
      name: "linux.sys_stats"
      sys_stats_config {
            stat_period_ms: 1000
            stat_counters: STAT_CPU_TIMES
            stat_counters: STAT_FORK_COUNT
            #cpufreq_period_ms: 1000
      }
    }
}
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: "gpu_mem/gpu_mem_total"
            ftrace_events: "power/gpu_work_period"
            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: "am"
            atrace_categories: "aidl"
            atrace_categories: "dalvik"
            atrace_categories: "binder_lock"
            atrace_categories: "camera"
            atrace_categories: "database"
            atrace_categories: "gfx"
            atrace_categories: "network"
            atrace_categories: "sm"
            atrace_categories: "ss"
            atrace_categories: "view"
            atrace_categories: "webview"
            atrace_apps: "com.taobao.android"
      }
    }
}
duration_ms: 40000
   实际运行时,因为手机系统版本不一致原因,可能碰到以下错误:
-:36:13 error: No field named "cpufreq_period_ms" in proto SysStatsConfig
            cpufreq_period_ms: 1000
如上面给出的设置示例,注释对应设置行后再实验。
在实际操纵中,会根据差别的场景和需求对抓 Trace 的设置文件做修改,怎样修改可以参考官网文档(https://perfetto.dev/docs/)数据源设置,文档位置如图九所示:
   https://i-blog.csdnimg.cn/blog_migrate/7c8c75483340c77ebd75e0ccc3cf70a7.png
(图九)生成后好放在电脑的本地(比如桌面),可以把它定名为 perfetto.pbtx。
2、按照设置抓 Perfetto Trace

让手机抓 Perfetto Trace 有 3 种方式:

[*]使用手机的 Traceur app 抓取,很多国产手机没有,本文不做介绍。
[*]使用 adb 下令抓取。
[*]使用 Perfetto UI 抓取。
[*]使用 Python 脚本抓取(推荐)。
值得留意的是 Perfetto 从 Android 9(P)开始集成,从 Android 11(R)开始默认开启。在 Android 9(P)和 Android 10(Q)上需要先确保开启 Trace 服务:
adb shell setprop persist.traced.enable 1
   如果你要在 Android 9(P)之前的设备上使用 Perfetto,请参考官网文档:
https://perfetto.dev/docs/quickstart/android-tracing#recording-a-trace-through-the-cmdline
如果实行了抓取后,没有抓到对应 App 的 Trace,启用systrace VERBOSE再试试:
adb shell setprop log.tag.enableSystrace VERBOSE
2.1、使用 adb 下令抓取


[*]通过 adb 把设置推送到手机:

[*]adb push ~/Desktop/perfetto.pbtx /data/local/tmp/perfetto.pbtx

[*]使用 adb 让手机以指定设置抓 Perfetto Trace:

[*]adb shell 'cat /data/local/tmp/perfetto.pbtx | perfetto --txt -c - -o /data/misc/perfetto-traces/trace'

[*]竣事抓取:

[*]adb shell 'perfetto --attach=perf_debug --stop'

抓取完成后,使用 adb 下令导出 Trace 文件到电脑,进行分析即可。该方式操纵步骤多略显繁琐,根据实际情况作为备用方式。
2.2、使用 Perfetto UI 抓取

使用 Perfetto UI 抓取 Trace 时,选择目的 Android 设备系统版本时,需要选择 ADB 连接的设备,在 Perfetto UI 上设置完成后,不消保存到本地,直接点击【Start Recording】开始录制,如图十所示:
   https://i-blog.csdnimg.cn/blog_migrate/b65fb5ee400274f56160d40d44c46c04.png
(图十)在 Perfetto UI 抓取 Trace 时不能手动竣事,需要等在【Recording Setting】中设置的【Max Duration】倒计时竣事,当我们点击了录制后会看到如下提示:
Recording in progress for 40000 ms...
录制竣事后,Perfetto UI 会直接在 Web 界面打开 Trace 文件,我们可以直接进行分析。该方式比较方便,设置可以在 Perfetto UI 长期化保存,美中不足的是 Perfetto UI 抓到的 Trace 文件不能下载发给别人。
2.3、使用 python 脚本抓取

使用 python 脚本抓取到 Trace 后,会把 Trace 文件保存到本地,也会自动在欣赏器通过 Perfetto UI 直接打开 Trace 文件,我们直接进行分析。
使用 python 脚本抓取时需要满足以下几个条件:

[*]Android 设备通过 adb 连接到电脑。
[*]把 python 脚本保存在本地,在本地运行 python 脚本。
[*]把抓 Trace 的设置保存在本地,运行 python 脚本时需要指定设置文件。
python 脚本在 GitHub 上的开源地点,可以通过欣赏器看源码:
https://github.com/google/perfetto/blob/main/tools/record_android_trace
raw.githubusercontent.com 下载链接:
https://raw.githubusercontent.com/google/perfetto/master/tools/record_android_trace
现在我们把 python 脚本和抓 Trace 的设置放在桌面,定名和目录结构如下:
~/Desktop$
├── perfetto.py
├── perfetto.pbtx
此时我们手机与电脑通过 adb 连接,然后运行以下下令抓取 Trace:
python3 perfetto.py -c perfetto.pbtx -o trace_file.perfetto-trace
   上述下令中,-c是指定设置文件位置,-o是指定 trace 文件保存位置。
运行下令后,我们开始操纵 App,然后以为抓取到目的 Trace 了,按下ctrl + c竣事即可,此时 Trace 文件会被放在-o指定的位置,且 Perfetto UI 会被自动打开,我们直接进行分析即可。
   而且我们不消担心抓的 Trace 太大网站内存绷不住,因为 python 脚本会自动下载当前系统的 trace_processor 来剖析,细节可以从官网了解。
https://perfetto.dev/docs/visualization/large-traces
3、Trace 分析

先大概了解下 Trace 的基本概念,一个 Trace 段在 Perfetto UI 中用 Slice 表示,它并不代表一个方法,而是一个 Trace 的开始到竣事,需要在代码中标记。
Trace.beginSection("Choreographer#doFrame");
...
Trace.endSection();
如上述代码所示,一个 Trace 段必须是一个Trace#beginSection(String)对应一个Trace#endSection()。
3.1、使用图形界面分析

正式操纵之前先掌握一些基本操纵技巧。
基本操纵 1,打开 Trace 文件后,可以团体上观察哪些线程有问题,比如耗时久、Uninterruptiable Sleep (non-IO)等,定位到有问题的线程大概 Trace 段后,我们就可以对线程和 Trace 段进行更详细的分析了,如图十一所示:
   https://i-blog.csdnimg.cn/blog_migrate/9933f3dcc4a60856a941cdf4172d4a02.gif
(图十一)基本操纵 2,在对线程和 Trace 段进行详细分析之前,对要分析的进程和线程做下钉操纵(如图十二所示):

[*]在列表中点击要分析的 App 进程,点击后该进程会展开子列表,展示所有线程。
[*]找到RenderThread(渲染线程)并点击名称右侧的钉图标,把该线程钉在列表顶部。
   https://i-blog.csdnimg.cn/blog_migrate/1d42191c7eff789f27b659eee17cb2aa.png
(图十二)这样做的目的是方便观察主线程和渲染线程发生了什么,找到性能瓶颈所在。
下面我们一起看 3 个例子。
3.1.1、Uninterruptible Sleep (non-IO)

我们看一个最经典的方法耗时分析,看过 Framework 源码的同学都知道系统层Choreographer#doFrame()是实行一帧绘制(包罗layout、measure、draw),如果我们页面操纵时(比如欣赏信息流)略显卡顿,那么肯定存在某一帧大概更多帧耗时久的情况。用 Perfetto UI 打开 Trace,一眼就能看到有些些 Trace 段比较长,选中该段 Trace(这个 Trace 是系统层加的),如图十三所示:
   https://i-blog.csdnimg.cn/blog_migrate/bc4f91a657e4667a228861fb1a14c548.png
(图十三)观察 Slice Details 后会很一目了然的看到该 Trace 的耗时:

[*]看到这一帧耗时 177ms,很明显这么久的一帧会肉眼可见的卡顿一下。
[*]检察 Duration 列表,可以看到有意外,存在耗时较久的Sleeping和Uninterruptible Sleep (non-IO)。
[*]Running 比较久,可以点击箭头展开检察细节。
很明显,主线程存在不符合预期的Uninterruptible Sleep (non-IO)状态,这个 case 中比较明显的棕色段就是Uninterruptible Sleep的信息(不明显的放大横轴段就能看到)。
重点来了,怎样定位是什么原因引起的 Uninterruptible Sleep?我们可以这样操纵,以Uninterruptible Sleep为中心放大横轴,到充足大了可以看到 App 主线程行逐步露出Runnable和Running,如图十四所示:
   https://i-blog.csdnimg.cn/blog_migrate/85e6f49d5b2b4342e88a8c1c0fe83a39.png
(图十四)
[*]Runnable不表示线程,而是表示当火线程可运行了。
[*]Running表示当火线程正在运行。
此时我们选中间隔Uninterruptible Sleep近来的Runnable,如图十五所示:
   https://i-blog.csdnimg.cn/blog_migrate/7f52a5687ad53288e18cd3968bf09f0a.png
(图十五)在Selection Details中可以看到Waker信息,它表示唤起当火线程(这里是主线程)的线程,可以看到这里是dp2ndk线程壅闭了主线程。同时,我们在 App 进程子列表中找到dp2ndk线程,并钉住。此时dp2ndk、渲染线程、主线程等就会同时钉在顶部,方便我们做对比确认信息。
从图十五可以看到,dp2ndk线程占用了 CPU,这里需要做优化。
3.1.2、monitor contention with owner

在 Perfetto UI 打开 Trace,放大缩小横轴到得当大小,横向滑动 Trace 段,可以看到类似monitor contention with owner 的 Trace 段,它会壅闭主线程大概渲染线程,点击主线程大概渲染线程背面的Runnable观察Waker,可以看到是哪个线程壅闭的,如图十六所示:
   https://i-blog.csdnimg.cn/blog_migrate/1b034e2a6a5eb5fed583d95e12e59d8c.png
(图十六)此时我们选中间隔Uninterruptible Sleep近来的Runnable,从图十六的Slice Details中可以看到,NetworkKit-GRS_线程占用了 CPU,这里需要做优化。
3.1.3、Lock contention on InternTable lock

Lock contention on 的类型比较多,都属于锁操纵,在 App 启动过程中大概页面操纵过程中非常常见,如图十七所示,发现某个 Trace 耗时较长时,单独选中该段 Trace,在 Slices 中会列出子 Trace 段列表,可以更好的看出耗时 Trace。
类似Lock contention on InternTable lock的渲染帧会影响页面流畅性,造成 App 卡顿,如图十七所示:
   https://i-blog.csdnimg.cn/blog_migrate/d358e66fa3a3a60bbe52125d35fbc0b8.gif
(图十七)3.2、使用 SQL 聚合分析

官网文档:


[*]常见查询:https://perfetto.dev/docs/analysis/common-queries
[*]语法:https://perfetto.dev/docs/analysis/perfetto-sql-syntax
[*]表字段介绍:https://perfetto.dev/docs/analysis/sql-tables。
使用图形界面只能一个个 Case 详细分析,当我们梳理出详细的多个 Cas 时,有时候需要拿到聚合数据给对应同事做优化,可以使用 SQL 查询。
比方,统计名为renderConvertView的 Trace 的平均耗时:
SELECT name, AVG(dur)/1000 AS dur FROM slice where name = 'renderConvertView' GROUP BY name
结果如图十八所示:
   https://i-blog.csdnimg.cn/blog_migrate/48f0d5854676a3187d5b484dce3b6083.png
(图十八)比方,统计名为Lock contention on thread list lock (owner tid: 4054)的 Trace,并按照耗时降序排序:
SELECT ts, dur, name FROM slice where name = 'read from memory' ORDER BY dur DESC
结果如图十九所示:
   https://i-blog.csdnimg.cn/blog_migrate/45ef1d47718e07b53a93c04ba603ca17.png
(图十九)4、结语

本文介绍了 Perfetto 设置生成、抓取 Trace、使用 Perfetto UI 分析 Trace 三块内容。在 Trace 分析时主要介绍了基于 CPU 的分析,没有介绍 GPU、内存、电量分析的细节,因为日常工作中本人用的不是很多,所以暂时写不了就不误导读者了,有机会再补充一篇文章。
本文介绍的内容是不到官网的 1/3 的,但是一些细节也是官网没有的,很多人在实操时就卡在这些细节上,这也是我写这篇文章的原因。
感兴趣的读者可以加我的 QQ 交流群:46523908。
本文完,感谢阅读。
   版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android Perfetto Trace性能分析