TL;DR
SystemTapをクロス開発環境でビルドして使う方法のメモ。
-
stap
コマンドをホストPC用にコンパイル -
staprun
コマンドをターゲット用にコンパイル -
stap
コマンドでsystemtapスクリプトから.ko
ファイルをビルド
(クロスコンパイラ、ターゲットのLinuxカーネルビルドツリーが必要) -
.ko
ファイルをターゲットにインストールし、staprun
コマンドでload -> probe実行
PC等のセルフホスト環境では、stap
コマンドはSystemTapのフロントエンドコマンドとして、ビルドからloadまでワンストップで実行してくれる。
クロス開発の場合、通常ビルドはホストPCで、.ko
ファイルのloadはターゲットで実行しなければならない。
このようなケースのために、load等のランタイム処理はstaprun
コマンドに分離してくれているので、staprun
コマンドのみクロスコンパイルすればよい。
そのためのconfigure
オプションとして、--disable-translator
が用意されている。
SystemTapとは
独自スクリプト言語で記述したprobe処理を、ローダブルカーネルモジュールにビルドしてloadすることで、カーネルを再ビルドすること無くカーネルを動的に解析するための仕組み。
kprobeをDSLで抽象度を上げたもの、と考えると理解しやすいかも。
よく比較されるものとして、perf, ftrace, eBPFなどがある。(比較表)
PC等のセルフホスト環境では、stap
コマンドはSystemTapのフロントエンドコマンドとして、ビルドからprobeまでワンストップで実行してくれる。
stap
コマンドは下記5個のpassからなる。(man stap(1) #PROCESSING)
-
Pass 1. parse
stpスクリプトから、コメントの除去、()
や;
の付与、$1
など引数の置換などを行う。
Cプログラムのビルドプロセスにおけるプリプロセスに近い。 -
Pass 2. elaborate
kernelのデバッグシンボルから、スクリプト内で参照するシンボルが解決可能か確認する。
不要なコードの削除など、最適化も行う。
ただし、あくまでもstpスクリプトレベルでの最適化。 -
Pass 3. translate
stpスクリプトをC言語に変換する。
併せてビルドに必要なMakefileも作成する。 -
Pass 4. compile
.c
ファイルをビルドし、.ko
ファイルを生成する。 -
Pass 5. run
.ko
ファイルをinsmodし、probeした結果を取得して標準出力 or ファイルに出力する。
実際にはstap
コマンドはこの処理を、staprun
コマンドに委譲している。
クロス開発環境で考えると、Pass 1〜4はホストPCで実施可能。
Pass 5はターゲット上での実行しなければならない。
QEMU(aarch64)上のlinux上でSystemTapを使うまでの手順
- Host PC : Ubunt 16.04 (x86_64)
- buildroot : 2019.02.4
- linux : 4.19.16
- qemu : 4.0.0
- systemtap : 4.1
1. QEMUをコンパイル
特別な手順はなし。
wget https://download.qemu.org/qemu-4.0.0.tar.bz2
tar xf qemu-4.0.0.tar.bz2
mkdir -p build-qemu-4.0.0
cd build-qemu-4.0.0
../qemu-4.0.0/configure --target-list=aarch64-softmmu
make -j$(getconf _NPROCESSORS_ONLN)
cd ..
2. buildrootでターゲットのlinux / rootfsをコンパイル
wget https://buildroot.org/downloads/buildroot-2019.02.4.tar.bz2
tar xf buildroot-2019.02.4.tar.bz2
cd buildroot-2019.02.4
buildrootのconfigを変更。
SystemTap観点で追加が必要なのはELFUTILSぐらい。
あとは効率化のためのCCACHE
やsshのためのDROPBEAR
など。
--- qemu_aarch64_virt_defconfig.org 2019-08-17 13:03:12.239335388 +0900
+++ qemu_aarch64_virt_defconfig 2019-08-17 13:03:44.551346868 +0900
@@ -21,3 +21,14 @@
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
+
+BR2_CCACHE=y
+BR2_TOOLCHAIN_BUILDROOT_WCHAR=y
+BR2_TOOLCHAIN_BUILDROOT_CXX=y
+BR2_TARGET_GENERIC_ROOT_PASSWD="password"
+BR2_PACKAGE_STRACE=y
+BR2_PACKAGE_STRESS=y
+BR2_PACKAGE_OPENSSL=y
+BR2_PACKAGE_ELFUTILS=y
+BR2_PACKAGE_DROPBEAR=y
+# BR2_PACKAGE_DROPBEAR_SMALL is not set
linuxのconfigも変更。
SystemTapを使うためには、RELAY
, KPROBE
, DEBUG_INFO
, DEBUG_KERNEL
の追加が必要だった。
GDB_SCRIPTS
とFUNCTION_TRACER
はおまけ。
--- linux.config.org 2019-08-17 13:08:45.843587678 +0900
+++ linux.config 2019-08-17 13:10:14.427693496 +0900
@@ -4,7 +4,9 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_TASKSTATS=y
CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
CONFIG_PROFILING=y
+CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_BLK_DEV_BSGLIB=y
@@ -52,3 +54,7 @@
CONFIG_EXT4_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_DEBUG_INFO=y
+CONFIG_GDB_SCRIPTS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_FUNCTION_TRACER=y
make qemu_aarch64_virt_defconfig
make BR2_JLEVEL=$(getconf _NPROCESSORS_ONLN)
cd ..
公式Wikiで指定されているカーネルコンフィグは以下の通り。
CONFIG_DEBUG_INFO_SPLIT
のみn
なので注意。
CONFIG_DEBUG_INFO=y # stpスクリプトで参照するシンボルを解決するために必要。
CONFIG_KPROBES=y # Kernel空間をprobeするための仕組み。SystemTapはバックエンドでkprobeが使われているため必須。
CONFIG_RELAY=y # CPUごとのカーネルバッファをユーザ空間に見せるための仕組み。probe結果の出力に使われている。
CONFIG_DEBUG_FS=y # 上記relay fileはdebugfs(/sys/kernel/debug/systemtap/)上に作られる。
CONFIG_MODULES=y # SystemTap(kprobe)はLoadable Kernel Moduleにprobe処理を入れることでkernel空間を盗み見る。
CONFIG_MODULE_UNLOAD=y # 同上。
CONFIG_UPROBES=y # kprobesのUser空間版。User空間のprobeもするためには必要。
CONFIG_DEBUG_INFO_SPLIT=n # stapはsplit debug infoに対応していないので、無効にする必要がある。
3. stap
コマンドをホストPC用にコンパイル
stap
コマンドはホストPC用なので、普通にビルドすればいい。
手元の環境ではlibdw-dev
パッケージが必要だった。
sudo apt install libdw-dev
wget https://sourceware.org/systemtap/ftp/releases/systemtap-4.1.tar.gz
tar xf systemtap-4.1.tar.gz
mkdir -p build-host-systemtap-4.1/
cd build-host-systemtap-4.1
../systemtap-4.1/configure --prefix=${PWD}/destdir
make -j$(getconf _NPROCESSORS_ONLN)
make install
cd ..
4. staprun
コマンドをターゲット用にコンパイル
続いて、staprun
コマンドをターゲット用にコンパイルする。
基本的には通常のクロスコンパイルだが、stap
コマンドが不要なので--disable-translator
オプションをつけておく。
ここではまとめてインストールしているが、個別でインストールする場合はstaprun
だけでなくstapio
も必要なので注意。
stapio
はstaprun
から更にフォークされる。
/sys/kernel/debug/systemtap/${MOD_NAME}/trace${CPU_ID}
からデータを読み出し、標準出力 or ファイルに出力する。
export PATH=${PWD}/buildroot-2019.02.4/output/host/bin/:${PATH}
mkdir -p build-aarch64-systemtap-4.1
cd build-aarch64-systemtap-4.1
../systemtap-4.1/configure \
--host=aarch64-buildroot-linux-uclibc \
--with-sysroot=${PWD}/../buildroot-2019.02.4/output/staging \
--disable-docs \
--disable-http \
--disable-server \
--disable-translator \
--disable-ssp
make -j$(getconf _NPROCESSORS_ONLN)
DESTDIR=${PWD}/../buildroot-2019.02.4/output/target/ make install
cd ..
staprun
コマンドがbuildrootのrootfsにインストールされたので、イメージを再生成する。
make -C buildroot-2019.02.4/ BR2_JLEVEL=$(getconf _NPROCESSORS_ONLN)
5. stap
コマンドで.ko
ファイルをビルド
PC用でも同じだが、任意のSystemTapスクリプトをビルドするためには、
stapdev
グループとstapusr
グループの両方のメンバーである必要がある。(man stap(1) #PERMIMSSIONS)
sudo adduser $(whoami) stapdev
sudo adduser $(whoami) stapusr
下記コマンドで、stap_hello_world.ko
が生成される。
export PATH=${PWD}/buildroot-2019.02.4/output/host/bin/:${PATH}
build-host-systemtap-4.1/destdir/bin/stap \
-a arm64 \
-B CROSS_COMPILE=aarch64-buildroot-linux-uclibc- \
-r ${PWD}/buildroot-2019.02.4/output/build/linux-4.19.16/ \
-e 'probe begin { log ("hello " . k) exit () } global k="world" ' \
-m stap_hello_world
6. qemuの起動
起動時のオプションはbuildroot-2019.02.4/board/qemu/aarch64-virt/readme.txt
を参考にする。
sshで接続用にTCPポートのフォワードだけ追加している。
build-qemu-4.0.0/aarch64-softmmu/qemu-system-aarch64 \
-M virt \
-cpu cortex-a53 \
-nographic \
-smp 1 \
-kernel buildroot-2019.02.4/output/images/Image \
-append "rootwait root=/dev/vda console=ttyAMA0" \
-netdev user,id=eth0,hostfwd=tcp::10022-:22 \
-device virtio-net-device,netdev=eth0 \
-drive file=buildroot-2019.02.4/output/images/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-gdb tcp::4321
7. staprun
コマンドでprobe実行
めでたしめでたし。
# scp -P 10022 stap_hello_world.ko root@localhost:
# ssh -p10022 root@localhost /usr/local/bin/staprun stap_hello_world.ko
hello world
# ssh -p10022 root@localhost /usr/local/bin/staprun stap_hello_world.ko k=hoge
hello hoge
参考
SystemTap公式
man stap(1)
man staprun(8)
SystemTap公式git README
SystemTap公式Wiki Using systemtap with self-built kernels
SystemTap公式Wiki CrossCompiling
SystemTap公式Wiki 比較表
Brendan Gregg's Blog Choosing a Linux Tracer (2015)
睡眠不足 perf, ftraceのしくみ