gdbの実践的使い方
「大規模ソフトウェアの効率的な理解(その1、2、3、4、5、6)」などという大袈裟なタイトルでブログを書いたが、今回は一気に実践編ということでフリーソフトウェア定番のデバッガ gdb の実践的使い方について記す。
プログラマの日々には、プログラムを書くためのエディタ、プログラムをコンパイル(あるいは実行)するためのコンパイラ(あるいはインタプリタ)、そしてプログラムを理解するためのデバッガという三種の神器が必須である。
この定番はわたしの場合xemacs/gcc/gdbである。前々職(DECという会社に務めていた)の場合、それぞれプロプライアトリな物を使っていたので微妙に異なるがやることは一緒である。
gdbは何のために利用するかというと、プログラムを理解するために利用する。デバッガなんだからデバッグのために利用するというのは、gdbの底力の半分も利用していないと言ってさしつかえない。
gdbはプログラムを動作させてその動的な振舞いを逐一追って理解するための道具なのである。
よくプログラムをデバッグするとき出力(C言語ならばprintf())を埋め込んでやる人がいるが、それはお勧めしない。お勧めしない理由は多分101以上あるが代表的なものを順不同で言えば、
- printf()の副作用で、printfを挿入した時と、挿入していない時で実行結果が異なる場合がある。
- printfを埋め込んだおかげでバイナリが異なってしまい、ダンプやスタックを追うのが困難になる。
- 複数のバイナリを管理しなければいけないのでコストがかかる。間違ってprintfを埋め込んだ版を本番用にインストールしてしまうかもしれない。
- printf版は徹底的にテストされないので、バグが残っているかもしれない。セキュリティホールになる可能性がある。
printfデバッグは百害あって一利なし。
でもやるやつは後をたたない。
それはgdbの使い方を十分伝承していないからである。gdbを使いこなせればprintfデバッグの呪縛から(多分)のがれられる。例外はLinux Kernelのように定番のデバッガがない場合で、その場合は泣く泣くprintk()というのを埋め込んでデバッグする必要があるかもしれないが、今回はふれない。
トップダウンアプローチ(巨視的理解)
gdbの起動は簡単だ。
gdb program
main()があるプログラムならとりあえづ、そこにブレークポイントを設定し実行する。
(gdb) break main
(gdb) run
そうするとmain()で一時停止する。ステップ実行をしたい場合はnext(n一文字だけでもOK)である。nは関数の中にもぐっていかないので、中身まで実行の様子をみたいのならstep(s一文字でもいい)する。
変数の値を見たい場合は print である。プログラムの理解のプロセスは、適当な位置にブレークポイント(b)を設定して、そこまで実行(r)し、変数の値を確認(p)する。これの繰り返しである。
xemacsのスクリーンショットを添付したので参考にしてほしい。デバッガの部分とソースコードの部分が表示されている。run の後にコマンドオプションをわたしている。
実行を再開するのは continue (c)である。そうすると次のブレークポイントまで全速力で実行する。ブレークポイントにたどりつく前に実行が終了(正常か異常かはとわず)する場合がある。
プログラムを理解するプロセスは、ブレークポイントで変数の確認をし、再実行をくりかえす、ということになる。ソースコードも同時に表示されるので、その前後のソースコードをじっくり読んで字面から理解を深めつつ、実際の変数の値を確認し、動的な側面からの理解をする。自分の予想と違う値であれば、それ以前にどこかでその変数の値を変更した場所(代入等)があるはづなので、そこまで遡って理解をする。ソースコードから追ってもいいし、ブレークポイントを実行順のより前に設定し、最初から再実行してもいい。
これを繰り返し理解を深めていく。
gdbを利用したソースコードの理解というのは必ずしもデバッグ(プログラムの不具合の修正)だけではなく、ソースコードを効率的に読む時にも利用できる。
gdbはソースコードの理解を助ける偉大なツールなのである。




コメント