Simpleperf 使用

谈到Android上的性能剖析,很多人会想到TraceView,SysTrace。TraceView擅长Method Tracing,与IDE集成,使用方便,不过有时候它的误差让人无法忍受;SysTrace的牛逼之处在于它可以给出整个系统的一些关键模块的性能信息,因此用途广泛;虽然它也支持分析自定义模块,但是使用起来稍显复杂。

事实上,在AOSP中有另外一个性能剖析工具不为人知,这个安静的小可爱就是今天要介绍的主角—— simpleperf。我们先来看看它的介绍:

Simpleperf is a native profiling tool for Android. Its command-line interface supports broadly the same options as the linux-tools perf, but also supports various Android-specific improvements.

native profiling?是不是很心动?

至于simpleperf的原理,简单来说,现代CPU一般都带有一个叫做性能监视单元(PMU)的组件,这个硬件能够记录诸如cpu周期数、执行的指令数、缓存失效次数等等关键信息;Linux内核对这个硬件做了一层封装,通过 `perf_event_open` 系统调用把接口暴露给用户空间;这就是simpleperf工具的由来。

简介和原理都表明这个工具貌似很牛逼,接下来我们就试一试。

首先把AOSP上的源码下载下来,点我

simpleperf是一个命令行工具,与systrace不同的是,它的工具集包涵client端和host端;client端运行在Android系统上,负责收集性能数据;host端则运行在你的开发机上,负责对数据进行分析和可视化。(这些可执行文件在下载后的bin文件夹的android和win/mac/linux下)整个工具链使用起来相对复杂,所幸simpleperf的创造者提供了一个炒鸡傻瓜的使用脚本,使用起来毫不费力,非常方便!这个脚本就是 `app_profile.py`。

我们把 simpleperf 下载下来之后,首先修改 `app_profile.config` 文件中的配置,主要是以下几个字段:

# 待分析的app的包名,需要是debuggable的
app_package_name = ""

# 分析的命令,体验的话使用默认即可;更详细的用途看文档。
record_options = "-e cpu-cycles:u -f 4000 -g --dump-symbols --duration 10"

# 待分析app的主界面,app_profile.py会通过am start 帮助开启app
main_activity = ''

然后在你的设备上安装待分析的App,要注意的是这个App必须是debuggable的,在AndroidManifest中把debuggable设置为true即可。接下来,就可以直接通过:

python app_profile.py

进行分析了,这个脚本帮你完成了simpleperf下载,权限设置,打开app,导出数据等等一系列过程;开始分析之后,就可以对App进行操作;在设置好的时间之后就会停止分析,跟SysTrace一样。分析完毕之后,脚本会自动把分析数据从设备上pull下来并存放在当前目录下,叫 perf.data。

接下来就是分析数据了;原始的数据是一个文本文件,长这样:

0.00%     0.00%  ORDERED_THREAD_  17046  18210  /system/lib/libart.so                                                                                         art::Mutex::ExclusiveLock(art::Thread*)
       |
       -- art::Mutex::ExclusiveLock(art::Thread*)
0.00%     0.00%  AsyncTaskExecut  17046  18216  /system/lib/libart.so                                                                                         art::Monitor::Lock(art::Thread*)
       |
       -- art::Monitor::Lock(art::Thread*)
          |
          |--52.01%-- art::Mutex::ExclusiveLock(art::Thread*)
          |
           --0.54%-- art::Mutex::ExclusiveUnlock(art::Thread*)

嵌套如果过深,基本就看不懂了;所幸我们有另外一个分析脚本!直接运行:

python report.py -g

会启动一个GUI显示分析得到的数据,这个GUI使用python的tk写的,实话说,长得比较丑。。

简单观察分析图,可以知道,的确支持native profiling;gui和TraceView差不多,比较直观,但是指标没有TraceView多。另外,这个工具由于硬件直接支持,对性能的影响非常小;光是这一点就好顶赞了。

你以为这就完了?No!这玩意儿还支持 火焰图! 如果你不知道火焰图为何物,建议看看这篇文章 动态追踪技术漫谈

然后,要绘制火焰图,我们需要借助 FlameGraph 这个工具;clone下来之后,里面的脚本就可以直接使用了。(perl脚本,windows系统需要安装Perl语言的支持)

