xxgdbデバッガによるデバッグ法
デバッグ・デバッガ
デバッグ(debug, 虫取り)とは,作成したプログラムの間違いを発見して直す作業のことであり,デバッガ(debugger)とは異常動作するプログラムの動作を追跡し誤りを見つけ出すためのツールである.
プログラムが期待した動作をしているか検証したり,うまく動作しない原因を追及するために用いる.通常の場合,デバッグはプログラム中の要所にfprintf(stderr,"・・・",・・・)関数を埋め込んで実行中の変数の値を画面表示して行うのが一般的だが,それだけでは不十分な場合もある.
デバッガは,プログラムが異常終了した位置を調べたり,プログラムの実行を順番に追跡して変数の値などを確認しながら実行させることができる.
Linuxで利用できるデバッガの一つにgdbがある.これを画面上でマウス操作が使えるようにしてくれるgdbのフロントエンドであるxxgdbの簡単な使い方を説明する.
xxgdbはgdbのGUI版であり,コマンドなどを全てマウスによるボタン操作で行える. また,ソースリストも同時に表示できる機能を持っている.
まず,デバック対象となるプログラムを -g オプションを付けてコンパイルする.
% gcc -g sample.c
これにより実行ファイル a.out にデバッグ情報が付加される(注:-oオプションで実行可能形式ファイルの名前を指定した場合は、その名前のファイルに付加される).
この実行ファイルの名前を引数としてxxgdbを起動する.
% xxgdb a.out &
これにより,コマンドラインウインドウが表示される.
↓xxgdbのコマンドライン
さらに,"Source Listing", "Command Buttons", "Display Window"ボタンをクリックすると,それぞれソ−スファイル表示ウィンドウ,コマンドボタンウィンドウ,変数表示ウィンドウが表示される.
↓ソースファイル表示ウィンドウ
↓コマンドボタンウィンドウ
↓変数表示ウインドウ
ここで、コマンドボタンの[run]をクリックすると,コマンドラインに run
コマンドが入力されプログラムが実行される.これはktermなどのコマンドラインから実行ファイルを起動した場合と同様に動作する.キ−ボ−ド入力もxxgdbのコマンドライン上で行う.なにも操作が必要なくエラーも出なければ,そのまま最後まで実行される.
[実行ファイルの指定]
もし実行ファイルを指定せずにxxgdbを起動してしまったり,途中で他のプログラムのデバッグへ切り替える場合は,[file]ボタンをクリックする.
[file]ボタンを押すとファイル選択ダイアログが現れるので,-gオプションをつけてコンパイルした実行ファイルを指定する.
[xxgdbの終了]
コマンドボタンの [quit]をクリックすれば終了する.またはコマンドラインで quit
又は q
と打ち込む.
存在しないメモリアドレスを参照した場合などにでる Segmentation fault エラーはプログラムの実行中に出るエラーであり,エラーの発生場所のわかりづらい 厄介なエラーの一つである. ここでxxgdbデバッガ使うとエラーの発生した箇所を調べることができる.
まず,前節で示した方法で実行ファイルにデバッグ情報を付加し,その実行ファイルを対象にxxgdbを立ち上げる.次に[run]ボタンをクリックしてプログラムを実行する. Segmentation faultが表示されたら [stack]ボタンをクリックすると,エラーが発生した箇所が表示される.
[実際のデバッグ例]
次のようなtest.cというプログラムを仮定する.このプログラムは実行するとSegmentation faultエラーが出て停止しまう.
#include <stdio.h> main() { int a; scanf( "%d", a ); printf( "%d¥n", a ); }
まず,デバッグ情報を付加してコンパイルし直す.
% gcc -g test.c
次に,ここで生成された実行ファイル(a.out)を対象にxxgdbを起動する.
% xxgdb a.out &
これでメインウインドウが表示される.
次に,メインウインドウの中にある[Command Buttons]をクリックすると,コマンドボタンウィンドウが表示される.コマンドディスプレイの中の[run]ボタンをクリックしてプログラムを実行し,コマンドラインに Segmentation faultが表示された時, [stack]ボタンをクリックするとエラーが発生した箇所が 以下のように表示される.
(xxgdb) run 10 Program received signal SIGSEGV, Segmentation fault. 0x400480eb in _IO_vfscanf (s=0x400a4fa0, format=0x80494fc "%d", argptr=0xbffff89 8, errp=0x0) at vfscanf.c:892 vfscanf.c:892: No such file or directory. (xxgdb) info stack #0 0x400480eb in _IO_vfscanf (s=0x400a4fa0, format=0x80494fc "%d", argptr=0xbff ff898, errp=0x0) at vfscanf.c:892 #1 0x4004b986 in _IO_vscanf (format=0x80494fc "%d", args=0xbffff898) at vscanf. c:35 #2 0x400490cd in scanf (format=0x80494fc "%d") at scanf.c:41 #3 0x8048624 in main () at test.c:7 (xxgdb)
これでmain()の中の scanf()でエラーが起きていることが分かる.
このエラー発生箇所からエラーの原因を探すこと. 注意することとして,Segmentation faultは、エラー発生箇所そのものが 原因となっている場合はあまり多くない.特にポインタを 用いている場合にはその傾向が顕著である.
しかし,デバッガによって発生箇所が分れば、そこから原因を類推することは難しくないはずである.その位置より前に エラーがあることが分かるだけでも利用価値はある.
また,Segmentation faultよりも厄介なものとして ,「プログラムは問題なく動作しエラーは起きないが,得られる結果や動作が異常である」 という症状がある.
このようなときはprinf()などを要所々々に挿入して,色々な変数の値を表示させてデバッグするのであるが,printf()は毎回変数を表示するので不必要に多くの情報が表示されてしまって,よく判らないことがある.そこで,プログラムの実行を一時停止し,例えば繰返し処理のある特定付近を詳細にゆっくり調べたい場合などに 役にたつのがbreak point設定である.
これまでと同様,実行ファイルにデバッグ情報を付加し,それを対象にしてxxgdbを立ち上げる.
[break pointの設定]
ソースファイルが表示されているウインドウで,break pointを設定したい場所にマウスクリックでカーソルを表示させておき,[break]ボタンを押すと,その行の先頭に手形マークが表示されbreak pointが設定される.設定が終わったら、[run]ボタンで実行を開始する.
実行が進み,break pointを設定した場所まで来ると処理が一時停止する.ここで,その時点での変数の内容の表示などの操作を行う.
また,[tbreak]ボタンは一回限り有効なbreak pointを設定する.
[break pointでの変数の内容表示]
ソースウインドウで、表示させたい変数名をドラッグして選択反転させる.そこで[print]ボタンを押すとその変数の内容がコマンドラインに 表示される.これは一回限りの表示である.
[display]ボタンを押すと,ディスプレイウインドウに変数の内容が表示され,break pointに止まるごとにその時点での変数の内容を表示してくれる.
[実行停止時からの操作]
contボタン: 次にbreak pointに到達するまで実行続行
nextボタン: 一行ずつ実行する.ただし関数呼出し時は,関数を実行して,その次の行に制御が移る(コマンドラインからnext 数値
とすると、数値行分まとめて実行する).
step 数値
とすると,数値行分まとめて実行する).
[break pointの削除]
break pointを表す手形マークをクリックして選択し, [delete]ボタンをクリックすると,手のマークが消えbreak pointが解除される.
原著:京都産業大学工学研究科 田中,安福