[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(2) /3 Ver 1.1

みなさんご機嫌よう。ういこです。

今日はデバッグが大嫌いなのに愛せそうも無いけど手っ取り早くありもので何とかしなきゃいけない主婦の皆様に送る Windbg 講座第二弾です。

巷で Live Search ると、ずいぶんたくさんの Windbg についてのブログがあり、ぶっちゃけ私もかなり参考にしてたりしますがそういう時に限って URL を控えてなかったりしちゃうのはなぁぜ?それはともかく、うちのブログは簡単クッキングがモットーなので、適当に使ってもそれなりにイケる例をご紹介させていただきます。

もし、もっと知りたいことが出てきたら、ヘルプをみるもよし、ブログをサーチするもよし。MSDN にもコマンドについて最近掲載されてますし(でも Windbg で F1 押して出てくるヘルプそのままにしか見えない)そちらをご覧ください。

 

ちなみに、今回はプログラムのソリューション、exe、pdb (私がこの記事を書くにあたり実際に使ったもの) を含め、zip にしてダウンロード可能な状態にしてあります。是非、お手元でお試しくださいませ。

素材のダウンロードは こちら   ( Feb25debug.zip )

(ソリューション一式、debug フォルダに pdb と exe が両方入っています。また、Feb25debug.txt は実際に下記をやった場合のコマンド履歴をログしたものそのものです。ご覧頂くことで処理の流れがわかると思います。)

 

  はじめに

Windbg 使えても、ダンプ解析できても、スタートラインであってそれだけでは問題の完全解決になるわけではないということは覚えておいてください。あくまでも問題の解決の過程で取りうることのできるひとつの手段に過ぎません。

これは、たとえて言えば包丁、なべなどの基本的な使い方を知ったからといって、おいしい料理を誰もが作れるわけではないということと似ています。

絶えずそのつど目新しい食材 (= 問題?) ばかりが運ばれてくるので、レシピが無いのが難点ですが…。ただ、たとえばへんな魚が運ばれてきたとしても、魚の基本的なさばき方を知っているかどうかで料理のバリエーションもずいぶん変わりますよね。まあそういうもんだと思ってください(適当)

あと、日ごろからデバッグシンボルは必ず作成後手元においておき、保存して置きましょう。

デバッグ セレブでないとまともに解析が出来ないなんて羽目にならないよう、リスクヘッジは怠らないようにするべきです。これが無いために大変なことになる例を多く見てきた経験からあえてそう申し添えさせていただきます。

 

というか、いるのかデバッグセレブって。

 

【今日のお題】

ステップ実行だけがデバッグじゃない!

Debugging Tools For Windows でご機嫌斜めなあのコをデバッグ!

その 2

過去の連載はこちら。

 

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(1)
https://blogs.technet.com/jpilmblg/archive/2009/02/21/debugging-windbg-1.aspx

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(2) Ver 1.1 ★ ← 今回
https://blogs.technet.com/jpilmblg/archive/2009/02/25/debugging-windbg-2.aspx

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(3)
https://blogs.technet.com/jpilmblg/archive/2009/03/06/debugging-windbg-3-tips.aspx 

 

今回は、ソースもダウンロード パッケージから取得可能ですが、解析を行わないといけない事態になったとき、ソースもシンボルも無いというのは日常茶飯事です。こういう場合に備えて Windbg をちょっと使いながら、うまくトラブルシュートしましょう。

ちなみに、以下は本当に簡単な使い方の例にすぎません。たとえば、先日ご紹介したチームメイトの Multimedia のこのブログのように濃い解析をすることも出来るし、Windbg って、いろんなことが出来るツールなのです。

デバッグ セレブを超越した神様たちに言わせれば、後述の k コマンドでスタック トレースを出せると思うこと自体ナンセンス、d コマンド (今回は触れていませんが、データをダンプしていくコマンドです。興味ある方はヘルプ見てくださいね) でスタックを追いかけろ、ということもあります。

ですが、まずはとっつきにくい Windbg の第一歩として、結構使えるんじゃね?くらいに思っていただくことから始めればと思っています。

あとは私たちの日ごろの調査がどんな風か、イメージしてもらえれば嬉しいです。

お問い合わせを受けたら、こんな風に調べてるんですよ…。

【今回の素材】

なんか System.DirectoryEntry クラス使ってユーザを作っちゃう処理してたけど問題が発生するプログラム。ただ、メッセージボックスに「fail」としか出なくて、例外が発生しました!見たいに判りやすくなくて、どこで対応したらいいかよくわからない、見たいな。以下処理抜粋。

(.NET Framework のデバッグ用エクステンション SOS.dll を使って対応します)

DirectoryEntry entry = null;

entry = new DirectoryEntry(LDAPStr, UserID, Password, AuthenticationTypes.Secure);

MessageBox.Show(entry.Name);

DirectoryEntry NewUser = entry.Children.Add("CN=" + NewUserID, "user");

NewUser.CommitChanges();

NewUser.Invoke("SetPassword", new object[] {NewPassword});

MessageBox.Show("OK");

【解析の流れ】

1. 解析対象のプログラムにアタッチする

2. 最初に神は仰られた、「ログ在れ」

3. とりあえず動かしてみて、オペレーションしてみる

4. とりあえずメッセージ ボックスが出てきたら、アンマネージのスレッドのコール スタックを見てみる

5. マネージのスレッドも見てみる

6. マネージのスレッドの直近の例外オブジェクトを見てみる

7. マネージ スタックのオブジェクトをダンプして、内容を見てみる

(おまけ : 暇だからお友達を呼んでみた。井戸端会議の傍らやってみた。)

1. 解析対象のプログラムにアタッチする

さて、Windbg を激しく起動します。Debugging Tools for Windows に含まれるので、これをインストールしたパスにもいますし、スタートメニューのプログラムのところにもいますよ。大体、フォルダ名は "Debugging Tools for Windows (x86)" とかそんな感じです。

起動したら、解析対象のプログラムを windbg に教えてやります。

教えてやる方法は主に二つです。

"Open Executable" で実行ファイルを指定(※)

"Attach to a process" でプロセス ID を指定

(※) もしくは、ActiveX など単体実行できない場合は実行の基点となるプログラムなど

どちらでも良いです。

よく考えたら当たり前だけどはまること

Vista の場合、解析対象のプログラムが管理者権限で実行されている場合、Windbg も管理者権限で実行されている必要があります。

そう無い場合、Windbg 上に「アタッチしようとしたら彼女に拒否られた」という切なさ炸裂なことが通知されます。悲しいですのでこの点注意。私は面倒なのでいつも管理者権限でやってますけど。

2. 最初に神は仰られた、「ログ在れ」

Windbg にアタッチすると、必ず一度ブレークします。この時点で、ログを作る設定をしてやりましょう。

まず、私なんていまだにそうなんですが、自分が見ていた変数や、コメントやオブジェクトを忘れちゃったり、メモリ リークしてるときなど、オブジェクトをダンプすると激しく出すぎて、Windbg の表示ウインドウのバッファの限界を超えてしまったときに後でしょんぼりするときなどに備え、log することをお勧めします。

log するコマンドは、.logopen です。/t をつけると、指定したファイル名にタイムスタンプをつけてユニークな log ファイルを作ってくれます。ああ便利。

下は c:\temp 配下に作る例です。

.logopen /t c:\temp\mylog.log

成功するとこんな感じに通知されます。

Opened log file 'c:\temp\mylog_1d28_2009-02-25_18-31-58-131.log'

3 とりあえず動かしてみる

一度ブレークした状態でログを作ったら、一旦処理を進めてみます。

g コマンドを使います。コマンド入れるところ ("0:000> " とか "0:004> " とか、そんなことが書いてあるところです。 " *busy* " と表示していたら、おおむねそのうち戻ってくるので、おやつでも食べながらまったりと待ちましょう。)

g

4 とりあえず動かしてみて、オペレーションしてみる

g コマンドを実行すると、プログラムの処理が実行されます。すかさずオペレーションをしてやりましょう。

今回のおまけのプログラムの場合は、何も考えずに無心な状態でボタンを押しましょう。そうすると悲しいことに、"failed" としか表示されていないメッセージ ボックスが出てきます。こいつをどうにかしてやら無くてはならないのです。早速どうにかしてやりましょう。

まず、この時点で一度とめてみて (タスクバーの一時停止ボタンみたいなやつがそのまま一時停止ボタンですので、押せば OK) 現在ロードされているモジュールのリストを表示します。deferred となっているモジュールはシンボルが読み込まれていない / もしくは無いもの。"System_Windows_Forms_ni C (pdb symbols)" のように "C" が表示されているものは、シンボルは読み込まれているが、チェックサムが一致しない状況を表します。

ちなみにこんな風に、コマンドを打つ際に "*" (半角アスタリスク) をつけると、コメントが表示できます~。後述のサーバにしてるときとかのやり取りの際に便利♪

0:004> * ぴろとくんがやってきたぁー

5 とりあえずメッセージボックスが出てきたら、アンマネージのスレッドのコールスタックを見てみる

アンマネージのスレッドを見るのは、k コマンドを使います。

これに v とか p とか n とかつけると色々出方が変わります。私が良く使うのは、~*kvnL 100 です。100 って何?っていうと、まあそのくらい指定しておけば長いスタックでも出るだろ、というそれだけのことです。" ~* " をつけると、その時点で実行されているスレッド全てのコール スタックを見れます。ものぐさな私にぴったり♪

 

ちなみに、コマンド実行するところが、"0:000>" とか、"0:004>" とか微妙に数字が変わっていますが、これは現在見ているスレッドの番号と一致します。たとえば、0:000 であれば、0 番です。これが、0:004 となっているとします。このとき、スレッド 4 番にはマネージ スタックは含まれない場合などに、後述のマネージ スタックを見るコマンド !clrstack を呼んじゃったりすると、そんなスレッドねえよと怒られたりします。

 

こういうときは ~* シリーズを使って一気に見ちゃうとか、スレッドを切り替えるとかします。

切り替え方はこうです。

~< スレッド番号 >s

スレッド 0 番にしたいときは、~0s と指定します。スレッド 3 番にしたいときは、~3s です。とりあえず今回はものぐさ主婦のステキ★デバッギングなのでまとめてレッツゴー三匹!

~*kvnL 100

これで実行キーを押すと死ぬほど出てきますが、これくらいでひるんだら負けです!

めげずに眺めてみましょう。きっと最初は「はあ、そうですか」と思うと思います。私も良くそう思います。まあ、呼び出し状況を把握できるので良いですね。

まあまじめな話として一例を挙げるとダンプなどの場合に、複数問題が発生して、ダンプ ファイルなどがパカパカ吐き出されるときなど、同じ問題であるかどうか判断したい、という場合などにこれによってコールスタックのパターンを見て、同じ / 違うなどの判断をするのに手っ取り早く役立ったりします。

ログをとっておいて見比べるとまあ便利!

6 マネージのスレッドも見てみる

SOS エクステンションを読み込みます。これを読み込まないと、マネージのデバッグ大変大変です。今回の場合は、Visual Studio 2005 で適当に作ったので .NET Framework 2.0 以降で動作するようになっています。.NET Framework 2.0 の場合は以下のようにコマンド行にタイプしてください。これがないとマネージのスレッドを見る !clrstack などが使えません。

.loadby sos mscorwks

 

今度はマネージのスレッドを見てみましょう。.NET Framework の共通言語ランタイム様の上で動作しているものは、アンマネージ スレッドを出してもどんな風に動作してるかさっぱりわかりません。

!clrstack コマンドを使うと、見ることが出来ます。ただし、このコマンドを実行する場合は SOS をロードしてなくてはなりません。大体、! (エクスクラメーションマーク、びっくらまーく) で始まるコマンドは、エクステンション系なのでなんかロードしなきゃダメだと覚えておけば困らないと思います。

さて一気に見てみたい場合は、こうです。

~*e!clrstack

これで、すべてのスレッドにいるマネージ スレッドを出してくれます。

たとえば今回の例を見てみましょう。アンマネージのスレッドはこうです。

各行の一番左端の、以下の場合は 00 から 2f まであるのは、フレーム番号です。

(以下は問題が発生した後のコール スタックです。)

 # ChildEBP RetAddr Args to Child

00 0025e450 75fb0dde 75f9b0b2 001509be 00000001 ntdll!KiFastSystemCallRet (FPO: [0,0,0])

01 0025e454 75f9b0b2 001509be 00000001 00000000 USER32!NtUserWaitMessage+0xc (FPO: [0,0,0])

02 0025e488 75f9bcda 0013048c 001509be 00000001 USER32!DialogBox2+0x202 (FPO: [4,8,4])

03 0025e4b0 75feccdc 75f90000 00447ba0 001509be USER32!InternalDialogBox+0xd0 (FPO: [6,1,4])

04 0025e550 75fed25e 00000000 0025e794 0025e774 USER32!SoftModalMessageBox+0x69f (FPO: [1,29,4])

05 0025e6a0 75fed394 0025e6ac 00000028 001509be USER32!MessageBoxWorker+0x2c7 (FPO: [1,78,4])

06 0025e6f8 75fed610 001509be 01e16294 01d611a4 USER32!MessageBoxTimeoutW+0x7f (FPO: [6,19,0])

07 0025e718 75fed6ac 001509be 01e16294 01d611a4 USER32!MessageBoxExW+0x1b (FPO: [5,0,0])

08 0025e734 00812b23 001509be 01e16294 01d611a4 USER32!MessageBoxW+0x45 (FPO: [4,0,0])

09 0025e75c 5d3bfcf8 0025e7dc 00122010 01dcfbe8 CLRStub[StubLinkStub]@812b23

0a 0025e810 5d3bf90e 00000000 00000000 00000000 System_Windows_Forms_ni !System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window, System.String, System.String, System.Windows.Forms.MessageBoxButtons, System.Windows.Forms.MessageBoxIcon, System.Windows.Forms.MessageBoxDefaultButton, System.Windows.Forms.MessageBoxOptions, Boolean)+0x220 (Managed)

