DBGファイルを活用する方法

戻る

WindowsNTでは,\WINNT\SYMBOLS に システムモジュールのDBGファイルをインストール することで,アプリケーションが異常終了したときの手がかりとすることができます. アプリケーションが異常終了したときに,即時対応しなければならない アプリケーションでは,リリース用のモジュールをデバッグバージョンのまま出荷 しているものを見かけますが,デバッグ情報をもったモジュールはそれだけで起動時の 速度を遅くしたり,リバースエンジニアリングの手助けをすることにもなりかね ません.そこで,DBGファイルを生成してモジュールからデバッグ情報を抜き出して, 必要に応じて利用する方法について考えます.

17-1 トラブル発生!

●トラブルの原因を突き止める古典的な方法
ユーザ先でアプリケーションがトラブルを起こした場合,そのときの状態を追跡する ための手段が必要なことがよくあります.古典的な方法としては,アプリケーションの の実行状態を構造体にパックしておき,なにか起こるとその構造体をファイルなどに 出力したり,ユーザに見えないような場所に随時ログを出力したりといった方法が よく使われます.
●トラブルの根元が自分の作った部分なのかをどうかを切り分ける
最近の開発ツールは大変よくできており,トラブルが発生しなければ 大変効率よく開発作業を行うことができます.しかし,なにかトラブルが発生すると とたんに中身がよく分からなく,特にMFC内部になにか起こるとお手上げ状態になる ことさえあります.トラブルの根元を探るには,まず何が原因であるかを調べる必要が ありますが,自分の造った部分でトラブルが発生しているのかどうかを切り分けること が重要になります.

●MFCやランタイムルーチンDLLのデバッグバージョンは再配布できない
トラブルの根元を突き止めやすくすることが目的ではありますが,リリースする モジュールにデバッグ用のシンボル情報をつけたままにしているものをよく見かけ ます.ここで注意しなければならないのは,MFCやランタイムルーチンDLLのデバッグ バージョンは再配布できませんので,デバッグバージョンはリンクしないようにする 必要があります.
(* デバッグバージョンなら問題は突き止めやすくなるが,最適化オプションは一切利用できない.)

●「ワトソン博士」のログも最適化コードでは対応が困難
そこで,活躍するのが「ワトソン博士」です. しかし,ワトソン博士のログも,コードが最適化されていると例外が発生した場所 を突き止めるのは大変です.
(* Windows95では例外発生時にダイアログボックスが表示される.ワトソン博士に該当するものはないようだ)
シンボル情報をモジュール内に持つと,モジュールのサイズが異常に大きくなり, 起動時間が必要以上に必要となりますが,シンボル情報を別ファイルに持つことで, モジュールサイズを小さくすることができます.また,最近のコンパイラは, PDB(Program Database)ファイルにシンボル情報を出力しますので,モジュール内部 にシンボル情報を埋め込むときと比べてモジュールサイズは小さくなります. PDBファイルは,Deveroper Studio では読み込むことができますが,Win32 SDK で 提供されているツールでは利用することができません.

17-2 DBGファイルの正体

●DBGファイルの中身は,単なるデバッグ情報
WindowsNTでは,マスターCD-ROMにシンボル情報があります.このシンボル情報は, システムDLLなどで例外が発生した場合,どこで発生したかを突き止めるためにある ものです.
システムは必要に応じて \WINNT\SYMBOLS にある DBGファイルを利用して,システムの 例外発生個所を突き止めるために利用します.
ところで,DBGファイルは実際には何が記録されているのかと,不思議に思われる方 も多いのではないかと思います.実は,通常モジュール内に埋め込まれているシンボル 情報をDBGファイルにまとめているだけの単なるデバッグ情報です.
DBGファイルを作成することによって,デバッグシンボル情報をモジュール内に持たずに 必要に応じてデバッグ情報を利用できるようにすることができます.

●DBGファイルのシンボル情報は,通常CodeView形式で生成する
DBGファイルは,
COFF(Common Object File Format)
FPO(Frame Pointer Omission)
Codeview 形式
のシンボル情報を持つことができます.
通常,実行可能ファイルは Codeview 形式のシンボル情報を生成するようにして, REBASE.EXE でシンボル情報を抜き出します.コンパイラオプションで /Z7 を指定する と Codeview 形式になります.
(* 最近はPDBファイルを生成することが一般的になっている).
Win32 SDK に付属している WINDBG.EXE はこの形式でなければ読み込むことが できません.また,最適化オプションは OFF にしておくことも忘れないように設定 しておきます.

