Island Life

< コードと別にドキュメントを書く意味 | 『おくりびと』 >

2009/04/13

デバッガが使えないパターン

少し前にprintfデバッグかデバッガ使うかっていう話がちょっと 盛り上がっていた (わたしがprintf()デバッグをしない理由から)。 まあ結局は適材適所ということになるのだけれど、どういうケースでは デバッガが(使えない|使いにくい)かっていうケースを集めとくと建設的かも。

shinhさんが ひとつケースをあげていて、これはコンパイラ1が生成したコンパイラ2が生成した コードに出てくる問題を追いかける、というもの。コンパイラ1はしっかりした 処理系でコンパイルされるのでデバッグ情報がついているけれど、コンパイラ2の デバッガ情報を出すべきコンパイラ1はそんなにしっかりしてないので、コンパイラ2中の どこがおかしなコードを出しているかはデバッガじゃ追えない、という話。

以前ここでも書いた、VMへコンパイルする処理系のデバッグにもちょっと似ている。 デバッガで追えるのはVMの動作なんだけれど、それは同じ所をぐるぐる回ってるだけに 見える(レイヤが違う)。まあshinhさんのtccのケースの方は「ぐるぐる回っているVM」 自体が無くなっちゃうのでもっと大変だけど。

あと、私自身がよく当たるケースは、外部コンポーネントと実時間で協調してないと 再現しないようなバグ。ネットワークパケット送ったら一定時間内に返事が来てそれに 返信しなくちゃだめ、とか。パケット送ったあとブレークポイントで止めてると タイムリーに返事が返せないので状況が再現できない。相手も自分の制御下にあれば 多少はなんとかしようがあるんだけど、まったく制御できない第三者の作ったやつと 通信しなくちゃならないことも多いんで困りもの。(吉岡さんもカーネルハックしてたら そういうケースに当たることはあるんじゃないかと思うんだけれど、 デバッガで何とかするうまいテクニックがあるのかなあ。Debug Hacksに書いてあるかな?)

明らかな症状が出る時点では既に痕跡が消えているケース、というのもある。 センサ入力を1/30秒ごとに取って行列演算に使ってるんだけれど、どっかで 演算結果がおかしくなる。本当の原因は数フレーム前のセンサ入力なんだけれど、 はっきりとおかしい結果が出る時には数フレーム前の状態は上書きされちゃってる、とか。 異常が「画面を目で見ればわかる」けれども、数値的にどういう条件でそうなるのか わからないっていうのもよくあるな。1フレームだけポリゴンがパカッとするんで、 目で見てておかしいと思ってすかさず止めても、その時にはメモリは更新されてる。 本当の原因となる条件がわかれば条件付きブレークポイントをしかけられるけど、 そもそもそれがわからない段階でどうするかっていうと、いろんな状態をダンプして にらめっこするしかない。

デバッガにも簡単な条件の元で状態を出力する機能はあるけれど、だいたい こういう厄介なケースってかなり複雑な条件で出力を絞らないと ダンプ量が多くなりすぎて役に立たないことが多い。

外部コンポーネントが関わるケースについては、最終的には外部コンポーネントを エミュレートするプログラムを作っちゃうしかない。 わりとそういうケースが多いんで、私自身の印象として、デバッグというのは 単にprintfやらデバッガを使って潜んでいるバグを探すのではなく、 むしろシステムをうまく作り替えて特定の場所にバグを追い込むようにして、 そこに罠を張って待ち構えているという感覚がある。 デバッガ専門家の方の意見も聞きたいところ。

状態が上書きされちゃうケースについては、理想的には メモリを仮想化してブレーク時から時間を遡れるようになってるのが良いと思う。 言語的には上書きしてるんだけれど、実は別の場所に書いていて 元の内容も保存されてる、という感じ。

逆にprintfが使えないパターンとして私が体験したのは、 ビルドに5分かかるプロジェクト (CPUプールを使ってビルド自体は並列に 進むようになってたんだけど、最後のリンクのところで4分くらいかかっちゃうので あまり効果が無かった)。ところがこいつが、「問題の所在が多数フレームに またがった値の変遷を調べないとわからない」というパターンのやつで、 デバッガも使いづらい。結局、問題が出るコンポーネントだけを 別プロセスで回して、本体からソケット経由でデータを流して処理して 本体に返すという仕組みを作った。当該コンポーネントのビルドは 容易なのでデバッグサイクルを速く回すことができた。

(追記 2009/04/14 12:01:38 PDT): おお、shinhさんから反応が。 Window Managerのデバッグが難しいというのは、 「デバッガが実行時にデバッギの提供する機能を使う場合」と言えるかも。 なんかコンソールゲーム機いじってた時にこのパターンに時々当たったような 気がする。詳しいことは忘れちゃったけど。

Tag: Programming

Past comment(s)

井上 (2009/05/08 19:24:57):

遅まきながら。状態が上書きされる場合については、ocamldebug だと時を巻き戻すことができます。でも結局「どこまで巻き戻せば良いか」探すのが一苦労で、結局関係ありそげな状態を垂れ流しにして記録取って、それを eyeball なりなんなりして探した方が手っ取り早かったりします。UI がもっと洗練されればそうでもないのかもしれませんけど、現状あんまり意味が無いなあと思ってます。