0b 0025e970 002f1257 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.MessageBox.Show(System.String)+0x26 (Managed)

0c 0025e970 5cd94190 00000000 00000000 00000000 WindbgTest01!WindbgTest01.Form1.button1_Click(System.Object, System.EventArgs)+0x3df (Managed)

0d 0025e98c 5cd8f57a 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(System.EventArgs)+0x70 (Managed)

0e 0025e99c 5d326ee4 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Button.OnClick(System.EventArgs)+0x4a (Managed)

0f 0025e9b8 5d2f76f3 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)+0xac (Managed)

10 0025ea3c 5d62a136 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)+0x28f (Managed)

11 0025ea9c 5d6288f5 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x861a86 (Managed)

12 0025eae0 5cdc25b0 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)+0x866325 (Managed)

13 0025eaec 5cdc86a0 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)+0x20 (Managed)

14 0025eaf4 5cdc8621 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)+0x10 (Managed)

15 0025eb08 5cdc84fa 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x31 (Managed)

16 0025eb48 002a09dc 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x5a (Managed)

WARNING: Frame IP not in any known module. Following frames may be wrong.

17 0025eb6c 75faf8d2 0011053a 00000202 00000000 0x2a09dc

18 0025eb98 75faf794 002a0c7a 0011053a 00000202 USER32!InternalCallWinProc+0x23