接下来我们执行命令:

$python report_sample.py >out.perf
$stackcollapse-perf.pl out.perf >out.folded
$./flamegraph.pl out.folded > graph.svg

我们用Chrome浏览器打开这个 `graph.svg` 即可得到火焰图:

通过火焰图,我们能非常直观地看到性能的瓶颈,对于分析性能问题很有帮助;相信这个图已经说明一切了。

通用技巧

这里有一些技巧给有root权限的Android系统开发者:

  1. 运行 adb root后, simpleperf可用于分析系统范围内任何进程。
  2. 如果不是在主分支上工作,建议使用AOSP main中最新的simpleperf。脚本位置在 system/extras/simpleperf/scripts,二进制程序在system/extras/simpleperf/scripts/bin/android.
  3. 推荐使用 app_profiler.py 抓trace,然后用 report_html.py 生成html报告。 下面是一个示例。
# Record surfaceflinger process for 10 seconds with dwarf based call graph. More examples are in
# scripts reference in the doc.
$ python app_profiler.py -np surfaceflinger -r "-g --duration 10"

# Generate html report.
$ python report_html.py
  1. 从 Android >= O 开始系统库默认有符号表,我们不需要用$ANDROID_PRODUCT_OUT/symbols 中未striped二进制文件来抓了。 但是,在报告中添加源代码和反汇编(带有行号)时需要它们。下面是一个例子。
# Doing recording with app_profiler.py or simpleperf on device, and generates perf.data on host.
$ python app_profiler.py -np surfaceflinger -r "--call-graph fp --duration 10"

# Collect unstripped binaries from $ANDROID_PRODUCT_OUT/symbols to binary_cache/.
$ python binary_cache_builder.py -lib $ANDROID_PRODUCT_OUT/symbols

# Report source code and disassembly. Disassembling all binaries is slow, so it's better to add
# --binary_filter option to only disassemble selected binaries.
$ python report_html.py --add_source_code --source_dirs $ANDROID_BUILD_TOP --add_disassembly \
  --binary_filter surfaceflinger.so

在system_server进程上抓simpleperf

有时我们希望在发生特殊情况时抓系统进程。在这种情况下,我们可以在检测到情况的点处添加SimplEperf的代码。

  1. 关掉selinux adb shell setenforce 0。因为selinux只允许simpleperf在shell或debuggable/profileable 应用中使用。
  2. 在检测到特殊情况的地方添加下面的代码。
try {
  // for capability check
  Os.prctl(OsConstants.PR_CAP_AMBIENT, OsConstants.PR_CAP_AMBIENT_RAISE,
           OsConstants.CAP_SYS_PTRACE, 0, 0);
  // Write to /data instead of /data/local/tmp. Because /data can be written by system user.
  Runtime.getRuntime().exec("/system/bin/simpleperf record -g -p " + String.valueOf(Process.myPid())
            + " -o /data/perf.data --duration 30 --log-to-android-buffer --log verbose");
} catch (Exception e) {
  Slog.e(TAG, "error while running simpleperf");
  e.printStackTrace();
}

硬件 PMU 计数器限制

监视指令和缓存相关的性能事件时 (在list命令列出的hw/cache/raw/pmu 类别),这些事件被映射到每个cpu核心上的PMU计数器。但每个核心只有有限数量的PMU计数器。如果事件数量 > PMU计数器的数量,然后计数器在事件之间多路复用,这可能不是我们想要的。

在Pixel设备上,每个核上的PMU计数器的数量通常是7个,其中4个被内核用于监视内存延迟。所以只有3个计数器可用。可以同时监控最多3个PMU事件。要监视3个以上的事件,可以使用 --use-devfreq-counters 选项借用内核使用的计数器。

定义

Simpleperf 是Google随NDK一起发布的一款profile工具(注:从NDK r13开始),它是针对Android平台的一个 native 层性能分析工具。

使用步骤

本篇是分析运行在android设备下的程序

1. 将NDK中Simpleperf工具的可执行程序 push 到手机上

cd <NDK>/simpleperf/bin/android/<对应的版本,根据被测程序和CPU,来选择对应的版本>

