次のページ 前のページ 目次へ

5. デバッグとプロファイリング

5.1 予防的メンテナンス(lint)

Linux では広く用いられている lint はありません。ほとんどの人が gcc の出す warning に満足しているからです。おそらく -Wall スイッチが もっとも役にたつでしょう。これは `Warnings, all' の意味です。しかしこ のスイッチからは、どちらかというと頭を叩き付けたくなるもの(wall)を連 想しそうですね。

パブリックドメインの lint が ftp://larch.lcs.mit.edu/pub/Larch/lclint から入手できます。どのくらい良いかについては私は知りません。

5.2 デバッグ

プログラムにデバッグ情報を埋め込むには

コンパイルとリンクの全ての段階において -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       }

core ファイル

Linux はブート時の設定では core ファイルを作らない設定になっていま す。作る設定にしたい場合は、シェルの組み込みコマンドを使って再設定して ください。 C シェル系(tcsh など)では

% limit core unlimited

とします。 Bourne シェル系(sh、bash、zsh、pdksh)では次のようにします。

$ ulimit -c unlimited

core ファイルの命名法をもうちょっと便利にしたい場合(例えばそれ自身良く 落ちるようなデバッガで他のプログラムが吐いた core ファイルをデバッグす るときなど)は、カーネルを少々変更すれば可能です。 fs/binfmt_aout.cfs/binfmt_elf.c のコードの中に、 以下のような部分があるはずです(新しいカーネルの場合です。古いカーネル では少々 grep してまわる必要があるかもしれません)。

        memcpy(corefile,"core.",5);
#if 0
        memcpy(corefile+5,current->comm,sizeof(current->comm));
#else
        corefile[4] = '\0';
#endif

この 01 に変えて memcpy(corefile+5.. の行の方を有効 にしてください。

5.3 プロファイリング

プロファイリングとはプログラムのどの部分が最も多く呼ばれ、最も長い時間 を食っているのかを調べることです。無駄な時間を使っているコードを最適化 するのに良い方法です。プロファイルを行うにはタイミング情報が必要なオブ ジェクトファイルを -p をつけてコンパイルします。出力されたファイ ルから情報を得るには gprof が必要になります(binutils のパッケー ジに入っています)。詳細は gprof の man ページを見てください。


次のページ 前のページ 目次へ