19 0025ec10 75fb0008 003f3154 002a0c7a 0011053a USER32!UserCallWinProcCheckWow+0x14b (FPO: [SEH])

1a 0025ec74 75fb0060 002a0c7a 00000000 0025eca0 USER32!DispatchMessageWorker+0x322 (FPO: [SEH])

1b 0025ec84 00811bbe 0025ed10 541be81d 00000000 USER32!DispatchMessageW+0xf (FPO: [1,0,0])

1c 0025eca0 5cdd8d2e 01d92024 00000001 01d6470c CLRStub[StubLinkStub]@811bbe

1d 0025ed54 5cdd8997 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)+0x24e (Managed)

1e 0025edac 5cdd87e1 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)+0x177 (Managed)

1f 0025eddc 5cd95931 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)+0x61 (Managed)

20 0025edf4 002f00ae 00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application.Run(System.Windows.Forms.Form)+0x31 (Managed)

21 0025ee00 6dc51b4c 00000000 00000000 00000000 WindbgTest01!WindbgTest01.Program.Main()+0x3e (Managed)

22 0025ee10 6dc621f9 0025eee0 00000000 0025eeb0 mscorwks!CallDescrWorker+0x33

23 0025ee90 6dc76571 0025eee0 00000000 0025eeb0 mscorwks!CallDescrWorkerWithHandler+0xa3 (FPO: [SEH])

