NotePublic/Software/Applications/BootChart/Android_8_上使用_BootChart.md

232 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Android 8 上使用 BootChart
## 1.基本原理
bootchart 可以在 Android 系统启动到 init 阶段时开始收集系统信息,并保存成 Log 文件。可在开发机上使用 bootchart 命令对这些 Log 文件进行分析,生成图表。
Android 8 上默认编译 bootchart 为 init 内置工具。使用方式可参考:
```bash
${AndroidSrc}/system/core/init/README.md
```
中与 bootchart 有关的内容。也可以通过分析以下文件了解 bootchart 的启动流程和触发条件。
```bash
${AndroidSrc}/system/core/init/Android.mk
${AndroidSrc}/system/core/init/builtins.cpp
${AndroidSrc}/system/core/init/bootchart.cpp
${AndroidOS}/init.rc
```
通过上述文件分析可知bootchart 会在
```bash
${AndroidOS}/data/bootchart/enabled
```
文件存在的情况下被使能。系统启动时,触发其执行的条件可以分析 init.rc 文件,主要触发流程为:
```ini
on property:sys.boot_from_charger_mode=1
trigger late-init
on late-init
trigger post-fs-data
on post-fs-data
bootchart start
```
结束 bootchart 执行的条件为:
```ini
on property:sys.boot_completed=1
bootchart stop
```
## 2.使用方法
在开发机上先安装 bootchart 和 pybootchartgui 用于处理和分析开机数据:
```bash
sudo apt install bootchart
sudo apt install pybootchartgui
```
接着在目标机上使能 bootchart
```bash
touch /data/bootchart/enabled
```
之后重启目标机系统bootchart 会在启动时自动开始和结束收集目标机系统的启动数据信息。系统启动后在 /data/bootchart/ 路径下会多出几个 Log 文件。使用 adb 命令将这些 Log 文件传输到开发机:
```bash
adb pull /data/bootchart/
```
接下来,在开发机上使用以下脚本文件对 Log 文件进行分析并生成 PNG 文件:
```bash
#!/bin/bash
FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
TMPTAR="bootchart.tgz"
tar -cvzf $TMPTAR $FILES
bootchart --format=svg -o bootchart.svg $TMPTAR
rm -rf $TMPTAR
```
或者可以使用源码中官方脚本一次完成 Log 文件的下载和分析工作:
```bash
${AndroidSrc}/system/core/init/grab-bootchart.sh
```
其内容如下:
```bash
#!/bin/sh
#
# This script is used to retrieve a bootchart log generated by init.
# All options are passed to adb, for better or for worse.
# See the readme in this directory for more on bootcharting.
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"
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"
```
## 3.性能对比
Google 还给我们提供了一个比较脚本,用来比较两次开机的数据:
```bash
${AndroidSrc}/system/core/init/compare-bootcharts.py
```
首先将两次得到的 Log 文件打包成 tgz 包并保存到不同的目录中如 cmp1_dir 和 cmp2_dir
```bash
FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
TMPTAR="bootchart.tgz"
cd cmp1_dir
tar -cvzf $TMPTAR $FILES
cd ../cmp2_dir
tar -cvzf $TMPTAR $FILES
cd ..
```
接着使用如下命令处理两次开机数据
```bash
${AndroidSrc}/compare-bootcharts.py cmp1_dir cmp2_dir
```
也可以将以上命令按自己的实际需求写成脚本文件来使用。
## 4.常见错误处理
### 4.1.ZeroDivisionError / substring not found
执行脚本时,如提示如下错误:
```bash
File "/usr/lib/pymodules/python2.6/pybootchartgui/draw.py", line
201, in draw_chart
yscale = float(chart_bounds[3]) / max(y for (x,y) in data)
ZeroDivisionError: float division
# 或
state = get_proc_state( sample.state )
File "/usr/lib/pymodules/python2.6/pybootchartgui/draw.py", line 105, in get_proc_state
return "RSDTZXW".index(flag) + 1
ValueError: substring not found
```
需修改 /usr/share/pyshared/pybootchartgui/ 或 /usr/lib/python2.7/dist-packages/pybootchartgui 目录的 draw.py parsing.py samples.py 三个文件。
```diff
diff a/draw.py b/draw.py
--- a/draw.py
+++ b/draw.py
@@ 195 @@ def draw_chart(ctx, color, fill, chart_bounds, data, proc_tree):
def transform_point_coords(point, x_base, y_base, xscale, yscale, x_trans, y_trans):
x = (point[0] - x_base) * xscale + x_trans
y = (point[1] - y_base) * -yscale + y_trans + bar_h
return x, y
- xscale = float(chart_bounds[2]) / max(x for (x,y) in data)
- yscale = float(chart_bounds[3]) / max(y for (x,y) in data)
+ xscale = float(chart_bounds[2]) / max(0.00001, max(x for (x,y) in data))
+ yscale = float(chart_bounds[3]) / max(0.00001, max(y for (x,y) in data))
first = transform_point_coords(data[0], x_shift, 0, xscale, yscale, chart_bounds[0], chart_bounds[1])
last = transform_point_coords(data[-1], x_shift, 0, xscale, yscale, chart_bounds[0], chart_bounds[1])
@@ 105 @@ def get_proc_state(flag):
- return "RSDTZXW".index(flag) + 1
+ return "RSDTZXW".find(flag) + 1
def draw_text(ctx, text, color, x, y):
diff a/parsing.py b/parsing.py
--- a/parsing.py
+++ b/parsing.py
@@ 166 @@ def _parse_proc_disk_stat_log(file, numCpu):
for sample1, sample2 in zip(disk_stat_samples[:-1], disk_stat_samples[1:]):
interval = sample1.time - sample2.time
sums = [ a - b for a, b in zip(sample1.diskdata, sample2.diskdata) ]
+ if interval == 0:
+ interval = 1
readTput = sums[0] / 2.0 * 100.0 / interval
writeTput = sums[1] / 2.0 * 100.0 / interval
diff a/samples.py b/samples.py
--- a/samples.py
+++ b/samples.py
@@ 82 @@ def calc_load(self, userCpu, sysCpu, interval):
+ if interval == 0:
+ interval = 1
userCpuLoad = float(userCpu - self.last_user_cpu_time) / interval
sysCpuLoad = float(sysCpu - self.last_sys_cpu_time) / interval
```