🐧 Linux 総合学習プラットフォーム
C・ビルドツール ・ 上級

gdbによるデバッグ

プログラムが期待通り動かないとき、gdb を使うと実行を一時停止させて内部状態を調べられます。-g 付きでビルドした実行ファイルを gdb ./prog のように起動し、break で止めたい行や関数にブレークポイントを設定、run で実行を開始します。停止したら next で1行ずつ進め、print 変数名 でその時点の値を確認できます。原因の見当がついたら continue で再開、調査が済んだら quit で終了します。printf を埋め込んで再コンパイルする方法より、はるかに素早く原因に近づけます。

プログラムを書いて動かしてみたのに、結果がどうもおかしい——計算が合わない、途中で落ちる、思った通りに分岐しない。そんなとき、頭の中だけで原因を考えても、なかなか正体はつかめません。確実なのは、プログラムを実際に動かしながら、その内部で何が起きているかを直接のぞくことです。それを可能にするのがデバッガで、Linuxでの代表が gdb(GNU Debugger)です。gdb を使うと、プログラムの実行を好きな地点で一時停止させ、そのときの変数の値を確認したり、1行ずつゆっくり進めたりできます。いわばプログラムの動きをコマ送りで観察する道具で、原因の見当をつけるための情報を、推測ではなく事実として手に入れられます。

準備 -g 付きでビルドする

gdb の力を引き出すには、デバッグ対象の実行ファイルを -g 付きでコンパイルしておく必要があります。-g はソースの行番号や変数名の情報を実行ファイルに埋め込むオプションで、これが無いと gdb はソースコードに即した表示ができず、機械語のアドレスだけを相手にすることになって、ひどく追いづらくなります。まず gcc -g prog.c -o prog のようにビルドしておきます。開発中は -Wall も併せて gcc -Wall -g prog.c -o prog とするのが定番です。準備ができたら、gdb ./prog と打って gdb を起動します。すると専用のプロンプト((gdb) のような表示)が出て、ここに対話的にコマンドを打ち込んでいく形になります。

止めたい場所を決める break

gdb の基本は「止めて、調べて、進める」のくり返しです。まず、プログラムの実行を一時停止させる地点であるブレークポイントを設定します。使うのは break コマンドで、止めたい場所を行番号か関数名で指定します。たとえば break main と打てば main 関数の入り口に、break 25 と打てばソースの25行目にブレークポイントが置かれます。怪しいと睨んでいる処理の直前に仕掛けておくのがコツです。ブレークポイントは複数設定でき、設定済みの一覧は info breakpoints で確認できます。不要になったものは delete で消せます。さらに進んだ使い方として、break 30 if i == 100 のように条件を付けると、ループの中でも特定の状況になったときだけ止める、といった絞り込みもできます。なお、このブレークポイントが行番号や関数名で正しく効くのは、先ほどの -g 付きビルドのおかげで、デバッグ情報が無いと位置の指定がうまくいきません。

実行して止め、値を見る run と print

ブレークポイントを置いたら、run コマンドでプログラムの実行を開始します。プログラムは普通に動き出し、設定したブレークポイントに到達したところでぴたりと一時停止します。停止すると、gdb はいま何行目で止まっているかを表示します。ここからが調査の本番です。print 変数名 と打つと、その時点での変数の値が表示されます。たとえば print i や print sum のように打てば、いまの i や sum の中身が分かります。配列や構造体の中身も見られますし、print x + y のような簡単な式の評価もできます。「この変数、本当に自分の思った値になっているか?」を、想像ではなく実際の値で突き合わせられるのが、デバッガの最大の価値です。

1行ずつ進める next と step

止めた地点から先は、1行ずつ進めながら変化を追います。next コマンドを打つと、現在の行を実行して次の行へ進みます。1行進めるたびに print で値を見れば、どの行で変数が想定外の値に変わったかを特定できます。next は関数呼び出しの行を「ひとまとめに1ステップ」として飛び越えますが、その関数の中まで入って追いたいときは step を使います。next は関数の中に立ち入らず、step は中へ踏み込む——この違いを使い分けると、調査の粒度をコントロールできます。多くの場合は next で大づかみに進め、怪しい関数だけ step で中に入る、という進め方が効率的です。

再開と終了 continue と quit

原因の見当がついた、あるいは次のブレークポイントまで一気に進めたいときは、continue コマンドで実行を再開します。continue を打つと、プログラムは次のブレークポイントに当たるか、最後まで実行し終わるまで通常の速度で走ります。ループの中にブレークポイントがある場合、continue を繰り返すたびに1周ずつ進められるので、特定の回数目の挙動を調べるのにも使えます。ひととおり調査が済んだら、quit コマンドで gdb を終了します(終了の確認が出たら y で答えます)。「break で止める場所を決め、run で動かし、print で値を見て、next/step で進め、continue で再開、quit で終わる」——この一連のコマンドが gdb 操作の骨格です。

printf デバッグとの比較・実務での使いどころ

原始的なデバッグ手法として、ソースのあちこちに printf を埋め込んで値を出力し、再コンパイルして動かす、という方法があります。手軽ではありますが、出力を仕込むたびに編集とコンパイルをやり直す必要があり、調べたい箇所が変わるたびにその往復が発生します。gdb なら、再コンパイルなしに止める場所や見る変数をその場で自由に変えられるため、はるかに素早く原因に近づけます。とくに、どこで落ちているのか見当もつかないクラッシュの調査や、特定の条件でだけ起きる不具合の追跡では、gdb の真価が際立ちます。プログラムが異常終了したときの状況を記録したコアダンプを後から gdb で読み込み、「どの関数のどの行で落ちたか」を事後に解析する、といった使い方も実務では重要です。最初は break・run・print・next・continue の5つを確実に使えるようにし、慣れてきたら step や条件付きブレークポイントなどへ広げていくと、デバッグの引き出しが着実に増えていきます。

この項目に出てくる用語

ブレークポイントぶれーくぽいんと
デバッガで、プログラムの実行を一時停止させる地点。
実行ファイルじっこうふぁいる
そのまま起動して動かせる、完成した機械語のプログラム。
コンパイルこんぱいる
人が書いたソースコードを、CPUが実行できる機械語へ翻訳する処理。

関連コマンド

gdbgcc

▶ 学習アプリでこの続きを学ぶ・演習する