例17-1:Microsoft 32bit CコンパイラでCodeview形式のシンボル情報を埋め込んでビルドを行う

cl -Z7 -Od -c -W3 -DCRTAPI1=_cdecl -DCRTAPI2=_cdecl -nologo -D_X86_=1 -D \
     ↑シンボル情報の設定(Codeview形式)・最適化を行わない

  _WINNT -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 -DWIN32  -D_WIN32 -D_MT -MT c.c


link -debug:full -debugtype:cv \
       ↑シンボル情報の設定(パブリックシンボル,行番号,プログラム名,
         ローカル変数と型を,Codeview形式で生成する)
     /INCREMENTAL:NO /PDB:NONE /NODEFAULTLIB
       ↑インクリメンタルリンクを行わない
         PDBを生成しない
    /RELEASE /NOLOGO -subsystem:console,4.0 -out:c.exe \
    c.obj libcmt.lib oldnames.lib \
    kernel32.lib  ws2_32.lib mswsock.lib advapi32.lib user32.lib gdi32.lib \
    comdlg32.lib winspool.lib

17-3 DBGファイルの生成と使い方

●WindowsNT4.0 Service Pack 3に,WINDBG.EXEが!
WindowsNT4.0 Service Pack 3 には Win32 SDK とはタイムスタンプが異なる WINDBG.EXE が含まれます.
実際にシステム内にはインストールされませんが,エンドユーザの手の届くところに デバッガを入れているところをみると,アプリケーションが例外を発生したときの対処 を行うという需要はかなり多いのではないかと推測できます.
特に最近,WindowsNTをUNIXの代わりにサーバとして利用することが増えてきており, パソコンのOSからワークステーションのOSとしての役割が増えてきている のでしょう.
Deveroper Studio のみでデバッグを行う場合には,PDBファイルのシンボル情報で 十分ですが,アプリケーションを実行する環境に必ずそのような開発環境が インストールされているとは限りません.WINDBG.EXE は非常に軽く,本体といくつか のDLLでデバッグを行うことができるため,DBGファイルを生成しておくと便利なことも 多いのではないかと思います. <WINDBG.EXEを実行するために必要なファイル>
WINDBG.EXE WINDBG.HLP WINDBGRM.EXE
EECXXALP.DLL EECXXMIP.DLL KDEXTALP.DLL TLPIPE.DLL
EECXXX86.DLL KDEXTMIP.DLL TLSER.DLL EMALP.DLL
KDEXTX86.DLL EMMIP.DLL SHCV.DLL EMX86.DLL
IMAGEHLP.DLL TLLOC.DLL SYMCVT.DLL

●DBGファイルとPDBファイルは使い分けが必要
DBGファイルは Deveroper Studio でも利用することが できますが,PDBファイルは Deveroper Studio でしか利用することができません. 通常のデバッグではそれで十分ですが,クラッシュダンプからデバッグを行ったり, プロセスにアタッチする必要があるときには思うようにデバッグを行うことができ ませんので,必要に応じてPDBファイルとDBGファイルを使い分ける 必要があるかもしれません.
■シンボル情報の管理,コードの最適化などについての注意点!■
(1)クラッシュ対策のためにシンボル情報をユーザの手に渡すということは, アプリケーション内部の構造をユーザに教えてしまうことにもなりかねないことに 注意する必要があります.
(2)デバッグ情報を有効にするには,コードの最適化も ある程度遠慮しなければならないこともあります.このため,アプリケーションの 実行環境や用途によってアプリケーションのビルドオプションを検討しなければ なりません.
(3)MFCやCランタイムルーチンのデバッグバージョンDLLは, 再配布できませんので,デバッグバージョンDLLをリンクしたアプリケーションを 再配布しないようにしなければなりません.

例17-2:EXEファイルのシンボル情報のDBGファイルに摘出する

D:\WORK\CRASH>DUMPBIN /headers crash.exe

Microsoft (R) COFF Binary File Dumper Version 5.00.7022
Copyright (C) Microsoft Corp 1992-1997. All rights reserved.


Dump of file crash.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
     14C machine (i386)
       4 number of sections
33C105A6 time date stamp Tue Jul 08 00:05:10 1997
       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
     30B characteristics
            Relocations stripped
            Executable
            Symbols stripped
            32 bit word machine
            Debug information stripped