例如 :我的路径是 /home/hqb/Android/Sdk/ndk/21.3.6528147/simpleperf/bin/android/arm64

cd /home/hqb/Android/Sdk/ndk/21.3.6528147/simpleperf/bin/android/arm64
adb push simpleperf /data/local/tmp
adb shell chmod 777 /data/local/tmp/simpleperf

2. 启动手机上的被测程序,ps 出该程序的进程ID

adb shell ps -ef | grep "需要分析的应用程序包名"

3. 使用Simpleperf工具进行分析

(1)record-记录运行结果数据:

adb shell /data/local/tmp/simpleperf record -p <进程号> --duration <持续的时间(秒为单位)> -o <输出文件名称> --call-graph fp

--call-graph dwarf 用在32位系统中,64位则采用--call-graph fp

例子:adb shell /data/local/tmp/simpleperf record -p 4844 --duration 10 -o /data/local/tmp/perf.data --call-graph fp

输出:simpleperf I cmd_record.cpp:658] Samples recorded: 12013. Samples lost: 0.

(2)报告结果数据

adb shell /data/local/tmp/simpleperf report -i /data/local/tmp/perf.data -o /data/local/tmp/perf_report.txt

4. 解析火焰图

(1)将data文件和txt文件,从手机pull到电脑
电脑上新建simpleperf_test目录,例子:/home/hqb/simpleperf_test

mkdir simpleperf_test
adb pull /data/local/tmp/perf.data /home/hqb/simpleperf_test
adb pull /data/local/tmp/perf_report.txt /home/hqb/simpleperf_test

经常使用simpleperf就用alias设置别名

vim ~/.bashrc
alias pf='python /home/hqb/Android/Sdk/ndk/21.3.6528147/simpleperf/report_html.py'
source ~/.bashrc

(2)data格式转化成html格式

cd /home/hqb/simpleperf_test

//设置别名
pf -i ./perf.data -o ./perf.html

//如果不设置别名
python /home/hqb/Android/Sdk/ndk/21.3.6528147/simpleperf/report_html.py -i ./perf.data -o ./perf.html

(3)下载FlameGraph到simpleperf_test目录下,将simpleperf复制到simpleperf_test目录下

git clone <https://github.com/brendangregg/FlameGraph.git>
chmod 777 FlameGraph/flamegraph.pl
chmod 777 FlameGraph/stackcollapse-perf.pl

cp -r /home/hqb/Android/Sdk/ndk/21.3.6528147/simpleperf /home/hqb/simpleperf_test

(4)生成火焰图

cd /home/hqb/simpleperf_test
python ./simpleperf/report_sample.py > out.perf
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
./FlameGraph/flamegraph.pl out.folded > out.svg

out.svg就是最后得到的火焰图,用浏览器打开就可以看

/

Android Studio 包含 Simpleperf 的图形前端,记录在使用 CPU Profiler 检查 CPU Activity 中。大多数用户更喜欢使用该图形前端,而不是直接使用 Simpleperf。

如果您更喜欢使用命令行,可以直接使用 Simpleperf。Simpleperf 是一个通用的命令行 CPU 性能剖析工具,包含在面向 Mac、Linux 和 Windows 的 NDK 中。

Simpleperf包含两部分:simpleperf可执行文件和Python脚本。路径:Android/sdk/ndk/20.0.5594570/simpleperf

simpleperf可执行文件的工作方式类似于linux-tools-perf,但是具有针对Android分析环境的一些特定功能:详情参看simpleperf

Python 脚本根据功能划成如下三部分:

  1. 用于记录事件的脚本, like app_profiler.py, run_simpleperf_without_usb_connection.py.
  2. 用于报告的脚本, like report.py, report_html.py, inferno.
  3. 用于分析 profiling data的脚本, like simpleperf_report_lib.py.

1.事件

2.分析Android App性能

分析一个 Android 应用的性能可以按照如下三步走:

  1. 准备一个要分析的APP.

例如Google官方的实例:

$ git clone https://android.googlesource.com/platform/system/extras
$ cd extras/simpleperf/demo
# Open SimpleperfExamplesWithNative project with Android studio, and build this project
# successfully, otherwise the `./gradlew` command below will fail.
$ cd SimpleperfExampleWithNative

