安卓Native内存分析工具LoliProfiler

/

项目背景

LoliProfiler是一款针对安卓项目(游戏或APP产品)的C/C++内存分析的自研工具,专为进行内存优化的游戏引擎工程师与游戏测试工程师开发。拥有用户友好的图形界面,资深开发者可对不同的引擎进行深度定制,以最大化发挥工具的作用。官方针对UnrealEngine4与Unity引擎有特殊优化,以达到最佳的性能表现。相较于同类型产品Perfetto,其更适合游戏引擎,支持的安卓系统版本更广泛,可深度定制。

LoliProfiler解决了安卓平台Native内存采集与分析的一系列痛点和问题,内部开源以来经过来自移动游戏项目与移动APP项目的诸多开发者的实践,协同团队的协作与持续迭代,目前的版本功能已逐渐完善与稳定,开发者也对此工具的未来充满了期待。我们希望能够与更多的开发者一起完善下去,共同将安卓平台C/C++内存分析的功能做到极致。

功能介绍

支持特性

•  调试器客户端支持Windows7/10,MacOS Mojave以及更新版本

•  可直接调试 Debuggable 的 APP

•  支持挂载到运行中的 APP 上

•  支持多种堆栈回溯技术

•  官方提供游戏引擎优化方案,支持调试大型游戏

•  在越狱设备上可调试发布版程序

•  支持多种数据展示模式:堆栈聚合列表、TreeMap、内存碎片

即插即用的体验

小型游戏/APP或简单的测试APP,不需要改动源码或重新打包,即可直接使用 LoliProfiler进行调试。在Root设备上可调式发布版的APP。

采集到足够的数据后,即可离线进行符号转换,将函数地址翻译为函数名称:

数据分析

LoliProfiler提供多种数据分析模式:

聚合堆栈数据的展示模式:

基于 TreeMap 的数据展示模式,哪里不对劲一目了然:

也可查看 proc/pid/smaps 的数据:

支持查看内存碎片情况:

https://github.com/Tencent/loli_profiler

/

如何使用

首先请关闭如 Android Studio / Unreal Engine 编辑器 等可能使用 adb 端口的软件。

在MacOS BigSur等权限管理严格的系统上,也需要执行此命令 sudo spctl --master-disable

配置

当你下载到最新版的 LoliProfiler后,首先需要设置 Android SDK / Android NDK 的路径:

小提示: 使用 Command+Shift+G 可以 Goto 到访达中的任意位置。

小提示: 使用 ESC来退出设置界面。

设置参数

接下来我们需要为你的安卓程序设置正确的调试参数:

编译器 Compiler

首先我们需要选择正确的编译器。对于老程序请选择 gcc 编译器 (即代码工程使用 NDK r16b 或更早的版本)。对于更新的程序,比如 UE4.25/4.26,请选择 llvm。

如果你混合使用了 gcc 和 clang 的动态库,那你就必须做出选择了,我们因为技术限制不支持同时调试 gcc 与 clang 的动态库。

你可以尝试使用此命令查看你的so库是用什么编译器构建的,但一般情况下这个都不太靠谱 ::smile::

# android-ndk-r16b\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-readelf.exe
readelf -p .comment path/to/your/library
# some library write compiler related info in comment section, but this may not be accurate. 
# String dump of section '.comment':
# [     1]  GCC: (GNU) 4.9.x 20150123 (prerelease)
模式 Mode

选择内存分配记录采集模式

严格模式 Strict Mode

当你初次使用时,请选择此模式,此模式可用于小的测试工程。

此模式会记录所有内存申请值大于 Threshold(阈值,单位Byte)的分配记录。

统计学模式 Loose Mode

此模式使用 perfetto 的 heapprofd 中相同的原理。

利用统计学原理,此模式会降低内存申请记录的采集频率,从而达到比严格模式更优的性能。

当严格模式太卡,可尝试使用此模式。

采集频率使用 Threshold(阈值,单位Byte)控制,建议的采集频率范围是 1024(1KiB) 到1048576(1MiB)之间。

无堆栈模式 No Stack Mode

顾名思义,此模式不记录调用堆栈,只记录内存申请大小。此模式常用于统计动态库的总体内存分配情况。

因为它不采集调用堆栈,因此不会对真机性能造成太大的影响。

打包 Build

当你自己使用我们提供的方法修改&重新打包了你的APK。

就可以在此选择你所使用的 堆栈回溯 优化技术。

默认回溯方案 Default

当你直接使用未修改的APK时,请使用此模式。

此模式使用安卓系统自带的 libunwind.so 进行堆栈回溯,此回溯方案性能不佳,但其可直接使用。

插桩回溯方案 Instrumented

此模式需要你重新编译APK,当你根据教程重新打包后,可使用此模式。

此模式比 libunwind.so 要快的多,单线程10倍,多线程50倍,其缺点是会增大APK包体。

寄存器回溯方案 Frame pointer

此方案是最快的堆栈回溯方案。当你使用比较新的 NDK (比如r20)并且使用 arm64-v8a 架构,建议根据教程重新打包你的APK。

