【Linux】カーネルビルドでも進捗が見たい!
2020/04/27私はカジュアルにLinux kernelをビルドするのですが,進捗が表示されないのは困ります.
そこで, ビルドの進捗状況を表示するCLIツール「kbuild-progress」を作りました. より効率的な手法を思いつきましたので実装し直しました.こちらのkbuild-progressをお使いください(2021/07/31追記)
背景
私は大学院でLinux kernelに関する研究をしているため,1日に何度もkernelをビルドします.kernelはとても巨大なプログラムなので,(設定にもよりますが)新しめのノートPCでも ビルドに約30分 かかります.
ビルド作業といっても,基本的にコマンドを実行して待つだけです.ビルドを実行すると次のようなログが表示されます.
あとはひたすら待つだけです.
「…」
「…」
「これどのくらいかかるんだろう…」
「…」
「…」
「早く終わんねーかな」
「…」
「…」
「ビルドしてて暇だし,息抜きにゲームでもするかな!」
(ゲームを起動する)
「よーし,osuやるぞー!」
「…ん?」
「 ビルドおわってる… 」
(ゲームを終了する)
そうです.ビルドがどのくらい進んでいるのか,あとどのくらいで終わるのか, ビルドログからわからない のです.ビルドの進捗状況がわかれば,カジュアルにゲームをしたり,カジュアルにコンビニに行ったり,時間を有効に使えます.
kernelビルドでもプログレスバーのようなものが表示されたら便利ですよね.
実装
ある時点での進捗は,「完了している仕事数 / 全体の仕事数」で表せます.どうにかしてこれら2つの数を取得しなくてはなりません.
そこでいろいろ考えた結果,ビルドをドライランする方法を思いつきました.kernelビルドにも使われているGNU Makeには,ドライランを行うオプションがあります.説明を見てみましょう.
-n, --just-print, --dry-run, --recon
実行するコマンドの表示だけを行い、 (特定の状況を除いては) 実際の実行を行わない。
いいですね.これでビルドコマンドの一覧がわかれば,ビルド対象ファイルの一覧が得られ,「全体の仕事数」がわかるかも.
make -n vmlinux
して,試しにコマンドを1つ見てみます.
set -e; echo ' CC scripts/mod/empty.o'; gcc -Wp,-MD,scripts/mod/.empty.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/7/include -I./arch/x86/inclu
de -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -includ
e ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -
fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3d
now -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmo
del=kernel -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_AVX5
12=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -DCONFIG_AS_ADX=1 -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect
-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -O2 --param=allow-store-data-ra
ces=0 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-unused-but-set-variable -Wimplicit-fallthrough -Wno-unused-const-variable -fomit-frame-pointer -fno
-var-tracking-assignments -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check
-fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -DKBUILD_MODFILE='"scripts/mod/empty"' -DKBUILD_BASENAME='"emp
ty"' -DKBUILD_MODNAME='"empty"' -c -o scripts/mod/empty.o scripts/mod/empty.c; scripts/basic/fixdep scripts/mod/.empty.o.d scripts/mod/empty.o 'gcc -Wp,-MD,scri
pts/mod/.empty.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/7/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/inc
lude/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_t
ypes.h -D__KERNEL__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-dec
laration -Werror=implicit-int -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -m
no-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DC
ONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_AVX512=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -DCONFIG_AS_
ADX=1 -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-chec
ks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -O2 --param=allow-store-data-races=0 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-un
used-but-set-variable -Wimplicit-fallthrough -Wno-unused-const-variable -fomit-frame-pointer -fno-var-tracking-assignments -Wdeclaration-after-statement -Wvla -
Wno-pointer-sign -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointe
r-types -Werror=designated-init -DKBUILD_MODFILE='\''"scripts/mod/empty"'\'' -DKBUILD_BASENAME='\''"empty"'\'' -DKBUILD_MODNAME='\''"empty"'\'' -c -o scripts
/mod/empty.o scripts/mod/empty.c' > scripts/mod/.empty.o.cmd; rm -f scripts/mod/.empty.o.d
長すぎぃ!
おちついて興味のない部分を消します…
gcc -o scripts/mod/empty.o scripts/mod/empty.c
おー!ビルド対象のオブジェクトファイルがわかりますね!テキスト処理でなんとかなりそうです!
では,「ある時点で完了している仕事数」はどのように分かるでしょうか.つまり,「各オブジェクトファイルのビルドが完了したか」判定したいわけです.
いろいろ考えた結果,タイムスタンプを見るのがもっとも手軽だろうと判断しました..config
ファイルと各オブジェクトファイルのタイムスタンプを比較します.
.config
というファイルには,kernelビルドのための設定が書き込まれており,ビルドを実行するごとに更新されます.そのため,
.config
より新しいタイムスタンプを持つオブジェクトファイルは,ビルドが完了しているファイル.config
より古いタイムスタンプを持っていたり,そもそもオブジェクトファイルが存在していない場合は,まだビルドが完了していないファイル
ということです.
Node.js + TypeScriptでCLIツール を書く
フロントエンドの人間なので,TSでさっくり書こうと思います.
まず,node.jsのspawnでmakeを呼び出し,ビルド対象のオブジェクトファイルの一覧を得ます.そして,定期的に,find
コマンドの-newer
オプションを使って,.config
ファイルよりも新しいオブジェクトファイルをリストします.ビルド対象のオブジェクトファイルが全て更新されたら,kernelビルド完了です!
プログレスバーの表示や残り時間の計算にはnpmのprogressを使います.シンプルでおすすめです.
あとはゴリゴリ実装すれば…
エモい!
さいごに
最近は新型コロナウイルスのせいで大学の高性能なサーバーが使えず,自宅のラップトップでビルド作業を行っていました.そのため,ビルドにかかる時間が大幅に伸びてしまい,暇だったのです.
今回のツールによって,暇な時間を有効活用できそうですし,開発自体が良い暇つぶしになりました.
ツールの開発をしていく上でLinux kernelのMakefileを読んだ話を別の記事に書きました.makeに興味がある方はどうぞご覧ください.
kernelビルドにプログレスバーがついて大変満足です.