WinDbgを使って.NETアプリをメモリダンプ解析することは滅多に無いのですが(コールスタックでメソッド名を見ればピンとひらめいて原因特定することが多いので、大抵はVisual Studioで事足りてます)、たまに使うたびに基本的なコマンドを忘れてしまい毎回ググってることに気づいたので、自分がよく使うWinDbg+sos/sosexのコマンドを個人メモとして記録しておきます。随時追加予定。
#注意点
デバッグターゲットが対象プラットフォームに「Any CPU
」を指定、かつ「32 ビットを選ぶ
」を有効にしてビルドしたプログラムである場合、実行ホストが64ビットOSであっても32ビットプロセスとして実行可能であれば32ビットプロセスとして実行されます。
32ビットプロセスとして実行された場合、プロセスのダンプファイルは32bit版のタスクマネージャー(%windir%\SysWOW64\taskmgr.exe
)から作成してください。また、WinDbgはWinDbg(x86)を、sosex.dll
は32bit版を使用して下さい。
ダンプファイルは、タスクマネージャーの[詳細]タブからターゲットプロセスを選択して右クリックメニューから「ダンプファイルの作成」を選択して作成します。
sosex.dllの入手
事前に、STEVE'S TECHSPOTからsosex.dll
を入手しておきます。ローカルの任意の場所に格納しておきます(本記事ではC:\local\sosex_32\sosex.dll
に格納)。
sos.dll、sosex.dllのロード
WinDbgを起動後、ダンプファイルをWinDbgにドラッグ&ドロップしたのち、以下の一連のコマンドを実行します。
0:000> .loadby sos clr
0:000> .load C:\local\sosex_32\sosex.dll
0:000> !chain
!chain
の実行結果にsosex.dll
のパスが含まれていればOKです。
0:000> !chain
Extension DLL chain:
C:\local\sosex_32\sosex.dll: image 4.6.0.0, API 1.0.0, built Mon Mar 14 23:10:18 2016
[path: C:\local\sosex_32\sosex.dll]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos: image 4.8.4400.0, API 1.0.0, built Fri Jun 4 01:26:38 2021
[path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll]
画面のクリア
.cls
を実行します。
インスタンスのメンバ変数の値を知る
まず、インスタンスのクラス名を指定して、!DumpHeap -type クラス名
を実行します。
0:000> !DumpHeap -type CSSandbox.SampleClass
Address MT Size
01bb24b4 01975f10 20
Statistics:
MT Count TotalSize Class Name
01975f10 1 20 CSSandbox.SampleClass
Total 1 objects
Address
フィールドに表示されたアドレス01bb24b4
を指定して、!DumpObj /d アドレス
を実行します。
0:000> !DumpObj /d 01bb24b4
Name: CSSandbox.SampleClass
MethodTable: 01975f10
EEClass: 01971644
Size: 20(0x14) bytes
File: C:\Users\yz2cm\source\repos\CSSandbox\CSSandbox\bin\Release\CSSandbox.exe
Fields:
MT Field Offset Type VT Attr Value Name
7299ff7c 400000a 4 ...eading.Tasks.Task 0 instance 01bb82a0 t
01975f80 400000b 8 ...viceSandboxWorker 0 instance 01bb24c8 worker
7295878c 400000c c System.Boolean 1 instance 0 sampleFlag_1
7295878c 400000d d System.Boolean 1 instance 1 sampleFlag_2
72952734 4000008 4 System.Object 0 static 01bb823c SampleLock_1
72952734 4000009 8 System.Object 0 static 01bb8248 SampleLock_2
7295878c 400000e 25 System.Boolean 1 static 0 static_sampleFlag_1
7295878c 400000f 26 System.Boolean 1 static 1 static_sampleFlag_2
dd アドレス
で、マネージドヒープをダンプできます。先頭にメソッドテーブルへのポインタの値0x01975f10
が入るため、0x04
バイトのオフセットを取っています。
0:000> dd 0x01bb24b4+0x04
01bb24b8 01bb82a0 01bb24c8 00000100 00000000
01bb24c8 01975f80 00000000 80000000 729524e4
01bb24d8 00000008 00650053 00760072 00630069
01bb24e8 00310065 00000000 00000000 71d72720
01bb24f8 00000000 00000000 00000000 01bb2514
01bb2508 00000000 00000000 00000000 72952734
01bb2518 00000000 00000000 72952c60 00000008
01bb2528 00650053 00760072 00630069 00310065
デッドロックを検出する
!dlk
コマンドはデッドロックを検出します。
0:000> !dlk
Examining SyncBlocks...
Scanning for ReaderWriterLock(Slim) instances...
Scanning for holders of ReaderWriterLock locks...
Scanning for holders of ReaderWriterLockSlim locks...
Examining CriticalSections...
Scanning for threads waiting on SyncBlocks...
*** WARNING: Unable to verify checksum for DeadLockSample.exe
Scanning for threads waiting on ReaderWriterLock locks...
Scanning for threads waiting on ReaderWriterLocksSlim locks...
Scanning for threads waiting on CriticalSections...
*** WARNING: Unable to verify checksum for System.ServiceProcess.ni.dll
*DEADLOCK DETECTED*
CLR thread 0x6 holds the lock on SyncBlock 01315580 OBJ:02018248[System.Object]
...and is waiting for the lock on SyncBlock 0131554c OBJ:0201823c[System.Object]
CLR thread 0x5 holds the lock on SyncBlock 0131554c OBJ:0201823c[System.Object]
...and is waiting for the lock on SyncBlock 01315580 OBJ:02018248[System.Object]
CLR Thread 0x6 is waiting at DeadLockSample.DeadlockSampleClass.RunWorker()(+0x2f IL,+0x7c Native) [C:\Users\yz2cm\source\repos\DeadLockSample\DeadLockSample\DeadlockSampleClass.cs @ 18,21]
CLR Thread 0x5 is waiting at DeadLockSample.DeadLockSampleClass.RunForever()(+0x4b IL,+0xb7 Native) [C:\Users\yz2cm\source\repos\CSharpSandboxDotNet45\DeadLockSample\DeadLockSampleClass.cs @ 32,21]
1 deadlock detected.
スレッド一覧を表示する
0:000> !threads
ThreadCount: 8
UnstartedThread: 0
BackgroundThread: 4
PendingThread: 0
DeadThread: 3
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 555c 012ec5c8 2a020 Preemptive 020127BC:00000000 012e6748 0 MTA
2 2 44c8 012fa1a8 2b220 Preemptive 00000000:00000000 012e6748 0 MTA (Finalizer)
XXXX 3 0 01324ea0 30820 Preemptive 00000000:00000000 012e6748 0 Ukn
XXXX 4 0 01327090 1039820 Preemptive 00000000:00000000 012e6748 0 Ukn (Threadpool Worker)
3 5 54c0 01329ec0 3029220 Preemptive 02019538:00000000 012e6748 1 MTA (Threadpool Worker)
7 6 4204 01340bf8 3029220 Preemptive 0201C318:00000000 012e6748 1 MTA (Threadpool Worker)
XXXX 7 0 013443d0 1039820 Preemptive 00000000:00000000 012e6748 0 Ukn (Threadpool Worker)
8 8 56a0 01345f08 102a220 Preemptive 00000000:00000000 012e6748 0 MTA (Threadpool Worker)
スレッドの切り替えとスタックの表示
!Threads
を実行して、調査対象のスレッドの番号を調べます。スレッドの番号は、ID
列の値ではなく、ID
列左にある一列目の値です。
0:000> !Threads
ThreadCount: 9
UnstartedThread: 0
BackgroundThread: 7
PendingThread: 0
DeadThread: 1
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 2230 012fc098 2a020 Preemptive 01BB27BC:00000000 012f5fe8 0 MTA
5 2 4174 0130dbd0 2b220 Preemptive 00000000:00000000 012f5fe8 0 MTA (Finalizer)
XXXX 3 0 01337f28 30820 Preemptive 00000000:00000000 012f5fe8 0 Ukn
6 4 31b0 0133a118 1029220 Preemptive 01BC6418:00000000 012f5fe8 0 MTA (Threadpool Worker)
7 5 4a68 0133d218 3029220 Preemptive 01BB9538:00000000 012f5fe8 1 MTA (Threadpool Worker)
11 6 430c 01354728 3029220 Preemptive 01BBC318:00000000 012f5fe8 1 MTA (Threadpool Worker)
12 7 3c0c 013584c0 1029220 Preemptive 01BC0270:00000000 012f5fe8 0 MTA (Threadpool Worker)
13 8 4f4c 0135d348 102a220 Preemptive 00000000:00000000 012f5fe8 0 MTA (Threadpool Worker)
14 9 3d38 01337858 1029220 Preemptive 00000000:00000000 012f5fe8 0 MTA (Threadpool Worker)
~スレッドの番号s
を実行して、当該スレッドに切り替えます。
0:005> ~7s
eax=00000000 ebx=00000001 ecx=00000000 edx=00000000 esi=00000001 edi=00000001
eip=76f02f8c esp=044fef88 ebp=044ff118 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
ntdll!NtWaitForMultipleObjects+0xc:
76f02f8c c21400 ret 14h
!ClrStack
を実行して、コールスタックを表示します。
0:007> !ClrStack
OS Thread Id: 0x4a68 (7)
Child SP IP Call Site
044ff2e0 76f02f8c [GCFrame: 044ff2e0]
044ff3c0 76f02f8c [GCFrame: 044ff3c0]
044ff3dc 76f02f8c [HelperMethodFrame_1OBJ: 044ff3dc] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
044ff458 019c0b0f ServiceSandbox.ServiceSandboxCore.RunForever_SyncContextNull() [C:\Users\yz2cm\source\repos\CSharpSandboxDotNet45\ServiceSandbox\ServiceSandboxCore.cs @ 32]
044ff490 72d9d4bb System.Threading.Tasks.Task.InnerInvoke() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2884]
044ff49c 72d9b731 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498]
044ff4c0 72d9b6fc System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2861]
044ff4c4 72d38604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 980]
044ff530 72d38537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 928]
044ff544 72d9b4b2 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827]
044ff5a8 72d9b357 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2767]
044ff5b8 72d9b29d System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2704]
044ff5bc 72d0eb7d System.Threading.ThreadPoolWorkQueue.Dispatch() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 820]
044ff60c 72d0e9db System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 1161]
044ff82c 73ecf066 [DebuggerU2MCatchHandlerFrame: 044ff82c]
すべてのスレッドでコールスタックを表示する
~*e!ClrStack
を実行します。
スレッドの状態を表示する
~スレッドの番号
を実行します。
0:011> ~7
7 Id: 1840.4a68 Suspend: 0 Teb: 00d9a000 Unfrozen
Start: clr!Thread::intermediateThreadProc (73f94be0)
Priority: 0 Priority class: 32 Affinity: ff
~*
で、すべてのスレッドの状態を表示します。
参考書籍・サイト
- Sasha Goldshteinほか (2013)『C#プログラマのための.NETアプリケーション最適化技法』翔泳社
- Mario Hewardt (2009)『Advanced .NET Debugging』Addison-Wesley Professional
- 「メモリダンプから.NETのメモリ状態を探りたい」