アルゴリズム工学データベース
---

gcc のデバッグ術

gdb, gprof, ElectricFence の簡単な使い方

---



 Unix系コマンドラインユーザーのための、 gcc/g++/g77 による開発におけるデバッグ術を簡単に紹介します。

 以下の内容は gcc 2.7.2.3 での動作は確認しています。 g++/g77 でも恐らくは通用すると思うのですが、 ひょっとすると異なる部分があるかもしれません。 筆者は g++/g77 の使用経験がないので、その場合は御容赦を願います。


実行前

キーワード「コンパイルオプション, -Wall, -O2, -O4」

 まずは gcc にオプション opt'-Wall' を付けてコンパイルし、 警告がなくなるまでソースを修正します。 これは 常識 です。

 次に opt'-O4 -Wall' でコンパイルします。 「未初期化変数の使用」の警告 (`foo' might be used uninitialized in this function) は、 opt'-O4' を付けないとチェックしてくれないようです。 でもコンパイルに時間がかかるんですよね。 「コンパイルなんかあっという間に終わっちゃうよ」という 高速なマシンを使っている幸せな人は、 いきなり opt'-O4' をつけておいてもいいでしょう。

 さてさて、実際に動かす場合は opt'-O4' でも構いませんが、 これから紹介するデバッグには opt'-O2' ぐらいが好都合です。 デバッグが終われば opt'-O4' にしてもいいでしょうが、 「コンパイラの最適化バグ」に当たるのが恐い心配性の人は opt'-O2' のままにしておきましょう。 「速く動けば何でもOK」というチャレンジャーな人は opt'-O5'opt'-O6' を検討してもよいでしょうが、 かえって遅くなることもあります。 このあたりの動作はコンパイラのバージョンによってかなり違いますので、 'man gcc' などで調べてみて下さい。


ようやく実行 - "Segmentation fault!!"

キーワード「core, gdb, -ggdb」

 ここまで来て、はじめてプログラムを実行させます。ちゃんと動きましたか? もし、"Segmentation fault" で止まってしまうようなら、 ソースのどの部分で止まるか調べたいですよね。 "core" というファイルができるなら、これを手がかりに調査しましょう。

 それにはコンパイルの時点からやり直した方が得策です。 opt'-ggdb' を付けてコンパイルしましょう。 後述 するように opt'-lefence' も付けると、 なお良いでしょう。 opt'-O4' はやめて opt'-O2' ぐらいにしておきましょう。 "core" のサイズが0バイトの場合は、 sh/bash 系では 'ulimit -c' コマンド、 csh/tcsh 系では 'limit -h' コマンドで、 "core" のファイルサイズ制限を確認しましょう。

 そして再コンパイル&実行させて、 もう一度 "core" を吐かせてから次を実行します。

% gdb a.out core

 こうすると、止まった関数の呼び出された引数と、 関数の中での止まった場所がわかります。 gdb 中で 'list' コマンドでソース表示、 'where' コマンドで関数の呼出し関係が表示されます。 これを手がかりに修正すると、非常に効率的です。

 opt'-ggdb' なしにコンパイルしていると、 ここで表示される情報が少なくなってしまいます。


普通に printf()

キーワード「デバッグライト, fflush, ANSI, エスケープシーケンス」

 さてさて、一通り実行できるようになりました。 でも、挙動不審な点があるのはいつものことですね。 そんな時は、まずはデバッグライト。 printf()で情報表示しましょう。 「そんなん常識やん」ですが、 printf( "%s \r", message ); fflush( stdout ); なんて技をお教えしましょう。 改行せずに同じ位置に情報表示できます。即座に表示するのが fflush()。 ANSI を使えば、色も付けられますし、表示位置も移動できます。 (内容の詳細は後日更新します。)


ElectricFence を使おう

キーワード「efence, core, -lefence」

 printf() 1つ入れるだけで "core" を吐かなくなった、 なんてことはありませんか。 原因は、初期化してない変数を使ってるか、 配列あふれであることが多いです。

 こんな時には "A malloc(3) debugger" である ElectricFence を使いましょう。 いえ、初めから、何も起こらなくても使っておくべきです。 配列をオーバーフローすると、即座に止まってくれるようになります。 opt'-lefence' を付けてコンパイルしておくと、 問題の箇所で "core" を吐いて止まるようになります。 これで、前述 のように gdb を使えば、 どこで止まるかわかります。

 ElectricFence が導入されていない場合は、システム管理者にお願いしましょう。 システム管理者の方は、以下の手順で導入して下さい。 (多くの Unix で使えるようですが、詳しくは付属の "README" で確認して下さい。) まずは 本体, パッチその1, パッチその2 をコピーして下さい。そして以下のように操作します。

% tar xzvf ElectricFence-2.0.5.tar.gz
% cd ElectricFence-2.0.5/
% patch -p1 < ../ElectricFence-2.0.5-glibc.patch
% patch -p1 < ../ElectricFence-2.0.5-longjmp.patch
% vi Makefile
--- OSに合わせて必要な編集を行う
    しかし install に $(MV) するのは いただけないなぁ
    ここは $(INSTALL) にしないとユーザー権限のファイルができてしまう ---
% make
--- 最後に "Electric Fence confidence test PASSED" が表示されれば成功 ---
% make -n install
--- 実行される内容を確認 ---
% su
--- ここで初めて root 権限に ---
# make install
# cd /usr/man/man3/
# ln -s libefence.3 efence.3
--- 'man libefence' 'man efence' 共に使えた方が親切でしょう ---

 ソースとパッチはElectricFence-2.0.5-11.src.rpm (ソースパッケージ)から取り出しました。
参考文献:memory leak check


高速化とデバッグに役立つ profiler

キーワード「gprof, -pg, 関数呼び出し回数, 計算時間」

 「この関数は1度きりしか実行しないはずなのに、何度も実行されているようだ」 なんて時には profiler を使いましょう。 本来 profiler は、高速化のポイントを探るための道具ですが、 デバッグにも役立ちます。

 それには、opt'-pg' を付けてコンパイルします。 最適化は opt'-O2' ぐらいを指定します。 そしてプログラムを実行します。 すると "gmon.out" というファイルができます。 できてないようだと、リンク時に opt'-pg' が付いていないことが考えられます。 'gprof a.out' を実行すると、 実行した関数の一覧と関数の呼び出し回数が表示されます。 これでちょっとは参考になるでしょう。

 また、本来の使い方ですが、関数ごとの計算時間がわかります。 「1つの関数での計算時間が全体の50%」なんてのはザラなので、 高速化する場合はこういう関数から攻撃しましょう。


gcc の最適化オプションについて

キーワード「-O?, -pg, -ggdb」

 コンパイラ一般には、デバッグオプションを指定すると 最適化オプションが無効になってしまうことが多いそうですが、 gcc では opt'-pg'opt'-ggdb' を付けても opt'-O?' の最適化と両立します。

 opt'-O4'は関数のインライン展開を行うので、 'gprof' の表示で、あるはずの関数が表示されないことがあります。 これは、「関数がマクロかのように」呼び出し元に展開されて、 独立した関数として存在していないためです。 opt'-O2' にするとそんなことは起こらなくなるので、 デバッグ中はこれを常用することになるのですが、 たまには opt'-O4' にしてやらないと、前述のように 「未初期化変数の使用」警告を出してくれないんですよね。 opt'-O3' はどうだったかなぁ?うーん、調べてみて下さい。(^^;)


終わりに

 一般に、デバッグのためのオプションを付けると、 実行時に計算時間が余計にかかります。 完成品には使わないようにしましょう。 デバッグライトは、後々のためにコメントで残しておくのは当然ですね。

 効率の良いデバッグのための基本は、こんなところでしょうか。 もちろん gdb バリバリとか、 MS-Windows によくある統合環境のような開発ツールを使うのも良い方法ですが、 「コマンドラインから gcc、printf()(デバッグライト)しか使わないよ」 という人にとっては、このぐらいがちょうど良い、 いやいや、必要十分かつ最適な方法だと思います。 もっと便利な方法があったら、教えて下さいね。


データベース [ 離散最適化 | グラフ | 幾何 | 並列/分散 ]
[ キーワード検索 ] [ リンク集 ] [ 動作確認環境 ]
参加メンバー [ 可視化 | パッケージ製作 | デバッグ術 | 標準入出力 | Makefile | 雛型 ]

土村 展之(tutimura@logopt.com)
<最終更新日 2000年3月4日 19時40分>