Linux では広く用いられている lint はありません。ほとんどの人が gcc
の出す warning に満足しているからです。おそらく -Wall
スイッチが
もっとも役にたつでしょう。これは `Warnings, all' の意味です。しかしこ
のスイッチからは、どちらかというと頭を叩き付けたくなるもの(wall)を連
想しそうですね。
パブリックドメインの lint が ftp://larch.lcs.mit.edu/pub/Larch/lclint から入手できます。どのくらい良いかについては私は知りません。
コンパイルとリンクの全ての段階において -g
スイッチを付け、
-fomit-frame-pointer
スイッチは付けない必要があります。実際には全
ての部分を再コンパイルする必要はなく、デバッグしたい部分のみをすれば
OK です。
a.out の共有ライブラリは -fomit-frame-pointer
を付けてコンパイル
されており、これに対して gdb を用いることはできません。 -g
オプショ
ンをリンクのときに付けると static リンクになってしまうのはこうした理由
からです。
「libg.a が見つからない」というメッセージが出てリンカが動かない場合は、
/usr/lib/libg.a
がないのです。これはデバッグが可能になっている
特殊な C ライブラリです。 libc のバイナリパッケージに入っているか、あ
るいは(最近の版の C ライブラリでは) libc のソースコードを手に入れて
自分でビルドする必要があります。しかし実際にはこれは必要なく、ほとんど
の場合は /usr/lib/libc.a
に対するシンボリックリンクを張れば、
必要な情報は得られるはずです。
GNU のソフトウェアの多くは -g
フラグがついた状態でコンパイル、
リンクされています。そのため実行ファイルは巨大(かつしばしば static リ
ンク)になっています。これはあまりありがたくないですね。
プログラムに autoconf で作った configure
スクリプトが付属している
場合は、./configure CFLAGS=
または ./configure
CFLAGS=-O2
とすることでデバッグ用のコードを付加しないようにできま
す。その他
の場合は Makefile を書き換えることになるでしょう。もちろん ELF を使っ
ている場合はプログラムは -g
の有無に関わらずダイナミックリンクと
なっていますから strip
すれば良いだけです。
gdb が最も広く使われています。ソースは GNU archive sites から、バイナリは tsx-11 から入手できます。 xxgdb は X 用のフロントエンドで(したがって先 に gdb をインストールしておく必要があります) 、ソースは ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz にあります。
UPS デバッガも Rick Sladkey によって移植されています。これも X で 動作しますが、 xxgdb とは違って単なるテキストベースデバッガのフロント エンドではありません。多くの便利な機能がありますので、デバッグ作業を行 う人はチェックしてみると良いでしょう。 Linux 用のコンパイル済みバイナ リと、オリジナルの UPS のソースコードへのパッチとが ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/ にあります。オリジナルのソースは ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z. です。
デバッグに便利なツールをもう一つ紹介しましょう。 strace はプログ ラムが発行するシステムコールを表示してくれます。またコンパイル済みの バイナリから呼び出されるパス名を表示してくれるので、ソースがなくてもど のファイルが使われているかを知ることができますし、プログラムが引き起こ す競合状態(race condition)のレポートをしてくれるなど、いろいろ便利に 使うことができます。プログラムがどのように動作しているかの学習にも適し ています。 strace の最新バージョン(現在 3.0.8)は ftp://ftp.std.com/pub/jrs/ にあります。
訳注:現在は 3.1.0.1 のようです。
典型的なデーモンプログラムでは fork()
を実行し、親プロセスを終
了します。するとデバッグのセッションもあっという間に終わってしまいます。
これを回避する一番簡単な方法は fork
にブレークポイントをセットし、
プログラムが停止したときに 0 を返すように強制することです。
(gdb) list
1 #include <stdio.h>
2
3 main()
4 {
5 if(fork()==0) printf("child\n");
6 else printf("parent\n");
7 }
(gdb) break fork
Breakpoint 1 at 0x80003b8
(gdb) run
Starting program: /home/dan/src/hello/./fork
Breakpoint 1 at 0x400177c4
Breakpoint 1, 0x400177c4 in fork ()
(gdb) return 0
Make selected stack frame return now? (y or n) y
#0 0x80004a8 in main ()
at fork.c:5
5 if(fork()==0) printf("child\n");
(gdb) next
Single stepping until exit from function fork,
which has no line number information.
child
7 }
Linux はブート時の設定では core ファイルを作らない設定になっていま す。作る設定にしたい場合は、シェルの組み込みコマンドを使って再設定して ください。 C シェル系(tcsh など)では
% limit core unlimited
とします。 Bourne シェル系(sh、bash、zsh、pdksh)では次のようにします。
$ ulimit -c unlimited
core ファイルの命名法をもうちょっと便利にしたい場合(例えばそれ自身良く
落ちるようなデバッガで他のプログラムが吐いた core ファイルをデバッグす
るときなど)は、カーネルを少々変更すれば可能です。
fs/binfmt_aout.c
と fs/binfmt_elf.c
のコードの中に、
以下のような部分があるはずです(新しいカーネルの場合です。古いカーネル
では少々 grep してまわる必要があるかもしれません)。
memcpy(corefile,"core.",5);
#if 0
memcpy(corefile+5,current->comm,sizeof(current->comm));
#else
corefile[4] = '\0';
#endif
この 0
を 1
に変えて memcpy(corefile+5..
の行の方を有効
にしてください。
プロファイリングとはプログラムのどの部分が最も多く呼ばれ、最も長い時間
を食っているのかを調べることです。無駄な時間を使っているコードを最適化
するのに良い方法です。プロファイルを行うにはタイミング情報が必要なオブ
ジェクトファイルを -p
をつけてコンパイルします。出力されたファイ
ルから情報を得るには gprof
が必要になります(binutils のパッケー
ジに入っています)。詳細は gprof
の man ページを見てください。