前言:

此篇讲的是自己经过一段时间的研究,对安卓内存分析工具 Perfetto 有了一些了解,在此分享一下,行为粗糙,见笑。

我们从如何使用工具、工具的优缺点、技术原理、落地实践,四个方面聊聊这两个工具的事。


背景:

前段时间在分析和解决安卓手机的内存问题,需要排查引擎、业务、SDK的内存问题,其对工具的需求是:整个游戏中各模块的内存分配、释放、占用的情况,某段时间各模块的内存分配情况,以及某个时间点内存占用的分布情况。

于是对内存分析工具做了一些研究,项目组同事也开发了些好用的内存工具坐等这些优秀同事分享。

这里主要介绍已经公开的内存分析工具中比较中意有两款,一款是google开发的Perfetto 性能分析工具。

我们就来聊聊这款工具的使用体验、使用步骤、以及它们的原理知识。


Perfetto 的作用范围如下图:



Perfetto是一款比较强大的安卓性能分析工具(它还可以用于分析其他系统),其功能涵盖了对CPU的追踪、电池耗电追踪、系统调用的追踪,内存分配与释放的追踪。

除了性能数据记录还,它还有自带的分析工具,分析工具包括:通过自定义追踪功能来拼接命令行,用SQL方式筛选数据,将数据转换成其他格式方便自定义数据处理,Web形式的火焰图(Web可离线),以及Web上的ADB功能(有bug)。

Perfetto已经内置在了Android10以上的系统中,与我们前面介绍过的Simpleperf的程序调用性能耗时分析工具一样是先由开发成一个独立软件,完毕后再嵌入到安卓系统和NDK中。

冲突:

在我们内存分析过程中,该选择怎样的内存分析工具?怎么用好它们?

为此我提出4个疑问:

1.Perfetto的优缺点是什么?

2.Perfetto的内存分析原理是什么?

3.如何使用Perfetto?

4.如何将这个工具融入到项目中?

下面是正文内容:


1.Perfetto的优缺点是什么?

Perfetto (只介绍内存分析 Heap profiler部分)

优点:

  1. 功能强大
  2. 效率高
  3. 可靠性强

缺点:

  1. 只针对Android 10及以上机型
  2. 学习门槛略高,至少需要知道些许ADB、Python、LLM、pprof、Perfetto命令行的知识点,才能熟练运用该工具
  3. 功能复杂且已嵌入安卓系统中修改难度大
  4. 无法过滤so,只能全部分析完了再筛选
  5. 暂时只能在Mac上操作,否则无法解析函数堆栈信息(需要自己编译一个windows的trace-to-text程序)
  6. 无对比功能

2.Perfetto的内存分析原理是什么?

Perfetto:

源码:git clone https://android.googlesource.com/platform/external/perfetto/

Perfetto也是使用注入(hook)的方式,将内存分配和释放的几个函数修改为自己的代码指令后,当内存分配和释放时进行统计。
与LoliPerfiler不同的是,它有得天独厚的优势,就是它本身就是安卓系统自带的程序,不需要用JDWP方式启动某个程序,直接启动执行程序就可以了。
并且Perfetto只注入libc的malloc和free,因为不只游戏引擎库使用内存分配,其他库要分配内存,它们都统一使用libc分配内存,因此注入libc是件一劳永逸的事,这样监控内存会更加全面。
Perfetto的这种注入方式,需要开启libc debug hook,即
   
adb shell setprop libc.debug.hooks.enable 1

它的具体hook方法请看这里: https://android.googlesource.com/platform/bionic/+/master/libc/malloc_hooks/


步骤简单描述为:

  1. 启动Perfetto程序
  2. 程序之间使用socket传递信息,由于都是本地程序所以socket其实就是一个文件句柄
  3. 调用memory程序,启动本地socket服务
  4. 启动memory producer程序,连接socket服务
  5. 注入malloc和free到libc
  6. 当进程调用内存分配和释放时,将数据和调用地址记录下来,并用socket形式发送给客户端
  7. 客户端收到消息调用DoUnwind进行调用堆栈回溯,并记录下来
  8. 结束时将所有数据导出,或者每隔XX秒dump一次(可选)
  9. 最后使用llvm,从有符号表的so中找出对应的函数名,写入数据文件。
  10. 上传到web站点(本地或远端都可以)解析数据并以火焰图的形式展示出函数分配内存的情况。



3.如何使用Perfetto

Perfetto:

官方说明,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


特色:可用SQL形式,对数据进行筛选

1.WEB网页中已经有对命令行的自动拼接工作,只要在图形上筛选要分析的数据就会生成一个命令行
2.在Cygwin64 Terminal 终端运行就可以,会生成一个 /data/misc/perfetto-traces/trace 文件,需要pull出来
3.也可以用heap_profile.py 脚本,自己跑数据内存数据


使用步骤:

  1. 必须是debug包,或者有profileable标记的,或者root手机
  2. 必须是安卓10及以上
  3. adb 1.0.4以上
  4. 需要python
  5. 需要安装llvm
  6. 最好是mac,因为windows上符号表解析暂时不能用

