未来のいつか/hyoshiokの日記

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

デバッグ方法論

「わたしがprintf()デバッグをしない理由」http://d.hatena.ne.jp/hyoshiok/20090322#p1 は思いのほか注目をあびた。せっかくなので、その続編とも言うべきことを書き記してみる。

ところで、皆さんは、誰にデバッグ方法を教えてもらったのだろうか?テストの方法を誰に教えてもらったのだろうか。あるいは、ソフトウェア開発方法論を誰に教えてもらったのだろうか。学校でならったのは、プログラミング言語の文法であり、アルゴリズムであり、コンピュータアーキテクチャであった。デバッグ方法は誰も教えてくれなかったので、見よう見まねで行っていたのが学生時代だったような気がする。

バイト先のちっちゃいソフトハウスでは、トラブルシューティングは二分検索でやるということを教えてもらったが、デバッグ方法についての、コーチはなかった。別のバイト先では、厳密なテスト方法について学んだが、やはりデバッグ方法は、教わらなかったような気がする。

25年前DECという米国の会社の日本法人に入って、最初の配属先で、VAX COBOLのプロジェクトリーダがソフトウェア工学のトレーニングをしてくれたが、その中にデバッグの方法とか質問の仕方などの講義があったような気がする。もう随分昔のことなので詳細は覚えていないが、随分実践的な話だったような印象がある。一応コンピュータサイエンスを学んだ身としては、コンパイラの構造とかについての耳学問はあって、LL(1)とかLALR(1)文法がどうだこうだとかいう知識もあった。VAX COBOLの内部ドキュメントを読んで、本物のコンパイラって、こうやって作るのだということに感動した覚えもある。もちろんドラゴンブックは、ぼろぼろになるまで読んだが、それとは別の感動があった。

VAX DEBUGというデバッガがあって、VAXの呼び出し規約というのにすべてのVAXコンパイラが従っていたので、VAXコンパイラ(FORTRAN/COBOL/BLISS/Adaなど)が生成するバイナリはデバッグ可能だった。で、デバッグの方法なのであるが、VAX DEBUGを使えということであった。どこにブレークポイントを設定し、どのデータ構造を見て、どのようにトラブルシューティングをするかはもちろん製品によって異なるが、その方法論は一緒だった。後にVAX Rdb/VMSの開発をすることになるのであるが、その時も、ダンプの読み方、ログの読み方、トラブルシューティングの仕方と同様にデバッグの仕方というのを教えてもらったような気がする。

それはなんでなんだろうか。

趣味のプログラムではなく、仕事としてのプログラムの作成は、チームとして行う以上、メンバーの生産性が全体の生産性になる。ひとりひとりの生産性を向上させることが全体の生産性向上になる。新人を手っ取り早く戦力にするために、テストの方法やデバッグの方法を教え込むことが理にかなっている。新人がのたのたprint文で、問題を理解しようとしていたとする。もっと素早く問題を理解する方法があって、そちらの方がはるかにコストが安いとしたら、コーチは躊躇なくその方法を教える。新人は、なぜその方法が優れているのかを聞く。コーチはそれを新人に理解できる形で言語化を試みる。そこには、先輩後輩の力関係ではなく、ベストプラクティスをどうにか言葉にして共有しようとする文化がある。ひょっとしたら新人の方法に新しいクレージーなアイデアが潜んでいるかもしれない。それを対話によって引き出そうとする貪欲さもある。

コンパイラや、RDBMSあるいはOSのような大規模ソフトウェアはビルドのコストが高いので、試行錯誤のプロセスとしてprint文を埋め込んでやるのは、イテレーションをまわす上で不利である。一方コンパイル、ビルドのコストがないスクリプト言語で記述するアプリケーションの場合、試行錯誤のイテレーションで変数の値を出力するコストは安い。

どのような条件の時、デバッガのような機械力を使うのが有利で、どのような条件の時に、print文を使うのが有利なのか。それを、このように明示的に言語化することが、われわれプログラマにとってデバッグ方法を理解する絶好のトレーニングになる。

なんでもかんでもデバッガを使えというのは、人生を7番アイアンだけでわたり歩くようなものである。(とか言いながらゴルフをやったことがないので、人生を7番アイアンだけで渡り歩くというのが、どーゆー意味かはわかっていないのだけど)。なんでもかんでもemacsだけでわたり歩くようなものかもしれない。わたしだってWindowsで、ちょこっとものを書くときはメモ帳だし。viだって使うし。適材適所は当然である。問題は、どのような適材にどのような適所があるのか。その前提条件を詳細化しようということである。

わたしは、いろいろなハッカー*1 に会うたびに、「どうやってデバッグしているのか」を聞いている。奥地さんにカーネル読書会GRUBのお話をしてもらったときも*2、一番前の席で、同じ質問をした。http://www.airs.com/ian/essays/debug/debug.html を教えてもらって、早速ブックマークした。*3

カーネル読書会の懇親会では、たいてい酔っ払っているので、次の日、聞いた内容を忘れてしまうのが玉に瑕である。残念。

しかし、「どうやってデバッグをしているのか」というわたしの質問に(奥地さんではないけれど)多くのハッカーは、きょとんとする。今まで誰にもそんなことを聞かれたことがなかったかのような顔をしたりする。言語化しなければ、ベストプラクティスを共有できないではないか、趣味の問題と片付けていたらそこから学べないではないか。

まあ、人生は短いので、デバッグしなくてもすむ方法を考えるのが重要だというのはおっしゃるとおり。バグを埋め込んでもすぐ見つけられるようにリグレッションテストをちゃんと作るとか、エラーを見つけやすいテストを開発するとか、エラーがおき難いような実装方法や設計方法を見出すとか、もちろんそのような方法を否定しているわけでは全然なく、だけど、誰かが作ったソフトウェアのどっかのバグをやっつけなくちゃいけないという仕事としてのプログラミングの場合、そうも言っていられないのも生々しい現実なわけで。

ソフトウェアのtar ballを解凍してビルドするときに、Makefileなりconfigureなりで最初に目を通すのはCFLAGSに-gが入っているかどうか、入っていなければ入れる、くらいのことはするのであるけど、それも、わたしは、こーするというスタイルであって、別に誰に強制するつもりもない。だけど、結局デバッグビルドをするのであれば、最初にやったほうが手間がないと思うけど、これも趣味の問題なのだろうか。

バージョン管理システムを利用すれば、print文を埋め込んでも問題はないだろうというコメントも頂いたが、ログ出力用のものと、単なるデバッグ用の試行錯誤用のprint文は別問題として、試行錯誤用のprint文付のバイナリを生成すれば、当然テスト等の工数は倍になることはあっても減ることはない。バージョン管理システムを利用しても、工数を減らすことはできないのである。

Algorithmic Program Debugging

計算速度が10万倍〜100万倍が早くなっているのにデバッグを機械力に頼らないというのも、不思議な気がする。

下記はデバッグ機械的に行おうという試み。そのプロトタイプは25年以上前に出たのであるが、この手のアプローチをモダンな言語で誰か実装しないかなあと妄想したりする。当時の非力な機械力では現実的でなかったクレージーなアイデアが、富豪的なアプローチで甦ったら面白いなあと思う。学生時代に読んで、どひゃーすげーと思ったのであるが、この分野、その後どう発展したのであろうか?識者の見解を待つ。

Twitterでぶつぶつデバッグのことをつぶやいていたら、*4 河野さんがつぶやき返してくれた。*5