意図と異なる動作をバグと呼ぶことが多い。意図通りに動かないソフトウェアなぞ全く役に立たないので、デバッグという作業は非常に大事な開発プロセスである。でも今までやっていたデバッグの作業は、クラッシュするまでステップオーバーし、printfとMessageBoxを元にtry&errorで繰り返しビルドするという単純なものだった。これじゃあイカンと思って、キッチリとした技術を学ぼうと思った。

以下本当にダラダラとした学習の記録

link *

Web *

The Code Project *

Toby Opfermanなる人が素晴らしい文章を書いている。コーディングの心構えなどには触れていないが、実際のデバッグの手続き、つまり「どういうソフトをどのように使ったら何がわかってどのように嬉しいか」について丁寧に解説している。基本的にデバッグはprintfに頼りきってる人(私)が見れば、世界がひとつ増える感じ。

書籍 *

雑多メモ *

ステップオーバー
main routineのみのtrace。そこでcallされた関数内部に行かない
ステップイン
main routineでcallされた関数内部に行く。外部ライブラリのコードの海に突き進むことも。
ステップアウト
現在のroutineを抜けて呼出し元に戻る

デバッグ出力 *

OutputDebugStringというAPIがあり、デバッグ出力にダラダラと出力させることが可能(いちいちMessageBoxを出すのはあまり良くない)。MFCにはTRACEマクロという形で提供されているようだ。たろの部屋−たまにはVC++−

この出力は、Visual C++なんかだと当然開発環境の中にそういった出力ウィンドウが用意されているが、この出力だけを取得する単体ソフトもある。

プロセスごとの区別なく手当たり次第に出力をかき集めてくれる。便利でしかもフリー。VC++なんかからデバッグありで実行した場合は、出力はそちらに取られてDebugViewの方には出てこないようである。

Dependency Walker *

便利

DevPartner Profiler *

プログラム中のどのコードがボトルネックになっているのかを調査するものがプロファイラである。とりあえずフリーのものとしてはDevPartner Profiler コミュニティ・エディション が挙げられる。Visual C++ .NET 2003にインストールしてみた。

全然使い方がわからんので調べていたら、わかりやすい備忘録を腕によりをかけないプログラマの駄文: 参照ソートと実体ソートでプロファイリング(DevPartner Profiler編)に見つけた。曰く

  1. ツール(T) - DevPartnerで"Native C/C++ Instrumentation Manager"を開き、所望のプロジェクトにチェック
  2. リビルド
  3. ツール(T) - DevPartnerでProfilerを有効にする(メニューの選択のみ)
  4. デバッグなしで実行
  5. 終了後にプロファイリングデータが表示される

プロファイリングが不要になったら、プロファイラを無効にした上でリビルドすると良い。リビルドせずに無効にするだけだとプロファイラのカケラが残るので、私の場合は実行時に

myprogram.exe の 0x7c809e3a で初回の例外が発生しました : 0xC0000005: 場所 0x765c0000 を読み込み中にアクセス違反が発生しました。 。

なんて出力が検出されてしまった。最初は何が原因かわからなかったが、例外出た時にデバッガで中断するようにし、そこでコールスタックを見てみたら、DevPartnerの一部っぽいTTCore.dllのロード直後に発生しているようだったので、上記の教訓を得ることができた。

Visual C++ *

GetLastError *

@err,hr

ウォッチで上記を評価させると最新のエラーの内容がわかる。@errは最新のGetLastError値で、hrというのはそれを説明に変換してくれるフォーマット指定で、HRESULT型にも適用できる。

STLのvectorの中身 *

vectorの中身をウォッチで確認するにはどうすればいいのだろう?と思っていたら、そのものズバリの回答があった

WinDbg *

Microsoftが無償で提供しているデバッグ用のツール。上のToby OpfermanはVisual Studioのデバッガよりこちらを使えと言い、John Robbinsは相補的に使えと言っている。要は使うべきなのだろう。Debugging Tools for Windowsで入手できる

memo *

Symbol *

いまいち理解が浅い。

作成 *

John Robbinsは、Releaseビルドでもシンボルを組込めと主張している。Visual Studio 6でそうするためには、プロジェクトの設定で

  1. C/C++ ==> 一般 ==> デバッグ情報 で「プログラムデータベースを使用」
  2. リンク ==> 一般 ==> デバッグ情報を生成する をチェック
  3. リンク ==> プロジェクトオプション に /OPT:REF を追加

で良いようだ。シンボルは拡張子がPDBのファイルに格納される。

MAP *

シンボルのテキストファイル版っていう理解でいいの?いずれにせよ作った方がいいみたい。プロジェクトの設定で、

  1. リンク ==> プロジェクトオプション に /MAPINFO:EXPORTS と /MAPINFO:LINES を追加
  2. リンク ==> カテゴリ でデバッグを選択し、 「MAPファイルを生成する」をチェック

すれば良いみたい。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

みたいになる。


Last-modified: Wed, 06 Sep 2006 13:44:00 JST