はじめに
組み込みの現場では、リソースの制約からデバッグシンボルがstrip
された実行ファイルが実機にインストールされていることが多いです。
結果として、perf top
を実行してもどの関数を実行中なのか基本的には解決できず、アドレスで表示されてしまいます。
perf
コマンドはそのような環境のために、実機で保存したprofile情報を、PCでデバッグシンボルとともに解析するオフライン解析の仕組みが用意されています。
本記事では、QEMU環境を例にクロス環境でperf
を使いオフライン解析をする流れを、備忘メモとして残しておきます。
今回使った環境は以下のとおりです。
- buildroot : 2020.02.3
- QEMU: 4.2.0
- OS (host) : Ubuntu 18.04.4
- linux kernel (host) : 5.3.0-62-generic
- perf (host) : 5.3.18
- linux kernel (target) : 4.19.91
- perf (target) : 4.19.91
実験環境構築
buildroot
を使って、aarch64
のQEMU環境を用意します。
(いつからかQEMUもビルドしてくれるようになったので、構築がさらに楽になってます。ありがたや。)
wget https://buildroot.org/downloads/buildroot-2020.02.3.tar.bz2
tar xf buildroot-2020.02.3.tar.bz2
cd buildroot-2020.02.3/
cat <<EOS >> configs/qemu_aarch64_virt_defconfig
BR2_ENABLE_DEBUG=y
BR2_PACKAGE_LINUX_TOOLS_PERF=y
EOS
cat <<EOS >> board/qemu/aarch64-virt/linux.config
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_KERNEL=y
EOS
make qemu_aarch64_virt_defconfig
make BR2_JLEVEL=$(nproc)
export PATH="$PWD/output/host/bin/:$PATH"
いくつか今回の実験用に、configを有効にしています。
-
Buildrootのconfig
-
BR2_ENABLE_DEBUG
: 各パッケージのビルド時に、デバッグシンボルを有効にします。staging
以下にデバッグシンボル付きのバイナリを保存します。 -
BR2_PACKAGE_LINUX_TOOLS_PERF
:perf
コマンドを追加します。
-
-
Linuxのconfig
-
CONFIG_DEBUG_INFO
: vmlinuxをデバッグシンボル付きでビルドします。 -
CONFIG_DEBUG_KERNEL
: kernelのデバッグ機能を有効にします。CONFIG_DEBUG_INFO
を有効にするために必要です。
-
readme.txtのとおり、下記コマンドでビルドした仮想環境を立ち上げることができます。
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -smp 1 -kernel output/images/Image -append "rootwait root=/dev/vda console=ttyAMA0" -netdev user,id=eth0 -device virtio-net-device,netdev=eth0 -drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
perf record
: プロファイル情報の取得
perf record
コマンドでプロファイル情報を取得できます。
取得するプロファイル情報はオプションによって色々と変更できますが、
ここでは-a
オプションでシステム全体のプロファイルを取得します。
また、--call-graph fp
オプションで、backtrace情報を併せて取得しています。
perf
はbacktraceの算出方法をいくつかサポートしていますが、ここではframe pointerを使た方式を指定しています。
[On Target (QEMU)]
perf record -a --call-graph fp
計測が完了したらCtrl-Cで終了します。
するとカレントディレクトリにperf.data
が保存されているので、これを解析するためホストPCに転送します。
ネットワーク転送など方法は色々ありますが、今回はQEMUなのでディスクイメージをそのまま覗きます。
[On Host PC]
cd output/images
mkdir -p mnt
sudo mount -t ext4 rootfs.ext2 mnt
sudo cp mnt/root/perf.data perf.data
sudo umount mnt
cd ../../
なお、多少負荷がかかっていないと見ても面白くないので、とりあえず計測中にseqを回しました。
[On Target (QEMU)]
seq 100000 > /dev/null
perf report
: 取得したプロファイル情報の解析
kernelのシンボルは-k
オプションで、ユーザ空間のシンボルは--symfs
で読み込むことができます。
確認した限り、kernelのシンボルとユーザ空間のシンボルを同時に読み込むことはできないようです。
少し手間ですが、都度切り替える必要がありそうです。
(2020/08/10追記) -k
オプションに対して絶対パスでkernelイメージを指定することで、kernelのシンボルとユーザ空間のシンボルを同時に読み込むことができます。
[On Host PC]
perf report -i output/images/perf.data --symfs output/staging/ -k ${PWD}/output/build/linux-4.19.91/vmlinux
seq
として起動されたbusybox
のseq_main
から呼び出されたprintf
で時間がかかっていることが読み取れます。
さらにa
を押下すると、命令・行レベルでどこに時間がかかったかを表示することができます。(perf annotate
相当)
[k]
となっているのがkernel空間で、[.]
となっているのがユーザ空間です。
例えばkernelで時間を使っているのは、swapper
コンテキストのarch_cpu_idle
であることが読み取れます。
なお、クロス環境でなければ-F srcline
や-F srcfile
などで、対応する行番号やファイル名も表示することができますが、
内部でaddr2line
が呼び分けられていないのか、今回の環境では表示できませんでした。
とはいえプロファイル情報全体に対してaddr2line
を実行するとかなり時間がかかり使い勝手が良くないので、
実際に行番号・ファイル名が必要になったときだけ別途addr2line
を呼び出す、というのはどちらにせよ現実的なやり方だと思います(言い訳)。
参考
Perf Wiki
Profiling your Applications using the Linux Perf Tools
Buildroot - Making Embedded Linux Easy