一、Bootchart
简介
Bootchart
官网
http://www.bootchart.org
,已经很久没有更新了。
Bootchart
的目的是将启动阶段的性能可视化(
Boot Process Performance Visualization
)。具体做法是在启动阶段通过采样
/proc
文件系统来搜集启动阶段的信息(如
CPU
负载,进程信息,磁盘访问等),然后通过另外的工具将搜集到的数据以可视化的方式进行输出。
因此,
bootchart
分为两个部分:
采样程序bootchartd
,系统启动后的第一个进程,采样并搜集启动过程中的/proc
数据,启动完成后将采样数据压缩存放到/var/log/bootchart/bootlog.tgz
文件中
外部工具pybootchartgui
,处理bootlog.tgz
文件,输出为图片
对于PC
,系统会在启动完成后自动生成bootlog.png
文件
对于嵌入式系统,需要将bootlog.tgz
导出到PC
上进行处理
下图是一个嵌入式系统上的
bootchart
输出的示例图片:
我在虚拟机上安装
bootchart
并抓取了启动过程数据,
点这里查看Ubuntu 12.04
启动后生成的图片 。
在
bootchart
生成的图像中,可以清楚的看到启动过程中
CPU
负载、磁盘吞吐和各进程实时的情况。
二、Bootchart
配置
Busybox
从
v1.17
版本开始引入
bootchartd
。
相比
PC
,嵌入式系统没有完备的
Bootchart
工具,启动过程中采样的数据需要导出在
PC
上进行处理。
Busybox
上通过执行
make menuconfig
配置
bootchartd
,具体位置如下:
ygu@ubuntu:/opt/work/busybox$ make menuconfig
Busybox Settings --->
--- Applets
...
Init Utilities --->
[*] bootchartd
[*] Compatible, bloated header
[*] Support bootchartd.conf
...
默认设置打开所有
bootchartd
设置:
设置总共有3项:
选项[*] bootchartd
,设置[BOOTCHARTD =y]
,是bootchart
功能开关
选项[*] Compatible, bloated header
,设置[FEATURE_BOOTCHARTD_BLOATED_HEADER =y]
,设置后bootchartd
会生成一个包含类似如下信息的header
文件:
version = 0.8
title = Boot chart for (none ) (Thu Jan 1 00 :01 :05 UTC 1970 )
system .uname = Linux 3.3 .8 -4.0
system .kernel.options = ubiroot init=/sbin/bootchartd ubi.mtd=rootfs rootfstype=ubifs root=ubi0:rootfs
选项[*] Support bootchartd.conf
,设置[FEATURE_BOOTCHARTD_CONFIG_FILE =y]
,设置后bootchartd
启动时会尝试读取并解析配置文件/etc/bootchartd.conf
,配置文件的格式类似如下:
#
# supported options:
#
# Sampling period (in seconds)
SAMPLE_PERIOD=0.2
#
# not yet supported:
#
# tmpfs size
# (32 MB should suffice for ~20 minutes worth of log data, but YMMV)
TMPFS_SIZE=32 m
# Whether to enable and store BSD process accounting information. The
# kernel needs to be configured to enable v3 accounting
# (CONFIG_BSD_PROCESS_ACCT_V3). accton from the GNU accounting utilities
# is also required.
PROCESS_ACCOUNTING="no"
# Tarball for the various boot log files
BOOTLOG_DEST=/var /log/bootchart.tgz
# Whether to automatically stop logging as the boot process completes.
# The logger will look for known processes that indicate bootup completion
# at a specific runlevel (e.g. gdm-binary, mingetty, etc.).
AUTO_STOP_LOGGER="yes"
# Whether to automatically generate the boot chart once the boot logger
# completes. The boot chart will be generated in $AUTO_RENDER_DIR.
# Note that the bootchart package must be installed.
AUTO_RENDER="no"
从
busybox
中
bootchartd
实现的代码来看,仅支持
SAMPLE_PERIOD
和
SAMPLE_PERIOD
两个选项。当然,也可以不用设置
/etc/bootchartd.conf
而使用代码中默认的设置。
三、Bootchart
运行
bootchart
的帮助信息:
Usage : bootchartd start [PROG ARGS]|stop|init
Options:
start: start background logging; with PROG, run PROG, then kill logging with USR1
stop: send USR1 to all bootchartd processes
init: start background logging; stop when getty/xdm is seen (for init scripts)
Under PID 1 : start background logging, then execute $bootchart_init, /init, /sbin/init
This makes it possible to start bootchartd even before init by booting kernel with :
init=/sbin/bootchartd bootchart_init=/path/to /regular/init
从帮助信息可见
bootchartd
有两个用途:
linux
启动时运行用于采样linux
启动过程中的各项数据
启动完成后运行,用于监测系统或指定应用程序
1. linux
启动时运行
这是使用最多的方式,在
linux
启动的命令行中指定
/sbin/bootchartd
为
init
进程。
带initramfs
的系统,需要在命令行指定rdinit
rdinit=/sbin/bootchartd
非initramfs
的系统,需要在命令行指定init
init=/sbin/bootchartd
linux
启动中,会用
/sbin/bootchartd
创建第一个进程,然后在
bootchartd
中再
fork
一个真正的
init
进程。如果在启动的同时通过命令行指定了
bootchart_init
参数,则用这个参数指定的程序用于
fork
生成的
init
进程,否则依次使用默认的
/init
或
/sbin/init
作为
init
进程。如:
init=/sbin/bootchartd bootchart_init=/path/to/regular/init
以下是我在博通7583参考平台上使用带文件系统的kernel启动的log:
CFE>
CFE> boot -z -elf 192.168 .1 .95 :7584 a0/vmlinuz-initrd-7584 a0 'rdinit=/sbin/bootchartd quiet'
Loader:elf Filesys:tftp Dev:eth0 File:192.168 .1 .95 :7584 a0/vmlinuz-initrd-7584 a0 Options:rdinit=/sbin/bootchartd quiet
Loading: 0x80001000 /11957248 0x80b68400 /110224 Entry address is 0x8045f360
Closing network.
Starting program at 0x8045f360
Linux version 3.3 .8 -4.0 (ygu@fs-ygu) (gcc version 4.5 .4 (Broadcom stbgcc-4.5 .4 -2.9 ) ) #5 SMP Tue Nov 29 14:23:04 CST 2016
Fetching vars from bootloader... found 14 vars.
Options: moca=0 sata=1 pcie=0 usb=1
Using 512 MB + 0 MB RAM (from CFE)
bootconsole [early0] enabled
CPU revision is : 0002 a065 (Broadcom BMIPS4380)
FPU revision is : 00130001
Determined physical RAM map:
memory: 10000000 @ 00000000 (usable)
memory: 10000000 @ 20000000 (usable)
No PHY detected, not registering interface :1
starting pid 429 , tty '' : '/etc/init.d/rcS'
Mounting virtual filesystems
Starting mdev
* WARNING: THIS STB CONTAINS GPLv3 SOFTWARE
* GPLv3 programs must be removed in order to enable security.
* See: http:
Configuring eth0 interface
Configuring lo interface
Starting network services
starting pid 459 , tty '' : '/bin/cttyhack /bin/sh -l'
#
# shell-init: error retrieving current directory: getcwd: cannot access parent directories: Success
#
# ls -lh /var/log/
-rw-r--r-- 1 root root 28.9 K Jan 1 00 :01 bootlog.tgz
#
可以看到,系统启动完成后会在
/var/log
目录下生成
bootlog.tgz
文件(
PC
上采样的数据文件位于
/var/log/bootchartd/
目录下)。将文件
/var/log/bootlog.tgz
复制到
PC
上备用。
在主机上安装
bootchart
工具,安装的同时还会安装
pybootchartgui
用于将采集的数据转换为图片。
ygu@ubuntu:~$ sudo apt-get install bootchart
[sudo] password for ygu:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
bootchart
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/11.0 kB of archives.
After this operation, 127 kB of additional disk space will be used.
Selecting previously unselected package bootchart.
(Reading database ... 170788 files and directories currently installed.)
Unpacking bootchart (from ... /bootchart_0.90.2-8ubuntu1_i386.deb) ...
Processing triggers for ureadahead ...
Setting up bootchart (0.90 .2 -8ubuntu1) ...
update-initramfs: Generating /boot/initrd.img-3.13 .0 -32 -generic
ygu@ubuntu:~$ scp ygu@192.168 .1 .95 :/opt/bootchartd/bootlog.tgz work/bootchart
bootlog.tgz 100 % 29KB 29. 3KB/s 00 :00
ygu@ubuntu:~$ cd work/bootchart
使用
pybootchartgui
处理
bootlog.tgz
:
ygu@ubuntu :~/work/bootchart $ ls -lh
total 32 K
-rw-r--r-- 1 ygu ygu 30 K Dec 1 10 : 28 bootlog.tgz
ygu@ubuntu :~/work/bootchart $
ygu@ubuntu :~/work/bootchart $ pybootchartgui bootlog.tgz
parsing 'bootlog.tgz'
parsing 'header'
parsing 'proc_diskstats.log'
parsing 'proc_ps.log'
warning: no parent for pid '2' with ppid '0'
parsing 'proc_stat.log'
merged 0 logger processes
pruned 29 process, 0 exploders, 0 threads, and 0 runs
False
Traceback (most recent call last):
File "/usr/bin/pybootchartgui" , line 23 , in <module >
sys.exit(main())
File "/usr/lib/pymodules/python2.7/pybootchartgui/main.py" , line 137 , in main
render()
File "/usr/lib/pymodules/python2.7/pybootchartgui/main.py" , line 128 , in render
batch.render(writer, res, options, filename)
File "/usr/lib/pymodules/python2.7/pybootchartgui/batch.py" , line 41 , in render
draw.render(ctx, options, *res)
File "/usr/lib/pymodules/python2.7/pybootchartgui/draw.py" , line 256 , in render
curr_y = draw_header(ctx, headers, off_x, duration)
File "/usr/lib/pymodules/python2.7/pybootchartgui/draw.py" , line 340 , in draw_header
txt = headertitle + ': ' + mangle(headers.get(headerkey))
TypeError : cannot concatenate 'str' and 'NoneType' objects
ygu@ubuntu :~/work/bootchart $
如上,由于
Ubuntu
版本的
pybootchartgui
不能解析
busybox
上的
bootchart
数据,所以这里出现了错误,需要用另外一个版本
bootchart2
的工具来处理。
用
git
下载
bootchart2
后需要执行
make
后才能使用
pybootchartgui
:
ygu@ubuntu:~/work/bootchart$ git clone https:
Cloning into 'bootchart' ...
remote: Counting objects: 2560 , done.
remote: Total 2560 (delta 0 ), reused 0 (delta 0 ), pack-reused 2560
Receiving objects: 100 % (2560 /2560 ), 1.79 MiB | 228 KiB/s, done.
Resolving deltas: 100 % (1600 /1600 ), done.
ygu@ubuntu:~/work/bootchart$ cd bootchart/
ygu@ubuntu:~/work/bootchart/bootchart$ make
cc -g -Wall -O0 -pthread
-DEARLY_PREFIX = '""'
-DLIBDIR = '"/lib"'
-DPKGLIBDIR = '"/lib/bootchart"'
-DPROGRAM_PREFIX = '""'
-DPROGRAM_SUFFIX = '""'
-DVERSION = '"0.14.8"'
-c collector/collector. c -o collector/collector. o
...
cc -g -Wall -O0 -pthread -Icollector -o bootchart-collector collector/collector. o collector/output. o collector/tasks. o collector/tasks-netlink . o collector/dump. o
sed -s -e "s:@LIBDIR@:/lib:g" -e "s:@PKGLIBDIR@:/lib/bootchart:" -e "s:@PROGRAM_PREFIX@::" -e "s:@PROGRAM_SUFFIX@::" -e "s:@EARLY_PREFIX@::" -e "s:@VER@:0.14.8:" bootchartd. in > bootchartd
...
sed -s -e "s:@LIBDIR@:/lib:g" -e "s:@PKGLIBDIR@:/lib/bootchart:" -e "s:@PROGRAM_PREFIX@::" -e "s:@PROGRAM_SUFFIX@::" -e "s:@EARLY_PREFIX@::" -e "s:@VER@:0.14.8:" pybootchartgui/main. py. in > pybootchartgui/main. py
ygu@ubuntu:~/work/bootchart/bootchart$
如果不编译,直接调用
pybootchartgui
会出现找不到
main
函数的错误:
ygu@ubuntu :~/work/bootchart $ ./bootchart/pybootchartgui.py bootlog.tgz
Traceback (most recent call last):
File "./bootchart/pybootchartgui.py" , line 20 , in <module >
from pybootchartgui.main import main
ImportError : No module named main
ygu@ubuntu :~/work/bootchart $
编译完成后,再次调用
pybootchartgui.py
处理
bootlog.tgz
:
ygu@ubuntu :~/work/bootchart $ ./bootchart/pybootchartgui.py bootlog.tgz
parsing 'bootlog.tgz'
parsing 'header'
parsing 'proc_diskstats.log'
parsing 'proc_ps.log'
parsing 'proc_stat.log'
merged 0 logger processes
pruned 29 process, 0 exploders, 0 threads, and 0 runs
bootchart written to 'bootchart.png'
ygu@ubuntu :~/work/bootchart $ ls -lh
total 96 K
drwxr-xr-x 6 ygu ygu 4.0 K Dec 1 10 : 45 bootchart
-rw-rw-r-- 1 ygu ygu 59 K Dec 1 10 : 46 bootchart.png
-rw-r--r-- 1 ygu ygu 30 K Dec 1 10 : 28 bootlog.tgz
ygu@ubuntu :~/work/bootchart $
将采集的数据转换为图片bootchart.png了,如下:
由于这里整个
linux
系统启动的任务比较简单,所以从
bootchart.png
上可见的任务也较少,这里寄希望于
bootchart
的结果来进行启动时间优化还是有些难度。
2. 监测系统或应用程序的运行情况
用于监测运行情况时需要给
bootchartd
指定参数,
start
参数开始监测,
stop
参数停止监测。
不过,系统启动后可以监测的手段较多,
bootchartd
工具并不是最优选择,非本文的介绍重点,暂略。
四、Bootchartd
源码分析
busyboxinitootchartd.c
:
int bootchartd_main(int argc UNUSED_PARAM, char **argv)
{
unsigned sample_period_us;
pid_t parent_pid, logger_pid;
smallint cmd;
int process_accounting;
enum {
CMD_STOP = 0 ,
CMD_START,
CMD_INIT,
CMD_PID1,
};
INIT_G();
parent_pid = getpid();
if (argv[1 ]) {
cmd = index_in_strings("stop