此方案比插桩方案更快,因为它将函数地址保存在了寄存器中,从而大大降低了堆栈回溯的额外负载。

当你使用 UE4.25/4.26 或者比较新的 Unity引擎时,一定要试试这种模式。并且此模式是对重型游戏的唯一推荐模式。

架构 Architecture

选择正确的 APP 架构,armeabi-v7a 或 arm64-v8a,我们也支持上古架构 armeabi (当你选择gcc编译器时)。

阈值 Threshold

阈值 与 模式 Mode需要一同配置,不同模式下其有不同的含义。

动态库列表 Type & Libraries

在此设置你需要采集数据的动态库的列表,此列表可选择是黑名单或白名单。

第一次使用的话,建议先用白名单。

比如如果你是 Unity 游戏,把 libunity 填到表格中即可,虚幻引擎就填 libUE4。

注意: 大型游戏建议重新打包,以获得最佳体验。详见 Game Engine.

白名单 White List

表示列表中配置的动态库就是我们需要Hook的目标。

黑名单 Black List

表示除了配置的库,其他加载进APP的动态库都自动尝试 Hook。

当你使用gcc编译器时,我建议把这些动态库加入到黑名单中,因为他们是用 clang 编译的:

libloli,libart,libc++,libc,libcutils,libart_base,libart_compiler

如果你同时 Hook 了 clang和gcc的动态库,你的程序会崩溃的,因为 ABI 不兼容的原因。

如果采集数据的过程导致你的APP非常卡,以至于无法正常使用。我们建议你使用文档中描述的方案选择合适的技术来重新打包你的APK。

选择目标APP

点击 Launch 旁边的 ... 按钮来选择你想要调试的程序。

拉起目标程序

点击 Launch 按钮拉起你的程序,然后选择使用 拉起 还是 挂载 的模式:

如果你需要使用挂载模式,选择后界面会停止在 “Injecting libloli.so xxx” 中。此时将你的APP缩小到后台,然后再切换回前台,就可以成功挂载上我们的 Debugger。这是因为我们 Debugger 的断点打在 Activity 的 onResume 函数中,因此需要这样操作。::smile::

如果你采集的是大型项目,需要打开 Enable memory optimization 开关来降低内存占用,此模式只保存没有被释放的内存分配记录。

关闭此优化则会保存所有内存分配记录,适用于Demo或小型项目。

当你点击拉起按钮后,Profiler内部做了下面的工作:

# 把 libloli push 到手机上
adb push remote/libloli.so  /data/local/tmp
# 标记 app 下次启动等待 debugger,并拉起进程
adb shell am set-debug-app -w com.company.app
adb shell monkey -p com.company.app -c android.intent.category.LAUNCHER 1
# 拿到 jdwp id,并做端口转发,用于传输数据
adb jdwp
adb forward tcp:8700 jdwp:xxxx
# 最后 使用 jdwp 协议来注入代码,以执行 libloli.so 中的代码逻辑
python jdwp-shellifier.py --target 127.0.0.1 --port 8700 --break-on android.app.Activity.onResume --loadlib libloli.so

采集数据

当你采集到足够的数据后,可点击 Stop Capture 来停止数据采集。

停止采集后,Log中会显示你所采集的数据总量:

# 意思是你采集了 189381 条内存申请、释放的记录
Captured 189381 records.

翻译数据

现在你可以切换到 StackTrace 页签,选择左侧任意一条记录,其调用堆栈会展示在右侧界面中。

可以看到调用堆栈是函数地址,这是因为函数地址翻译函数名称的过程是非常耗费CPU的,因此我们将此步骤做到PC上离线去处理,以提高调试过程中的性能。

切换至 Dashboard 页签,点击 Load Symbols 按钮,在这里选择正确的符号表文件(如果堆栈中有多个动态库,可依次将其符号加载进来):

Unity引擎的 libil2cpp.so/libunity.so 符号表在如下目录:

Temp\StagingArea\libs\armeabi-v7a\
Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Development\Symbols\armeabi-v7a\

可拷贝出单条记录的详细信息:

查看数据

Profiler支持多种数据查看过滤器,可以通过申请记录大小、是否常驻、所属动态库进行数据过滤。

Persistent 选项会筛选出常驻内存,即未被释放的内存申请记录。

也可以通过时间轴对数据进行时间上的过滤:

在 Mac OS 的触摸板上,用一个手指点击,另一个手指拖拽,即可选择时间区间。

Windows 上使用鼠标同时按住 Shift 按键,可选择时间区间。

点击 Tools->Show Merged Callstacks 则会展示出聚合后的堆栈:

我们提供两种数据展示模式:

TreeView模式:与Mac OS Instrument 工具包中的 Allocation 工具相同

同时支持模糊搜索,可根据函数名称进行搜索

TreeMap模式:此模式比TreeView模式更直观,适合检查筛选问题最大的地方

我们也支持内存泄漏检测模式:

通过 Tools->Show Leaks 菜单进入

同样可通过Tree View或者Tree Map模式查看可能存在内存泄漏的调用堆栈。

首先跑几次游戏单局,如图中跑了两次单局,波谷处为游戏大厅

