クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

gdbでクラッシュの行を探す

このレッスンのあとで: デバッガを使って、プログラムがクラッシュする行を見つけられる。

55分のレッスンコマンド6件コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

デバッガはプログラムを止めて中を見る

デバッガは、プログラムを途中で止めて、変数の中身を見る道具です。gdb は Linux でよく使われるデバッガで、クラッシュした行を教えてくれます。

セグメンテーション違反と出て止まったプログラムでも、gdb で動かせば、どの行で何が起きたかが分かります。

このレッスンのゴール

メモ

デバッガを使って、プログラムがクラッシュする行を見つけられる。

重要な言葉

ことばいみれい
デバッガプログラムを止めて、中を調べる道具gdb
ブレークポイントプログラムを止める行の印
バックトレース今の関数と、呼び出し元の一覧
シグナルOSがプロセスに送る知らせSIGSEGV
セグメンテーション違反使えないメモリに触って、OSに止められること
デバッグ情報行番号を実行ファイルに残すもの。gcc -g で付く

学生が入力するセルは、このプレビューでは空白です。

-gを付けると行番号が分かる

gcc -g で作ると、実行ファイルにソースの行番号が残ります。gdb で run すると、クラッシュした行で止まります。

OSは、使えないメモリに触ったプロセスに、シグナルを送って止めます。その名前が SIGSEGV で、セグメンテーション違反の知らせです。

4行目で落ちる小さなプログラム

コード: c
int main(void)
{
    int *p = 0;
    *p = 1;
    return 0;
}

ソースを書いて -g で作る

コマンド
printf 'int main(void)\n{\n    int *p = 0;\n    *p = 1;\n    return 0;\n}\n' > /tmp/crash.c && gcc -g -o /tmp/crash /tmp/crash.c

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

バックトレースで落ちた行を見る

コマンド
gdb -batch -ex 'set debuginfod enabled off' -ex run -ex bt /tmp/crash 2>&1 | grep '^#0' | grep -o 'main () at .*'

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

落ちた時点の p を見る

コマンド
gdb -batch -ex 'set debuginfod enabled off' -ex run -ex 'print p' /tmp/crash 2>&1 | tail -1

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

ブレークポイントで落ちる前に止める

break 4 は、4行目を実行する直前で止める印です。止まったら、print p で変数を見て、next で1行進めます。

落ちる前の変数を見られるので、原因に近づけます。quit で gdb を終わります。

gdb のよく使うコマンド

コマンド働き
runプログラムを始める
break 44行目で止まる印を付ける
btバックトレースを出す
print p変数 p の値を見る
next1行進める
quitgdb を終わる

学生が入力するセルは、このプレビューでは空白です。

4行目の手前で止めて p を見る

コマンド
gdb -batch -ex 'set debuginfod enabled off' -ex 'break 4' -ex run -ex 'print p' /tmp/crash 2>&1 | grep -E 'Breakpoint 1,|^\$1'

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

クラッシュの形から原因の見当がつく

クラッシュの形を見ると、原因の見当がつきます。同じ SIGSEGV でも、場所とバックトレースの形が違います。

終わらない再帰は、スタックを使い切って落ちます。バックトレースが、同じ関数で何千行も続きます。下の Run は、2000行で打ち切って数えます。

症状と原因

症状考えられる原因
SIGSEGV で、番地が0の近くNULLポインタを読み書きした
SIGSEGV で、同じ関数が何千行も続く終わらない再帰(スタックあふれ)
SIGSEGV で、free 済みの場所解放後の使用
SIGFPE整数を0で割った(機種による)
SIGABRTプログラムが自分で止めた

学生が入力するセルは、このプレビューでは空白です。

終わらない再帰のプログラムを作る

コマンド
printf 'int f(int n)\n{\n    return f(n + 1) + 1;\n}\nint main(void)\n{\n    return f(0);\n}\n' > /tmp/deep.c && gcc -g -o /tmp/deep /tmp/deep.c

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

2000フレームまで数える

コマンド
gdb -batch -ex 'set debuginfod enabled off' -ex run -ex 'bt 2000' /tmp/deep 2>&1 | grep -c '^#'

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

よくある間違い

注意

クラッシュした行が、誤りのある行だ、と思いがちです。その行は、誤りの結果が現れた場所にすぎません。p が0になったのは、もっと前の行かもしれません。バックトレースと変数の値から、さかのぼって探します。

さらに挑戦: 自分のクラッシュを作る

課題が通ったら、自分で小さなプログラムを書き、わざと落としましょう。free したポインタを使う、または終わらない再帰、のどちらかです。

gdb で動かして、どのシグナルで止まり、バックトレースがどうなるかを、課題の報告の形で書き出しましょう。

このラボとソースについて: コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。