意図と異なる動作をバグと呼ぶことが多い。意図通りに動かないソフトウェアなぞ全く役に立たないので、デバッグという作業は非常に大事な開発プロセスである。でも今までやっていたデバッグの作業は、クラッシュするまでステップオーバーし、printfとMessageBoxを元にtry&errorで繰り返しビルドするという単純なものだった。これじゃあイカンと思って、キッチリとした技術を学ぼうと思った。
以下本当にダラダラとした学習の記録
Toby Opfermanなる人が素晴らしい文章を書いている。コーディングの心構えなどには触れていないが、実際のデバッグの手続き、つまり「どういうソフトをどのように使ったら何がわかってどのように嬉しいか」について丁寧に解説している。基本的にデバッグはprintfに頼りきってる人(私)が見れば、世界がひとつ増える感じ。
OutputDebugStringというAPIがあり、デバッグ出力にダラダラと出力させることが可能(いちいちMessageBoxを出すのはあまり良くない)。MFCにはTRACEマクロという形で提供されているようだ。たろの部屋−たまにはVC++−
この出力は、Visual C++なんかだと当然開発環境の中にそういった出力ウィンドウが用意されているが、この出力だけを取得する単体ソフトもある。
プロセスごとの区別なく手当たり次第に出力をかき集めてくれる。便利でしかもフリー。VC++なんかからデバッグありで実行した場合は、出力はそちらに取られてDebugViewの方には出てこないようである。
便利
プログラム中のどのコードがボトルネックになっているのかを調査するものがプロファイラである。とりあえずフリーのものとしてはDevPartner Profiler コミュニティ・エディション が挙げられる。Visual C++ .NET 2003にインストールしてみた。
全然使い方がわからんので調べていたら、わかりやすい備忘録を腕によりをかけないプログラマの駄文: 参照ソートと実体ソートでプロファイリング(DevPartner Profiler編)に見つけた。曰く
プロファイリングが不要になったら、プロファイラを無効にした上でリビルドすると良い。リビルドせずに無効にするだけだとプロファイラのカケラが残るので、私の場合は実行時に
myprogram.exe の 0x7c809e3a で初回の例外が発生しました : 0xC0000005: 場所 0x765c0000 を読み込み中にアクセス違反が発生しました。 。
なんて出力が検出されてしまった。最初は何が原因かわからなかったが、例外出た時にデバッガで中断するようにし、そこでコールスタックを見てみたら、DevPartnerの一部っぽいTTCore.dllのロード直後に発生しているようだったので、上記の教訓を得ることができた。
@err,hr
ウォッチで上記を評価させると最新のエラーの内容がわかる。@errは最新のGetLastError値で、hrというのはそれを説明に変換してくれるフォーマット指定で、HRESULT型にも適用できる。
vectorの中身をウォッチで確認するにはどうすればいいのだろう?と思っていたら、そのものズバリの回答があった
Microsoftが無償で提供しているデバッグ用のツール。上のToby OpfermanはVisual Studioのデバッガよりこちらを使えと言い、John Robbinsは相補的に使えと言っている。要は使うべきなのだろう。Debugging Tools for Windowsで入手できる
いまいち理解が浅い。
John Robbinsは、Releaseビルドでもシンボルを組込めと主張している。Visual Studio 6でそうするためには、プロジェクトの設定で
で良いようだ。シンボルは拡張子がPDBのファイルに格納される。
シンボルのテキストファイル版っていう理解でいいの?いずれにせよ作った方がいいみたい。プロジェクトの設定で、
すれば良いみたい。Finding crash information using the MAP file - The Code Project - Debug tipsという記事にも同様なことが書いてある。
シンボルを参照するパスは、とりあえず File ==> Symbol File Path から以下の様に指定できるみたい。
SRV*c:\temp*http://msdl.microsoft.com/download/symbols
httpとあるのはシンボルサーバというやつらしく、必要な時にOSのシンボルをリモートから取得してくれるみたい。_NT_SYMBOL_PATHなる環境変数でもいいようだ。自プログラムのシンボルファイルは、.exeと同じディレクトリに置いてもいいのか?
xなるコマンドで見えた。ワイルドカードが使えるみたい。モジュール名の右隣に (pdb symbol) とあるのは、ちゃんとPDBから読めましたという意味なのだろう。 (deferred) とあるのは lazy mode(Deferred Symbol Loading)というやつなのだろう。
"Debugging Tools for Windows"のヘルプの"Symbol Syntax and Symbol Matching"という項目に書いてあった。大文字小文字の区別は無く、モジュール名とシンボル名は'!'で区切り、シンボル名はアルファベットか'_'か'$'か'?'で初まるのだそうだ。debugtestをモジュール名とすると
debugtest!myfunc debugtest!_c_exit
みたいになる。