24 0025efcc 6dc765a4 0012c020 0025f098 0025f064 mscorwks!MethodDesc::CallDescr+0x19c (FPO: [5,12,4])

25 0025efe8 6dc765c2 0012c020 0025f098 0025f064 mscorwks!MethodDesc::CallTargetWorker+0x1f (FPO: [4,0,0])

26 0025f000 6dccfac5 0025f064 52c887ea 00000000 mscorwks!MethodDescCallSite::CallWithValueTypes+0x1a (FPO: [1,0,0])

27 0025f164 6dccf9e5 00123010 00000001 0025f1a0 mscorwks!ClassLoader::RunMain+0x223 (FPO: [SEH])

28 0025f3cc 6dccff35 00000000 52c88e12 00000001 mscorwks!Assembly::ExecuteMainMethod+0xa6 (FPO: [1,144,4])

29 0025f89c 6dcd011f 00840000 00000000 52c88e62 mscorwks!SystemDomain::ExecuteMainMethod+0x456 (FPO: [2,301,4])

2a 0025f8ec 6dcd004f 00840000 52c88fba 00000000 mscorwks!ExecuteEXE+0x59 (FPO: [SEH])

2b 0025f934 6e797c24 00000000 6dc50000 0025f950 mscorwks!_CorExeMain+0x15c (FPO: [0,11,4])

