一、Simpleperf介绍
Simpleperf是一个强大的命令行工具,它包含在NDK中,可以帮助我们分析应用的CPU性能。Simpleperf可以帮助我们找到应用的热点,而热点往往与性能问题相关,这样我们就可以分析修复热点源。
如果您更喜欢使用命令行,可以直接使用 Simpleperf。Simpleperf 是一个通用的命令行 CPU 性能剖析工具,包含在面向 Mac、Linux 和 Windows 的 NDK 中。
如需查看完整的文档,请先阅读 Simpleperf https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md。
官方的资料:
二、基本工作原理
现代 CPU 具有一个硬件组件,称为性能监控单元(PMU)。PMU 具有一些硬件计数器,计数一些诸如经历了多少次 CPU 周期,执行了多少条指令,或发生了多少次缓存未命中等事件。
Linux 内核将这些硬件计数器包装到硬件 perf 事件中。此外,Linux 内核还提供了独立于硬件的软件事件和跟踪点事件。Linux 内核通过 perf_event_open 系统调用将这些暴露给用户空间,这正是 simpleperf 所使用的机制。
Simpleperf 具有三个主要的功能:stat,record 和 report。
Stat 命令给出了在一段时间内被剖析的进程中发生了多少事件的摘要。以下是它的工作原理:
- 给定用户选项,simpleperf 通过对 linux 内核执行系统调用来启用剖析。
- Linux 内核在调度到被剖析进程时启用计数器。
- 剖析之后,simpleperf 从内核读取计数器,并报告计数器摘要。
Record 命令记录一段时间内被剖析进程的采样。它的工作原理如下:
- 给定用户选项,simpleperf 通过对 linux 内核执行系统调用来启用剖析。
- Simpleperf 在 simpleperf 和 linux 内核之间创建映射缓冲区。
- Linux 内核在调度到被剖析进程时启用计数器。
- 每次给定数量的事件发生时,linux 内核将样本转储到映射缓冲区。
- Simpleperf 从映射缓冲区读取样本并stat生成 perf.data。
Report 命令读取 “perf.data” 文件及所有被剖析进程用到的共享库,并输出一份报告,展示时间消耗在了哪里。
三、环境要求
为了使用Simpleperf, 需要以下环境:
- 待分析的App应运行在Android 5.0或者更高版本的设备上
- 手机的USB 连接到操作机器
- 应用程序应该是 debuggable 的。由于安全限制的原因,只有 android::debuggable 设置为 true 的应用程序才能剖析。(在一个已经 root 的设备上,所有应用程序都可以剖析。)在 Android Studio 中,这意味着我们需要使用 debug 构建类型,而不是 release 构建类型。
- 为了能够运行Python scripts,宿主机器应安装:
-
- Python 2.7或者更高版本
- NDK的版本应不低于r13b
通常剖析 Android 应用程序性能包含三个步骤:
- 准备应用程序。
- 记录剖析数据。
- 生成剖析数据的分析报告。
Simpleperf的获取路径:https://android.googlesource.com/platform/prebuilts/simpleperf/
在页面内,可以直接压缩包下载,既可以选择NDK相对应的版本(R13~N21),也可以选择master。建议直接选择NDK对应的版本即可。当然,也可以git直接拉取仓库。
git clone https://android.googlesource.com/platform/prebuilts/simpleperf
查看下载的simpleperf目录,可以看出:它的工具集包涵client端和host端;client端运行在Android系统上,负责收集性能数据;host端则运行在开发机上,负责对数据进行分析和可视化(这些可执行文件在下载后的bin文件夹的android和win/linux下)。
除了bin文件夹之外,最上层还有很多.py的脚本文件。这些脚本和配置文件主要是官方写的一些傻瓜式的使用脚本,只需要对配置文件进行配置,就可以在直接在开发机上直接运行脚本,一键生成最终的结果。
Python 脚本根据它们的功能被分为三个部分:
- 用于简化剖析数据记录的脚本,如 app_profiler.py。
- 用于生成剖析报告的脚本,如 report.py,report_html.py,inferno。
- 用于解析剖析数据的脚本,如 simpleperf_report_lib.py。
主要的脚本是:app_profiler.py和report.py两个。
在android-ndk\simpleperf中包含了 simpleperf 可执行文件和 Python 脚本,它们的功能如下:
- bin/:包含可执行文件及共享库,里面包含了android和windows。
- bin/android/${arch}/simpleperf:设备上运行的静态 simpleperf 可执行文件。其中 ${arch} 为目标设备的 CPU 架构,如 arm 和 arm64。
- bin/${host}/${arch}/simpleperf:用于主机的 simpleperf 可执行文件,只支持生成报告。其中 ${host} 为主机的操作系统平台,如 linux,${arch} 为主机的 CPU 架构,如 x86_64。
- bin/${host}/${arch}/libsimpleperf_report.${so/dylib/dll}:用于主机的报告生成库。其中 ${host} 指主机的操作系统平台,${arch} 为主机的 CPU 架构。
- app_profiler.py:用于记录剖析数据的 Python 脚本。
- binary_cache_builder.py:用于为剖析数据构建二进制缓存的 Python 脚本。
- report.py:用于生成剖析报告并输出到标准输出的 Python 脚本。
- report_html.py:用于生成剖析报告并输出为 html 文件的 Python 脚本。
- inferno.sh (或 Windows 平台的 inferno.bat ):用于生成火焰图并输出为 html 文件的脚本工具。
- inferno/:inferno 的实现。由 inferno.sh 使用。
- pprof_proto_generator.py:将剖析数据的格式转换为 pprof 使用的格式的 Python 脚本。
- report_sample.py:将剖析数据的格式转换为 FlameGraph 使用的格式的 Python 脚本。
- simpleperf_report_lib.py:解析剖析数据的库。
脚本的主要内容,就是读取配置文件,然后执行adb shell ...的命令,其实本质上和命令行的输入没什么区别。但是,如果直接运行,不仅仅需要查看配置文件各个配置项的含义,还可能会出现许多意想不到的BUG,不太建议直接使用脚本,不得精髓啊。
四、支持的命令
debug-unwind命令:基于debug / test dwarf的离线展开,用于调试simpleperf。
dump命令:转储perf.data中的内容,用于调试simpleperf。
help命令:打印其他命令的帮助信息。
kmem命令:收集内核内存分配信息(将被Python脚本替换)。
list命令:列出Android设备支持的所有事件类型。
记录命令:配置文件处理并在perf.data中存储分析数据。
report命令:报告perf.data中的分析数据。
report-sample命令:报告perf.data中的每个样本,用于支持集成Android Studio中的simpleperf。
stat命令:profiles处理并打印计数器摘要。
每个命令都支持不同的选项,可以通过帮助消息查看,如下示例:
# List all commands. $ simpleperf --help # Print help message for record command. $ simpleperf record --help
五、操作流程
方式一:使用adb shell进入手机页面操作
1、将simpleperf文件push到手机
在simpleperf/bin/android目录下包含有不同体系架构的 Android 上运行的静态二进制文件,在arm目录下打开命令窗口,执行命令:
adb push simpleperf data/data/
2、将simpleperf授权为可读可写可执行文件:
adb shell cd data/data/ chmod 777 simpleperf
3、对某些特定进程或者线程监控
./simpleperf record -p 4281(pid 或tid) --duration 30(时间/s)
得到错误提示,说只读分区无法写入 perf.data:
simpleperf E 04-19 15:09:29 4109 4109 record_file_writer.cpp:47] failed to open record file 'perf.data': Read-only f
4、 用 -o 参数设置存储记录的路径
simpleperf I cmd_record.cpp:729] Recorded for 29.9851 seconds. Start post processing. simpleperf I cmd_record.cpp:809] Samples recorded: 1457. Samples lost: 0.
5、用report 报告perf.data中的分析数据
simpleperf record -p 17465--duration 4 -f 1000 -o /data/local/tmp/perf.data --call-graph fp
simpleperf report -i /data/perf.data -n --sort dso
simpleperf W 04-19 15:31:17 4564 4564 dso.cpp:274] /data/local/rvdecApp doesn't contain symbol table simpleperf W 04-19 15:31:17 4564 4564 dso.cpp:335] Symbol addresses in /proc/kallsyms are all zero. `echo 0 >/proc/sys/kernel/kptr_restrict` if possible. Cmdline: /data/local/tmp/simpleperf record -p 4281 --duration 30 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 125526 Event count: 43633757353 Overhead Sample Shared Object 88.93% 106529 /data/local/rvdecApp 8.05% 10560 /system/lib/libc.so 3.01% 8437 [kernel.kallsyms]
其中的 –sort 参数是用来指定结果显示哪些列,我们这里只写了一个 dso(即 dynamic shared object),所以结果只显示一列 “Shared Object”,而且按 dso 分类,结果也只有三行而已。
如果不加 –sort 参数,默认显示这几列:Command,Pid,Tid,Shared Object,Symbol,相当于:
--sort comm,pid,tid,dso,symbol
- -n 参数用来显示 Sample 那列,表示该行共命中了多少个 Sample,加不加随意。
- 可以看到,百分之 88.93 的时间都耗费在了我们的被测程序上,这是预期中的。
6、查看app 内部,函数占比:
simpleperf report -i /data/perf.data --dsos /data/local/rvdecApp --sort symbol
- 结果如下:
impleperf W 04-19 15:57:34 5046 5046 dso.cpp:274] /data/local/rvdecApp doesn't contain symbol table simpleperf W 04-19 15:57:34 5046 5046 dso.cpp:335] Symbol addresses in /proc/kallsyms are all zero. `echo 0 >/proc/sys/kernel/kptr_restrict` if possible. Cmdline: /data/local/tmp/simpleperf record -p 4281 --duration 30 -o /sdcard/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 106529 Event count: 38804869540 Overhead Sample Symbol 5.06% 5373 rvdecApp[+24380] 4.57% 4890 rvdecApp[+24420] 1.43% 1588 rvdecApp[+13a44] 1.01% 1083 rvdecApp[+21f94] 0.94% 999 rvdecApp[+20188] ...
其中的 –dsos 参数是 simpleperf 的 5 个 filter 之一,意思是按照指定的 dynamic shared objects 进行过滤,只显示参数指定的 dso 里面的结果。全部 5 个 filter 是:
–comms: 按 command 过滤,比如:--comm rvdecApp
–pids: 按 pid 过来
–tids: 按 tid(线程id)过滤
–dsos: 按库文件/可执行文件名过滤
–symbols: 按函数名过滤,比如: --symbols "RVComFunc::getPUMVPredictor(RefBlockInfo*, unsigned int, int, int, unsigned int)",注意函数里有空格的,需要用双引号引起来。
可以看到,结果里没有函数名字。那是因为我们的 rvdecApp 是没有符号表的版本。我们用带符号表的 app 进行分析即可。
带符号表的 app 可执行文件可以在 obj 目录下找到。把它 push 到手机上,覆盖原来的可执行文件。
注意,不用重新执行 rvdecApp 并重新采集 perf.data, 只需要在分析时使用带有符号表的 rvdecApp 即可。
还用刚才的命令:
./simpleperf report -i /data/perf.data -n --dsos /data/local/rvdecApp --sort symbol
- 得到如下结果:
Overhead Sample Symbol 10.45% 5354 RVComFunc::DBFShiftedProcess8x8(unsigned char**, int*, unsigned char*, int, unsigned char*, int, bool, bool, bool, bool, unsigned char) 5.55% 3722 RVComFunc::deblockCUTree(TCBDataTree*, unsigned char**, unsigned int*, int, int, RefBlockInfo*, int, unsigned char**, int*, unsigned char) 4.49% 3675 RVComFunc::reconstructInterPredictors(TCUData*, unsigned char**, unsigned int*, TRefPicList*, RefBlockInfo*, unsigned int, unsigned int, unsigned int, unsigned int) 2.25% 3518 RVComFunc::deriveDBFStrengthFUbyMotionInfo(unsigned char*, unsigned char*, int, int, RefBlockInfo*, int, int, unsigned char, unsigned char, bool, bool) 2.68% 3320 Decoder::parseBitStream_FrameNew() 2.79% 2927 NEON_DBF_EdgeFilter4_Vertical 2.52% 2651 RVComFunc::DBFShiftedProcessFu(unsigned char**, int*, unsigned char*, int, unsigned char*, int, int, bool, bool, bool, bool, unsigned char) 2.36% 2553 (anonymous namespace)::decode_gen_vlc(unsigned long const*, int, (anonymous namespace)::VLC*, int, int) ...
可以看到DBFShiftedProcess8x8函数是最耗时的函数,需要被优化。
方式二:使用linux环境,使用python脚本操作
1、 运行app-profiler.py
python app_profiler.py -p com.android.settings
进入simpleperf目录下,我们可以使用 app-profiler.py 剖析 Android 应用程序。输出路径等可以修改app_profiler.py配置文件
14:47:33,547 [INFO] (app_profiler.py:206) prepare profiling 14:47:33,730 [INFO] (app_profiler.py:208) start profiling 14:47:33,806 [INFO] (app_profiler.py:244) run adb cmd: ['adb', 'shell', '/data/local/tmp/simpleperf', 'record', '-o', '/data/local/tmp/perf.data', '-e task-clock:u -f 1000 -g --duration 10', '--log', 'info', '--app', 'com.afmobi.boomplayer'] simpleperf I cmd_record.cpp:696] Recorded for 9.96625 seconds. Start post processing. simpleperf W dso.cpp:446] /vendor/lib64/egl/libGLES_mali.so doesn't contain symbol table simpleperf W dso.cpp:446] /vendor/lib64/hw/[email protected] doesn't contain symbol table simpleperf W dso.cpp:446] /data/app/~~EKnZoTKaDLH3FYfxteqUvg==/com.google.android.trichromelibrary_541411734-ozjH9V7NI4SknoCd0t2CPg==/base.apk!/lib/arm64-v8a/libmonochrome_64.so doesn't contain symbol table simpleperf W dso.cpp:446] /data/app/~~3WfX5_4-OAPL17xSEBkRfg==/com.afmobi.boomplayer-yFLZq2TqjDUJV1OB-um-UQ==/lib/arm64/libmmkv.so doesn't contain symbol table simpleperf I cmd_record.cpp:771] Samples recorded: 8647. Samples lost: 0. 14:47:45,689 [INFO] (app_profiler.py:211) collect profiling data 14:47:46,745 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\apex\com.android.runtime\lib64\bionic\libc.so 14:47:46,770 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\vendor\lib64\libged.so 14:47:46,800 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\vendor\lib64\egl\libGLES_mali.so 14:47:46,835 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libEGL.so 14:47:46,867 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libhwui.so 14:47:46,888 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libutils.so 14:47:46,913 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libgui.so 14:47:46,932 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libnativewindow.so 14:47:46,992 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\framework\arm64\boot-framework.oat 14:47:47,069 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\apex\com.android.art\lib64\libart.so 14:47:47,130 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\framework\arm64\boot.oat 14:47:47,159 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libandroid_runtime.so 14:47:47,179 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\bin\app_process64 14:47:47,206 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\apex\com.android.vndk.v31\lib64\libhidlbase.so 14:47:47,230 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libhidlbase.so 14:47:47,260 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\[email protected] 14:47:47,301 [INFO] (binary_cache_builder.py:184) use current file in binary_cache: binary_cache\system\lib64\libui.so
2、运行 report_html.py脚本, 剖析报告并输出
python report_html.py
simpleperf W dso.cpp:448] failed to read symbols from /data/app/~~3WfX5_4-OAPL17xSEBkRfg==/com.afmobi.boomplayer-yFLZq2TqjDUJV1OB-um-UQ==/lib/arm64/libmmkv.so: File not found Cmdline: /data/local/tmp/simpleperf record -o /data/local/tmp/perf.data -e task-clock:u -f 1000 -g --duration 10 --app com.afmobi.boomplayer Arch: arm64 Event: task-clock:u (type 1, config 1) Samples: 8647 Event count: 8647000000 Overhead Shared Object Pid 27.69% /system/lib64/libhwui.so 31183 17.32% /apex/com.android.art/lib64/libart.so 31183 9.06% /apex/com.android.runtime/lib64/bionic/libc.so 31183 8.60% /vendor/lib64/egl/libGLES_mali.so 31183 8.50% /system/framework/arm64/boot-framework.oat 31183 4.75% /data/app/~~3WfX5_4-OAPL17xSEBkRfg==/com.afmobi.boomplayer-yFLZq2TqjDUJV1OB-um-UQ==/oat/arm64/base.odex 31183 3.05% /system/framework/arm64/boot.oat 31183 2.67% /system/lib64/libandroidfw.so 31183 2.58% [JIT app cache] 31183 1.78% /system/lib64/libgui.so 31183 1.58% /data/app/~~EKnZoTKaDLH3FYfxteqUvg==/com.google.android.trichromelibrary_541411734-ozjH9V7NI4SknoCd0t2CPg==/base.apk!/lib/arm64-v8a/libmonochrome_64.so 31183 1.02% /system/lib64/libbinder.so 31183 0.75% /apex/com.android.art/lib64/libart-compiler.so 31183 0.74% /system/lib64/libminikin.so 31183 0.73% /system/lib64/libz.so 31183 0.68% /system/lib64/libsqlite.so 31183 0.65% /system/lib64/libutils.so 31183 0.64% /system/lib64/libjpeg.so 31183 0.53% /apex/com.android.conscrypt/lib64/libcrypto.so 31183 0.43% /apex/com.android.i18n/lib64/libicuuc.so 31183 0.39% /apex/com.android.runtime/bin/linker64 31183 0.39% /system/framework/arm64/boot-core-libart.oat 31183 0.36% /system/lib64/libc++.so 31183 0.27% /system/lib64/libandroid_runtime.so 31183 0.25% /system/framework/arm64/boot-okhttp.oat 31183 0.24% [vdso] 31183 0.22% /apex/com.android.conscrypt/lib64/libssl.so 31183 0.21% /system/lib64/libcutils.so 31183 0.21% /system/lib64/libui.so 31183 0.19% /apex/com.android.vndk.v31/lib64/libgralloctypes.so 31183 .....
3、 产生调用图的报告,并输出至标准输出:
Cmdline: /data/local/tmp/simpleperf record -o /data/local/tmp/perf.data -e task-clock:u -f 1000 -g --duration 10 --app com.afmobi.boomplayer Arch: arm64 Event: task-clock:u (type 1, config 1) Samples: 10841 Event count: 10841000000 Children Self Command Pid Tid Shared Object Symbol 66.11% 0.00% RenderThread 31183 11235 /apex/com.android.runtime/lib64/bionic/libc.so __start_thread | -- __start_thread | -- __pthread_start(void*) android::Thread::_threadLoop(void*) android::uirenderer::renderthread::RenderThread::threadLoop() |--0.01%-- [hit in function] | |--99.96%-- android::uirenderer::WorkQueue::process() | |--0.03%-- [hit in function] | | | |--99.94%-- std::__1::__function::__func<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0, std::__1::allocator<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0>, void ()>::operator()() (.c1671e787f244890c877724752face20) | | |--0.01%-- [hit in function] | | | | | |--86.61%-- android::uirenderer::renderthread::CanvasContext::draw() | | | |--0.11%-- [hit in function] | | | | | | | |--77.75%-- android::uirenderer::skiapipeline::SkiaOpenGLPipeline::draw(android::uirenderer::renderthread::Frame const&, SkRect const&, SkRect const&, android::uirenderer::LightGeometry const&, android::uirenderer::LayerUpdateQueue*, android::uirenderer::Rect const&, bool, android::uirenderer::LightInfo const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, android::uirenderer::FrameInfoVisualizer*) | | | | |--0.08%-- [hit in function] | | | | | | | | | |--58.87%-- android::uirenderer::skiapipeline::SkiaPipeline::renderFrame(android::uirenderer::LayerUpdateQueue const&, SkRect const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, bool, android::uirenderer::Rect const&, sk_sp<SkSurface>, SkMatrix const&) | | | | | | | | | | | |--86.22%-- android::uirenderer::skiapipeline::SkiaPipeline::renderFrameImpl(SkRect const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, bool, android::uirenderer::Rect const&, SkCanvas*, SkMatrix const&) | | | | | | |--0.12%-- [hit in function]
4、 展示火焰图
为了展示火焰图,我们首先需要记录调用图。火焰图由 report_html.py 在 Flamegraph 标签中展示。也可以直接双击inferno.bat展示火焰图。
我们也可以使用 GitHub - brendangregg/FlameGraph: Stack trace visualizer 构建火焰图。请确保已经安装了 perl。
$ git clone https://github.com/brendangregg/FlameGraph.git $ python report_sample.py --symfs binary_cache >out.perf $ FlameGraph/stackcollapse-perf.pl out.perf >out.folded $ FlameGraph/flamegraph.pl out.folded >a.svg
用浏览器打开就可以看,解析出来的火焰图如下:
- 从上面的火焰图里面就可以看到netd一直在执行什么,这样就可以交由netd的同仁继续跟踪
y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。
详细火焰图使用可以参考如何读懂火焰图? - 阮一峰的网络日志
六、提示与诀窍
如果您刚开始使用 Simpleperf,不妨试试以下一些特别实用的命令。如需了解更多命令和选项,请参阅 Simpleperf 命令和选项参考。
1.查找执行时间最长的共享库
您可以运行此命令来查看哪些 .so 文件占用了最大的执行时间百分比(基于 CPU 周期数)。启动性能分析会话时,首先运行此命令是个不错的选择。
simpleperf report --sort dso
例:
Cmdline: /system/bin/simpleperf record -p 10167 --duration 5 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 3294 Event count: 1193555038 Overhead Sample Shared Object 57.18% 2114 [kernel.kallsyms] 8.01% 225 /system/lib64/libhwui.so 7.16% 192 /apex/com.android.runtime/lib64/bionic/libc.so 5.96% 169 /vendor/lib64/egl/libGLES_mali.so 4.73% 125 /apex/com.android.art/lib64/libart.so 4.55% 128 /system/framework/arm64/boot-framework.oat 3.84% 96 /system/lib64/libgui.so 1.39% 44 /system/framework/arm64/boot.oat 1.06% 28 /system/lib64/libutils.so 0.74% 24 /system/lib64/libbinder.so 0.43% 12 /system/lib64/libui.so 0.42% 10 /system/lib64/libminikin.so 0.34% 10 /system/lib64/libandroid_runtime.so 0.34% 10 /system/lib64/libc++.so 0.29% 7 /apex/com.android.vndk.v31/lib64/libgralloctypes.so 0.27% 8 /vendor/lib64/egl/libGLES_meow.so 0.27% 6 /system/framework/arm64/boot-mediatek-framework.oat 0.24% 8 [JIT app cache] 0.22% 7 /system/lib64/libEGL.so 0.19% 5 /vendor/lib64/libged.so 0.19% 6 /apex/com.android.vndk.v31/lib64/libhidlbase.so 0.18% 4 unknown 0.16% 5 /system/lib64/libcutils.so 0.15% 2 /apex/com.android.i18n/lib64/libicuuc.so 0.15% 5 /system/framework/arm64/boot-core-libart.oat ...............
2.查找执行时间最长的函数
当您确定占用最多执行时间的共享库后,就可以运行此命令来查看执行该 .so 文件的函数所用时间的百分比。
simpleperf report --dsos library.so --sort symbol
例:
simpleperf E command.cpp:59] Unknown option -dsos. Try `simpleperf help report`. 1|TECNO-KI7:/ # simpleperf report -i /data/perf.data --dsos [kernel.kallsyms] --sort symbol Cmdline: /system/bin/simpleperf record -p 10167 --duration 5 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 2114 Event count: 682442263 Overhead Symbol 8.22% _raw_spin_unlock_irqrestore 6.87% __blockdev_direct_IO 6.72% get_user_pages_fast 6.61% dio_bio_complete 4.13% memblock_start_of_DRAM 3.97% el0_da 3.71% blk_queue_split 3.39% iov_iter_fault_in_readable 2.72% _raw_spin_unlock_irq 2.30% fscrypt_mergeable_bio 2.29% el0_svc_common 2.23% free_unref_page_list 2.02% queue_work_on 1.98% mod_delayed_work_on 1.83% blk_crypto_submit_bio 1.79% clear_page 1.68% _mtk_btag_pidlog_set_pid 1.47% get_page_from_freelist 1.35% f2fs_is_valid_blkaddr 1.18% fscrypt_generate_dun 1.02% __save_stack_trace 0.97% pagecache_get_page 0.95% __handle_speculative_fault 0.82% depot_save_stack 0.80% f2fs_map_blocks 0.73% __rcu_read_unlock 0.67% __pi_memset 0.64% f2fs_wait_on_block_writeback 0.56% __rcu_read_lock 0.54% kmem_cache_alloc
3.查找线程中所用时间的百分比
.so 文件中的执行时间可以跨多个线程分配。您可以运行此命令来查看每个线程所用时间的百分比。
simpleperf report --sort tid,comm
例:
Cmdline: /system/bin/simpleperf record -p 10167 --duration 5 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 3294 Event count: 1193555038 Overhead Tid Command 42.01% 12182 Thread-4 29.48% 11834 RenderThread 16.96% 10167 com.andromeda.androbench2 6.04% 11853 mali-cmar-backe 2.70% 11876 binder:10167_4 1.24% 11854 ged-swd 0.67% 11844 mali-mem-purge 0.62% 12087 binder:10167_6 0.26% 11968 binder:10167_2 0.01% 11820 Jit thread pool 0.00% 11824 FinalizerWatchd 0.00% 11859 GPU completion
4.查找对象模块中所用时间的百分比
在找到占用大部分执行时间的线程之后,可以使用此命令来隔离在这些线程上占用最长执行时间的对象模块。
simpleperf report --tids threadID --sort dso
例:
Cmdline: /system/bin/simpleperf record -p 10167 --duration 5 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 1457 Event count: 501433272 Overhead Shared Object 96.72% [kernel.kallsyms] 2.80% /apex/com.android.runtime/lib64/bionic/libc.so 0.33% /apex/com.android.art/lib64/libart.so 0.08% /vendor/lib/modules/memfusion.ko 0.07% /system/lib64/libutils.so
5.了解函数调用的相关性
调用图可直观呈现 Simpleperf 在对会话进行性能剖析期间记录的堆栈轨迹。
您可以使用 report -g 命令打印调用图,以查看其他函数调用的函数。这有助于确定是某个函数本身运行缓慢还是因为它调用的一个或多个函数运行较慢。
您还可以使用 Python 脚本 report.py -g 来启动显示函数的交互式工具。您可以点击每个函数,查看它的子函数所用的时间。
simpleperf report -g
例:
TECNO-KI7:/ # simpleperf report -g simpleperf E record_file_reader.cpp:83] failed to open record file 'perf.data': No such file or directory 1|TECNO-KI7:/ # simpleperf report -i /data/perf.data -g Cmdline: /system/bin/simpleperf record -p 10167 --duration 5 -o /data/perf.data Arch: arm64 Event: cpu-cycles (type 0, config 0) Samples: 3294 Event count: 1193555038 Children Self Command Pid Tid Shared Object Symbol 3.93% 3.93% Thread-4 10167 12182 [kernel.kallsyms] __blockdev_direct_IO 3.84% 3.84% Thread-4 10167 12182 [kernel.kallsyms] get_user_pages_fast 3.78% 3.78% Thread-4 10167 12182 [kernel.kallsyms] dio_bio_complete 2.55% 2.55% mali-cmar-backe 10167 11853 [kernel.kallsyms] _raw_spin_unlock_irqrestore 2.36% 2.36% Thread-4 10167 12182 [kernel.kallsyms] memblock_start_of_DRAM 2.27% 2.27% Thread-4 10167 12182 [kernel.kallsyms] el0_da 2.12% 2.12% Thread-4 10167 12182 [kernel.kallsyms] blk_queue_split 1.94% 1.94% Thread-4 10167 12182 [kernel.kallsyms] iov_iter_fault_in_readable 1.31% 1.31% Thread-4 10167 12182 [kernel.kallsyms] fscrypt_mergeable_bio 1.27% 1.27% Thread-4 10167 12182 [kernel.kallsyms] free_unref_page_list 1.15% 1.15% RenderThread 10167 11834 [kernel.kallsyms] queue_work_on 1.15% 1.15% Thread-4 10167 12182 /apex/com.android.runtime/lib64/bionic/libc.so memset 1.13% 1.13% Thread-4 10167 12182 [kernel.kallsyms] mod_delayed_work_on 1.05% 1.05% Thread-4 10167 12182 [kernel.kallsyms] blk_crypto_submit_bio 1.02% 1.02% Thread-4 10167 12182 [kernel.kallsyms] clear_page 1.01% 1.01% RenderThread 10167 11834 [kernel.kallsyms] _raw_spin_unlock_irqrestore 0.96% 0.96% Thread-4 10167 12182 [kernel.kallsyms] _mtk_btag_pidlog_set_pid 0.77% 0.77% Thread-4 10167 12182 [kernel.kallsyms] f2fs_is_valid_blkaddr 0.76% 0.76% RenderThread 10167 11834 /apex/com.android.runtime/lib64/bionic/libc.so __vfprintf 0.70% 0.70% RenderThread 10167 11834 [kernel.kallsyms] _raw_spin_unlock_irq 0.68% 0.68% Thread-4 10167 12182 [kernel.kallsyms] fscrypt_generate_dun 0.67% 0.67% Thread-4 10167 12182 [kernel.kallsyms] get_page_from_freelist 0.64% 0.64% RenderThread 10167 11834 [kernel.kallsyms] el0_svc_common 0.62% 0.62% Thread-4 10167 12182 [kernel.kallsyms] _raw_spin_unlock_irqrestore 0.58% 0.58% Thread-4 10167 12182 [kernel.kallsyms] __save_stack_trace 0.55% 0.55% Thread-4 10167 12182 [kernel.kallsyms] pagecache_get_page 0.54% 0.54% com.andromeda.androbench2 10167 10167 /apex/com.android.art/lib64/libart.so ExecuteNterpImpl 0.51% 0.51% Thread-4 10167 12182 [kernel.kallsyms] __handle_speculative_fault 0.46% 0.46% Thread-4 10167 12182 [kernel.kallsyms] f2fs_map_blocks 0.45% 0.45% RenderThread 10167 11834 /apex/com.android.runtime/lib64/bionic/libc.so scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)> rigin, unsigned long, bool) 0.44% 0.44% RenderThread 10167 11834 /system/lib64/libgui.so @plt 0.41% 0.41% Thread-4 10167 12182 [kernel.kallsyms] depot_save_stack 0.36% 0.36% Thread-4 10167 12182 [kernel.kallsyms] f2fs_wait_on_block_writeback 0.36% 0.36% Thread-4 10167 12182 [kernel.kallsyms] _raw_spin_unlock_irq 0.34% 0.34% Thread-4 10167 12182 [kernel.kallsyms] __rcu_read_unlock 0.34% 0.34% com.andromeda.androbench2 10167 10167 [kernel.kallsyms] _raw_spin_unlock_irq 0.32% 0.32% Thread-4 10167 12182 [kernel.kallsyms] __pi_memset 0.31% 0.31% Thread-4 10167 12182 [kernel.kallsyms] kmem_cache_alloc 0.31% 0.31% com.andromeda.androbench2 10167 10167 /apex/com.android.art/lib64/libart.so artAllocStringFromCharsFromCodeRegionTLAB 0.26% 0.26% mali-cmar-backe 10167 11853 [kernel.kallsyms] el0_svc_common 0.26% 0.26% RenderThread 10167 11834 /apex/com.android.runtime/lib64/bionic/libc.so write 0.25% 0.25% Thread-4 10167 12182 [kernel.kallsyms] pte_map_lock ........
6. 获取被剖析程序或系统范围内的原始事件计数器信息
simpleperf stat 被用于获取被剖析程序或系统范围内的原始事件计数器信息。通过传入选项,我们可以选择使用哪些事件,监视哪个进程/线程,监视多长时间,以及打印的间隔。
Performance counter statistics: # count event_name # count / runtime 17,839,958,825 cpu-cycles # 1.758283 GHz 6,411,685,476 stalled-cycles-frontend # 632.274 M/sec 5,413,391,502 stalled-cycles-backend # 534.113 M/sec 11,755,131,810 instructions # 1.160 G/sec 1,409,829,788 branch-instructions # 139.262 M/sec 171,458,771 branch-misses # 16.946 M/sec 10070.704634(ms) task-clock # 1.006817 cpus used 7,210 context-switches # 716.330 /sec 94,430 page-faults # 9.387 K/sec Total test time: 10.002513 seconds.
七、参考文档
【Simpleperf】Android的CPU分析,性能优化利器_android perf_Yngz_Miao的博客-CSDN博客
Profile 工具系列之四:simpleperf_old_man的博客-CSDN博客
使用 Simpleperf 分析本地代码性能 | WolfcsTech