デバッグ方法論

吉岡さんが釣り糸を垂れているので、釣られてみるよ。


と言っても、多分、僕の気力が続くのは、こんなトピックがあるよ、って事までかな。
細かい話が気になるって人達は、スターとか、ブクマとかで催促すると、
書く気になるかもしれないし、どっかの宴会で、続き喋れって言われれば喋るかもしれない。




前提として、Javaで書かれたコードをデバッグすると考えてクダサシ。
ある程度実行環境やコードの種類に依存しないデバッグの方法論ってのもあるんだろうけど、
僕が気付かないウチに、Javaに強く依存したデバッグの方法論を抱え込んでるって事もあるだし。


まず、デバッグと言う作業が、どの様な構造を持っていると、僕が理解しているか、書いてみる。

この4つだに。
ちなみに、コードを修正する事は、デバッグでは無いものとするよ。
それぞれは、作業手順として、ある程度の依存関係はあるのだけど、
厳密な順序性があるワケでも無い事がポイントだに。
それぞれの構造は、めっさ不安定で厳密な定義は無いのだけど、
ボチボチとしたそれっぽさを求めて作業する感じで。
つまり、デバッグは、本質的にイテレーティブな作業だと思いますですな。

「バグの定義」について

バグだって誰かが断定してなくても、バグはバグだって事もある。
つまり、調べてみたら、バグでも無いんじゃね?みたいな。
確かに、

  • 例外が飛んでたり、
  • 画面が真っ白くなったり、
  • なんぞダンプ吐いてアプリケーションが死んだり、
  • メシ食って帰ってきてんのに、処理が終ってなかったり、

大体、まぁ、そういう事があって、「バグあるんじゃね?」みたいな疑義を持つ事になるヨネ。
このバグの定義を厳密にやれれば、やれるほど、他の作業が楽になるんだけど、
そうは上手くいかんのだよね。


「ナニがバグなのか?」


これを、スッゲー考えてちゃんと定義出来たら、殆どの場合、有限時間でデバッグが終了する。
僕の経験から言うと、バグを定義する事と、バグを解消する事を同時に考えると、
大概碌な事にならない。っつうか、時間がかかる。
バグの定義と、バグの解消は、全く別な作業だという事を強く意識すると、上手くいき易いんじゃね?と思う。

「静的デバッグ」と「動的デバッグ」について

静的デバッグと動的デバッグには、いくつか作業単位がある。

  • 仕様の厳密な理解
  • 実装の厳密な理解
  • 仕様と実装の齟齬の発見

理解ってトコロが、結構ポイントで、どの程度理解したら厳密なのかを定義する事は出来ないよ?
問題を解決出来るレベルまで理解したら、それが厳密な理解でもいい。
もっと広い視点で理解出来ればもっといい。
仕様と実装を適切に理解する事、その間にある齟齬を発見する事が出来る筈だよね?
本当にバグってんなら。
仕様がバグってんなら、仕様と実装はあってるかもしれないけどね。
でも、期待される動作と、実際の動作が違うんだから、これは仕様と実装に齟齬があるって考える。
つまり、話がちとややこしいので、ここでは、その場合、「仕様書」がバグってると考える。


ここでは、仕様と実装を、どうやって理解するか?
と言う具体的な方法の違いを指して「静的」とか「動的」とか言ってる。
ナニがどう違うかと言うと、それは、コードを実行するか否か?
ここは、相応に厳密だと思う。
つまり、

  • 仕様レビュー
  • コードレビュー
  • 喫煙室及び休憩室における雑談

は、静的デバッグ

  • デバッガを使う
  • プロファイラを使う
  • printfを埋めて、出力を見る

は、動的デバッグ


んじゃ、ま、静的デバッグについてもう少しだけ詳しく説明するよ。


仕様書とか、要件定義書とかメールのログとか、コードのあるべき姿について、
自然言語で書かれたものを、出来るだけ沢山読んで理解する事は、
デバッグ作業の一部と看做すべきだと思うます。
自然言語で書かれた文書は、基本的にある一定以上の曖昧さを持っているます。
つまり、その曖昧さが、実装において許容されうる曖昧さなのかに注意して、
それらの文書を読んでいく事が肝要であります。
これには、相応に実装の経験が必要になるかとオモイマス。
静的デバッグを適切に行う為には、単に、コードの量を多く書くという事ではなくて、
文書化された仕様を元にコードを書いた経験が重要なのです。