2c 0025f944 75da4911 7ffda000 0025f990 76dee4b6 mscoree!_CorExeMain+0x2c (FPO: [0,1,4])

2d 0025f950 76dee4b6 7ffda000 70342624 00000000 KERNEL32!BaseThreadInitThunk+0xe (FPO: [1,0,0])

2e 0025f990 76dee489 6e797bf0 7ffda000 00000000 ntdll!__RtlUserThreadStart+0x23 (FPO: [SEH])

2f 0025f9a8 00000000 6e797bf0 7ffda000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])

以下はマネージ スタックです。

OS Thread Id: 0x1f64 (0)

ESP EIP

0025e774 76e09a94 [NDirectMethodFrameStandalone: 0025e774] System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)

0025e790 5d3bfcf8 System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window, System.String, System.String, System.Windows.Forms.MessageBoxButtons, System.Windows.Forms.MessageBoxIcon, System.Windows.Forms.MessageBoxDefaultButton, System.Windows.Forms.MessageBoxOptions, Boolean)

0025e830 5d3bf90e System.Windows.Forms.MessageBox.Show(System.String)

0025e838 002f1257 WindbgTest01.Form1.button1_Click(System.Object, System.EventArgs)

0025e97c 5cd94190 System.Windows.Forms.Control.OnClick(System.EventArgs)

