LoginSignup
3
4

More than 3 years have passed since last update.

[Android] Bootchartを描く

Last updated at Posted at 2019-09-10

AndroidでのBootchartの描き方

$ adb shell 'touch /data/bootchart/enabled'
$ adb reboot

これだけ。
簡単すぎて鼻血が出そう。
再起動後、PCからgrab-bootchart.shを実行すると、このとおり。

bootchart.png

実装

ちょっとこれだけだとあまりにもなので、実装を確認してみる。
ソースコード及び動作確認はAndroid 8.1.0。

/init.rcにて、post-fs-dataトリガで/dataパーティションにアクセスできるようになるとbootchart startコマンドが実行される。

/system/core/rootdir/init.rc
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プロセスのビルトイン関数の一つとして定義されている。

/system/core/init/builtins.cpp
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()が実行される。

/system/core/init/bootchart.cpp
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
/system/core/init/builtins.cpp
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_completedbootchartスレッドを停止する。

/system/core/rootdir/init.rc
on property:sys.boot_completed=1
    bootchart stop

grab-bootchart.shで、adb pullを使って収集データを転送し、
bootchart (pybootchartgui)コマンドでグラフ化している。
スクリプト実行後、グラフは./bootchart.pngに、生データは/tmp/android-bootchart/に残っているが、
再度スクリプトを走らせると消されるので注意。

/system/core/init/grab-bootchart.sh
#!/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'

参考

bootchart - Android source

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4