デバッガ:デバッグ情報:windows

デバッガ:デバッグ情報:windows

windowsでデバッグ情報を扱う場合には、このdbghelpというライブラリを使います。シンボル情報の読み込みや型情報の取得などはすべてこのライブラリが担っています。

このライブラリには多少扱いづらいところがあるため、一番最初にはまりやすい注意事項について書いてあります。

参考


dbghelp

注意事項1

実は、似たようなライブラリとしてimagehlpというのもあるのですが、これは古いバージョンのdbghelpだと考えていいでしょう。昔はimagehlpといえばデバッグ情報やイメージ情報を統合的に扱うライブラリだったのですが、ちょっと巨大すぎるということで一部の分割&切り出しが行われる予定でした。dbghelpがデバッグ情報部分のみを扱うライブラリになるはずだったのだと思います。名前にもそういう雰囲気がありますね。

現在のimagehlpとdbghelpを比較する限り、両者はバージョンが違う同じライブラリです。dllの中身もヘッダファイルの内容もほぼ同じです。ヘッダファイルの場合、APIの内容が同じでもインクルードガード部分が違うので、両者を同じプロジェクトで使うと確実にエラーを引き起こします。これは深刻な問題ですので、windowsではimagehlpは使わずに必ずdbghelpを使うようにしましょう。

注意事項2

dbghelpは今でも頻繁なアップデートが繰り返されています。理由は違うページにも書いてありますが、本質的にデバッグ情報はコンパイラやリンカと共にあるものだからです。このため古いOSにデフォルトで付属されているものを使ってしまうと、満足な結果が得られない場合があります。新しい関数が使えないのはもちろん、場合によっては正しくエクスポートされている古い関数ですら失敗することがあります。例えばwindows2000では、ほとんどのシンボル関連関数が引数によらず失敗します。

そのためdbghelp.dllについては、必ず新しいバージョンのdllを使う必要があります。ダウンロードは

などから行うことができます。ところが理由は不明なんですが、バージョンを調べるための関数ImagehlpApiVersionの値は当てになりません。dbghelp.dllが正しいバージョンかどうかは、比較的新しい関数をエクスポートしているかどうかで判断するとよいでしょう。

具体的には、このライブラリを使う場合は、

  1. アプリにdbghelp.dllの最新版を付属する。
  2. dbghelp.dllから使うすべての関数を動的にインポートし、新しいバージョンの関数に対応しているか調べる。
  3. 対応していない場合は、エラーを返す。

ということを守る必要があります。少し(?)面倒なんですが、これをしない場合、「WinXPでもWin2000でも初期化には成功するが、なぜかWin2000だと動かない」といった非常に分かりにくいバグを生む原因になります。気をつけましょう。

典型的な使い方

  1. SymGetOption/SymSetOptionでロードする情報や扱う情報の種類を指定します。
  2. SymInitializeで全体の初期化を行います。
  3. SymLoadModule(64) などの関数で、シンボル情報が必要なモジュールを読み込みます。これはSymInitializeで特殊なオプションを指定することにより、同様のことが行えます。
  4. シンボル情報が欲しい場合は、SymEnumSymbolsSymFromAddrなどのAPIを用いて取得します。前者の場合はそのスコープ(モジュールなど)のシンボルをまとめて列挙、後者の場合はアドレスからシンボルを取得することができます。
  5. 型情報が欲しい場合は、SymGetTypeInfo関数を用いて型のさまざまな情報を取得できます。
  6. アドレスから対応するステートメントを取得する場合は、SymGetLineFromAddr(64)を用いてソースファイルのフルパスや行番号を取得できます。
  7. 不必要なモジュールはSymUnloadModule(64)でアンロードします。
  8. 最後はSymCleanupで終了します。

ローカル変数情報が欲しい場合には、SymEnumSymbolsを行う前にSymSetContextで関数アドレスを指定します。するとローカル変数の一覧を列挙することができます。

関数名の最後に"(64)"とついているものは、64ビット対応の関数が存在するという意味です。32ビットコンピュータでも、たまに64ビットバージョンを使った方がいい場合もあるので、可能な限り常にこちらを使うようにしましょう。ちなみに、ついていないものは最初から32/64ビットのどちらにも対応しているAPIです。

具体的な扱い方についてはmsdnやpdbdumpのソースを参考にしてください。

型情報について

型情報は、SymGetTypeInfoAPIで取得できます。ただし、型情報を構成する葉構造は少し分かりにくいと思うので軽く紹介しておきます。
詳しくはこちらHow to use DbgHelp (SymGetTypeInfo) to access type information

ざっくりといえば、以下のような感じで型が葉構造を形成しています。「次の型」というのがなんともいえないのですが、型を示す上で必要な情報がリスト状、もしくは木構造となっていて、デバッガはこの情報から型を文字列に変えたりします。例えば、int*型はint型へのリンクをもっています。また、関数型は仮引数を子要素としてもっています。かなり恣意的な構造なので、気をつけましょう。

図表の見方

 |-------------------------------------|
 |        Tag(型の大まかな種類)        | 
 |-------------------------------------|
 |       Index(型のインデックス)       |
 |-------------------------------------|
 | (Next)Type (型リスト上の「次の型」) |
 |             Name (型名)             |
 |-------------------------------------|

