3 デバッグのお供: ”gdb のススメ”

杉本典 充

PIC
____________________________________________________________________

3.1 は じ め に

インターネットでは様々なオープンソースのプログラムが公開されています。 それらのプログラムは開発者の手によるデバッグ だけでなく、 多くの人もデバッグ作業に参画することによって品質を高めていきます。 そのデバッグ作業を終えたプログラムが いわゆる「安定したプログラム」 であり、 多くの人が安心して使えるレベルになるにはデバッグ作業はとて も大切な作業の 1 つです。 今回は、 Debian を使ってプログラムをデバッグする手法についてまとめてみま した。

3.2 gdb とは

gdb とは GNU Debugger*1 の ことで、 C 言語・ C++ 向けのソースレベルデバッガです。 開発者は gdb を使うことでプログラムが今どこの部分を実行してい るか、 プログラムの状態はどうなっているかを知ることができるため、 デバッグ作業を効率的に行うことができます。 「man gdb(1)」 によると、 gdb には大きく 4 つの機能があると書かれています。

gdb が使用する設定ファイルは”〜/.gdbinit”であり、 gdb の初期設定値をこのファイルに定義することで変更でき ます。

3.3 開発環境と gdb のインストール

C 言語のプログラムを開発するためにはコンパイラが必要です。 gdb の他にプログラムを作成するために必要なソフトウェア 一式もインストールします。

$ sudo apt-get update
$ sudo apt-get install gcc make
$ sudo apt-get install gdb

環境も整ったところで、 プログラムを作成します。 今回は「FizzBuzz」 *2といわ れているプログラムを例にしてみます。

3.4 gdb を使ってみましょう

3.4.1 まずはデバッグビルドします

プログラムを gdb で操作するためにはプログラムにデバッグ情報を付与してビルドする必要があります。 gcc のコンパイルオ プション及びビルドオプションにデバッグシンボルを付与する”-g”オプションをつけてビルドします。 (デバッグ時の最適化レ ベルは開発者によって指定が違うこともあります。 ここでは最適化レベルは無指定 (=”-O0”、 最適化なし) としてビルドし ます。 )

3.4.2 gdb 単体でプログラムを追いかけてみる

それではシェルから gdb を起動します。 gdb を起動すると以下のような入力受付状態になります。

$ gdb
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
(gdb)

gdb のプロンプトでコマンドを入力することによりデバッガを通してプログラムを動かすことができます。 デバッグ作業でよ く使う gdb のコマンドを表 3.1に示します。



表 3.1: gdb を操作するコマンド (一部抜粋)



コマンド 省略コマンド

説明







run (引数) r

プログラムを最初から実行します。

引数を指定する場合は、 run の後に引数を指定します。




break (停止位置) b

ブレークポイントを設定します。 ブレークポイントは「関数 名」 と「ソースコード:行番号」 のいずれかで指定できます。




delete (breakpoint 番号) d

ブレークポイントを削除します。 単に delete だけを実行する とすべてのブレークポイントを削除します。




list l

現在実行中の近くのソースコードをある行数表示します。 (初 期設定値は 10 行)




step s

ステップイン実行します。




next n

ステップオーバー実行します。




finish fin

ステップアウト実行します。




continue c

現在停止中の位置からプログラムを再開します。




print (変数名) p

プログラム中の (変数名) の内容を表示します。 ポインタ変数 の場合は「print *ポインタ変数名」 と指定することでポイン トが指し示す値を表示できます。




set var (変数名)=(設定値) なし

プログラム中の (変数名) の値を (設定値) に変更します。




quit q

gdb を終了します。




attach (プロセス ID) なし

実行中の (プロセス ID) を gdb で制御できるようにします。




detach なし

attach 中のプロセスを gdb の制御下から切り離します。 切り 離されたプログラムはそのまま動作し続けます。




shell なし

shell を起動します。 shell を exit すると gdb プロンプトに戻 ります。




help h

gdb のコマンドに関するヘルプを表示します。




info (コマンド) i

様々な情報を表示します。





3.5 gdb のフロントエンドツール

gdb は単体でも十分デバッグ可能ですが、 よりデバッグ作業を行いやすいように gdb のフロントエンドツールが 多くあります。 X Window System 上で動作する統合開発環境 (IDE) では KDevelop、 Anjuta、 Eclipse、 NetBeans など、 コマンドライン上でも動作する Emacs、 Vim などもフロントエンドとして利用することができ ます。

