デバッグとデバッガ

このへんについて。

hyoshiok氏の

  • 場当たり的なprintfはよろしくない
  • デバッガを活用しよう

というのは、個別にはそれぞれ同意なのだけど、

  • printfデバッグの延長線上は、まともなロギング
  • デバッガが使えるシチュエーションは限られる

のではと思う。

そもデバッグとは何ぞやという話なのだが上のDebuggingにもあるが、デバッグとはバグを除去するプロセスである。ソフトウェア開発の流れとしては

  1. 設計
  2. コーディング
  3. バグ発見(不具合の有無をテストによって調べる)
  4. バグ再現(不具合の発生条件をテストやデバッガで調べる)
  5. バグ調査(不具合の原因や場所をログやデバッガで調べる)
  6. バグ修正

という感じで、狭義ではバグ修正の部分のみ、広義では発見から修正までがデバッグかなと思う。
そしてデバッガであるが、Debuggingにもデバッガは実質的にはinspectorだと書いてあるがその通りで、デバッガはここでいうところの調査フェーズ用のツールである。一方、適切なロギングは、発見から調査まで役に立つ。
例外発生やクラッシュダンプからアタッチするというのが典型的なデバッガの使い方だが、途中にどんな状態遷移を経ておかしくなったのかまではわからないことが多い。なので、デバッガが使えないよりは使えたほうがよいのはもちろんだが、システムやバグの全体像をつかむのにはデバッガだけでは力不足なことがよくある。

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

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

ブレークポイントを、適切な場所に適切な条件で設定するというのは経験がものを言う世界であり、それはシステマティックではなく「勘」に頼った方法論ではないかと思う。重要なトレースポイントのログを全部取れるようにしておき、出力時あるいは出力後にフィルタリングをかけるようなやり方のほうが、より敷居が低く、より機械的に処理できるんじゃないかなと。

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

デバッグ方法論

私も同じ質問を何回かしたことがあるのだが、どうやらハッカーはデバッガを使わないらしい。曰く、適切なコードレビューを行えば動かしてみないとわからないようなバグは出ないので結果的に使わない、ということであった。
ただこれには同意しにくい気持ちがあって、コードレビューさえ行えばよいという環境を実現するためには、

  1. ソフトウェア基盤が信頼できる(ハードウェアやコンパイラ、OS、ライブラリが十分信頼できる)
  2. コードの実行結果を完璧にイメージできる(コードを把握する能力が、コードの複雑さを上回っている)

という二点をクリアする必要があると思っている。
前者だと、自分で記述したソフトウェアが正しくてもOSやハードウェアに不具合があればシステムとしてはバグになるし、後者では、他のシステムや他人の書いたブラックボックスの部分まで含めると全体の挙動が把握できないことはあるだろう。

一般則としては、

  1. ハードウェア制御など言語レイヤが下がるほどデバッガが必要になり、
  2. スクリプト言語など言語レイヤが上がるほどデバッガは不要になる

ということのようである。