然后选择一段时间范围,从第二个单局的起始位置到结束位置

Profiler就会去Diff 时间范围在[0s-250s]与[0s-450s]的常驻内存数据

最终将有增长的堆栈输出出来:

也可查看进程的 proc/pid/smaps 数据:

并且可以查看内存碎片的情况:

保存数据

退出Profiler之前别忘了保存你的数据 ::smile::

///

软件运行图


自己简单看了下,基于QT5开发,cmake编译的,内部用到了XHook等三方库来hook底层

编译

我下载源码,在mac和windows上都尝试编码,奈何能力有限(windows编译总失败)

我说下mac上编译的流程和注意点

下载源代码后,配置环境

1、下载QT5 SDK 我基本把相关选项都选择安装
参考文章
2、NDK配置,我原本就开发Android,有NDK
3、配置Cmake环境;我开始使用Android SDK的cmake,编译异常,改为官方cmake安装就正常了
参考文章

修改源码的build.sh

set -xeuo pipefail

export QT5Path=/Users/fucongping/Qt5.14.1/5.14.1
# for gcc
export Ndk_R16_CMD=/Users/fucongping/Library/Android/sdk/ndk/16.1.4479499/ndk-build
# for llvm
export Ndk_R20_CMD=/Users/fucongping/Library/Android/sdk/ndk/20.0.5594570/ndk-build

# if [ ! -f $QT5Path ];then
#     echo QT5Path not found ERROR: $QT5Path
#     exit -1
# fi
# if [ ! -f $Ndk_R16_CMD ];then
#     echo Ndk_R16_CMD not found ERROR: $Ndk_R16_CMD
#     exit -1
# fi
# if [ ! -f $Ndk_R20_CMD ];then
#     echo Ndk_R20_CMD not found ERROR: $Ndk_R20_CMD
#     exit -1
# fi

echo QT5Path path: $QT5Path
echo Ndk_R16_CMD: $Ndk_R16_CMD
echo Ndk_R20_CMD: $Ndk_R20_CMD

sh scripts/EnterPoint.sh

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

但是最后还是失败,报错macdeployqt命令参数无效

分析后,是编译脚本存在问题,在scripts/EnterPoint.sh
MacdeployqtPath=find $QT5Path -name "macdeployqt",这个命令会在我的Mac中找到三个路径,导致异常;
我直接修改成对应文件路径

set -xeuo pipefail

export AndroidPluginPath="./plugins/Android"
export ReleasePath="./build/cmake/bin/release/LoliProfiler.app"
#export MacdeployqtPath=`find $QT5Path -name "macdeployqt"`
export MacdeployqtPath=/Users/fucongping/Qt5.14.1/5.14.1/clang_64/bin/macdeployqt
export QT5_Ctoken variable">$QT5Path -name "clang_64"`
export DeployPath="./dist"
echo deleting dir
rm -rf ./build
rm -rf ./dist

sh scripts/BuildProject.sh
sh scripts/BuildAndroidLibs.sh
sh scripts/CopyConfig.sh
sh scripts/Deployqt.sh
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

最后在build/cmake/bin/release/下可以获取到应用安装包

使用

我一开始拿到源码有点迷,没有现成的应用直接用吗?
后经同事指点,github上有release版本:
release版本页面
路径:https://github.com/Tencent/loli_profiler/releases/tag/v1.1.2
 

资源


macos版本

windows版本

使用上,直接看官方的文档就非常好:
使用文档

/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android内存分析是指检测和分析Android应用程序中的内存使用情况,以便优化应用程序性能和解决内存泄漏问题。 首先,我们可以使用Android Studio提供的内存分析工具来对应用程序进行分析。该工具可以显示应用程序的内存使用情况,并提供了堆转储文件的功能。通过查看堆转储文件,我们可以检查应用程序的堆内存使用情况,包括对象的分配、引用和释放情况。从而找到可能的内存泄漏问题,如未及时回收的对象或无效的引用。此外,我们可以利用工具中的多种图表和报告来分析内存泄漏的原因和位置。 其次,我们可以使用MAT(Memory Analyzer Tool)工具来对堆转储文件进行更深入的分析。MAT工具提供了更多的工具和报告,可以帮助我们找出内存泄漏的根本原因,如长生命周期的对象、静态引用、未正确关闭的资源等。此外,MAT还可以分析内存中的对象实例占用及其关系,帮助我们了解对象之间的引用关系并发现潜在的内存泄漏。 最后,我们还可以使用一些第三方库和工具来辅助进行内存分析,如LeakCanary和Facebook的Stetho。LeakCanary可以实时监测内存泄漏,并在检测到泄漏时提供详细的报告和堆转储文件。Stetho可以实时查看应用程序内存使用情况,并提供调试界面和API,方便开发人员进行内存分析和调试。 总之,Android native内存分析是通过使用内置工具、第三方库和工具来检测和分析应用程序的内存使用情况,以优化性能和解决内存泄漏问题。通过分析堆转储文件和使用各种工具和报告,开发人员可以快速定位和解决内存问题,提升应用程序的质量和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值