OPTIONAL HEADER VALUES
     10B magic #
    5.00 linker version
    AE00 size of code
    6000 size of initialized data
       0 size of uninitialized data
    1420 address of entry point
    1000 base of code
    C000 base of data
         ----- new -----
  400000 image base            ← 現在設定されているイメージベースアドレス
    1000 section alignment
     200 file alignment
       3 subsystem (Windows CUI)
    4.00 operating system version
    0.00 image version
            :
            :
            :


  ベースアドレスを変更せずにDBGファイルを出力する
    (DBGファイルはカレントディレクトリに生成される)

D:\WORK\CRASH>REBASE -b 400000 -x . -a crash.exe

REBASE: Total Size of mapping 0x00000000
REBASE: Range 0x00400000 -0x00400000


D:\WORK\C>DUMPBIN /all crash.exe


                      :
                      :
                      :
                      :
Debug Directories(2)
        Type       Size     Address  Pointer

        misc        110    00000000    10800    Image Name: crash.dbg
        fpo         C90    00000000    10910

FPO Data (201)
                                       Use Has  Frame
 Address  Proc Size   Locals   Prolog  BP  SEH  Type   Params
000010B0         E0        0        0   N   N    fpo        8  
00001190         5B        0        0   N   N    fpo        4  
                      :
                      :
                      :
                      :

17-4 WindowsNTのワトソン博士とWINDBGの関係

●異常発生時の情報収集
一般ユーザが利用するシステムに開発環境がインストールされていることはあまり なく,ユーザ先でアプリケーションが異常終了したり不正な動作を起こしても, 簡単に調べる手段がありません.
WINDBG.EXE を利用することができる環境もあるかもしれませんが, 異常が発生したときに利用者が適切な対処ができるということを 前提条件にすることはできませんので,自動的に情報を収集しその結果を受け取ると といったことのほうが一般的ではないかと思われます.

●ワトソン博士が読み込むDBGファイルと出力するログファイル
デバッガは,シンボル情報など頼りに異常の起きた場所を突き止めますが,ワトソン 博士も同様に,アプリケーションとシステムモジュールのシンボル情報をインストール してあれば,その情報を利用してログ \WINNT\DRWATSON.LOG を作成します.
アプリケーションのDBGファイルは,\WINNT(\WINNT\SYMBOLS) ディレクトリにコピー しておくと,ワトソン博士が自動的に読み込みます.もし,シンボル情報がない状態で アプリケーション内部で例外が発生したときには,自力でアプリケーションをリンク する時に出力されるMAPファイルを利用して,例外が発生したアドレスを探し出す必要 があります.
WindowsNTのマスターCDから \WINNT\SYMBOLS にシステムモジュールのシンボルファイル をインストールしておけば,少なくともAPIの内部で例外が発生したときには, 例外発生時のスタックトレース情報からどのAPIで例外が発生しているかを知ることが できるログを生成してくれるでしょう.

●WINDBG.EXEでDMPファイルを読み込む
ワトソン博士は出力するDMPファイルは,WINDBG.EXE で読み込むことができ, これによりアプリケーションが例外を発生したときと同様の状態を再現することが できます.
WINDBG.EXEでDMPファイルを読み込むには,
>WINDBG -z filename.dmp -y シンボルファイルパス
でデバッガを起動するか,起動後に EXEファイルを読み込むときと同様の手順で, [Program]-[open] で開くダイアログボックスで[New]ボタンで読み込みます.
このとき,DBGファイルは,カレントディレクトリか,\WINNT\SYTEM32 または, \WINNT\SYMBOLS にコピーしておく必要があります.
ワトソン博士がクラッシュダンプを生成するときには,DBGファイルを必要とせず, クラッシュダンプをデバッガに読み込ませるときに,デバッガがDBGファイルを 読み込めるディレクトリにコピーしておけば,ソースレベルでデバッグを行うことが できるようになります.このため,実行環境にDBGファイルがなくても,リリースを 行う実行ファイルのDBGファイルを生成しておいて,アプリケーションの実行環境で 生成された,ワトソン博士の出力したログファイルとクラッシュダンプファイルを 開発環境に持ち込めば,デバッグを行うための情報をかなり得ることができるように なります.

17-5 Visual C++ ver5.0 では