0025e994 5cd8f57a System.Windows.Forms.Button.OnClick(System.EventArgs)

0025e9a4 5d326ee4 System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)

0025e9c0 5d2f76f3 System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)

0025ea4c 5d62a136 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)

0025ea50 5d6288f5 [InlinedCallFrame: 0025ea50]

0025eae8 5cdc25b0 System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)

0025eaf4 5cdc86a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)

0025eafc 5cdc8621 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)

0025eb10 5cdc84fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)

0025ecb8 002a09dc [NDirectMethodFrameStandalone: 0025ecb8] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)

0025ecc8 5cdd8d2e System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)

0025ed64 5cdd8997 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)

0025edb8 5cdd87e1 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)

0025ede8 5cd95931 System.Windows.Forms.Application.Run(System.Windows.Forms.Form)

0025edfc 002f00ae WindbgTest01.Program.Main()

0025f020 6dc51b4c [GCFrame: 0025f020]

ビミョーに違うのが判ります。たとえば、マネージ スタックの、ShowCore の次は、System.Windows.Forms.SafeNativeMethods.MessageBoxですね。

ところが、アンマネージ スタックを見ると、System.Windows.Forms.SafeNativeMethods.MessageBox じゃなくて、CLRStub を経由して USER32!MessageBoxW+0x45 を呼んでいることが判ります。

7 マネージのスレッドの直近の例外オブジェクトを見てみる

.NET Framework 2.0 の場合、 !PrintException コマンドを使うと直近の例外を見ることが出来ます。手っ取り早くて楽です。

ただし、この場合コマンド実行時にマネージ スレッドにコンテキストをあわさないといけません。

たとえば、アンマネージしかないスレッドにコンテキストがいる場合に実行すると以下みたいに怒られちゃいます ( 赤字部分 )

0:004> !PrintException

The current thread is unmanaged

こういうときは面倒なので、全スレッドを対象にして、一気にオブジェクトを見ちゃいましょう。上の場合の、 ~*e! を応用してみると便利便利。

~*e!PrintException

見たいにすると出てきます。スレッド ID も判るので、便利便利。

今回の場合は、スレッドのコンテキストの変更もかねてみます。

~0s

これで、コマンド行が "0:000>" 見たいになれば、スレッド0 番にコンテキストが変わってます。これで満を持してやっとこさ !PrintException を実行できます。よかったよかった。

0:000> !PrintException

Exception object: 01dfe204

Exception type: System.Runtime.InteropServices.COMException

Message: サーバーは使用可能ではありません。

InnerException: <none>

StackTrace (generated):

    SP IP Function

    0025E848 5BEC5887 System_DirectoryServices_ni!System.DirectoryServices.DirectoryEntry.Bind(Boolean)+0x5c34f

    0025E88C 5BE69529 System_DirectoryServices_ni!System.DirectoryServices.DirectoryEntry.Bind()+0x25

    0025E89C 5BE68F91 System_DirectoryServices_ni!System.DirectoryServices.DirectoryEntry.get_Name()+0x21

    0025E8B0 002F112A WindbgTest01!WindbgTest01.Form1.button1_Click(System.Object, System.EventArgs)+0x2b2

StackTraceString: <none>

HResult: 8007203a

おージーザス、サーバが使用可能ではないんですね。ついでに Hresult 8007203a だということも判りました。

残念です。ではなんで使用可能じゃないんだろーと、Ping をうってみたり、LDP ツールで同じ引数を与えてどうなるかといったテストをするもよいでしょう。

ただ、せっかくデバッガつなげてるんだから、見るもの見てからにしますか、と。

