未来のいつか/hyoshiokの日記

hyoshiokの日々思うことをあれやこれや

わたしがprintf()デバッグをしない理由

プログラマという職業について、もう25年くらいになるのであるが、その間にコンピュータのコストパフォーマンスは、それこそムーアの法則に従って、10万倍〜100万倍くらい向上した。にもかかわらづ、デバッグの方法というものの劇的な変化はほとんどみられない。

プログラミング入門書では、デバッグについて、ほとんど議論されていないし、仮にふれられていても、おざなりな方法というか、かなり邪険にあつかわれていたりする。プログラマの多くの時間がデバッグについやされていたとしてもだ。

たまたま手元にあった、C実践プログラミング(ISBN4-900900-64-8)という10年くらい前に買った参考書では、450ページのうちデバッガの利用については、4行ほど記述がある。たった4行である。診断用のprintf()を挿入するということは3ページにわたって記述されているのにだ。

流石に21世紀になってprintf()デバッグもなかろうと思うのだが、綺麗なIDEで、ブレークポイントを設定し、華麗にデバッグをしている姿というのが、どれくらい一般的かはわからないが、printf()デバッグはいかがなものかと思う。

新卒で入社したところでは、先輩がデバッグのイロハを教えてくれたので、printf()デバッグの悪弊にそまることはなかったのだが、なんで、printf()デバッグがよろしくないのか考えてみた。

確かにprintf()はお手軽である。変数の値を表示するのにこれほどお手軽な事はない。Cの教科書の最初のサンプルは'Hello World'である。それが、そもそもの間違いのような気もしないでもないが。

仕事で作ってきたものは、わたしの場合、ソフトウェア製品(コンパイラ、データベース管理システム、OSなど)なので、ちょっとデバッグするのにprint文を埋めてみようということには、ならなかったという事情があった。

ビルドに当時のマシンで数10分もかかるとなると、お気楽にそのような事はできない。デバッガを使うのが基本中の基本である。昨今のスクリプト言語全盛の場合、そもそもビルドなんていうことは初めから必要ないので、printして様子を見て、試行錯誤して、理解を深めるというプロセスも、なんとなくわからないでもないが、旧来型のビルド、テスト、デバッグというプロセスではビルドのコストがいかんせん高すぎる。

ハードウェアが高速化したんだから、ビルドなんていうのはmake一発だろう、機械力を使えよという話になるのであるが、ハードウェアが高速化した分、ソフトウェアも複雑化し、結果として肥大化してビルド時間もそれに比例してかかるようになって、やはり巨大ソフトウェアのビルド時間は、ほぼ一定という皮肉な結果になっている。

Linuxなんかもモジュールやドライバーをテンコ盛にすると、平気で、何10分もビルドにかかってしまう。

ということで、使いすてのプログラムや小規模なプログラムならまだしも、ちょっとした規模のものや、商用ソフトウェアの場合、printデバッグというのはお勧めできない。というかありえない。

それでも、kernelのように便利なデバッガというのが簡単に手にはいらない場合、どうしても要所要所にprintkなるものを埋め込んでデバッグすることもあるが、アプリケーションの場合は、便利なデバッガはあるので、それを使わない手はない。というか使うだろう、フツーは。

学生のころは、デバッグの方法というかスタイルも実に場当たり的で、適当にprint文を埋め込んで状態を確認し、テストをし、試行錯誤しているうちに、どれがデバッグ版で、どれが正式版かわけがわからなくなって、デバッグしているんだかバグをせっせと作り込んでいるのだか、何をやっているか、わからないということが良くあった。

デバッグの方法がデバッガを使うことが基本になると、どこでブレークポイントを設定するのか、どの変数の値を確認するのか、というスタイルになってきて、もうちっとシステマティックになってくるような気がする。すくなくとも、わけのわからないデバッグ文を埋め込んだバージョンを管理するという、まったく非生産的な、いたずらに問題を複雑化することはしていない分だけでも、デバッグに専念できる。

デバッグ対象のプログラムを正しく理解するためには、そのプログラムは変更してはいけないのである。動作を虚心坦懐に理解するために、1バイトたりともいじってはいけないのである。そして、そのためにデバッガを使えということになる。

アプリケーションプログラムの場合は、それでいけるのであるが、幸いなことにカーネルの分野においても昨今ではkprobe/jprobe/systemtapなどのプローブ用ツールが進歩したおかげで、printk()にたよることなく、ある程度まであたりをつけることが可能になってきた。また、oprofileなどのプロファイリングツールによってソースコードを変更することなくプロファイリングデータの計測が手軽に出来るようになってきている。

デバッグやテストをするときには、ソースコードを変更してはいけない。デバッグをするためにprint文を埋めこむなどというのは以てのほかである。(1)ビルドのコストがかかる。(2)デバッグ版と正式版という複数のものを管理するコストがかかる。問題をいたづらに複雑化している。(3)デバッガなどの機械力を使うのが正しい姿である。

まあ、こーゆーことを書くと、デバッガがない環境があるとか、タイミング的な問題はデバッガではおえないとか言う人が出てくるのであるが、そーゆー環境の場合でも、ソースコードを変更しないでデバッグする方法を考えるなり、構築するのが正しいアプローチだと思う。