2019-02-28 10:44:12 +08:00
|
|
|
|
# [BootChart 分析 Android 6.0 开机性能](https://blog.csdn.net/whurs/article/details/67062678)
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
Bootchart 是一个用于 Linux 启动过程性能分析的开源软件工具,以可视化的方式对 GUN/Linux 的开机启动过程进行性能分析,包括资源的使用(如 CPU,磁盘等),各进程的执行时间信息等。根据分析结果,确定系统启动的性能瓶颈,制定相应的优化策略。由于 Android 系统是基于 Linux 的,所以我们可以使用 Bootchart 来分析开机性能。实际上在 Android 中已经集成了 Bootchart 这一开源工具供我们使用,只是在 Android 5.1 之前默认是没有编译进系统的,需要我们手动编译进去使用,关于如何在 Android 5.1 以下的系统使用 Bootchart 网上有很攻略,这里就不在赘述,这里可以参考这篇帖子-《bootchart 工具在 Android 系统开机测量中的应用》,来实现在 Android 5.1 以下的系统中集成 Bootchart,然后手机开机日志和数据并利用收集的数据生成可视化图片,供我们分析 Android 的开机性能。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
从 Android 6.0 开始,Google 已经在 Android 系统中默认集成了 Bootchart。使用 adb shell 命令进入 Android 虚拟机,我们可以在 data 目录下查看到 bootchart 这一子目录。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
因为使用它来进行性能分析时,本身就是比较耗性能的。所以默认是没有打开 Bootchart 开关的,我们也可以进入 bootchart 目录查看,里面什么也没有。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
## 1、性能数据收集
|
|
|
|
|
|
2020-12-28 12:39:13 +08:00
|
|
|
|
### 1.1、打开 Bootchart
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
我们使用下面的命令打开 Bootchart 来收集开机性能数据(主要就是记录日志文件):
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 14:48:51 +08:00
|
|
|
|
# 在data/bootchart/目录中新建start文件
|
2020-02-10 13:14:48 +08:00
|
|
|
|
adb shell 'touch /data/bootchart/start'
|
2020-02-10 14:48:51 +08:00
|
|
|
|
# 在start文件中写入采用时间timeout=120s
|
|
|
|
|
# 这里的时间可以自定义,通过查看源代码可知最长时间不能超过10*60 s
|
2020-02-10 13:14:48 +08:00
|
|
|
|
adb shell 'echo 120 > /data/bootchart/start'
|
2020-02-10 14:48:51 +08:00
|
|
|
|
# 在data/bootchart/目录中新建stop文件
|
2020-02-10 13:14:48 +08:00
|
|
|
|
adb shell 'touch /data/bootchart/stop'
|
2020-02-10 14:48:51 +08:00
|
|
|
|
# 在stop文件中写入1标记,用于停止采集数据
|
2020-02-10 13:14:48 +08:00
|
|
|
|
adb shell 'echo 1 > /data/bootchart/stop'
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
### 1.2、收集开机性能数据
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
开启 Bootchart 后,我们就可以重启手机并收集数据了。待手机开机后,我们再进入 /data/bootchart/ 目录下,就发现其中包含如下文件(header、kernel_pacct、proc_diskstats.log、proc_ps.log、proc_stat.log)。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
## 2、开机性能数据处理
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
得到上述的开机性能数据后,我们需要在 Linux 环境下打包这些数据并做可视化处理输出性能分析图表。为此 Google 已经给我们在源码 /system/core/init/ 目录下写了一个 grab-bootchart.sh 脚本,专门用来处理这些数据并做可视化处理:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2019-02-28 10:42:14 +08:00
|
|
|
|
TMPDIR=/tmp/android-bootchart
|
|
|
|
|
rm -rf $TMPDIR
|
|
|
|
|
mkdir -p $TMPDIR
|
|
|
|
|
|
|
|
|
|
LOGROOT=/data/bootchart
|
|
|
|
|
TARBALL=bootchart.tgz
|
|
|
|
|
|
|
|
|
|
FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
|
|
|
|
|
|
|
|
|
|
for f in $FILES; do
|
|
|
|
|
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
|
|
|
|
|
done
|
|
|
|
|
(cd $TMPDIR && tar -czf $TARBALL $FILES)
|
|
|
|
|
bootchart ${TMPDIR}/${TARBALL}
|
|
|
|
|
gnome-open ${TARBALL%.tgz}.png
|
|
|
|
|
echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
|
|
|
|
|
```
|
|
|
|
|
|
2020-12-28 12:39:13 +08:00
|
|
|
|
### 2.1、配置 Linux 环境
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
参照上面 Google 的脚本,我们在自己的 Linux 环境中处理这些日志数据,首先需要在 Linux 中安装 bootchart、pybootchartgui 和 gnome-open 工具:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
sudo apt-get install bootchart
|
|
|
|
|
sudo apt-get install pybootchartgui
|
|
|
|
|
sudo apt-get install gnome-open
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
### 2.2、处理数据
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
在 Linux 中安装好必备软件后,将手机 /data/bootchart/ 中的日志数据导入到 Linux 环境中,并并参照 Google 的处理脚本,根据自己实际情况写了一个处理脚本 handle―bootchart.sh
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
TARBALL=bootchart.tgz
|
|
|
|
|
FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
|
|
|
|
|
tar -czf $TARBALL $FILES
|
|
|
|
|
bootchart $TARBALL
|
|
|
|
|
gnome-open ${TARBALL%.tgz}.png
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
最终将分析的结果生成可视化的图片。
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
通过图片中的时间线 timeline 上各个进程的启动、IO 处理等,我们大致可以分析在开机过程中哪个部分比较耗时,待查找出后去重点优化。
|
|
|
|
|
从 Android 6.0 开始,Google 还给我们提供了一个比较脚本 /system/core/init/compare-bootcharts.py 用来比较两次开机的数据。以我上面的 Linux 环境为基础,将这个 python 脚本导入 Linux 环境中,打开该 python 脚本:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
def main():
|
|
|
|
|
print "sys.argv[1]:" + sys.argv[1]
|
|
|
|
|
print "sys.argv[2]:" + sys.argv[2]
|
|
|
|
|
if len(sys.argv) != 3:
|
|
|
|
|
print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
process_map1 = {}
|
|
|
|
|
process_map2 = {}
|
|
|
|
|
parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
|
|
|
|
|
parse_proc_file(sys.argv[2], process_map2)
|
|
|
|
|
analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|
|
|
|
|
```
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
我们这里需要将两次打包得到的两个压缩包 bootchart.tgz 分别保存在 base_bootchart_dir 和 exp_bootchart_dir 目录中,然后运行下面的命令来执行脚本:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
./compare-bootcharts.py base_bootchart_dir exp_bootchart_dir
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
得到两个比较的结果如下图所示:
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
这个脚本中只比较了几个重要的核心进程(init、surfacefliger、bootanimation、zygote64、zygote)的启动时间。例如两次的 bootanimation 启动时间都是 5530ms,因此第二次比第一次 +0ms。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
ps:在我的 Linux 环境使用这个脚本时遇到了如下几个问题,在此贴出来仅供大家参考
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
1、Linux 环境中软件或者对应的包没有,导致运行脚本时报错
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
这是没有安装 imagemagick 和 graphicsmagick-imagemagick-compat 的原因,按照提示使用 apt-get install <选定的软件包> 命令安装对应的软件。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
2、注意 Linux 环境下 python 解析器的安装位置与脚本中指定的解析器位置是否一致,一般我们写的 python 脚本第一行是:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```python
|
2020-02-10 13:14:48 +08:00
|
|
|
|
#!/usr/bin/python
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
这就限定死了解析器的位置,而 Google 的这个解析脚本中的第一行是:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```python
|
2020-02-10 13:14:48 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
总之要让脚本找到对应的解析器来解析这个 python 脚本,至于这两种写法的区别,参考博客-《#!/usr/bin/env python 与 #!/usr/bin/python 的区别》,推荐使用 #!/usr/bin/env python 这种写法。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
3、Linux 下 python 脚本的文档格式使用 Unix,不要使用 Windows。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
参考:
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
其中涉及到的源码文件和说明文件都在 system/core/init/ 目录下
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
bootchart.cpp
|
|
|
|
|
compare-bootcharts.py
|
|
|
|
|
grab-bootchart.sh
|
|
|
|
|
readme.txt
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
## 3、部分源码解读
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
// bootchart.cpp
|
|
|
|
|
|
2020-02-10 13:14:48 +08:00
|
|
|
|
...
|
2019-02-28 10:42:14 +08:00
|
|
|
|
// Max polling time in seconds.
|
|
|
|
|
static const int BOOTCHART_MAX_TIME_SEC = 10*60;
|
2020-02-10 13:14:48 +08:00
|
|
|
|
...
|
2019-02-28 10:42:14 +08:00
|
|
|
|
static int bootchart_init() {
|
|
|
|
|
int timeout = 0;
|
2020-02-10 15:37:43 +08:00
|
|
|
|
// 从start文件中读取采集时间并赋给timeout
|
2019-02-28 10:42:14 +08:00
|
|
|
|
std::string start;
|
|
|
|
|
android::base::ReadFileToString(LOG_STARTFILE, &start);
|
|
|
|
|
if (!start.empty()) {
|
|
|
|
|
timeout = atoi(start.c_str());
|
|
|
|
|
} else {
|
|
|
|
|
// When running with emulator, androidboot.bootchart=<timeout>
|
|
|
|
|
// might be passed by as kernel parameters to specify the bootchart
|
|
|
|
|
// timeout. this is useful when using -wipe-data since the /data
|
|
|
|
|
// partition is fresh.
|
|
|
|
|
std::string cmdline;
|
|
|
|
|
const char* s;
|
|
|
|
|
android::base::ReadFileToString("/proc/cmdline", &cmdline);
|
|
|
|
|
#define KERNEL_OPTION "androidboot.bootchart="
|
|
|
|
|
if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
|
|
|
|
|
timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
return 0;
|
2020-02-10 15:37:43 +08:00
|
|
|
|
// 最长的采集时间不能超过BOOTCHART_MAX_TIME_SEC=10*60s
|
2019-02-28 10:42:14 +08:00
|
|
|
|
if (timeout > BOOTCHART_MAX_TIME_SEC)
|
|
|
|
|
timeout = BOOTCHART_MAX_TIME_SEC;
|
2020-02-10 13:14:48 +08:00
|
|
|
|
...
|
2020-12-28 12:43:51 +08:00
|
|
|
|
}
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
|
|
|
|
static int bootchart_step() {
|
|
|
|
|
do_log_file(log_stat, "/proc/stat");
|
|
|
|
|
do_log_file(log_disks, "/proc/diskstats");
|
|
|
|
|
do_log_procs(log_procs);
|
|
|
|
|
//当stop文件中包含1时,就停止采集
|
|
|
|
|
// Stop if /data/bootchart/stop contains 1.
|
|
|
|
|
std::string stop;
|
|
|
|
|
if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 4、to do
|
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
在 Android 7.0 中,当我们按照 Android 6.0 的方式打开 Bootchart 采集数据并关机重启时,会出现系统起不来的情况,参考了网上的帖子-《Android 7.0 bootchart 工具使用说明》,是由于 bootchart.cpp 文件中存在 bug。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
按照上面的方法打开 Bootchart 采集数据并分析的方案,不能使用在恢复出厂设置或者手机刚烧录好版本后第一次开机的情况,而往往在这种情况下,手机开机比较慢,也是我们重点需要优化的。但通过查看源代码,我们可以在 bootchart.cpp 中写死一个 timeout 值,然后重新编译进系统刷机。
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
before:
|
|
|
|
|
int timeout = 0;
|
|
|
|
|
|
|
|
|
|
after:
|
|
|
|
|
int timeout = 120;
|
|
|
|
|
```
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
由于 Android 是架构在 Linux 虚拟机上的,因此对 Android 的开机优化与 Linux 虚拟机有着很大的联系。通过上面得出的图表分析出哪些耗时进程,然后去重点优化便可提升 Android 的开机速度。下面是几篇比较好的使用 Bootchart 来优化 Android 开机时间的帖子,仅供参考:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 13:14:48 +08:00
|
|
|
|
* [《Android/Linux boot time分析优化》](http://www.cnblogs.com/arnoldlu/p/6266331.html)
|
|
|
|
|
* [《android启动速度优化》](http://blog.chinaunix.net/uid-21564437-id-3954560.html)
|
|
|
|
|
* [《Android开机速度优化简单回顾》](http://blog.csdn.net/freshui/article/details/53700771)
|
|
|
|
|
* [《Android bootchart分析》](http://blog.csdn.net/weiqifa0/article/details/48996033)
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
Android O 中对开机速度做了一个很大的提升,以下是官方给出的一张开机时间线图:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-02-10 15:37:43 +08:00
|
|
|
|
从 Android N 的 27.6s 到 Android O 的 13.3s,可以看出 Google 在 Android O 上对开机时间做了很大的优化。我们要在 Android O 中打开 Bootchart 并分析开机时间,使用如下命令:
|
2019-02-28 10:42:14 +08:00
|
|
|
|
|
2020-05-20 16:18:39 +08:00
|
|
|
|
```bash
|
2020-02-10 13:14:48 +08:00
|
|
|
|
# To enable bootchart:
|
|
|
|
|
adb shell 'touch /data/bootchart/enabled'
|
|
|
|
|
adb reboot
|
|
|
|
|
```
|