ついでに、実際の問題が System.DirectoryServices.DirectoryEntry.Bind() で起きていることが判りました。

次にもっかい実行する際に、ブレークポイントを貼ってやろうとメモっておきましょう。

7. マネージ スタックのオブジェクトをダンプして、内容を見てみる

マネージ スタックに引数などのオブジェクト情報が残っている場合があります。

見るだけ見てみましょう。

0:000> !dso

OS Thread Id: 0x1f64 (0)

ESP/REG Object Name

0025e754 01d61198 System.String

0025e758 01e16288 System.String failed 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e76c 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e7ac 01d65a9c System.Globalization.CultureInfo

0025e7b0 01e15e18 System.Object[] (System.Object[])

0025e7c0 01e16288 System.String failed 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e804 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e808 01e16288 System.String failed 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e80c 01d82b4c System.Windows.Forms.Button

0025e82c 01d61198 System.String

0025e830 01d8509c System.EventHandler

0025e87c 01dd26b8 System.DirectoryServices.DirectoryEntry

0025e8a0 01d8509c System.EventHandler

0025e8a4 01d82b4c System.Windows.Forms.Button

0025e8b0 01e16288 System.String failed 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e8b4 01e15e60 System.String 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e8b8 01e15e60 System.String 場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

   場所 System.DirectoryServices.DirectoryEntry.Bind()

   場所 System.DirectoryServices.DirectoryEntry.get_Name()

   場所 WindbgTest01.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindbgTest01\WindbgTest01\Form1.cs:行 72

0025e8bc 01dd1e90 System.String failed

0025e8c0 01dfe204 System.Runtime.InteropServices.COMException

0025e8e0 01dd26b8 System.DirectoryServices.DirectoryEntry

0025e8e4 01dd2698 System.String #123abc

0025e8e8 01dd2624 System.String #123abc

0025e8ec 01dd25a8 System.String User0010101

0025e8f0 01dd251c System.String User0010101

0025e8f4 01dd2418 System.String LDAP://testserver.contoso.com/CN=Users,DC=testserver,DC=contoso,DC=com

0025e8f8 01dd2228 System.String LDAP://testserver.contoso.com/CN=Users,DC=testserver,DC=contoso,DC=com

0025e8fc 01dd20b4 System.String 1qaz!QAZ

0025e900 01dd2038 System.String 1qaz!QAZ

0025e904 01dd1fb4 System.String Administrator

0025e908 01dd1f1c System.String Administrator

0025e910 01dfe204 System.Runtime.InteropServices.COMException

0025e918 01dd26b8 System.DirectoryServices.DirectoryEntry

0025e91c 01dd2418 System.String LDAP://testserver.contoso.com/CN=Users,DC=testserver,DC=contoso,DC=com

0025e920 01dd2698 System.String #123abc

0025e924 01dd25a8 System.String User0010101

0025e928 01dd20b4 System.String 1qaz!QAZ

0025e92c 01dd1fb4 System.String Administrator

0025e930 01d63cb8 WindbgTest01.Form1

0025e94c 01d82b4c System.Windows.Forms.Button

0025e960 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e964 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e968 01d8509c System.EventHandler

0025e96c 01d82b4c System.Windows.Forms.Button

0025e978 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e97c 01d844a4 System.ComponentModel.EventHandlerList

0025e984 01d82b4c System.Windows.Forms.Button

0025e988 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e994 01d82b4c System.Windows.Forms.Button

0025e998 01dcfbe8 System.Windows.Forms.MouseEventArgs

0025e9c0 01d82b4c System.Windows.Forms.Button

0025ea7c 01d82b4c System.Windows.Forms.Button

0025ea98 01d82c1c System.Windows.Forms.Control+ControlNativeWindow

0025eaa4 01d82b4c System.Windows.Forms.Button

0025ead8 01d82b4c System.Windows.Forms.Button

0025eadc 01d82c1c System.Windows.Forms.Control+ControlNativeWindow