Visual C++ ver5.0 で生成した DBGファイルは,WINDBG.EXE を利用してデバッグを 行うことができなくなってしまいました.また,ワトソン博士が出力したDMPファイル のデバッグを実行させると,WINDBG.EXE が異常終了していまいます.
現状では,Deveroper Studio で ワトソン博士が出力したDMPファイルの 読み込みをサポートするか,WINDBG.EXEがVC5.0が生成したDBGファイルの読み込みを サポートするまで黙って待つしかないようです.
それにしても,WINDBG.EXEは切り捨てられるにはあまりにも惜しいデバッガだとは 思いますが,これも時代の流れでしょうか?
せめてもの救いは,アプリケーションの例外発生に再現性があるときは, Deveroper Studio でアプリケーションを実行させて利用することができますし, ワトソン博士はこの新しいDBGファイルを読み込んでログを生成することができる ことでしょう.

例17-3:WINDBG.EXEにVC5.0のDBGファイルでデバッグを行わせようとした

Module Load: D:\WORK\MSC\GENKOU\DBGFILE\SRC\CRASH.EXE  (symbol loading deferred)
Thread Create:  Process=0, Thread=0
Module Load: C:\WINNT\system32\NTDLL.DLL  (symbol loading deferred)
Module Load: C:\WINNT\SYSTEM32\KERNEL32.DLL  (symbol loading deferred)
Module Load: C:\WINNT\symbols\dll\ntdll.dbg  (symbols loaded)
*** WARNING: symbols checksum is wrong 0x00018342 0x0001716e for D:\WORK\MSC\GENKOU\DBGFILE\SRC\CRASH.dbg
Module Load: D:\WORK\MSC\GENKOU\DBGFILE\SRC\CRASH.dbg  (symbol format not supported)
First chance exception c0000005 (Unknown) occurred
Thread stopped.


例17-4: ジャストインタイムデバッガの設定


\\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug


Debugger = C:\DevStudio\SharedIDE\BIN\msdev.exe -p %ld -e %ld

     Deveroper Studio をデバッガとして利用する
       (Visual C++をインストールすると設定される)

Debugger = C:\MSTOOLS\bin\windbg -p %ld -e %ld

      WINDBGを利用する(Win32 SDK をインストールすると設定される)

Debugger = drwtsn32 -p %ld -e %ld -g

         ワトソン博士を利用する(>drwtsn32 -i で自動設定される)


Auto = 0  ... システムはメッセージボックスを表示してユーザーに
              アプリケーションでエラーが起きたことを知らせます
Auto = 1  ... Debugger値にデバッガが指定されている場合システムは
              メッセージボックスを表示せず自動的にデバッガを起動する

例17-5:生成されたDRWATSN32.LOG(CRASH 3 を実行)

Microsoft(R) Windows NT(R) Version 4.00 DrWtsn32
Copyright (C) 1985-1996 Microsoft Corp. All rights reserved.



アプリケーション例外が発生しました:
        アプリケーション: crash.dbg (pid=179)
        発生時間:  7/8/1997 @ 21:23:19.123
        例外番号:  c0000005 (アクセス違反)

----> システム情報 <----*
        コンピュータ名: MYSTIY
        ユーザー名: shinji
        プロセッサの数: 1
        プロセッサの種類: x86 Family 5 Model 4 Stepping 4
        Windows Version: 4.0
        現在のビルド: 1381
        現在のタイプ: Uniprocessor Free
        登録されている会社名: 
        登録されている所有者: S.Tsuneoka

*----> タスク リスト <----*
   0 Idle.exe
   2 System.exe
        :
        :
 162 drwtsn32.exe
   0 _Total.exe

(00400000 - 00413000) C:\WINNT\crash.dbg
(77f50000 - 77fba000) C:\WINNT\Symbols\dll\ntdll.dbg
(70000000 - 70071000) C:\WINNT\Symbols\dll\kernel32.dbg

スレッド ID 0xb4 のステート ダンプ

eax=0014120a ebx=7ffd0044 ecx=00000000 edx=00000000 esi=0000002b edi=7ffb7c22
eip=77f56e45 esp=0012ff00 ebp=0012ff10 iopl=0         nv up ei pl zr na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246


ファンクション: RtlUnicodeToMultiByteN
        77f56e2d 668b1c4f   mov     bx,[edi+ecx*2]         ds:00000000=????
        77f56e31 8acf       mov     cl,bh
        77f56e33 84c9       test    cl,cl