基本型

 int i;
 
 |-----------------|              |------------------|
 |      Data       |              |     BaseType     |
 |-----------------|              |------------------|
 |   Index = 15    | (Next)Type   |    Index = 16    |
 |-----------------| -----------> |------------------|
 | (Next)Type = 16 |              | BaseType = btInt |
 |    Name = "i"   |              |     Length = 4   |
 |-----------------|              |------------------|

ポインタ型

 int *p;
 
 |-----------------|              |-----------------|              |------------------|
 |      Data       |              |   PointerType   |              |     BaseType     |
 |-----------------|  (Next)Type  |-----------------|              |------------------|
 |   Index = 15    | -----------> |   Index = 16    | (Next)Type   |    Index = 17    |
 |-----------------|              |-----------------| -----------> |------------------|
 | (Next)Type = 16 |              | (Next)Type = 17 |              | BaseType = btInt |
 |    Name = "p"   |              |    Length = 4   |              |     Length = 4   |
 |-----------------|              |-----------------|              |------------------|

配列型

 int array[3][4];
 
 |-----------------|              |-----------------|              |------------------|              |------------------|
 |      Data       |              |    ArrayType    |              |     BaseType     |              |     BaseType     |
 |-----------------|  (Next)Type  |-----------------|              |------------------|              |------------------|
 |   Index = 15    | -----------> |   Index = 16    | (Next)Type   |    Index = 17    |              |    Index = 18    |
 |-----------------|              |-----------------| -----------> |------------------|  (Next)Type  |------------------|
 | (Next)Type = 16 |              | (Next)Type = 17 |              | (Next)Type = 18  | -----------> | BaseType = btInt |
 |  Name = "array" |              |   Length = 48   |              |    Length = 16   |              |     Length = 4   |
 |-----------------|              |    Count = 3    |              |     Count = 4    |              |------------------|
                                  |-----------------|              |------------------|

関数型

 int CText::DoText(long Num);
 
 |------------------------|              |--------------------|              |----------------------|
 |        Function        |              |    FunctionType    |              | UDT(UserDefinedType) |
 |------------------------|  (Next)Type  |--------------------|              |----------------------|
 |       Index = 15       | -----------> |     Index = 16     | ClassParent  |     Index = 18       |
 |------------------------|              |--------------------| -----------> |----------------------|
 |     (Next)Type = 16    |              |   (Next)Type = 17  |              |    Name = "CTest"    |
 | Name = "CTest::DoTest" |              |  ClassParent = 18  |-------       |----------------------|
 |------------------------|              |      Count = 2     |      |    (これは本当はメソッドリストなどを持ちます)
                                         |--------------------|      |
                                                   ↑                | (Next)Type
                                                   |                |
                                                   | parent/child   |       |------------------|
                                                   |                |-------|     BaseType     |
                                                   ↓                        |------------------|
                                         |-------------------|               |    Index = 17    |
                                         |  FunctionArgType  |               |------------------|
                                         |-------------------|               | BaseType = btInt |
                                         |    Index = 19     |               |     Length = 4   |
                                         |-------------------|               |------------------|
                                         |  (Next)Type = 20  |              (これが戻り値となります)
                                         |-------------------|
                              (仮引数が複数あれば、この要素は複数存在します)
                                                   ↑             
                                                   |             
                                                   | (Next)Type
                                                   ↓             
                                              |-------------------|
                                              |      BaseType     |
                                              |-------------------|
                                              |     Index = 17    |
                                              |-------------------|
                                              | BaseType = btLong |
                                              |     Length = 4    |
                                              |-------------------|

pdbdump

サンプルとして、pdbに含まれているシンボル情報と型情報をそのまま出力するツールを作りました。何かの参考になれば幸いです。

 使い方
 
   pdbdump [イメージファイル名(.exe .dll など)]
 
 とすると、そのイメージがあるディレクトリに
 
  1, [inputname].symbols.xml
  2, [inputname].types.xml
 
 という二つのファイルが作られます。
 1,のファイルがシンボル情報を、2,のファイルが型情報を含んでいます。
 出力がxmlなのは単なる趣味です。
 
 オプションを全く用意していないため日常的に使うツールとしては不十分ですが、
 サンプルとしては十分でしょう。

代表的なAPI

 BOOL SymInitialize(HANDLE hProcess,    
                    PSTR UserSearchPath, 
                    BOOL fInvadeProcess)
 BOOL SymCleanup(HANDLE hProcess)
 DWORD SymLoadModule(HANDLE hProcess,
                     HANDLE hFile, 
                     PSTR ImageName,
                     PSTR ModuleName,
                     DWORD BaseOfDll, 
                     DWORD SizeOfDll)
 BOOL SymUnloadModule(HANDLE hProcess,
                      DWORD BaseOfDll)
 BOOL SymGetTypeInfo(HANDLE hProcess,
                     DWORD64 ModBase,
                     ULONG TypeId,
                     IMAGEHLP_SYMBOL_TYPE_INFO GetType,
                     PVOID pInfo)
 BOOL SymGetLineFromAddr(HANDLE hProcess,
                         DWORD dwAddr,
                         DWORD *pdwDisplacement,
                         IMAGEHLP_LINE *Line)

Last modified : 2012/02/02 11:50:50 UTC