SimplePerf 性能分析火焰图
https://xiaomi.f.mioffice.cn/docs/dock4H4YGLmcxzi44iXyL93nMpd
通用方法:
- 拉代码:https://android.googlesource.com/platform/prebuilts/simpleperf/
- 机器需要刷ROOT版本,方可debug三方应用等;
- 抓取特定进程:python3 run_simpleperf_on_device.py record -p 【pid of process】 —call-graph dwarf 【注:此处会直接在/data/local/tmp下push一个simpleperf,后续也可直接通过此抓取数据////// system/bin下面本身也有一個】
- 从手机拉取抓取的数据:adb pull /data/local/tmp/perf.data
- 解析为火焰图:python3.10 report_html.py -i perf.data 【python版本有要求】
源码及clone地址:https://android.googlesource.com/platform/prebuilts/simpleperf/
直接下载压缩文件:
https://android.googlesource.com/platform111/prebuilts/simpleperf/+archive/master.tar.gz
谷歌的定位
Simpleperf is a native CPU profiling tool for Android. It can be used to profile both Android applications and native processes running on Android. It can profile both Java and C++ code on Android. The simpleperf executable can run on Android >=L, and Python scripts can be used on Android >= N.
“Android的原生CPU分析工具,可用于分析第三方APP和系统原生进程”
简介
Simpleperf包括两部分:Simpleperf可执行文件和Python脚本。
Simpleperf可执行文件会在分析数据时记录更多的信息,不仅会收集待分析数据样本,还会收集设备信息和记录时间等。
api_profiler.py //含有prepare和collect两个命令,用于无收集期间不连接USB的情况
run_simpleperf_without_usb_connection.py //同api_profiler.py
run_simpleperf_on_device.py //在设备上布置simpleperf所需环境并收集数据,省去adb命令
app_profiler.py //收集数据,默认10S,生成perf.data数据
binary_cache_builder.py //获取二进制文件用于report.py的反汇编
report.py //统计分析生成的perf.data数据,窗口展示
report_html.py //统计分析生成的perf.data数据,并生成火焰图,HTML展示inferno.sh //只生成火焰图,并在HTML界面展示,可分析现有数据也可该脚本抓取
app_profiler.py 被用于剖析一个 android 应用程序。它准备剖析环境,下载 simpleperf 到设备上,在主机上生成并拉出 perf.data。它由 app_profiler.config 配置。binary_cache_builder.py 被用于从设备拉出本地层二进制文件到主机上。它由 app_profiler.py 使用。annotate.py 被用于使用 perf.data 注解源文件。它由 annotate.config 配置。report.py 在一个 GUI 窗口中报告 perf.data。simpleperf_report_lib.py 被用于枚举 perf.data 中的样本。在内部它使用 libsimpleperf_report.so 来解析 perf.data。它可被用于将 perf.data 中的样本翻译为其它形式。使用 simpleperf_report_lib.py 的一个例子是 report_sample.py。
APP分析前提条件
“对于Android系统开发者,Root环境下可分析任意APP,该部分无需阅读,仅需注意非Root包大概率无法分析APP是正常的”
- 分析APP的调试版本[debug]:在Manifest中设置android:debuggable=“true”,打开JNI检查并且不优化C/C++代码。——无需更改可直接使用Simpleperf分析
- 分析APP的发行版本[release]:在Manifest中设置android:debuggable=“false”,关闭JNI检查并且优化C/C++代码。当且仅当以下三种情况才可以分析发行版本的APP:
- Root系统可分析任何APP
- Android > Q,在Manifest中设置profileableFromShell
<manifest ...>
<application ...>
<profileable android:shell="true" />
</application>
</manifest>- Android > O,在Manifest中添加debuggable为“true”,并在lib/arch目录下添加wrap.sh文件,无需向ART传递调试标志,因此本身还是发行版本
android {
buildTypes {
release {
sourceSets {
release {
resources {
srcDir {
"wrap_sh_lib_dir"
}
}
}
}
}
}
}task createWrapShLibDir
for (String abi : [“armeabi”, “armeabi-v7a”, “arm64-v8a”, “x86”, “x86_64”]) {
def dir = new File(“app/wrap_sh_lib_dir/lib/” + abi)
dir.mkdirs()
def wrapFile = new File(dir, “wrap.sh”)
wrapFile.withWriter { writer →
writer.write(’#!/system/bin/sh\n$@\n’)
}
}
}
- 分析C/C++代码:AS会在apk中删除本地库的符号表和调试信息,分析结果会包含未知符号等,因此Simpleperf可通过app_profiler.py -lib传递一个包含未删除本地库的目录,通常为AS的路径。
- 分析Java代码:
- Android ⇐ M,Simpleperf不支持分析Java
- Android == N,需要wrap.sh文件,通过app_profiler.py -compile_java_code进行分析
- Android == O,可直接通过app_profiler.py -compile_java_code进行分析
- Android >= P,Simpleperf可直接分析
APP分析步骤
极简用法
- 【Way1-STEP 1】通过app_profiler.py收集数据
具体可选参数详情键入”-h/—help”可查看,在此仅说明几个重要属性:
- -p PACKAGE_NAME: 指定待分析的APP,APP的具体包名可通过systrace等工具确认,也可通过” adb shell pm list packages”筛选确认。
- -np NATIVE_PROGRAM: 指定待分析的原生程序,如”-np surfaceflinger”。
- —pid PID..: 指定待分析的原生进程。
- —tid TID..: 指定待分析的原生线程。
- -a ACTIVITY: 指定待分析的activity,需与”-p”搭配使用,适用与冷热起等场景。
python3 app_profiler.py -p com.miui.home
与抓取systrace一样,务必在抓取期间复现问题。该脚本会在当前目录下生成perf.data文件,默认同时在binary_cache/下生成二进制文件用于反编译等,二进制文件可于收集数据时键入”-nb”取消。
同时,该脚本默认收集10S数据,如有修改时间需求,可通过’-r “—duration 20“‘来指定,单位为秒。另外也可通过run_simpleperf_on_device.py脚本执行record命令,并通过”—duration”指定具体秒数,默认perf.data会在手机/data/local/tmp/目录下,具体参数请自行查阅,与app_profile.py存在差异。
—call-graph dwarf 若使用run_simpleperf_on_device在手机生成data,务必带上上述参数,否则火焰图会有异常
python3 run_simpleperf_on_device.py ``record`` --duration 60- 【Way1-STEP 2】通过report_html.py解析数据
在HTML视图中打开解析的数据,且包含火焰图。
python3 report_html.py
- 【Way2】一键式生成火焰图
该命令会同时执行收集surfaceflinger的数据与HTML展示火焰图的功能,达到上面STEP1和2的效果。
./inferno.sh -np surfaceflinger
或通过如下命令可生成HTML页面的火焰图。
./inferno.sh -sc进阶用法
-
在run_simpleperf_on_device.py脚本中,还支持如下命令:
api-collect Collect recording data generated by app api
api-prepare Prepare recording via app api
debug-unwind Debug/test offline unwinding.
dump dump perf record file
help print help information for simpleperf
inject parse etm instruction tracing data
kmem collect kernel memory allocation information
list list available event types
record record sampling info in perf.data
report report sampling information in perf.data
report-sample report raw sample information in perf.data
stat gather performance counter information
trace-sched Trace system-wide process runtime events.- 通过kmem查看slab分配器的性能
python3 run_simpleperf_on_device.py kmem record
python3 run_simpleperf_on_device.py kmem report通过如上命令可查看到slab的性能,输出如下。
Slab allocation information:
Total requested bytes: 12313833
Total allocated bytes: 13104992
Total fragment: 791159, 6.037081%
Total allocations: 13308
Total frees: 16595
Total cross cpu allocation/free: 2610, 19.612263%
Hit Caller BytesReq BytesAlloc Fragment Pingpong
13308 unknown 12313833 13104992 791159 2610- 通过stat查看一段时间内某特定事件的运行次数
具体可查看事件可通过以下命令,窗口中仅展示大部分,除了经典的tracepoint如sched_switch等以外,还包括cache-misses等硬件软件事件。
python3 run_simpleperf_on_device.py list
在确认事件名称后,可通过以下命令来获取指定时间内指定线程的指定时间的统计运行次数,其中”-e”指定事件,“-p”指定进程或”-a”不限进程,“—interval”指定时间间隔打印,“—duration”指定总的统计时间。
python3 run_simpleperf_on_device.py stat -e block:block_bio_queue,block:block_bio_remap -a —interval 300—duration 15
或者通过”ls”命令来查看性能事件的统计。
python3 run_simpleperf_on_device.py stat ls
\# count event_name # count / runtime, runtime / enabled_time
7,874,067 cpu-cycles # 1.321198 GHz (100%)
4,943,625 stalled-cycles-frontend # 319.791 M/sec (100%)
12,966,162 stalled-cycles-backend # 792.961 M/sec (100%)
5,908,816 instructions # 568.605 M/sec (64%)
3,469 branch-misses # 3.886 M/sec (5%)
16.330986(ms) task-clock # 0.793354 cpus used (100%)
16 context-switches # 979.733 /sec (100%) //上下文切换
405 page-faults # 24.799 K/sec (100%) //缺页异常的次数Total test time: 0.020585 seconds.
- 通过record转存记录
可通过”-f”或”-c”设置转存的频率,前者单位为”T次/每秒”,后者单位为”次/每T次”,其他参数与stat相近,具体设置可通过”—help”查看。
如下示例对dso共享库的使用次数进行排序,进一步可查看指定so库下各方法的调用频率,
> python3 report.py -n --sort dso
Cmdline: /data/local/tmp/simpleperf record -o /data/local/tmp/perf.data -e task-clock:u -f 1000 -g --duration 10 --app com.miui.home
Arch: arm64
Event: task-clock:u (type 1, config 1)
Samples: 1489
Event count: 1489000000
Overhead Sample Shared Object
33.58% 500 /system/lib64/libhwui.so
11.48% 171 /vendor/lib64/egl/libGLESv2_adreno.so
10.54% 157 /apex/com.android.art/lib64/libart.so
10.48% 156 /system/framework/arm64/boot-framework.oat
7.05% 105 /apex/com.android.runtime/lib64/bionic/libc.so
5.51% 82 /apex/com.android.art/javalib/arm64/boot.oat
4.97% 74 /system/priv-app/MiuiHome/oat/arm64/MiuiHome.odex
2.55% 38 /system/lib64/libbinder.so
2.28% 34 [JIT app cache]
1.81% 27 /system/lib64/libgui.so
1.48% 22 /system/lib64/libutils.so
0.94% 14 /system/lib64/libandroid_runtime.so
0.60% 9 /apex/com.android.art/javalib/arm64/boot-core-icu4j.oat> python3 report.py --dsos /system/lib64/libhwui.so --sort symbol
Cmdline: /data/local/tmp/simpleperf record -o /data/local/tmp/perf.data -e task-clock:u -f 1000 -g --duration 10 --app com.miui.home
Arch: arm64
Event: task-clock:u (type 1, config 1)
Samples: 500
Event count: 500000000
Overhead Symbol
14.40% android::uirenderer::skiapipeline::SkiaDisplayList::prepareListAndChildren(android::uirenderer::TreeObserver&, android::uirenderer::TreeInfo&, bool, std::__1::function<void (android::uirenderer::RenderNode*, android::uirenderer::TreeObserver&, android::uirenderer::TreeInfo&, bool)>)
10.80% android::uirenderer::RenderNode::prepareTreeImpl(android::uirenderer::TreeObserver&, android::uirenderer::TreeInfo&, bool, int)
10.60% android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*) const
3.00% android::uirenderer::$_24::__invoke(void const*, SkCanvas*, SkMatrix const&)
3.00% android::uirenderer::skiapipeline::RenderNodeDrawable::onDraw(SkCanvas*)
2.20% android::uirenderer::skiapipeline::StartReorderBarrierDrawable::onDraw(SkCanvas*)工作机制
CPU具有一个硬件组件——PMU(性能监控单元),在CPU运行时会收集各种统计信息,内核将PMU的硬件计数器包装到硬件perf事件中,同事提供独立与硬件的软件时间和跟踪点事件,通过perf_event_open暴露给用户空间,达成simpleperf的使用基础。
假如一秒内读取四次CPU上运行的事件,采样时分别为 A,B,C,C,而且其之间存在调用关系,那么最后组合栈的关系A(4),B(1),C(2):最底层为A,其上为BC分别占比25% ,50%。
def A():
B()
C()红蓝差分火焰图
由于火焰图的颜色是随机的,深浅并不具有任何的现实意义,FlameGrdph中实现了一个脚本生成红蓝差分火焰图,用以区分修改前后的差别。