フォールト ->77f56e45 881a       mov     [edx],bl               ds:00000000=??
        77f56e47 ff4d0c     dec     dword ptr [ebp+0xc]    ss:0130e916=????????
                          :
                          :

*----> スタック バック トレース <----*

FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4 ファンクション名
0012ff10 70013f16 00000000 00000103 0012ff3c 00141208 ntdll!RtlUnicodeToMultiByteN (FPO: Non-FPO [5,2,3])
0012ff40 0040105d 00000000 00000000 00000104 77f955b0 kernel32!GetModuleFileNameA (FPO: Non-FPO [3,4,2])
0012ff68 004010cf 00000003 77f955b0 77f5c1da 7ffdf000 crash!Crash 
0012ff80 00401555 00000002 004201a0 004201c0 77f955b0 crash!main 
0012ffc0 7001b623 77f955b0 77f5c1da 7ffdf000 c0000005 crash!mainCRTStartup 
0012fff0 00000000 00401470 00000000 00000000 77f95aa0 kernel32!BaseProcessStart (FPO: Non-FPO [1,8,3])
00000000 00000000 00000000 00000000 00000000 00000000 crash!<シンボルなし> 

17-6 アプリケーションでDBGファイルを生成するには

BIND.EXE を利用することにより実行ファイル内んぼシンボル情報を摘出することが できますが,Win32 API SplitSymbols を利用すれば,自分のアプリケーションでも 同様の処理を行うことができます.
このAPIで注意しなければならないことは,このAPIに指定する DBGファイル格納 ディレクトリにDBGファイルが直接出力される訳ではないというところです. つまり,A.EXE のシンボル情報を C:\WINNT\SYMBOLS に出力するように指定した場合, C:\WINNT\SYMBOLS\EXE\A.DBG が,生成され lpszSymbolFile にフルパスが設定 されます.
このAPIを利用することにより,アプリケーションが自由にDBGファイルを出力させ, 必要に応じてアプリケーションの利用者の環境にDBGファイルをインストールすることが できます.

表17-1:シンボル情報を摘出するAPI
#include &lgt;imagehlp.h>
BOOL SplitSymbols(lpszImageName, lpszSymbolPath szSymbolFile, dwFlags)
イメージからシンボル情報を抜き出す

引数
LPSTR lpszImageName ... シンボル情報を抜き出すモジュールのファイル名
LPSTR lpszSymbolPath ... シンボルファイル(.DBGファイル)を格納するディレクトリのパス
LPSTR lpszSymbolFile ... 生成されたシンボルファイルのパス
DWORD dwFlags ... フラグ
SPLITSYM_EXTRACT_ALL
 すべての情報を抜き出す.モジュールはマイクロソフトのリンカで -debug:none を指定した場合と同様になる
SPLITSYM_REMOVE_PRIVATE
 CodeViewのシンボル情報のみを抜き出す Win32 API RemovePrivateCvSymbolic と同様の動作になる

戻り値
正常終了 TRUE
異常終了 FALSE

17-7 サンプルアプリケーション

●シンボル情報を抜き出すアプリケーション
モジュールのシンボル情報を任意のディレクトリに抜き出すアプリケーション です.

●利用方法
>split モジュールファイル名 [-sympath DBGファイルパス]
-sympath を指定しなければ,ディレクトリに生成します.
<例>
>split a.exe -sympath c:\appname\symbols
この場合,c:\appname\symbols\exe\a.dbgが生成されます.

●ソースファイル
SPLIT.EXE
 SPLIT.C (ソースファイル)
 MAKEFILE (メイクファイル)

●クラッシュするアプリケーション
ワトソン博士が出力するログをMAPファイルから読み出す練習用と,テスト用 アプリケーションです.ログの読み方に関しては,\WINNT\SYTEM32\DRWTSN32.HLP に 詳しく説明されていますのでそちらを参照してください.
DBGファイルは,REBASE.EXEで抜き出しています,このため,ワトソン博士にDBG ファイルを読み込み込ませるには,\WINNT ディレクトリにシンボル情報をコピーして おく必要があります.

●利用方法
>crash [1 - 3]
1 ... アプリケーション内部で例外を発生させる
2 ... Cランタイムルーチン内部で例外を発生させる(strcat)
3 ... API内部で例外を発生させる(GetModuleFileName)

●ソースファイル
CRASH.EXE  CRASH.C (ソースファイル)
 CRASH.DBG (シンボルファイル)
 CRASH.MAP (マップファイル)
 MAKEFILE (メイクファイル)