【第2回】kgdbによるデバッグ
本文中に記載のある#で始まるコマンド例はroot権限で実行することを示しています。自分の環境で実行する際に間違えてしまうとシステムを壊してしまう危険があります。引数などを良く確認してから実行して下さい。
第2回はwriteシステムコールの動作を追跡するためにkgdbを使って実際にカーネルの内部の動きを追ってみます。kgdbはシリアル接続を使ってgdbからカーネルをソースコードデバッグするための機能です。
kgdbはカーネルに含まれるデバッグ機能としては最も歴史のある、かつ最強の機能かと思います。
ArmなどのCPUでは比較的ポピュラーですが、x86 CPUで使っている人は少ないのではないでしょうか。またエラーインジェクションとして前回とは違うfault injection機能を使ってみます。
今回はkgdbだけでなく、initramfsを使わなくても起動できるようにブートに必要なドライバも組み込んでカーネルをビルドします。
kgdbを使うためにはシリアルポートでリモート側PCと接続する必要があります。ターゲット側(デバッグするCPUボード側をこう呼んでいます)とリモート側の両側でシリアルポートを準備して、その間をシリアルのクロスケーブルで接続します。
1. カーネルのリビルド
前回は動いているカーネルをそのまま使ったデバッグが中心でしたが、今回はカーネルのコンフィグを変更し、リビルドしたカーネルを使ったデバッグの話になります。
writeシステムコールの追跡
writeからsubmit_bio(実際のデバイスにIOのデータを渡す関数)の間でエラーが起きているとすると、その間の関数を順に追いかける必要があります。tracepointは調べたい関数がわかっていれば、その関数のログを残すことができますが、writeシステムコールのように呼び出す関数が関数ポインタの場合は関数の特定が面倒です。
そこで、kgdbを使ってwriteがその実行の過程でどの関数を呼び出しているか追いかけて行きます。
ソースコードの取得
kgdbを使うためにはカーネルにその機能を組み込んでビルドする必要がありますが通常のdebian12カーネルには組み込まれていません。そこでカーネルのソースコードを入手してリビルドする必要があります。 カーネルのソースコードの取得方法はいくつか考えられまが、今回はlinux-sourceパッケージでインストールを行います。
(カーネルのソースコードの取得方法については第1回の11. tracepointにある(注2)を参照して下さい。)
ビルドに必要なパッケージの追加
ソースコードを apt-get install linux-source で取得した場合はカーネルのビルドに必要なbuild-essentialなどのdebパッケージは自動的にインストールされます。
しかし、一部不足しているので、自分でインストールしておきます。
# apt-get install dwarves ncurses-dev
2. カーネルのコンフィグ
カーネルにkgdb機能を追加してリビルドを行うのですから、デバッグに使うカーネルとしてあると便利な他の機能についても検討してみます。
initramfs無しにブート可能なカーネル
カーネルをデバッグするために、コンフィグを変更したり、ドライバを追加したりする場合は少しやっかいです。今のディストリビューションではinitramfsを使って、モジュールをロードしています。カーネルのコンフィグを変更した場合はinitramfsの中のモジュールも更新する必要があります。今回は起動に最低限必要なモジュールをカーネル組込みにして、initramfsは使わずに起動する方向でカーネルをリビルドします。
ドライバの組込み
initramfsを使わずに起動してkgdbを使用するためにコンフィグの変更を行います。
まずは起動に必要な以下のドライバを組み込みます。
ext4, ahci, sd_mod, xhci, ehci, usbhid
ext4
最近はファイルシステムもモジュールになっているので、EXT4を組込みます。
File systems
The Extended 4 (ext4) filesystem M --> y (画面の表示は*です)
ahci
SATAのドライバを組込みます。
Device Drivers
Serial ATA and Parallel ATA drivers (libata) M --> y
AHCI SATA support M --> y
scsi disk
Device Drivers > SCSI device support
SCSI disk support M --> y
USBドライバ
ホストコントローラにxhci,ehciを組込みます。
Device Drivers > USB support
Support for Host-side USB M --> y
xHCI HCD (USB 3.0) support M --> y
EHCI HCD (USB 2.0) support M --> y
キーボードドライバ
USBキーボードのドライバを組込みます。
Device Drivers > HID support
HID bus support M --> y
Generic HID driver M --> y
USB HID support
USB HID transport layer M --> y
kgdbの組み込み
Kernel hacking > Generic Kernel Debugging Instruments
KGDB: kernel debugger M --> y
KGDB: use kgdb over the serial console (NEW) これが*であることを確認
.configに以下が追加されていることを確認します。
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
SDカードアクセス
ddコマンドの確認にUSB接続のカードリーダを使う場合はusb-storageを組込みます。
Device Drivers > USB support USB Mass Storage support M --> y
fault injectionの組込み
ここまではdevice mapperを使った仮想的なデバイスで調べてきましたが、実際のデバイスではどうでしょうか?
ここで説明するfault injectionの機能はデバイスドライバに擬似的にエラーを発生させることができます。この機能を使って実際に/dev/sdxをアクセスしてI/Oエラーを確認します。
カーネルのコンフィグで以下を変更します。
Kernel hacking -> Kernel Testing and Coverage
Fault-injection framework ここをy
Fault-injection capability for disk IO (NEW) ここをy
Debugfs entries for fault-injection capabilities ここをy
参考までにこの設定を行った.configファイルを添付します。
3. カーネルのビルド
まずはカーネルだけビルドします。-j4は同時に4プロセスでコンパイルを行う指定です。
ここから先は
¥ 300