プログラマyasuhoの隠れ家

某ソフトウェア企業に勤務するおじさんプログラマyasuhoです

デバッグを想定しながらコードを書こう


バグの原因を調査することはもちろん大切なことですが、私はデバッグを想定しながらコードを書くことが大切ではないかと思います。よく考えられて作られたコードは問題も少なく、問題が発生しても調査しやすいものです。開発スケジュールとのバランスをとりながら、なるべく多くの気遣いが行き届いたプログラムを作ることが、結果的にはデバッグの手間を減らすことになるのではないでしょうか。

効率よいデバッグ


プログラムを作るということは、同時にデバッグをするという作業を伴います。デバッグというと、なんかすごく大変で苦痛を伴うイメージかもしれませんが、これは一種のパズルのようなもので、そういうのが好きな人には楽しい一面もあります。


プログラムの品質を高め、よりよいものにするためには、デバッグを効率よく行う必要があります。そこで、私なりに問題の解決方法について考えてみました。


一般論については以前書いたので、今回はより技術的な側面から。

デバッグ手順

まずはアタリをつける


何はともあれ、まずは状況の分析から。正しい値を入力しているのにエラーになる。アプリがクラッシュする。反応しなくなる。そういった状況から考えられる原因を推測しよう。原因個所を絞り込むことは大切だけど「ここは動いてるはず」といった思い込みは禁物。


データは正しく初期化されているか。設定ファイルやレジストリの値は的確か。C/C++等であれば領域は十分か。等々。まずは基本的な部分を疑ってみよう。分かってみればバグは単純なミスであることがほとんどなのだから。

情報収集


簡単に原因が分かる場合を除き、通常は問題の情報を収集することになる。


再現する環境がある場合はデバッガで止めたり、いわゆるprintfデバッグなどで情報収集するだろう。この場合も、まずはポイントを絞ることを心掛けたい。やみくもにトラップをはっても、大半は必要のない情報だし、何より時間がもったいない。


ユーザ先でしか再現しない場合は、デバッグ用モジュールを提供することがある。この時気をつけたいのは、できる限り製品版に近い状態にすること。多くの情報が出るからと安易にデバッグ版を使うと、現象が起きなくなったり、最悪の場合通常動作もできなくなることがある。


システムのLogやデバッグ情報を活用することは言うまでもないが、万が一に備えて情報を収集する仕組みを組み込んでおくと、なかなか再現テストが行えない場合に便利なことがある。ただ、過剰なデバッグ機能は性能を落とすことがあるのでバランスには気を使おう。

問題が再現しない場合


問題が特定のタイミングでしか発生しないような場合はやっかいだ。情報を得ようとデバッグ出力をしたら再現しなくなることも多い。特にスレッドやプロセスのタイミングに絡む問題などは再現が難しい。


Log収集の負荷を抑えるため、メモリ上にLog用バッファを取り、後でファイル等へ出力する方法がある。I/Oを伴うLogだと、どうしてもタイミングが変わりがちだからだ。


それでもなかなか情報は取れないものだ。その際は辛抱強くデータを取り続けることはもちろん、机上チェックから予想される要因を絞り込んでいこう。


CPUやメモリに負荷をかけるストレステストの環境があると問題の再現がしやすいことがあるので、調べてみるといいかもしれない。

メモリリーク問題


C/C++などではメモリ破壊や空きメモリの現象等、いわゆるメモリリークの問題が顕著になりがち。問題が発生した時は原因となった処理はすでに終了しているので、解析が困難な場合も多い。しかもセキュリティホールの原因ともなりがちだ。


幸い近年はメモリリークの検出ツールや手法が多くあるので、可能であればそれらを活用しよう。ヒープ破壊などではMicrosoftが提供するPageHeapが便利。最近のVisual Studioではメモリの解放し忘れをチェックしてくれるようだ。

カーネルレベルのデバッグ


カーネルモードのデバイスドライバや組み込みソフトウェアなど、比較的ハードウェアに近いモジュールでは便利なデバッグ機能が使えないことが多い。タイミングによってはprintfデバッグすら使えない場合もあり、なかなか大変な面もある。


このような場合は、とにかくアセンブラと友達になっておくことをオススメする(笑)逆アセンブラからソースの個所を推測したり、レジスタとメモリダンプしか手段がないことも多いからだ。対応するシンボルファイルを用意しておくことも大切なポイント。

デバッグを想定しながらコードを書く


これらはデバッグ方法のほんの一例にすぎません。デバッグ手法はソフトウェアとその動作環境の数だけ存在すると言っても過言ではないでしょう。問題や現象も様々です。


バグの原因を調査することはもちろん大切なことですが、私はデバッグを想定しながらコードを書くことが大切ではないかと思います。よく考えられて作られたコードは問題も少なく、問題が発生しても調査しやすいものです。よいモジュール構成のプログラムは、原因の絞り込みも楽になります。


とはいえ、プログラミングはある程度早く仕上げる必要があります。あまりにも慎重では開発全体に影響するかもしれません。それでも後の手間を考えると、初期の段階でコードを修正しておくことには意味があります。なぜならソフトウェアは後になればなるほど大規模な修正がしづらくなってくるからです。開発スケジュールとのバランスをとりながら、なるべく多くの気遣いが行き届いたプログラムを作ることが、結果的にはデバッグの手間を減らすことになるのではないでしょうか。




過去の関連記事:
yasuhoの隠れ家 - デバッグ゙は最高に面白いパズルゲーム