AndroidでのBootchartの描き方
$ adb shell 'touch /data/bootchart/enabled'
$ adb reboot
これだけ。
簡単すぎて鼻血が出そう。
再起動後、PCからgrab-bootchart.shを実行すると、このとおり。
実装
ちょっとこれだけだとあまりにもなので、実装を確認してみる。
ソースコード及び動作確認はAndroid 8.1.0。
/init.rc
にて、post-fs-data
トリガで/data
パーティションにアクセスできるようになるとbootchart start
コマンドが実行される。
on post-fs-data
...
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
bootchart start
bootchart
コマンドは、initプロセスのビルトイン関数の一つとして定義されている。
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, do_bootchart}},
do_bootchart_start()
が呼びだされ、そこで/data/bootchart/enabled
ファイルの有無が確認される。
中身によらずファイルが存在する場合は、新しいスレッドにてbootchart_thread_main()
が実行される。
static int do_bootchart_start() {
// We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
std::string start;
if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
LOG(VERBOSE) << "Not bootcharting";
return 0;
}
g_bootcharting_thread = new std::thread(bootchart_thread_main);
return 0;
}
bootchart_thread_main()
では、200msに一度procfsの中身を/data/bootchart/
に収集する。
収集しているのは、下記の4種。
/proc/stat
/proc/diskstats
/proc/<pid>/cmdline
/proc/<pid>/stat
static void bootchart_thread_main() {
LOG(INFO) << "Bootcharting started";
// Open log files.
auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
if (!stat_log) return;
auto proc_log = fopen_unique("/data/bootchart/proc_ps.log", "we");
if (!proc_log) return;
auto disk_log = fopen_unique("/data/bootchart/proc_diskstats.log", "we");
if (!disk_log) return;
log_header();
while (true) {
{
std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex);
g_bootcharting_finished_cv.wait_for(lock, 200ms);
if (g_bootcharting_finished) break;
}
log_file(&*stat_log, "/proc/stat");
log_file(&*disk_log, "/proc/diskstats");
log_processes(&*proc_log);
}
LOG(INFO) << "Bootcharting finished";
}
boot_completed
でbootchart
スレッドを停止する。
on property:sys.boot_completed=1
bootchart stop
grab-bootchart.sh
で、adb pull
を使って収集データを転送し、
bootchart (pybootchartgui)
コマンドでグラフ化している。
スクリプト実行後、グラフは./bootchart.png
に、生データは/tmp/android-bootchart/
に残っているが、
再度スクリプトを走らせると消されるので注意。
#!/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"
おわりに
こういうお膳立てを知ると、「おぉさすがAndroid。。。」と感じる。
まだまだいっぱいあるんだろうなぁ。
ちなみに/data/bootchart/enabled
が残ってると、
無駄にデータ収集が走りつづけるので、忘れずに消しましょう。
$ adb shell 'rm /data/bootchart/enabled'