また、他人が書いたコードを静的デバッグするにあたっては、
他人が書いた文書をまた別の他人が実装すると、どの様になるのか?
と言う、文書とコードの対応関係に気を使って読んだ文書の量とコードの量が重要になりまつ。


置かれている状況にも拠るのだけども、
コードを読むと言う作業を行うのは、なるべく最後にするのがヨロシイかと、僕は思います。
まぁ、コードリーディングと言うと、それはそれで、大変な量の文章が書けてしまうし、
コードリーディングに関するドキュメントは、Google先生に聞けば相応に転がってると思いますので、
そっちに任せるって事で1つよろしくお願いします。


プロジェクトのメンバーとの雑談もデバッグ作業の中に、含めるとヨロシイかとオモイマス。
何せ、仕様を決めるのも、コードを書くのも人間だもの。
つまり、バグを埋め込んだのも、それを発見するのも、人間だもの。
後、経験則的に言うと、相対的に、バグを埋め込む量が多い人ってのは、確実に存在しるます。
プロジェクトが大きければ、大きいほど、沢山のモジュールを結合して実行する事になる筈だけど、
二分探索方みたいな、機械的アプローチと共に、バグを埋める頻度が高い人が書いたコードを、
重点的にチェックするってのも、それはそれで、効果があるんだぜ?
それと、普段の雑談の中で、モノゴトの理解の深度みたいなものを、人ごとに把握すると、
相対的に、仕様を誤解し易い人ってのも、おのずと分るってもんです。


既に、スゲェ長文になってるけど、次は、動的デバッグについて。
まず、


eclipse のデバッガ使え


これで、Javaの場合、半分以上の問題は、簡単に解決しるます。
ブレークポイントの設定方法には、スゲェ色んなノウハウがあるんだけど、今回はパスで。
これはこれで、コードリーディング程では無いけど、それだけで膨大な文章が書けますデスナ。
ザックリとポイントだけ列挙しておくと、

  • エントリポイント
  • イグジットポイント
  • インターフェース
  • ライフサイクル
  • ループ
  • 条件付ブレークポイント

この辺がキーワードになりまつ。


次に、バグの定義がしっかりしてれば、プロファイラを使うべきか否かは判断が可能な筈なのさー。
ヒープのダンプが必要なのか、埋め込んだプローブを使った情報が必要なのか、そういう事は、
作業として、バグの定義の中でやる感じで。
世の中便利なツールが色々あるでよ、武器はトラブる前に集めておく事。これは、デバッグとは関係ない話か。


Javaの場合には、printfデバッグって積極的に使う理由が余り無いのだけど、
幾つかの例外的な状況において、標準出力する事に妥当性がある場合もある。
それは、何かと言うと、

  • バグの場所がサッパリ分からない場合
  • バグの種類が、データのバリエーションに対して処理が間に合っていない事が明らかな場合

前者の場合には、日常的に、何らかのLoggingAPIをせっせと使って、
ログが出るようにしてあれば、バグった時に敢えて埋め込みなおす必要も無い筈なんだけど、どうでっしゃろ?


後者の場合、バグの原因になっているデータのバリエーションをある程度単純なブラックリスト形式で、
明らかに出来るんなら、条件付ブレークポイントで何とか出来る事もあるし、出来るならその方が良いかな。
物凄い膨大な量のデータバリエーションの中で、どっかにバグっぽいのが含まれているんだけど、
ようワカランっつう時には、LoggingAPIでゴッソリログを出して、Excel先生に助けて貰ったりするとイイと思うよ。
Excel先生で処理しきれない程、膨大な量なら、RDBに突っ込んでSQL投げるとか、そういう手もある。
データマイニング的な手法は、デバッグ作業の中でも勿論使えるんだぜ?

「フォースを感じる」について

これは、方法論と言うには、余りにうそ臭い話なんだけど、実際ある。
色々やってみて、やっぱりワカンネって時は、風呂に入ったり、神に祈ったり、護摩を焚いたりすると、イイと思う。
啓示的なナニかが、脳内に弾けて、一挙に問題解決まで持ってけるって事はある。
その為の前準備として、コードを大量に脳内に入れたり、
仕様を大量に脳内に入れたりしなきゃいけないかどうかは、人に拠るトコロ。
ホントかウソか僕はよく分らないんだけど、
人間は、スゲー脳を使ってる人でも、全体の3割位しか使ってないらしいし。
残りの7割のどっかが稼動すれば、何でも出来るんじゃねぇの?とか、そういう感じ。