0025eb00 01d82c1c System.Windows.Forms.Control+ControlNativeWindow

0025eb10 01d82c1c System.Windows.Forms.Control+ControlNativeWindow

0025ec58 01d6470c System.Windows.Forms.Application+ThreadContext

0025ecb0 01d6470c System.Windows.Forms.Application+ThreadContext

0025ecf4 01d92000 System.Windows.Forms.NativeMethods+MSG[]

0025ecf8 01d6470c System.Windows.Forms.Application+ThreadContext

0025ed00 01d8b848 System.Windows.Forms.Application+ComponentManager

0025ed48 01d86624 System.Windows.Forms.ApplicationContext

0025ed50 01d86624 System.Windows.Forms.ApplicationContext

0025ed78 01d6470c System.Windows.Forms.Application+ThreadContext

0025eda0 01d86624 System.Windows.Forms.ApplicationContext

0025eda4 01d6470c System.Windows.Forms.Application+ThreadContext

0025edb4 01d86624 System.Windows.Forms.ApplicationContext

0025edd0 01d63cb8 WindbgTest01.Form1

0025edd4 01d86624 System.Windows.Forms.ApplicationContext

0025edd8 01d6470c System.Windows.Forms.Application+ThreadContext

0025ede4 01d86624 System.Windows.Forms.ApplicationContext

0025edfc 01d63cb8 WindbgTest01.Form1

0025e8f4 01dd2418 System.String LDAP://testserver.contoso.com/CN=Users,DC=testserver,DC=contoso,DC=com あたりを見ると文字列が入ってます。明らかにそんなサーバ無いじゃん。ということでこれが原因だとあたりがついた、と。

おまけ : 暇だからお友達を呼んでみた。井戸端会議の傍らやってみた。

デバッグしてて、飽きちゃう場合とか、誰か僕をひっそりヘルプミーって場合には、.server コマンドを使うと良いです。これを使うと、デバッグセッションにお友達が参加できるようになります!バンザイ。これでおしゃべりしながら出来るよ!!

.server を使う場合は、通信ポートを指定します。明らかに誰も使わないようなポートを指定してやりましょう。

.server tcp:port=12345

これで実行すると、こんな風に文字列が出てきますので控えておきます。以下はポート 12345 番をつかって、サーバ名 uikouvista である場合の実行例です。(勝手にサーバ名をデバッガ様が入れてくれるので、tcp:port= でとりあえずうまくいくことが多いです)

0:004> .server tcp:port=12345

Server started. Client can connect with any of these command lines

0: <debugger> -remote tcp:Port=12345,Server=UIKOUVISTA

上記の黄色部分をお友達に教えてあげます。

お友達は、Windbg の [File] - [Connect to Remote Session] をクリックして出てくるダイアログの "Connection String:" のところに、黄色い部分を入れてもらって OK を押させればよいのです。簡単!OK を押したがらなかったら、押してやりましょう。

上記の場合はこの黄色部分を指定すればよいですね。ちなみに、Server が別フォレストにいる場合などは、Server= のあとに IP アドレスを指定してもつながります。

tcp:Port=12345,Server=UIKOUVISTA

つながると、サーバ側に誰が来たか判ります。以下の例では、hirotoh01\hirotoh がアクセスしてきています。

 

HIROTOH01\hirotoh (tcp 157.60.18.1:60238) connected at Wed Feb 25 18:43:23 2009

0:004> * ぴろとくんがやってきたぁー

[HIROTOH01\hirotoh (tcp 157.60.18.1:60238)] 0:004> * 土足でお邪魔します by hiroto

*****

さて長々となったので次に続きます。次はブレークポイントの貼り方と、その他役立ちコマンドの紹介です。

~ ういこう@風邪が悪化して声が出ない ~

 

(2009/02/26 画像とか追加したりした ver 1.1 by ういこう)

Feb25Debug.zip