GDB
ツイートGDBはGNU製のデバッガで、gcc等でコンパイルしたプログラムのデバッグを行うツールです。
gccが対象となりますので、C言語以外にも、C++、FORTRAN、Ada等様々な言語に対応しています。
GDB
GDBの基本的な操作
定番なHello Worldから。
#include <stdio.h> int main(void) { printf("Hello World\n"); return 0; }
コンパイル時には'-g'オプションを使用します。
できれば、'-O0'オプションも使用しましょう。これは最適化を停止させます。
$ gcc -g -O0 -o hello hello.c $ ./hello Hello World
GDBを起動するには、gdbに実行するコマンドを渡します。
$ gdb ./hello (gdb)
GDBは起動すると入力プロンプトを表示します。gdbはここにコマンドを入力して操作してく形となります
この時点ではまだデバッグ対象のプログラムは起動していません。起動するには'run'と入力します
'run'は'r'と省略できます。引数が必要な場合には'run 引数'としてください
$ gdb ./hello (gdb) run
プログラムが起動します。
今回は簡単なプログラムでしたので、そのままプログラムは正常終了しました。
Starting program: hello Hello World [Inferior 1 (process 12258) exited normally] (gdb)
GDBを終了するには'quit'、ヘルプは'help'です
GDBでデバッグの基本
ここではブレイクポイントの設定、ステップ実行、コードやデータ表示等を紹介します
まずは、GDBでのデバッグの為に、Hello Worldを少し修正します
hello関数を追加しています。
#include <stdio.h> void hello() { printf("Hello World\n"); } int main(void) { hello(); return 0; }
このコードをコンパイルしてgdbでデバッグしましょう。
hello関数にブレイクポイントを設定します。
ブレイクポイントの設定には'break'コマンドで行います。省略形は'b'です
$ gcc -g -O0 -o hello hello.c $ gdb ./hello (gdb) break hello
ブレイクポイントを設定を行ったので、実行します。
実行後、ブレイクポイントを設定したhello関数で止まります
行数や関数名が表示されています。
(gdb) r Starting program: hello Breakpoint 1, hello () at hello.c:6 6 printf("Hello World\n");
'break'コマンドに関数名を渡しましたが、行番号、現在から前後に何行か?、アドレスも渡せます。
必要に応じて使い分けをしましょう。
ステップ実行で1行ずつ実行しましょう
ステップ実行は'step'省略形は's'です。
(gdb) step Hello World 7 } (gdb) step main () at hoge.c:13 13 return 0; (gdb) s 14 }
ステップ実行にはいくつかのバージョンが存在します
'next'があります。
- 'step [count]'は異なる行に移動します。countに数値を入れるとその回数分繰り返します。
- 'next'は'step'と似ていますが、同一スタックトレース内の次の行に移動します。
- 'finish'は同一スタックトレース内のreturnまで移動します。
- 'until'は'next'と似ています。ただし、プログラムカウンタがジャンプアドレスより大きくなるまで実行します。これはループ内のデバッグに有効です。
- 'finish'は同一スタックトレース内のreturnまで移動します。
- 'stepi'は単一マシン命令を実行します。'step'は言語レベルでのステップです。省略形は'si'です。
- 'nexti'は'stepi'の'next'版です。省略形は'ni'です。
- 'continue'はステップ実行ではなく処理を最後まで実行します。
ソースコードを見るには、'list'、'l'です。
ただし、コンパイルオプションに'-g'が必要です。忘れずに。
(gdb) list 1 #include <stdio.h> 2 3 void hello() 4 { 5 printf("Hello World\n"); 6 } 7 8 int main(void) 9 {
デバッグには変数の値の表示は必須です。
データ表示は'print'、'p'コマンドです。
変数xの情報を見たければ'p x'となります。
また、データの表示が出来るのですから変数にデータを設定することも可能です。
'set x=100'で変数xに100を設定できます。ただし、データの型には注意してください。
変数の型は'whatis x'です。分からなければ'list'でコードを確認するのが良いでしょう
GDBの少し高度な使い方
C言語プログラマはエラー解析の為にデバッグを行います。
ある程度のプログラマは知っているかと思いますが、関数の呼び出しはスタック構造となっています。
main(),func1()...と呼ぶ場合、main関数が持つレジスタ値等が設定されていますが、func1()を呼ぶ前にレジスタ値など関数を動作させるための情報を一度スタックにpushを行い、その後必要な値の設定をしfunc1()を呼びます。
func1()からメイン関数に戻る場合はfunc1()の情報を全て削除し、main()の情報をスタックからpopで戻します。
フレームはこれらの関数毎に作られるデータの塊と考えてください。
バックトレースはどのようにプログラムが動作して現在の状態になったか?という情報です。
つまり、バックトレースは最初のフレームから現在のフレームまでの情報を1行毎に表示する機能です。
バックトレースコマンドは'backtrace'、または'bt'です。
別名として、'where'と'info stack'が設定されています。
エラーや例外での終了時には非常に有効な情報が含まれているでしょう
(gdb) bt #0 __libc_start_main (main=0x400537, argc=1, argv=0x7fffffffd868, init= , fini= , # rtld_fini= , stack_end=0x7fffffffd858) at ../csu/libc-start.c:325 # #1 0x0000000000400459 in _start () #
'frame'コマンドでフレームを操作する事も可能です。
'frame'で現在のフレームを表示します。'frame 数値'でフレームを選択することもできます
'up','down'でフレームの上下移動も可能です。
(gdb) frame #0 __libc_start_main (main=0x400537, argc=1, argv=0x7fffffffd868, init= , fini= , # rtld_fini= , stack_end=0x7fffffffd858) at ../csu/libc-start.c:325 # 325 in ../csu/libc-start.c (gdb) frame 1 # #1 0x0000000000400459 in _start () #
他にもシグナルの制御やスレッドのデバッグ、coreファイルによるデバッグ等様々な機能が存在します。
あまりにも多いのでここでは(筆者の体力的に)紹介し切れませんが、Web上では詳しく解説しているサイトが有りますのでそちらに譲りましょう
まとめ
デバッグではprintデバッグが存在しますが、コードを書き換える必要があったりあまりお勧め出来ないデバッグ方法です。
そのGDBはGNU製でgccを前提としていますから非常に有効なデバッグ方法です。多少複雑ではありますが、是非有効に使いたいものです。
実践 デバッグ技法 ―GDB、DDD、Eclipseによるデバッギング Norman Matloff,Peter Salzman,相川 愛三 オライリージャパン 売り上げランキング : 76448 Amazonで詳しく見る by AZlink |
デバッグの理論と実践 ―なぜプログラムはうまく動かないのか Andreas Zeller,中田 秀基,今田 昌宏,大岩 尚宏,竹田 香苗,宮原 久美子,宗形 紗織 オライリージャパン 売り上げランキング : 141412 Amazonで詳しく見る by AZlink |
Debug Hacks -デバッグを極めるテクニック&ツール 吉岡 弘隆,大和 一洋,大岩 尚宏,安部 東洋,吉田 俊輔 オライリージャパン 売り上げランキング : 63505 Amazonで詳しく見る by AZlink |