# On windows, use "gradlew" instead.
$ ./gradlew clean assemble
$ adb install -r app/build/outputs/apk/profiling/app-profiling.apk

2.抓取性能数据:

python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative --compile_java_code  -a .MixActivity --lib app/build/intermediates/cmake/debug/obj/arm64-v8a/

3.分析性能数据:

python report_html.py

4.查看对应的性能分析数据:

通过数据我们可以看到各个线程占用的CPU时钟周期

/

了多少事件的摘要。以下是它的工作原理:

给定用户选项,simpleperf通过对linux内核进行系统调用来启用分析;
Linux 内核在调度到被分析进程时启用计数器;
分析之后,simpleperf从内核读取计数器,并报告计数器摘要。
Record命令在一段时间内记录剖析进程的样本。它的工作原理如下:

给定用户选项,simpleperf通过对linux内核进行系统调用来启用分析;
Simpleperf在simpleperf和linux内核之间创建映射缓冲区;
Linux内核在调度到被分析进程时启用计数器;
每次给定数量的事件发生时,linux内核将样本转储到映射缓冲区;
Simpleperf从映射缓冲区读取样本并生成perf.data。
Report命令读取perf.data文件及所有被剖析进程用到的共享库,并输出一份报告,展示时间消耗在了哪里。


Simpleperf的使用
尽管不推荐使用一键脚本,但得益于Simpleperf的命令不多,使用起来也很简单快捷。

文件准备
将simpleperf可执行文件传输到Android设备上:

adb push 文件源地址 文件目的地址
1
可执行文件都存储在bin/android/目录下,可自行获取。

性能分析
在性能分析前,首先启动需要分析的APP,获取APP的进程号:

adb shell am start -n 包名/启动程序
adb shell pidof 包名
1
2
接下来,就可以使用命令三连击了:

adb shell
cd simpleperf所在目录
./simpleperf stat [options] [command [command-args]]
./simpleperf record [options] [command [command-args]]
./simpleperf report [options]
1
2
3
4
5
stat命令获取一段时间内已分析进程中发生的事件数摘要。 最常使用的选项为:

./simpleperf stat -p 进程号 --duration 检测进程的持续时间(秒)
1
record命令记录一段时间内已分析进程的样本,这是simpleperf的主命令。最常使用的选项为:

./simpleperf record -p 进程号 -o 输出文件(默认perf.data) --duration 监测进程的持续时间(秒)
1
需要注意的是,如果出现Access to kernel symbol addresses is restricted的警告,需要使用一下命令来取消:

echo 0>/proc/sys/kernel/kptr_restrict
1
report命令读取perf.data文件(由simpleperf record创建),并显示报告,表明花费时间的地方。最常使用的选项为:

./simpleperf report --dsos 选定动态共享对象(so库)  -f 记录文件(默认perf.data) --sort 用于排序和打印报告的键 -n
1
注意点
如果使用report命令进行查找的时候,发现so现实的Symbol都是地址,而不是函数内容。这多数是因为在安卓编译的时候,设备上使用的so库已经被strip过,也就是说,已经抛离了.symbol段的内容。

那么,我们需要将带有Symbol信息的so下载到设备上。同时需要将so放置到perf.data中记录的相同的路径(否则,simpleperf无法找到它)。

如果找不到路径,可以在perf.data文件中直接搜索需要选定的so库的名称,即可查看到路径。


实例
设备上执行命令:

./simpleperf  stat -p 5932 --duration 10
./simpleperf record -p 5932 -o dms.data --duration 10
./simpleperf report --dsos /data/app/com.hobot.dms.sample-1/lib/arm/libhobot_dms.so  --sort comm,pid,tid,symbol -n
1
2
3
通过report命令可以看到,主要是集中在libhobot_dms.so中。但是,显示的都是地址,并非函数调用。这是因为Android的so库已经抛离了.symbol段的内容。可以重新指定完整的so文件。


将perf.data从设备上拷贝下来,传输到Linux开发机上,执行命令:

./bin/linux/x86_64/simpleperf report --dsos /data/app/com.hobot.dms.sample-1/lib/arm/libhobot_dms.so  --sort comm,pid,tid,symbol
1

/

/

/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值