安装
1.下载最新版本 heap_profile.py 脚本  https://raw.githubusercontent.com/google/perfetto/master/tools/heap_profile
2. llvm-symbolizer 用于符号表对应联接
    Brew install llvm
    将/usr/local/opt/llvm/bin放入.bash_profile中,perfetto符号表工具需要用到里面的 llvm-symbolizer


录入record步骤

1.允许 perfetto,允许hook
adb shell setprop persist.traced.enable 1
adb shell setprop libc.debug.hooks.enable 1

2.设置
adb shell su root setenforce 0

3.设置符号表将libil2cpp.so,libunity.so两个文件放在 PERFETTO_BINARY_PATH 指定的目录下(请看注解1)

4.开始记录内存数据到文件夹(文件夹必须是空的)
python .\heap_profile.py -n com.company.xxx -o ./文件夹路径

5.用web站点数据分析
将symbolized-trace 这个文件上传到web站点

6.用sql语句筛选数据,并导出数据(按ctrl+回车执行)
Sql 数据参考:https://perfetto.dev/docs/analysis/sql-tables#heap_profile_allocation

select * from heap_profile_allocation;



7.对比两者数据
暂无对比需要增加功能,
可以根据sql导出的数据做对比,
也可以根据自己去解析proto格式的数据再对比

更细的录入步骤



1.在web站点上选指令
可选择更详细的指令,包括cpu综合数据、gpu综合数据、函数调用堆栈、内存memory分配情况

2.开始录入,并选择每5秒dump一次(或一次性导出)

3.结束后,调用heap_profile.py改造后的脚本,只对load数据和分析符号表

    改造heap_profile.py内容为,只读取数据和分析符号表,把前面的分析部分去掉。

4.上传数据到web站点分析(同上)

5.用sql分析数据,下载并导出数据(同上)

6.对比两者数据(同上)


注解1:
这个脚本中的符号分析部分,不支持windows,只能再mac或linux中进行,可以在windows中的cygwin中试试
设置符号表路径 PERFETTO_BINARY_PATH = XXX,把so文件放在这个目录下。


自己部署一个Perfetto的web分析站点

可以自己部署在机器上,这样录入和分析会更自主一些。
部署说明:https://perfetto.dev/docs/contributing/build-instructions
步骤:下载,编译,运行,启动web站点


4.如何将这个工具融入到项目中?

由于还没有完全融入到项目中,所以这里大都是我的想法和思考。

我们在优化中最重要的是找出问题的关键,用分析工具就能辅助我们更加准确的定位问题的关键。
我秉持着找出问题,并且找出问题的关键的做事方针去思考我们在使用内存工具的时候能为项目做些什么。

前言说我们的需求是:

  1. 整个游戏中各模块的内存分配、释放、占用的情况
  2. 某段时间各模块函数的内存分配情况
  3. 某个时间点内存占用的分布情况
  4. 自动统计内存泄漏问题

实际项目中,我们常常

  1. 通过方法一和方法二,来查看各函数的GC分配异常情况
  2. 通过方法三,对比两个时间点的内存快照,来查找内存泄漏和不合理的内存分配情况
  3. 通过方法四,程序化的数据统计方法来查找内存泄漏情况

Perfetto能满足我们前两个需求,因为它们都没有快照功能,而且也只能作为协助查找内存泄漏的工具,不能完全定位内存泄漏问题。

有了所有内存分配和释放的数据,可以通过数据统计程序,将内存泄漏以及可疑的内存泄漏部分定位出来。查看次到达同一时间点时的内存分配情况就能知道哪些内存泄露了,哪些内存过大或异常。

建立内存每日分析流水线:

由于它们都可以使用root过的手机分析app包的内存数据,所以我们可以将这些数据加入到流水线中去,定位app中每天各模块的内存涨幅情况。

  1. 每天打包完,启动内存分析流水线
  2. 开启游戏,开启Perfetto或LoliProfiler
  3. 自动跑一局游戏,将内存数据dump出来
  4. 加入有符号表so,将数据解析为带函数名的内存数据
  5. 将内存数据解析,统计各模块的内存分配情况
  6. 将数据上传到后台服务器,用趋势图的方式展示在web页面上
  7. 查看各模块的内存分配是否上升异常,如果异常,启动人工分析流程

各函数内存分配趋势图:


内存比较示意图:



人工分析内存流程

  1. 下载有内存问题的数据源,和一个前期没有问题的内存数据源
  2. 用对比功能对比分析这两个数据源,对内存上升模块做具体分析,查出是哪些函数细节调用导致内存上升
  3. 由具体函数查业务逻辑中的内存分配问题
  4. 如果仍然无法定位具体内存问题,再用其他工具,
                例如内存快照工具,以及xcode的Instruments再做细致定位,Xcode Instruments确实是最好用的性能分析工具。

如果没有可对比数据,则只能用单数据分析各模块的内存调用情况,这在优化初期比较容易找出内存问题,到了优化后期则难以找出问题所在,因为小块内存问题不容易查找,只能通过监控和对比的方式,会更加容易些。


技术能力有限,分析和分享暂到此为止,继续学习,希望和大家一起进步。