[この節は, freebsd-current mailing list に Bill Paul が投稿したメールを, Dag-Erling Coïdan Smørgrav が校正し,括弧内のコ メントを追加して引用したものです. ]
From: Bill Paul <wpaul@skynet.ctr.columbia.edu> Subject: Re: the fs fun never stops To: ben@rosengart.com Date: Sun, 20 Sep 1998 15:22:50 -0400 (EDT) Cc: current@FreeBSD.ORG
[<ben@rosengart.com> が以下のパニックメッセージを 投稿しました.]
> Fatal trap 12: page fault while in kernel mode > fault virtual address = 0x40 > fault code = supervisor read, page not present > instruction pointer = 0x8:0xf014a7e5 ^^^^^^^^^^ > stack pointer = 0x10:0xf4ed6f24 > frame pointer = 0x10:0xf4ed6f28 > code segment = base 0x0, limit 0xfffff, type 0x1b > = DPL 0, pres 1, def32 1, gran 1 > processor eflags = interrupt enabled, resume, IOPL = 0 > current process = 80 (mount) > interrupt mask = > trap number = 12 > panic: page fault
このようなメッセージが表示された場合, 問題が起きる状況を確認し て, 情報を送るだけでは十分ではありません. 下線をつけた命令ポインタ 値は重要な値ですが, 残念ながらこの値は構成に依存します. つまり, こ の値は使っているカーネルのイメージに依存するのです. もしスナップショッ トなどの GENERIC カーネルを使っているのであれば, 他の人間が問題の ある関数について追試をすることができますが, カスタマイズされたカー ネルの場合は, 使っている本人にしか問題の起こった場所は特定できない のです.
何をすれば良いのでしょう?
0x8:
という部分は今回必
要ありません. 必要なのは 0xf0xxxxxx
という部分です. % nm /kernel.that.caused.the.panic | grep f0xxxxxxここで,
f0xxxxxx
は命令ポインタ値です. カーネルシンボルの
テーブルは関数のエントリポイントを含み, 命令ポインタ値は,
関数内部のある点であり, 最初の点ではないため, この操作を行っても
完全に一致するものが表示されない場合もあります. この場合は,
最後の桁を省いてもういちどやってみてください. このようになりま
す:
% nm /kernel.that.caused.the.panic | grep f0xxxxxこれでも一致しない場合は, 桁を減らしながら何らかの出力があるま で繰り返してください. 何か出力されたら, それがカーネルパニック を引き起こした可能性のある関数のリストです. これは, 問題点を見付ける 正確な方法ではありませんが, 何もないよりましです.
このようなパニックメッセージを投稿している人はよく見掛けますが, このように, 命令ポインタ値を, カーネルシンボルテーブルの中の関数 とつき合わせて調べている人はまれです.
パニックの原因を突き止める最良の方法は, クラッシュダンプをとり,
gdb(1)
でスタックトレースを行うことです. もちろん -current
で gdb(1)
がちゃんと動いていればですが (私は動くことを保証
できません. ELF 化された gdb(1)
はカーネルクラッシュダンプを
正しく扱えないと言っている人がいました. 3.0 がβテストを終える前,
に調べなければいけません. さもないと CD 出荷後に大顰蹙を買うことに
なります).
どっちにしろ, 私は普通以下のようにします.
config -g KERNELCONFIG
としてビルドディレクトリを設
定します.
cd /sys/compile/KERNELCONFIG; make
cp kernel kernel.debug
strip -d kernel
mv
kernel /kernel.orig/cp kernel /
[注: 現在 FreeBSD 3.x kernel はデフォルトで ELF 形式となっており,
strip -d
の代りに strip -g
を使う必要があります.
何らかの理由でまだ a.out 形式の kernel を使っている場合は,
strip -aout -d
を使ってください. ]
全てのデバッグシンボルを含んだカーネルを, 実際にブートする必要は
ありません. -g
をつけてコンパイルされたカーネルは,
簡単に 10MB 近くの大きさになってしまいます. こんな大きなカーネル
を実際にブートする必要はありません. この大きなカーネルイメージは
後でgdb(1)
を使うときにのみ必要です(gdb(1)
がシンボル
テーブルを必要とするため). シンボルを含んだカーネルのコピーを保
存 しておき, strip -d
を使ってシンボルを除いたカーネルを作
成して ブートします.
確実にクラッシュダンプをとるには, /etc/rc.conf
を編
集して dumpdev
を使用しているスワップパーティションに指定す
る必 要があります. こうすると rc(8)
スクリプトから
dumpon(8)
コマンドが実行されクラッシュダンプ機能が有効にな
ります. 手動で dumpon(8)
コマンドを実行してもかまいませ
ん. パニックの後, クラッシュダンプは savecore(8)
コマンドを
使用して取り出すこと ができます. dumpdev
が
/etc/rc.conf
で設定されていれ ば, rc(8)
スクリプト
から savecore(8)
が自動的に実行され, クラッシュダンプを
/var/crash
に保存します.
注: FreeBSD のクラッシュダンプのサイズは, ふつう物理メモリサ
イズと同じです. つまり 64MB のメモリを積んでいれば, 64MB のクラッ
シュ ダンプが生成されることになります. /var/crash
に十
分な空き 容量があることを確認してください. 手動で
savecore(8)
を実行す れば, もっと空き容量のあるディレクトリ
にクラッシュダンプを保存でき ます. options MAXMEM=(foo)
と
いう行をカーネルコンフィグファイ ルに追加することで, カーネルの
メモリ使用量を制限できます. たとえば 128MB のメモリがある場合も,
カーネルのメモリ使用量を 16MB に制限し クラッシュダンプのサイズ
も 128MB ではなく 16MB にすることができます.
クラッシュダンプを取り出せたら, 以下のように gdb(1)
を使っ
てスタックトレースをとります:
% gdb -k /sys/compile/KERNELCONFIG/kernel.debug /var/crash/vmcore.0 (gdb) where
必要な情報が 1 画面に収まらないことも多いので, できれば
script(1)
を使って出力を記録します. strip していないカーネ
ル イメージを使うことで, 全てのデバッグシンボルが参照でき, パニッ
ク の発生したカーネルのソースコードの行が表示されているはずです.
通常, 正確なクラッシュへの過程を追跡するには, 出力を最後の行から
逆方向に読まなければなりません. また gdb(1)
を使って, 変数
や 構造体の内容を表示させ, クラッシュした時のシステムの状態を調
べられ ます.
もしあなたがデバッグ狂で, 同時に別のコンピュータを利用できる
環境にあれば, gdb(1)
をリモートデバッグに使うこともできます.
リモートデバッグを使うと, あるコンピュータ上の gdb(1)
を使っ
て, 別のコンピュータのカーネルをデバッグできます. ブレークポイン
トの設 定, カーネルコードのステップ実行など, ふつうのプログラム
のデバッグ と変わりません. コンピュータを 2 台並べてデバッグする
チャンスには, なかなか恵まれないので, 私はまだリモートデバッグを
試したことはあり ません.
[Bill による注: DDB を有効にしていてカーネルがデバッガに 落ちたら, ddb のプロンプトで 'panic' と入力すれば, 強制的にパニッ クを 起こしクラッシュダンプさせることができます. パニックの途中 で, 再び デバッガに落ちるかもしれませんが, 'continue' と入力すれ ば, クラッシュダンプを最後まで実行させられます.]