3.5.1 Emacs GUD モードで gdb を使ってみる

Emacs には GUD(Grand Unified Debugger) という機能があり、 Emacs 上で様々なデバッガと連携することができる仕組み です。 GUD は gdb に限らず、 perldb(perl 用デバッガ) や pdb(python 用デバッガ) なども起動することができ ます。

Emacs の実行中に以下のキーを入力して gdb を起動します。

M-x gdb

その後、 ミニバッファで実行ファイルを指定して Enter キーを入力します。

Run gdb (like this): gdb --annotate=3 ../a.out

すると図 3.2、 図 3.3のような画面に切り替わります。


PIC

図 3.2: Emacs GUD モードで gdb を起動した画面 (1)



PIC

図 3.3: Emacs GUD モードでデバッグ中の画面 (2)


また、 Emacs の設定ファイル”〜/.emacs.el”に「(setq gdb-many-windows t)」 を指定しておくと、 すると図 3.4、 図 3.5の ような画面で gdb が起動します。 この画面を表示するには「gud.el」 というファイルが必要であり。 lenny の場合は Emacs を インストールすると一緒にインストールされます。


PIC

図 3.4: Emacs GUD モードで gdb を起動した画面 (3)



PIC

図 3.5: Emacs GUD モードでデバッグ中の画面 (4)


3.6 いろいろなプログラムのデバッグ方法

私がプログラムを gdb を使ってデバッグするときの操作例を挙げてみます。

3.6.1 単発実行系プログラムのデバッグ

単発実行するプログラムの場合はデバッガでプログラムの起動を行い、 その後にデバッグ作業を開始することになり ます。

  1. gdb を起動します。
  2. break コマンドでブレークポイントを指定します。 (実際には「b main」 と入力してプログラムの最初で止める ことも多いです。 )
  3. run コマンドでプログラムを開始します。
  4. step コマンド、 next コマンドでプログラムを追いかけます。
  5. デバッグが完了したら、 continue コマンドで残りのプログラムすべてを実行します。
  6. quit コマンドで gdb を終了します。
3.6.2 デーモン系プログラムのデバッグ

デーモンとして動作しているプログラムの場合は、 動作中のプログラムを gdb で制御する必要があるためアタッチする必要が あります。

  1. デバッグするデーモンプログラムを実行します。
  2. デバッグするデーモンプロセスのプロセス ID を調べます。
  3. gdb を起動します。
  4. プロセス ID を指定して attach コマンドを実行し、 デバッグするデーモンプロセスにアタッチします。
  5. break コマンドでブレークポイントを指定します。 (おそらく無限ループ処理のどこかで停止させることになる と思います。 )
  6. ブレークポイントを設定したところでプログラムが一時停止しますので、 step コマンドや next コマンドを実 行してプログラムを追いかけます。
  7. デバッグが終了したら、 detach コマンドでプロセスからデタッチします。
  8. quit コマンドで gdb を終了します。
3.6.3 fork するプログラムのデバッグ

fork するプログラムの場合、 fork() 後に親プロセスと子プロセスのどちらを追いかけるのかを「set follow-fork-mode parent」 などと設定しておく必要があります。

  1. gdb を起動します。
  2. 「set follow-fork-mode」 を設定し、 fork() 後にデバッガを追うプロセスを親プロセスにするか、 子プロセスに するか設定します。
  3. break コマンドでブレークポイントを指定します。
  4. run コマンドでプログラムを開始します。
  5. fork() した後は「set follow-fork-mode」 で指定した親プロセスか子プロセスのいずれかを追従しますのでそ のまま続けてデバッグします。
  6. quit コマンドでプログラムを終了します。

3.7 まとめ

今回は gdb の紹介と Emacs GUD モードにおいてプログラムをデバッグする一例を紹介しました。 Emacs GUD モードを使っ たデバッグ操作は X Window System 上だけでなくコンソール環境でも同様の手順で実行できるため、 telnet 環境や ssh 環境 でも同じスタイルでプログラムのデバッグ作業を行うことができます。

みなさんも Debian を使ってたくさんデバッグしてみましょう。

3.8 参考資料

第 28 回関西 Debian 勉強会 2009 年 10 月
____________________________________________________________________________________________