一、介绍
这是我的《Advanced .Net Debugging》这个系列的第五篇文章。今天这篇文章的标题虽然叫做“基本调试任务”,但是这章的内容还是挺多的。上一篇我们了解了一些调.NET 框架中必要的概念,比如:内存转储、值类型转储、引用类型转储、数组转储和异常转储等,我们既能做到知其然,又能做到眼见为实,知其所以然,对我们分析.NET 程序有很大的帮助。今天这篇文章主要涉及的内容是线程的操作、代码的审查和诊断命令等。SOSEX扩展的内容我就省略了,因为我这个系列的是基于 .NET 8 版本来写的,SOSEX是基于 .NET Framework 版本的,如果大家想了解其内容,可以查看我的【高级调试】系列(我当前写的是《Advanced .Net Debugging》系列,是不一样的),当然,也可以看原书。【高级调试】系列主要是集中在 .NET Framework 版本的。如果我们想成为一名合格程序员,这些调试技巧都是必须要掌握的。
如果在没有说明的情况下,所有代码的测试环境都是 Net 8.0,如果有变动,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
操作系统:Windows Professional 10
调试工具:Windbg Preview(Debugger Client:1.2306.1401.0,Debugger engine:10.0.25877.1004)和 NTSD(10.0.22621.2428 AMD64)
下载地址:可以去Microsoft Store 去下载
开发工具:Microsoft Visual Studio Community 2022 (64 位) – Current版本 17.8.3
Net 版本:.Net 8.0
CoreCLR源码:源码下载
二、调试源码
废话不多说,本节是调试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。
2.1、ExampleCore_3_1_9
1 namespace ExampleCore_3_1_9 2 { 3 internal class Program 4 { 5 static void Main(string[] args) 6 { 7 int a = 10; 8 int b = 11; 9 Console.WriteLine("X={0},Y={1}", a, b); 10 Test(12); 11 Console.ReadKey(); 12 } 13 14 private static void Test(int c) 15 { 16 Task.Run(Run1); 17 Task.Run(Run2); 18 Task.Run(Run3); 19 } 20 21 private static void Run1() 22 { 23 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run1 正在运行"); 24 Console.ReadLine(); 25 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run1 结束运行"); 26 } 27 28 private static void Run2() 29 { 30 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run2 正在运行"); 31 Console.ReadLine(); 32 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run2 结束运行"); 33 } 34 35 private static void Run3() 36 { 37 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run3 正在运行"); 38 Console.ReadLine(); 39 Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run3 结束运行"); 40 } 41 } 42 }
View Code
2.2、ExampleCore_3_1_10
1 namespace ExampleCore_3_1_10 2 { 3 internal class Program 4 { 5 static void Main(string[] args) 6 { 7 var sum = Sum(10, 11); 8 Console.WriteLine($"sum={sum}"); 9 Console.ReadLine(); 10 } 11 12 private static int Sum(int a, int b) 13 { 14 int i = a; 15 int j = b; 16 var sum = i + j; 17 return sum; 18 } 19 } 20 }
View Code
2.3、ExampleCore_3_1_11
1 namespace ExampleCore_3_1_11 2 { 3 internal class Program 4 { 5 private static Listbyte[]> list = new Listbyte[]>(); 6 7 static void Main(string[] args) 8 { 9 for (int i = 0; i 100; i++) 10 { 11 list.Add(new byte[100000]); 12 } 13 Console.WriteLine("数据添加完毕!"); 14 Console.ReadLine(); 15 } 16 } 17 }
View Code
2.4、ExampleCore_3_1_12
1 namespace ExampleCore_3_1_12 2 { 3 internal class Program 4 { 5 static void Main(string[] args) 6 { 7 Console.WriteLine("请输入一个除数:"); 8 var num = Console.ReadLine(); 9 var result = 10 / Convert.ToInt32(num); 10 11 Console.ReadLine(); 12 } 13 } 14 }
View Code
三、基础知识
3.1、线程的操作
在非托管代码调试中,所有与线程相关的调试器命令都是以非托管 Windows 线程为基础的,换个说法就是,用于调试非托管代码的调试器命令使用的就是非托管 Windows 线程,调试器命令知道如何转储我们要转储的对象。但是,托管代码就不一样了,它的线程有自己的结构,调试器本身是无法对调用栈进行遍历的。
我们知道,CLR 能对托管代码进行动态转换,并且 JIT 编译器可以将生成的机器代码放在它认为合适的地方。非托管调试器是不知道 JIT 编译器的任何知识,它也不知道生成的代码放在何处,因此就不能正确的显示栈回溯。
3.1.1、ClrStack
A、基础知识
SOS 调试器扩展提供了一个专门查看托管函数调用栈的命令,毕竟只有 JIT 编译器更熟悉托管函数,也知道编译后的机器码放在什么位置。
这个命令就是【!clrstack】。
!clrstack -a(all):这个命令表示将线程栈中的所有局部变量和参数全部输出。
!clrstack -p(parameter):这个命令表示将线程栈中的参数全部输出。
!clrstack -l(locals):这个命令表示将线程栈中的所有局部变量全部输出。
我们还可以查看所有托管线程栈,可以使用【~*e】命令。
请注意:如果 ClrStack 命令在非托管线程的上下文中运行,将会显示一个错误:
1 0:006> !clrstack 2 OS Thread Id: 0x335c (6) 3 Unable to walk the managed stack. The current thread is likely not a 4 managed thread. You can run !clrthreads to get a list of managed threads in 5 the process 6 Failed to start stack walk: 80070057
B、眼见为实
调试源码:ExampleCore_3_1_9
调试任务:ClrStack 命令使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9binDebugnet8.0ExampleCore_3_1_9.exe】。
打开【NTSD】调试器窗口。
使用【g】命令运行调试器,知道调试器有如图输出,调试器会卡住暂停。
按组合键【ctrl+c】进入中断模式,在所有操作之前,我们先切换到托管线程,执行命令【~0s】。
1 0:003> ~0s 2 coreclr!CorSigUncompressElementType_EndPtr+0x18 [inlined in coreclr!MetaSig::CompareElementType+0x1d0]: 3 00007ffb`98b68e40 48ffc0 inc rax
我们先使用【!clrstack】命令,后面不跟任何命令开关。
1 0:000> !clrstack 2 OS Thread Id: 0x3de4 (0) 3 Child SP IP Call Site 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8] 5 000000F08ABCE5D0 00007FFC5382F269 System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) 6 000000F08ABCE660 00007FFB92A28A51 System.IO.StreamReader.ReadBuffer() 7 000000F08ABCE6B0 00007FFB92A290A4 System.IO.StreamReader.ReadLine() 8 000000F08ABCE760 00007FFC5383005D System.IO.SyncTextReader.ReadLine() 9 000000F08ABCE7B0 00007FFC53829319 System.Console.ReadLine() 10 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[])
它罗列出了调用栈。如果想查看每个栈帧的参数,我们可以使用【!clrstack -p】命令,很简单,就不多说了。
1 0:000> !clrstack -p 2 OS Thread Id: 0x3de4 (0) 3 Child SP IP Call Site 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8] 5 。。。。。。(省略了) 6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[]) 7 PARAMETERS:(这里就是参数所在地) 8 args (0x000000F08ABCE830) = 0x000002664e008ea0
如果想查看每个栈帧的局部变量,可以使用【!clrstack -l】命令。
1 0:000> !clrstack -l 2 OS Thread Id: 0x3de4 (0) 3 Child SP IP Call Site 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8] 5 。。。。。。(省略了) 6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[]) 7 LOCALS:(以下就是局部变量) 8 0x000000F08ABCE81C = 0x000000000000000a 9 0x000000F08ABCE818 = 0x000000000000000b
如果想查看每一栈帧的局部变量和参数,可以使用【!clrstack -a】命令。
1 0:000> !clrstack -a 2 OS Thread Id: 0x3de4 (0) 3 Child SP IP Call Site 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8] 5 。。。。。。(省略了) 6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[]) 7 PARAMETERS:(这里是参数区域) 8 args (0x000000F08ABCE830) = 0x000002664e008ea0 9 LOCALS:(这里是局部变量区域) 10 0x000000F08ABCE81C = 0x000000000000000a 11 0x000000F08ABCE818 = 0x000000000000000b
很简单,就不多数了。
还有一个命令,我们要熟悉一下,那就是~*e 命令。
~ 命令输出所有线程列表。
1 0:000> ~ 2 . 0 Id: 3f64.3de4 Suspend: 1 Teb: 000000f0`8acb8000 Unfrozen 3 1 Id: 3f64.ab4 Suspend: 1 Teb: 000000f0`8acd6000 Unfrozen ".NET Tiered Compilation Worker" 4 2 Id: 3f64.b08 Suspend: 1 Teb: 000000f0`8acd8000 Unfrozen 5 # 3 Id: 3f64.6d0 Suspend: 1 Teb: 000000f0`8acda000 Unfrozen 6 4 Id: 3f64.220c Suspend: 1 Teb: 000000f0`8acc0000 Unfrozen ".NET EventPipe" 7 5 Id: 3f64.1830 Suspend: 1 Teb: 000000f0`8acc2000 Unfrozen ".NET Debugger" 8 6 Id: 3f64.3c34 Suspend: 1 Teb: 000000f0`8acc4000 Unfrozen ".NET Finalizer" 9 8 Id: 3f64.10d0 Suspend: 1 Teb: 000000f0`8acca000 Unfrozen ".NET TP Worker" 10 9 Id: 3f64.3e9c Suspend: 1 Teb: 000000f0`8accc000 Unfrozen ".NET TP Gate" 11 10 Id: 3f64.3d90 Suspend: 1 Teb: 000000f0`8acce000 Unfrozen ".NET TP Worker" 12 11 Id: 3f64.1e48 Suspend: 1 Teb: 000000f0`8acd0000 Unfrozen ".NET TP Worker"
~* 命令,显示所有线程列表和入口函数。
1 0:000> ~* 2 . 0 Id: 3f64.3de4 Suspend: 1 Teb: 000000f0`8acb8000 Unfrozen 3 Start: apphost!wmainCRTStartup (00007ff7`8a2b1360) 4 Priority: 0 Priority class: 32 Affinity: f 5 1 Id: 3f64.ab4 Suspend: 1 Teb: 000000f0`8acd6000 Unfrozen ".NET Tiered Compilation Worker" 6 Start: coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper0 (00007ffb`98c833f0) 7 Priority: 0 Priority class: 32 Affinity: f 8 2 Id: 3f64.b08 Suspend: 1 Teb: 000000f0`8acd8000 Unfrozen 9 Start: KERNELBASE!CtrlRoutine (00007ffc`651d9c80) 10 Priority: 2 Priority class: 32 Affinity: f 11 # 3 Id: 3f64.6d0 Suspend: 1 Teb: 000000f0`8acda000 Unfrozen 12 Start: ntdll!DbgUiRemoteBreakin (00007ffc`6791ba10) 13 Priority: 0 Priority class: 32 Affinity: f 14 4 Id: 3f64.220c Suspend: 1 Teb: 000000f0`8acc0000 Unfrozen ".NET EventPipe" 15 Start: coreclr!server_thread (00007ffb`98c6b530) 16 Priority: 0 Priority class: 32 Affinity: f 17 5 Id: 3f64.1830 Suspend: 1 Teb: 000000f0`8acc2000 Unfrozen ".NET Debugger" 18 Start: coreclr!DebuggerRCThread::ThreadProcStatic (00007ffb`98c667f0) 19 Priority: 0 Priority class: 32 Affinity: f 20 6 Id: 3f64.3c34 Suspend: 1 Teb: 000000f0`8acc4000 Unfrozen ".NET Finalizer" 21 Start: coreclr!FinalizerThread::FinalizerThreadStart (00007ffb`98c4e370) 22 Priority: 2 Priority class: 32 Affinity: f 23 8 Id: 3f64.10d0 Suspend: 1 Teb: 000000f0`8acca000 Unfrozen ".NET TP Worker" 24 Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340) 25 Priority: 0 Priority class: 32 Affinity: f 26 9 Id: 3f64.3e9c Suspend: 1 Teb: 000000f0`8accc000 Unfrozen ".NET TP Gate" 27 Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340) 28 Priority: 0 Priority class: 32 Affinity: f 29 10 Id: 3f64.3d90 Suspend: 1 Teb: 000000f0`8acce000 Unfrozen ".NET TP Worker" 30 Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340) 31 Priority: 0 Priority class: 32 Affinity: f 32 11 Id: 3f64.1e48 Suspend: 1 Teb: 000000f0`8acd0000 Unfrozen ".NET TP Worker" 33 Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340) 34 Priority: 0 Priority class: 32 Affinity: f
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_9.exe,进入调试器。继续使用【g】命令,运行调试器。我们的控制台程序输出如下内容,如图:
点击【break】按钮,中断调试器的执行,由于,我们手动中断程序执行,需要将线程切换到托管线程上,执行命令【~0s】。
1 0:001> ~0s 2 ntdll!NtReadFile+0x14: 3 00007ffc`678eae54 c3 ret
我们继续使用【!clrstack】命令,输出托管线程的调用栈。
1 0:000> !clrstack 2 OS Thread Id: 0x3b40 (0)【底层操作系统的ID】 3 Child SP IP Call Site 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 6 000000D3A5F7E530 00007ffc0bf676eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/.../LibraryImports.g.cs @ 412] 7 000000D3A5F7E620 00007ffc0bf6c9c0 System.ConsolePal+WindowsConsoleStream..../System/ConsolePal.Windows.cs @ 1150] 8 000000D3A5F7E680 00007ffc0bf6c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/....Windows.cs @ 1108] 9 000000D3A5F7E6C0 00007ffc0bf6fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/.../ConsoleStream.cs @ 34] 10 000000D3A5F7E730 00007ffb8d1589c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/.../IO/StreamReader.cs @ 613] 11 000000D3A5F7E780 00007ffb8d1590a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/.../StreamReader.cs @ 802] 12 000000D3A5F7E830 00007ffc0bf7005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 13 000000D3A5F7E880 00007ffc0bf69319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 14 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:Visual Studio...ExampleCore_3_1_9Program.cs @ 11]
我们可以使用【!clrstack -l】,查看托管线程调用栈,并显示每个栈帧的局部变量。
1 0:000> !clrstack -l 2 OS Thread Id: 0x3b40 (0) 3 Child SP IP Call Site 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 。。。。。。(省略了)49 50 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:Visual Studio...ExampleCore_3_1_9Program.cs @ 11] 51 LOCALS: 52 0x000000D3A5F7E8EC = 0x000000000000000a 53 0x000000D3A5F7E8E8 = 0x000000000000000b 这就是我们的局部变量
如果我们想查看托管调用栈的参数,可以使用【!clrstack -p】命令,p(parameter)参数。
1 0:000> !clrstack -p 2 OS Thread Id: 0x3b40 (0) 3 Child SP IP Call Site 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 。。。。。。(省略了)47 48 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:Visual Studio...ExampleCore_3_1_9Program.cs @ 11] 49 PARAMETERS: 50 args (0x000000D3A5F7E900) = 0x000001efb4408ea0(这个就是 Main 方法的 args 参数)
0x000001efb4408ea0 这个地址就是 Program.Main 方法的 args 参数,args 是字符串数组,是引用类型,我们可以直接使用【!dumpobj】来确认。
1 0:000> !dumpobj 0x000001efb4408ea0 2 Name: System.String[] 3 MethodTable: 00007ffb2e3cfab0 4 EEClass: 00007ffb2e27c440 5 Tracked Type: false 6 Size: 24(0x18) bytes 7 Array: Rank 1, Number of elements 0, Type CLASS (Print Array) 8 Fields: 9 None
如果想同时查看局部变量和参数,可以使用【!clrstack -a】命令。
1 0:000> !clrstack -a 2 OS Thread Id: 0x3b40 (0) 3 Child SP IP Call Site 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 6 。。。。。。(省略了) 7 8 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:Visual Studio...ExampleCore_3_1_9Program.cs @ 11] 9 PARAMETERS: 10 args (0x000000D3A5F7E900) = 0x000001efb4408ea0 (这是参数) 11 LOCALS: 12 0x000000D3A5F7E8EC = 0x000000000000000a (这是局部变量) 13 0x000000D3A5F7E8E8 = 0x000000000000000b (这是局部变量)
3.1.2、Threads
A、基础知识
如何你想枚举出进程中所有的托管代码线程,可以使用【!threads】命令,或者简写形式【!t】。当然,这个命令也有一些开关设置,-live 只罗列出那些处于活跃状态的线程的信息,-special 只输出进程中所有“特殊的”线程,比如:垃圾收集线程、调试器线程、线程池定时器线程等。
B、眼见为实
调试源码:ExampleCore_3_1_9
调试任务:Threads 命令的使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9binDebugnet8.0ExampleCore_3_1_9.exe】,打开调试器窗口。
使用【g】命令,继续运行调试器,等到调试器输出以下内容:
按【ctrl+c】组合键进入调试模式。
1 0:013> !threads 2 ThreadCount: 8 3 UnstartedThread: 1 4 BackgroundThread: 5 5 PendingThread: 1 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA 11 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer) 12 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker) 13 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker) 14 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 15 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 00000250B8A2A330 1039820 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn (Threadpool Worker) 17 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
【!t】命令效果一样,就不一一展示了。
ThreadCount:有多少个托管线程,这个进程里面有8个。
UnstartedThread:已经创建,但是还没有使用的线程,当前数量是1。
BackgroundThread:后台线程的数量,这个进程里面有5个。
PendingThread:阻塞的线程的数量,当前这个进程是1个阻塞的线程。
DeadThread:当一个线程完成任务,但是还没有被回收,这个阶段的线程就是死线程,也就是说这个线程对象底层的数据结构的 OSID 已经销毁。
以上就是介绍的主要名称,下面接着介绍,列表的每一项。我们使用 C# Thread 类型声明一个线程的时候,其实在操作系统和CLR都有一个数据结构相对应,有了 OSID我们才可以在任务管理器中看到线程对象。
第一列 DBG:是 Windbg 调试器的线程 ID,Windbg 给自己的线程起了一个标识,便于以后使用,也可以区分托管线程和非托管线程。
第二列 ID:托管线程 ID 值,就是 这段代码的值:Environment.CurrentManagedThreadId
第三列 OSID:操作系统线程的 ID。
第四列 ThreadOBJ:指向底层 CLR 线程数据结构的指针,可以使用【dp】命令观察其中的内容,我们查看托管线程 ID=4 的【0000021021F60190】内容,红色标注就是托管线程的ID=00000004,就是4。
1 0:013> dp 0000021021F60190 2 00000210`21f60190 00000000`00000000 00000000`0302b220(线程状态) 3 00000210`21f601a0 00000013`9097f108 00000210`21f2fd20 4 00000210`21f601b0 baadf00d`00000004(线程ID) 00000210`21f601c0 5 00000210`21f601c0 00000210`21f601c0 00000210`21f601c0 6 00000210`21f601d0 00000000`00000000 baadf00d`baad0000 7 00000210`21f601e0 00000000`00000000 00000210`2640d408 8 00000210`21f601f0 00000210`2640e658 00000000`00002008 9 00000210`21f60200 00000000`00000000 00000000`00000000
红色标注的就是线程的状态。我们可以使用【dt coreclr!Thread 0000021021F60190】查看 ThreadOBJ 的数据结构。
1 0:013> dt coreclr!Thread 0000021021F60190 2 +0x000 m_stackLocalAllocator : (null) 3 =00007ffd`26cc9b34 m_DetachCount : 0n0 4 =00007ffd`26cc9b30 m_ActiveDetachCount : 0n0 5 +0x008 m_State : Volatileenum Thread::ThreadState> 6 +0x00c m_fPreemptiveGCDisabled : Volatilelong> 7 +0x010 m_pFrame : 0x00000013`9097f108 Frame 8 +0x018 m_pDomain : 0x00000210`21f2fd20 AppDomain 9 +0x020 m_ThreadId : 4(托管线程的 ID) 10 +0x028 m_pHead : 0x00000210`21f601c0 LockEntry 11 +0x030 m_embeddedEntry : LockEntry 12 +0x050 m_pBlockingLock : VolatilePtr 13 +0x058 m_alloc_context : gc_alloc_context 14 +0x090 m_thAllocContextObj : TypeHandle 15 +0x098 m_pTEB : 0x00000013`8fae5000 _NT_TIB 16 +0x0a0 m_pRCWStack : 0x00000210`23a0aa20 RCWStackHeader 17 +0x0a8 m_ThreadTasks : 0 (No matching name) 18 +0x0ac m_StateNC : 0x1180 (No matching name) 19 +0x0b0 m_dwForbidSuspendThread : Volatilelong> 20 +0x0b4 m_dwHashCodeSeed : 0x170e7536 21 +0x0b8 m_pLoadLimiter : (null) 22 +0x0c0 m_AbortType : 0 23 +0x0c8 m_AbortEndTime : 0xffffffff`ffffffff 24 +0x0d0 m_RudeAbortEndTime : 0xffffffff`ffffffff 25 +0x0d8 m_fRudeAbortInitiated : 0n0 26 +0x0dc m_AbortController : 0n0 27 +0x0e0 m_AbortRequestLock : 0n0 28 +0x0e4 m_ThrewControlForThread : 0 29 +0x0e8 m_OSContext : 0x00000210`21f60d40 _CONTEXT 30 +0x0f0 m_pPendingTypeLoad : (null) 31 +0x0f8 m_Link : SLink 32 +0x100 m_dwLastError : 0 33 +0x108 m_CacheStackBase : 0x00000013`90980000 Void 34 +0x110 m_CacheStackLimit : 0x00000013`90800000 Void 35 +0x118 m_CacheStackSufficientExecutionLimit : 0x00000013`90820000 36 +0x120 m_CacheStackStackAllocNonRiskyExecutionLimit : 0x00000013`90880000 37 +0x128 m_pvHJRetAddr : 0xcccccccc`cccccccc Void 38 +0x130 m_ppvHJRetAddrPtr : 0xcccccccc`cccccccc -> ???? 39 +0x138 m_HijackedFunction : 0xbaadf00d`baadf00d MethodDesc 40 +0x140 m_UserInterrupt : 0n0 41 +0x148 m_DebugSuspendEvent : CLREvent 42 +0x158 m_EventWait : CLREvent 43 +0x168 m_WaitEventLink : WaitEventLink 44 +0x198 m_ThreadHandle : 0x00000000`0000027c Void 45 +0x1a0 m_ThreadHandleForClose : 0xffffffff`ffffffff Void 46 +0x1a8 m_ThreadHandleForResume : 0xffffffff`ffffffff Void 47 +0x1b0 m_WeOwnThreadHandle : 0n1 48 +0x1b8 m_OSThreadId : 0x41ec 49 +0x1c0 m_ExposedObject : 0x00000210`23861180 OBJECTHANDLE__ 50 +0x1c8 m_StrongHndToExposedObject : 0x00000210`23861368 OBJECTHANDLE__ 51 +0x1d0 m_Priority : 0x80000000 52 +0x1d4 m_ExternalRefCount : 2 53 +0x1d8 m_TraceCallCount : 0n0 54 +0x1e0 m_LastThrownObjectHandle : (null) 55 +0x1e8 m_ltoIsUnhandled : 0n0 56 +0x1f0 m_ExceptionState : ThreadExceptionState 57 +0x398 m_debuggerFilterContext : (null) 58 +0x3a0 m_pProfilerFilterContext : (null) 59 +0x3a8 m_hijackLock : Volatilelong> 60 +0x3b0 m_hCurrNotification : 0xbaadf00d`baadf00d OBJECTHANDLE__ 61 +0x3b8 m_fInteropDebuggingHijacked : 0n0 62 +0x3bc m_profilerCallbackState : 0 63 +0x3c0 m_dwProfilerEvacuationCounters : [33] Volatilelong> 64 +0x444 m_monitorLockContentionCount : 2 65 =00007ffd`26cc9b18 s_monitorLockContentionCountOverflow : 0 66 +0x448 m_PreventAsync : 0n0 67 =00007ffd`26cc7310 m_DebugWillSyncCount : 0n-1 68 +0x450 m_pSavedRedirectContext : (null) 69 +0x458 m_pOSContextBuffer : (null) 70 +0x460 m_ThreadLocalBlock : ThreadLocalBlock 71 +0x488 m_tailCallTls : TailCallTls 72 +0x498 m_dwAVInRuntimeImplOkayCount : 0 73 +0x4a0 m_pExceptionDuringStartup : (null) 74 +0x4a8 m_debuggerActivePatchSkipper : VolatilePtr 75 +0x4b0 m_fAllowProfilerCallbacks : 0n1 76 +0x4b4 m_dwThreadHandleBeingUsed : Volatilelong> 77 =00007ffd`26cc9b10 s_fCleanFinalizedThread : 0n0 78 +0x4b8 m_pCreatingThrowableForException : (null) 79 +0x4c0 m_dwIndexClauseForCatch : 0 80 +0x4c8 m_sfEstablisherOfActualHandlerFrame : StackFrame 81 +0x4d0 DebugBlockingInfo : ThreadDebugBlockingInfo 82 +0x4d8 m_fDisableComObjectEagerCleanup : 0 83 +0x4d9 m_fHasDeadThreadBeenConsideredForGCTrigger : 0 84 +0x4dc m_random : CLRRandom 85 +0x5c8 m_uliInitializeSpyCookie : _ULARGE_INTEGER 0x0 86 +0x5d0 m_fInitializeSpyRegistered : 0 87 +0x5d8 m_pLastSTACtxCookie : (null) 88 +0x5e0 m_fGCSpecial : 0 89 +0x5e8 m_pGCFrame : 0x00000013`9097ef98 GCFrame 90 +0x5f0 m_wCPUGroup : 0 91 +0x5f8 m_pAffinityMask : 0 92 +0x600 m_pAllLoggedTypes : (null) 93 +0x608 m_gcModeOnSuspension : Volatilelong> 94 +0x60c m_activityId : _GUID {00000000-0000-0000-0000-000000000000} 95 +0x61c m_HijackReturnKind : ff ( RT_Illegal ) 96 =00007ffd`26cc9b80 dead_threads_non_alloc_bytes : 0xa00 97 +0x620 m_currentPrepareCodeConfig : (null) 98 +0x628 m_isInForbidSuspendForDebuggerRegion : 0 99 =00007ffd`26cdb7e0 s_pfnQueueUserAPC2Proc : (null) 100 +0x629 m_hasPendingActivation : 0
+0x020 m_ThreadId: 4 表示托管线程的ID。因为我打印的是 ThreadOBJ 为00000199DFD0CAE0 线程的结构。m 表示Management 托管的。
我们可以继续看看 GC Alloc Context,在输出的结构中有这样一行代码:+0x058 m_alloc_context : gc_alloc_context,蓝色的字体,可以点击,相当于执行【dx】命令。
第五列 State:CLR 层面的线程状态,0302b220 就是托管线程的状态。我们可以点进去或者使用【!threadstate】命令查看线程状态详情,当前就是托管线程是3的状态。
1 0:013> !threadstate 0302b220 2 Legal to Join(可以执行 join 操作) 3 Background(是后台线程) 4 CLR Owns(是CLR 拥有的线程) 5 CoInitialized 6 In Multi Threaded Apartment(是MTA模式) 7 Fully initialized(已经完全初始化) 8 Thread Pool Worker Thread(是线程池线程) 9 Interruptible(可执行中断操作)
第六列 GC Mode:表示当前的线程有没有操作托管堆的权限。
第七列 GC Alloc Context:缓冲区的开始节点和结束节点,每个线程在托管堆中分配一个对象,都有一个缓冲区,这个就是确定了缓冲区起始和结束。
第八列 Domain:表示当前线程所属于的域。
第九列 Lock Count:表示当前的线程有多少个托管锁。
第十列 Apt:表示线程是 STA(线程串行模式,WPF、WinForm) 模式还是 MTA (多线程并行模式)模式。
第十一列 Exception:在当前线程上发生了异常,会把这个异常和这个线程关联起来。
Finalizer 表示当前是终结器线程,Threadpool Worker 表示线程是线程池的线程。
我们先来看看【-live】开关的使用。
1 0:013> !threads -live 2 ThreadCount: 8 3 UnstartedThread: 1 4 BackgroundThread: 5 5 PendingThread: 1 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA 11 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer) 12 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker) 13 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker) 14 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 15 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 16 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn 17 0:013> !t -live 18 ThreadCount: 8 19 UnstartedThread: 1 20 BackgroundThread: 5 21 PendingThread: 1 22 DeadThread: 1 23 Hosted Runtime: no 24 Lock 25 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 26 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA 27 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer) 28 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker) 29 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker) 30 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 31 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 32 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
我们先来看看【-special】开关的使用。
1 0:013> !t -special 2 ThreadCount: 8 3 UnstartedThread: 1 4 BackgroundThread: 5 5 PendingThread: 1 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA 11 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer) 12 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker) 13 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker) 14 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 15 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 00000250B8A2A330 1039820 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn (Threadpool Worker) 17 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn 18 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly. 19 For more information see https://go.microsoft.com/fwlink/?linkid=2135652 20 21 22 OSID Special thread type 23 5 1464 DbgHelper 24 6 2fec Finalizer 25 26 0:013> !threads -special 27 ThreadCount: 8 28 UnstartedThread: 1 29 BackgroundThread: 5 30 PendingThread: 1 31 DeadThread: 1 32 Hosted Runtime: no 33 Lock 34 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 35 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA 36 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer) 37 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker) 38 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker) 39 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 40 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker) 41 XXXX 8 0 00000250B8A2A330 1039820 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn (Threadpool Worker) 42 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn 43 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly. 44 For more information see https://go.microsoft.com/fwlink/?linkid=2135652 45 46 47 OSID Special thread type 48 5 1464 DbgHelper 49 6 2fec Finalizer
上面的参数使用都很简单,就不多数了。
2)、Windbg Preview 调试
编译项目,打开 Windbg Preview 调试器,依次点击【文件】—>【Launch executable】加载我们的项目文件:ExampleCore_3_1_9.exe,进入调试器。【g】继续运行调试器,等我们的控制台程序输出如图:
此时,我们的调试器也处于暂停状态,点击【break】按钮,进入调试器的中断模式。
我们可以使用【!threads】命令或者【!t】命令查看托管线程的内容。
1 0:001> !t 2 ThreadCount: 7 3 UnstartedThread: 0 4 BackgroundThread: 5 5 PendingThread: 0 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker) 17 18 19 0:001> !threads 20 ThreadCount: 7 21 UnstartedThread: 0 22 BackgroundThread: 5 23 PendingThread: 0 24 DeadThread: 1 25 Hosted Runtime: no 26 Lock 27 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 28 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 29 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 30 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 31 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 32 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 33 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 34 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker)
ThreadCount:有多少个托管线程,这个进程里面有7个。
UnstartedThread:已经创建,但是还没有使用的线程,当前数量是0。
BackgroundThread:后台线程的数量,这个进程里面有5个。
PendingThread:阻塞的线程的数量,当前这个进程是0个阻塞的线程。
DeadThread:当一个线程完成任务,但是还没有被回收,这个阶段的线程就是死线程,也就是说这个线程对象底层的数据结构的 OSID 已经销毁。
以上就是介绍的主要名称,下面接着介绍,列表的每一项。我们使用 C# Thread 类型声明一个线程的时候,其实在操作系统和CLR都有一个数据结构相对应,有了 OSID我们才可以在任务管理器中看到线程对象。
第一列 DBG:是 Windbg 调试器自己的线程 ID,也可以区分托管线程和非托管线程。
第二列 ID:是托管线程,也就是我们 Thread 类型的标识符 Id,就是这段代码的值:Environment.CurrentManagedThreadId
第三列 OSID:操作系统线程的 ID。
第四列 ThreadOBJ:指向底层 CLR 线程数据结构的指针,可以使用【dp】命令观察其中的内容,我们查看托管线程 ID=4 的【0000032708325930】内容,红色标注就是托管线程的ID=00000004,就是4。
1 0:001> dp 0000032708325930 2 00000327`08325930 00000000`00000000 00000000`0302b220 3 00000327`08325940 000000e8`2457f518 000002e6`719d7b00 4 00000327`08325950 baadf00d`00000004 00000327`08325960 5 00000327`08325960 00000327`08325960 00000327`08325960 6 00000327`08325970 00000000`00000000 baadf00d`baad0000 7 00000327`08325980 00000000`00000000 000002e6`75c0ceb8 8 00000327`08325990 000002e6`75c0e658 00000000`00002008 9 00000327`083259a0 00000000`00000000 00000000`00000000
第五列 State:CLR 层面的线程状态,00000000`0302b220 就是托管线程的状态。我们可以点进去或者使用【!threadstate】命令查看线程状态详情,当前就是托管线程是4的状态。
1 0:001> !threadstate 00000000`0302b220 2 Legal to Join(可以执行 join 操作) 3 Background(是后台线程) 4 CLR Owns(是CLR 拥有的线程) 5 CoInitialized(是以单线程方式创建的) 6 In Multi Threaded Apartment(是MTA模式) 7 Fully initialized(已经完全初始化) 8 Thread Pool Worker Thread(是线程池线程) 9 Interruptible(可执行中断操作)
第六列 GC Mode:表示当前的线程有没有操作托管堆的权限。
第七列 GC Alloc Context:缓冲区的开始节点和结束节点,每个线程在托管堆中分配一个对象,都有一个缓冲区,这个就是确定了缓冲区起始和结束。
第八列 Domain:表示当前线程所属于的域。
第九列 Lock Count:表示当前的线程有多少个托管锁。
第十列 Apt:表示线程是 STA(线程串行模式,WPF、WinForm) 模式还是 MTA (多线程并行模式)模式。
第十一列 Exception:在当前线程上发生了异常,会把这个异常和这个线程关联起来。
Finalizer 表示当前是终结器线程,Threadpool Worker 表示线程是线程池的线程。
我们可以使用【!t -live】或者【!threads -live】输出活跃的线程。
1 0:001> !t -live 2 ThreadCount: 7 3 UnstartedThread: 0 4 BackgroundThread: 5 5 PendingThread: 0 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 16 17 0:001> !threads -live 18 ThreadCount: 7 19 UnstartedThread: 0 20 BackgroundThread: 5 21 PendingThread: 0 22 DeadThread: 1 23 Hosted Runtime: no 24 Lock 25 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 26 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 27 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 28 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 29 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 30 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 31 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
这里就没有了 DBG 为XXXX 的线程了。
接下来,我们在看看【!threads -special】或者【!t -special】命令的区别。效果很明显,就不多说了。
1 0:001> !t -special 2 ThreadCount: 7 3 UnstartedThread: 0 4 BackgroundThread: 5 5 PendingThread: 0 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker) 17 18 OSID Special thread type 19 5 1220 DbgHelper 20 6 16f4 Finalizer 21 22 23 0:001> !threads -special 24 ThreadCount: 7 25 UnstartedThread: 0 26 BackgroundThread: 5 27 PendingThread: 0 28 DeadThread: 1 29 Hosted Runtime: no 30 Lock 31 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 32 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA 33 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer) 34 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker) 35 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker) 36 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 37 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker) 38 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker) 39 40 OSID Special thread type 41 5 1220 DbgHelper 42 6 16f4 Finalizer
红色标注的就是输出的区别信息,很简单,就不多说了。
3.1.3、DumpStack
A、基础知识
ClrStack 命令只输出托管代码调用栈,内容更详尽(比如:包含方法名称和方法所需的参数),k 系列命令既可以输出非托管代码调用栈,也可以输出托管代码调用栈,这里说明一下,和原书是不一样的,原书中讲只能输出非托管调用栈,可能因为我使用的 .NET 8.0 平台的原因,两种调用栈都可以输出。如果想同时获取托管代码调用栈和非托管代码调用栈,可以使用 DumpStack 命令。
B、眼见为实
调试源码:namespace ExampleCore_3_1_9
调试任务:DumpStack 命令和 k 命令使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9binDebugnet8.0ExampleCore_3_1_9.EXE】,打开调试器窗口。
进入调试器后,我们【g】继续运行调试器,直到调试器输出如图内容,我们按【ctrl+c】进入调试器的中断模式。
我们先切换到托管线程上,执行命令【~0s】。
1 0:012> ~0s 2 coreclr!CLREventWaitWithTry: 3 00007ffe`1c3c5074 48895c2408 mov qword ptr [rsp+8],rbx ss:000000dc`87d7e860=0000000000000001
。。。。。。。。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:namespace ExampleCore_3_1_9.exe。进入调试器后,【g】继续运行调试器,直到我们的控制台程序有如下输出为止。
此时,我们的调试器也处于等待状态,按【break】按钮,进入中断模式,开始我们的调试。
首先,我们先切换到托管线程上下文,执行【~0s】命令。
1 0:001> ~0s 2 ntdll!NtReadFile+0x14: 3 00007ffe`d6faae54 c3 ret
然后,我们执行【k】命令,查看一下非托管代码的调用栈。
1 0:000> k 2 # Child-SP RetAddr Call Site 3 00 000000da`5b97e318 00007ffe`d44d8a53 ntdll!NtReadFile+0x14 4 01 000000da`5b97e320 00007ffe`c2c37704 KERNELBASE!ReadFile+0x73 5 02 000000da`5b97e3a0 00007ffe`c2c3c9c0 System_Console!Interop.Kernel32.ReadFile+0x84 [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 6 03 000000da`5b97e490 00007ffe`c2c3c8bb System_Console!System.ConsolePal.WindowsConsoleStream.ReadFileNative+0x60 [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 7 04 000000da`5b97e4f0 00007ffe`c2c3fb84 System_Console!System.ConsolePal.WindowsConsoleStream.Read+0x2b [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 8 05 000000da`5b97e530 00007ffd`fcc489c1 System_Console!System.IO.ConsoleStream.Read+0x74 [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 9 06 000000da`5b97e5a0 00007ffd`fcc490a4 System_Private_CoreLib!System.IO.StreamReader.ReadBuffer+0x41 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 10 07 000000da`5b97e5f0 00007ffe`c2c4005d System_Private_CoreLib!System.IO.StreamReader.ReadLine+0x64 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 11 08 000000da`5b97e6a0 00007ffe`c2c39319 System_Console!System.IO.SyncTextReader.ReadLine+0x3d [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 12 09 000000da`5b97e6f0 00007ffd`9e0c19cf System_Console!System.Console.ReadLine+0x19 [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 13 0a 000000da`5b97e720 00007ffd`fdc3a1a3 ExampleCore_3_1_9!ExampleCore_3_1_9.Program.Main+0x9f [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9Program.cs @ 11] 14 0b 000000da`5b97e770 00007ffd`fdbc14c9 coreclr!CallDescrWorkerInternal+0x83 [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm @ 100] 15 0c (Inline Function) --------`-------- coreclr!CallDescrWorkerWithHandler+0x56 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 67] 16 0d 000000da`5b97e7b0 00007ffd`fdae75ac coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 570] 17 0e (Inline Function) --------`-------- coreclr!MethodDescCallSite::Call+0xb [D:a_work1ssrccoreclrvmcallhelpers.h @ 458] 18 0f 000000da`5b97e8f0 00007ffd`fdae6f7a coreclr!RunMainInternal+0x11c [D:a_work1ssrccoreclrvmassembly.cpp @ 1304] 19 10 000000da`5b97ea10 00007ffd`fdae6b17 coreclr!RunMain+0xd2 [D:a_work1ssrccoreclrvmassembly.cpp @ 1375] 20 11 000000da`5b97eac0 00007ffd`fdae7321 coreclr!Assembly::ExecuteMainMethod+0x1bf [D:a_work1ssrccoreclrvmassembly.cpp @ 1504] 21 12 000000da`5b97ed90 00007ffd`fdbf7768 coreclr!CorHost2::ExecuteAssembly+0x281 [D:a_work1ssrccoreclrvmcorhost.cpp @ 349] 22 13 000000da`5b97ef00 00007ffe`c2c82c36 coreclr!coreclr_execute_assembly+0xd8 [D:a_work1ssrccoreclrdllsmscoreeexports.cpp @ 504] 23 14 (Inline Function) --------`-------- hostpolicy!coreclr_t::execute_assembly+0x2a [D:a_work1ssrcnativecorehosthostpolicycoreclr.cpp @ 109] 24 15 000000da`5b97efa0 00007ffe`c2c82f1c hostpolicy!run_app_for_context+0x596 [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 256] 25 16 000000da`5b97f130 00007ffe`c2c8385a hostpolicy!run_app+0x3c [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 285] 26 17 000000da`5b97f170 00007ffe`c2cdb5c9 hostpolicy!corehost_main+0x15a [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 426] 27 18 000000da`5b97f270 00007ffe`c2cde066 hostfxr!execute_app+0x2e9 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 145] 28 19 000000da`5b97f370 00007ffe`c2ce02ec hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 532] 29 1a 000000da`5b97f460 00007ffe`c2cde644 hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 1007] 30 1b 000000da`5b97f510 00007ffe`c2cd85a0 hostfxr!fx_muxer_t::execute+0x494 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 578] 31 1c 000000da`5b97f650 00007ff7`e1a5f998 hostfxr!hostfxr_main_startupinfo+0xa0 [D:a_work1ssrcnativecorehostfxrhostfxr.cpp @ 62] 32 1d 000000da`5b97f750 00007ff7`e1a5fda6 apphost!exe_start+0x878 [D:a_work1ssrcnativecorehostcorehost.cpp @ 240] 33 1e 000000da`5b97f920 00007ff7`e1a612e8 apphost!wmain+0x146 [D:a_work1ssrcnativecorehostcorehost.cpp @ 311] 34 1f (Inline Function) --------`-------- apphost!invoke_main+0x22 [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 90] 35 20 000000da`5b97f990 00007ffe`d5416fd4 apphost!__scrt_common_main_seh+0x10c [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 288] 36 21 000000da`5b97f9d0 00007ffe`d6f5cec1 KERNEL32!BaseThreadInitThunk+0x14 37 22 000000da`5b97fa00 00000000`00000000 ntdll!RtlUserThreadStart+0x21
我们再使用【!ClrStack】命令查看一下托管调用栈,有什么不同。
0:000> !clrstack OS Thread Id: 0x10d4 (0) Child SP IP Call Site 000000DA5B97E3D0 00007ffed6faae54 [InlinedCallFrame: 000000da5b97e3d0] 000000DA5B97E3D0 00007ffec2c376eb [InlinedCallFrame: 000000da5b97e3d0] 000000DA5B97E3A0 00007ffec2c376eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 000000DA5B97E490 00007ffec2c3c9c0 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 000000DA5B97E4F0 00007ffec2c3c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 000000DA5B97E530 00007ffec2c3fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 000000DA5B97E5A0 00007ffdfcc489c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 000000DA5B97E5F0 00007ffdfcc490a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 000000DA5B97E6A0 00007ffec2c4005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 000000DA5B97E6F0 00007ffec2c39319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 000000DA5B97E720 00007ffd9e0c19cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9Program.cs @ 11]
我们可以看到,【k】命令的输出中包含【!clrstack】命令的输出,只不过【!clrstack】命令,更详细一点,每个方法的名称和包含的参数都输出了。
k 系列命令里有一个命令很有用,那就是【kb】,这个命令可以提取方法的参数,我们可以尝试提取【ntdll!NtReadFile+0x14】这个方法的第一个参数,了解它的用法。
1 0:000> kb 2 # RetAddr : Args to Child : Call Site 3 00 00007ffe`d44d8a53 : 00000000`00001000 00007ffd`fdc39da3 00000000`0000005c 000000da`5b97e3d0 : ntdll!NtReadFile+0x14 4 01 00007ffe`c2c37704 : 00000000`0000005c 00000000`0000005c 00000000`00001000 000000da`5b97e520 : KERNELBASE!ReadFile+0x73 5 02 00007ffe`c2c3c9c0 : 00000000`0000005c 00000251`29815038 00000000`00001000 000000da`5b97e520 : System_Console!Interop.Kernel32.ReadFile+0x84 [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 6 03 00007ffe`c2c3c8bb : 00000000`0000005c 000000da`5b97e558 00000000`00000000 000000da`5b97e520 : System_Console!System.ConsolePal.WindowsConsoleStream.ReadFileNative+0x60 [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 7 04 00007ffe`c2c3fb84 : 00000251`2980c0d0 000000da`5b97e558 00000000`00000000 00000000`00001000 : System_Console!System.ConsolePal.WindowsConsoleStream.Read+0x2b [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 8 05 00007ffd`fcc489c1 : 00000251`2980c0d0 00000251`29815028 00000000`00000000 00000000`00001000 : System_Console!System.IO.ConsoleStream.Read+0x74 [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 9 06 00007ffd`fcc490a4 : 00000251`2980c138 00000251`25198390 00000251`29809670 00000000`00000001 : System_Private_CoreLib!System.IO.StreamReader.ReadBuffer+0x41 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 10 07 00007ffe`c2c4005d : 00000251`2980c138 00000251`25198390 00000251`29809670 00000000`00000001 : System_Private_CoreLib!System.IO.StreamReader.ReadLine+0x64 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 11 08 00007ffe`c2c39319 : 00000251`2981e070 00000000`00000000 00000251`29809670 00000251`29c00000 : System_Console!System.IO.SyncTextReader.ReadLine+0x3d [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 12 09 00007ffd`9e0c19cf : 00000000`00000001 00000251`2980c020 00000251`2980b150 00000000`00000007 : System_Console!System.Console.ReadLine+0x19 [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 13 0a 00007ffd`fdc3a1a3 : 00000251`29808ea0 000000da`5b97edb8 000000da`5b97edb8 000000da`5b97e9a9 : ExampleCore_3_1_9!ExampleCore_3_1_9.Program.Main+0x9f [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9Program.cs @ 11] 14 0b 00007ffd`fdbc14c9 : 00000000`00000000 00000000`00000130 000000da`5b97e9b8 00007ffd`fdaea456 : coreclr!CallDescrWorkerInternal+0x83 [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm @ 100] 15 0c (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 67] 16 0d 00007ffd`fdae75ac : 000000da`5b97ea38 00000000`00000000 00000000`00000048 00007ffd`fdbd28a6 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 570] 17 0e (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb [D:a_work1ssrccoreclrvmcallhelpers.h @ 458] 18 0f 00007ffd`fdae6f7a : 00000251`29808ea0 00000251`29808ea0 00000000`00000000 000000da`5b97edb8 : coreclr!RunMainInternal+0x11c [D:a_work1ssrccoreclrvmassembly.cpp @ 1304] 19 10 00007ffd`fdae6b17 : 00000251`25198390 00000251`00000000 00000251`25198390 00000000`00000000 : coreclr!RunMain+0xd2 [D:a_work1ssrccoreclrvmassembly.cpp @ 1375] 20 11 00007ffd`fdae7321 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf [D:a_work1ssrccoreclrvmassembly.cpp @ 1504] 21 12 00007ffd`fdbf7768 : 00000000`00000001 000000da`5b97ef01 000000da`5b97efe0 00007ffe`c2c623ea : coreclr!CorHost2::ExecuteAssembly+0x281 [D:a_work1ssrccoreclrvmcorhost.cpp @ 349] 22 13 00007ffe`c2c82c36 : 00000251`2516a900 00000251`2516a660 00000000`00000000 00000251`2516a660 : coreclr!coreclr_execute_assembly+0xd8 [D:a_work1ssrccoreclrdllsmscoreeexports.cpp @ 504] 23 14 (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a [D:a_work1ssrcnativecorehosthostpolicycoreclr.cpp @ 109] 24 15 00007ffe`c2c82f1c : 00000251`25158228 000000da`5b97f209 00007ffe`c2cbc9c0 00000251`25158228 : hostpolicy!run_app_for_context+0x596 [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 256] 25 16 00007ffe`c2c8385a : 00000000`00000000 00000251`25158220 00000251`25158220 00000000`00000000 : hostpolicy!run_app+0x3c [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 285] 26 17 00007ffe`c2cdb5c9 : 00000251`251669e8 00000251`251668d0 00000000`00000000 000000da`5b97f309 : hostpolicy!corehost_main+0x15a [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 426] 27 18 00007ffe`c2cde066 : 00000251`25167540 000000da`5b97f690 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 145] 28 19 00007ffe`c2ce02ec : 00007ffe`c2d125f8 00000251`251680d0 000000da`5b97f5d0 000000da`5b97f580 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 532] 29 1a 00007ffe`c2cde644 : 000000da`5b97f690 000000da`5b97f6b0 000000da`5b97f601 00000251`25168401 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 1007] 30 1b 00007ffe`c2cd85a0 : 000000da`5b97f6b0 00000251`25166690 00000000`00000001 00000251`25150000 : hostfxr!fx_muxer_t::execute+0x494 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 578] 31 1c 00007ff7`e1a5f998 : 00007ffe`d48ef4e8 00007ffe`c2cd9b10 000000da`5b97f850 00000251`25166380 : hostfxr!hostfxr_main_startupinfo+0xa0 [D:a_work1ssrcnativecorehostfxrhostfxr.cpp @ 62] 32 1d 00007ff7`e1a5fda6 : 00007ff7`e1a6b6c0 00000000`00000007 00000251`25158220 00000000`0000005e : apphost!exe_start+0x878 [D:a_work1ssrcnativecorehostcorehost.cpp @ 240] 33 1e 00007ff7`e1a612e8 : 00000000`00000000 00000000`00000000 00000251`25158220 00000000`00000000 : apphost!wmain+0x146 [D:a_work1ssrcnativecorehostcorehost.cpp @ 311] 34 1f (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22 [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 90] 35 20 00007ffe`d5416fd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : apphost!__scrt_common_main_seh+0x10c [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 288] 36 21 00007ffe`d6f5cec1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14 37 22 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
00000000`00001000 00007ffd`fdc39da3 00000000`0000005c 000000da`5b97e3d0 这些就是 ntdll!NtReadFile 方法所需的具体参数。
我们使用【!DumpStack】命令,看看输出的内容。
1 0:000> !dumpstack 2 OS Thread Id: 0x10d4 (0) 3 Current frame: ntdll!NtReadFile + 0x14 4 Child-SP RetAddr Caller, Callee 5 000000DA5B97E310 00007ffed44d8a53 KERNELBASE!ReadFile + 0x73, calling ntdll!NtReadFile 6 000000DA5B97E320 00007ffdfdc39da3 coreclr!NDirectImportThunk + 0x43 [D:a_work1ssrccoreclrvmamd64AsmHelpers.asm:185], calling coreclr!NDirectImportWorker [D:a_work1ssrccoreclrvmdllimport.cpp:5834] 7 000000DA5B97E390 00007ffec2c37704 (MethodDesc 00007ffd9e17dff8 + 0x84 Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 8 000000DA5B97E3F0 00007ffec2c376eb (MethodDesc 00007ffd9e17dff8 + 0x6b Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)), calling coreclr!JIT_PInvokeBegin [D:a_work1ssrccoreclrvmamd64PInvokeStubs.asm:142] 9 000000DA5B97E480 00007ffec2c3c9c0 (MethodDesc 00007ffd9e1a2cc0 + 0x60 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean)), calling System_Console + 0x10b04 10 000000DA5B97E4E0 00007ffec2c3c8bb (MethodDesc 00007ffd9e1a2c90 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1)), calling System_Console + 0x10b04 11 000000DA5B97E520 00007ffec2c3fb84 (MethodDesc 00007ffd9e1a2968 + 0x74 System.IO.ConsoleStream.Read(Byte[], Int32, Int32)) 12 000000DA5B97E590 00007ffdfcc489c1 (MethodDesc 00007ffd9e1f6080 + 0x41 System.IO.StreamReader.ReadBuffer()) 13 000000DA5B97E5E0 00007ffdfcc490a4 (MethodDesc 00007ffd9e1f60a8 + 0x64 System.IO.StreamReader.ReadLine()) 14 000000DA5B97E690 00007ffec2c4005d (MethodDesc 00007ffd9e1ff650 + 0x3d System.IO.SyncTextReader.ReadLine()) 15 000000DA5B97E6E0 00007ffec2c39319 (MethodDesc 00007ffd9e17a350 + 0x19 System.Console.ReadLine()) 16 000000DA5B97E710 00007ffd9e0c19cf (MethodDesc 00007ffd9e1700c0 + 0x9f ExampleCore_3_1_9.Program.Main(System.String[])), calling 00007ffd9e2213e0 17 000000DA5B97E760 00007ffdfdc3a1a3 coreclr!CallDescrWorkerInternal + 0x83 [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm:100] 18 000000DA5B97E7A0 00007ffdfdbc14c9 coreclr!MethodDescCallSite::CallTargetWorker + 0x2a1 [D:a_work1ssrccoreclrvmcallhelpers.cpp:570], calling coreclr!CallDescrWorkerInternal [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm:37] 19 000000DA5B97E7C0 00007ffdfdaea456 coreclr!AppDomain::LoadDomainAssembly + 0xee [D:a_work1ssrccoreclrvmappdomain.cpp:2596], calling coreclr!AppDomain::LoadDomainAssembly [D:a_work1ssrccoreclrvmappdomain.cpp:2809] 20 000000DA5B97E830 00007ffdfdae708b coreclr!MethodDesc::IsVoid + 0x2f [D:a_work1ssrccoreclrvmmethod.cpp:987], calling coreclr!SigPointer::PeekElemTypeClosed [D:a_work1ssrccoreclrvmsiginfo.cpp:2429] 21 000000DA5B97E8E0 00007ffdfdae75ac coreclr!RunMainInternal + 0x11c [D:a_work1ssrccoreclrvmassembly.cpp:1304], calling coreclr!MethodDescCallSite::CallTargetWorker [D:a_work1ssrccoreclrvmcallhelpers.cpp:260] 22 000000DA5B97E900 00007ffdfdbd28a6 coreclr!MDInternalRO::GetSigOfMethodDef + 0x86 [D:a_work1ssrccoreclrmdruntimemdinternalro.cpp:1403], calling ntdll!LdrpDispatchUserCallTarget 23 000000DA5B97EA00 00007ffdfdae6f7a coreclr!RunMain + 0xd2 [D:a_work1ssrccoreclrvmassembly.cpp:1375], calling coreclr!RunMainInternal [D:a_work1ssrccoreclrvmassembly.cpp:1275] 24 000000DA5B97EAB0 00007ffdfdae6b17 coreclr!Assembly::ExecuteMainMethod + 0x1bf [D:a_work1ssrccoreclrvmassembly.cpp:1504], calling coreclr!RunMain [D:a_work1ssrccoreclrvmassembly.cpp:1324] 25 000000DA5B97EAF0 00007ffdfca71ae3 (MethodDesc 00007ffd9e14f460 + 0xa3 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)), calling 00007ffd9e0c0378 26 000000DA5B97EB30 00007ffdfdc3b74a coreclr!DelayLoad_Helper + 0x7a [D:a_work1ssrccoreclrvmamd64ExternalMethodFixupThunk.asm:61], calling coreclr!DynamicHelperWorker [D:a_work1ssrccoreclrvmprestub.cpp:3776] 27 000000DA5B97EBF0 00007ffdfca71af0 (MethodDesc 00007ffd9e14f460 + 0xb0 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)), calling 00007ffd9df50010 28 000000DA5B97EC50 00007ffdfdc3a1a3 coreclr!CallDescrWorkerInternal + 0x83 [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm:100] 29 000000DA5B97EC90 00007ffdfdb2389e coreclr!DispatchCallSimple + 0x72 [D:a_work1ssrccoreclrvmcallhelpers.cpp:222], calling coreclr!__security_check_cookie [D:a_work1ssrcvctoolscrtvcstartupsrcgsamd64amdsecgs.asm:45] 30 000000DA5B97ECB0 00007ffdfdbc1abf coreclr!MethodDesc::TryGetMultiCallableAddrOfCode + 0x87 [D:a_work1ssrccoreclrvmmethod.cpp:2071], calling coreclr!MethodDesc::GetMethodEntryPoint [D:a_work1ssrccoreclrvmmethod.cpp:477] 31 000000DA5B97ECF0 00007ffdfdbf9eb2 coreclr!`anonymous namespace'::GetConfigDWORD + 0x32 [D:a_work1ssrccoreclrutilcodeclrconfig.cpp:247], calling coreclr!`anonymous namespace'::EnvGetString [D:a_work1ssrccoreclrutilcodeclrconfig.cpp:134] 32 000000DA5B97ED80 00007ffdfdae7321 coreclr!CorHost2::ExecuteAssembly + 0x281 [D:a_work1ssrccoreclrvmcorhost.cpp:349], calling coreclr!Assembly::ExecuteMainMethod [D:a_work1ssrccoreclrvmassembly.cpp:1440] 33 000000DA5B97EDE0 00007ffed44ce8e6 KERNELBASE!MultiByteToWideChar + 0x186, calling KERNELBASE!_security_check_cookie 34 000000DA5B97EEB0 00007ffdfdbf7ad4 coreclr!StringToUnicode + 0x94 [D:a_work1ssrccoreclrdllsmscoreeexports.cpp:87], calling KERNEL32!MultiByteToWideCharStub 35 000000DA5B97EEF0 00007ffdfdbf7768 coreclr!coreclr_execute_assembly + 0xd8 [D:a_work1ssrccoreclrdllsmscoreeexports.cpp:504], calling ntdll!LdrpDispatchUserCallTarget 36 000000DA5B97EF10 00007ffec2c623ea hostpolicy!breadcrumb_writer_t::begin_write + 0x1ba [D:a_work1ssrcnativecorehosthostpolicybreadcrumbs.cpp:39], calling hostpolicy!trace::verbose [D:a_work1ssrcnativecorehosthostmisctrace.cpp:131] 37 000000DA5B97EF30 00007ffed4817381 ucrtbase!is_stream_flushable_or_commitable + 0x9, calling ucrtbase!is_stream_flushable 38 000000DA5B97EF60 00007ffed4818996 ucrtbase!fflush + 0x16, calling ucrtbase!is_stream_flushable_or_commitable 39 000000DA5B97EF90 00007ffec2c82c36 hostpolicy!run_app_for_context + 0x596 [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:256], calling ntdll!LdrpDispatchUserCallTarget 40 000000DA5B97EFD0 00007ffed6f295f1 ntdll!RtlpFreeHeapInternal + 0x491, calling ntdll!RtlpHpStackLoggingEnabled 41 000000DA5B97F070 00007ffec2c9bd64 hostpolicy!mtx_do_lock + 0x11c [D:a_work1ssrcvctoolscrtgithubstlsrcmutex.cpp:146], calling hostpolicy!__security_check_cookie [D:a_work1ssrcvctoolscrtvcstartupsrcgsamd64amdsecgs.asm:45] 42 000000DA5B97F090 00007ffec2c9bc40 hostpolicy!_Mtx_unlock + 0x18 [D:a_work1ssrcvctoolscrtgithubstlsrcmutex.cpp:159], calling ntdll!RtlReleaseSRWLockExclusive 43 000000DA5B97F0C0 00007ffec2c82316 hostpolicy!`anonymous namespace'::get_hostpolicy_context + 0x116 [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:168], calling hostpolicy!_Mtx_unlock [D:a_work1ssrcvctoolscrtgithubstlsrcmutex.cpp:148] 44 000000DA5B97F120 00007ffec2c82f1c hostpolicy!run_app + 0x3c [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:285], calling hostpolicy!run_app_for_context [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:208] 45 000000DA5B97F160 00007ffec2c8385a hostpolicy!corehost_main + 0x15a [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:426], calling hostpolicy!run_app [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp:280] 46 000000DA5B97F260 00007ffec2cdb5c9 hostfxr!execute_app + 0x2e9 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:145], calling ntdll!LdrpDispatchUserCallTarget 47 000000DA5B97F360 00007ffec2cde066 hostfxr!`anonymous namespace'::read_config_and_execute + 0xa6 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:532], calling hostfxr!execute_app [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:87] 48 000000DA5B97F3B0 00007ffed6f25d21 ntdll!RtlFreeHeap + 0x51, calling ntdll!RtlpFreeHeapInternal 49 000000DA5B97F450 00007ffec2ce02ec hostfxr!fx_muxer_t::handle_exec_host_command + 0x16c [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:1007], calling hostfxr!`anonymous namespace'::read_config_and_execute [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:515] 50 000000DA5B97F500 00007ffec2cde644 hostfxr!fx_muxer_t::execute + 0x494 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:578], calling hostfxr!fx_muxer_t::handle_exec_host_command [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:990] 51 000000DA5B97F640 00007ffec2cd85a0 hostfxr!hostfxr_main_startupinfo + 0xa0 [D:a_work1ssrcnativecorehostfxrhostfxr.cpp:62], calling hostfxr!fx_muxer_t::execute [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp:555] 52 000000DA5B97F680 00007ffed450f3ad KERNELBASE!GetModuleHandleExW + 0xad, calling ntdll!LdrAddRefDll 53 000000DA5B97F710 00007ffed4818996 ucrtbase!fflush + 0x16, calling ucrtbase!is_stream_flushable_or_commitable 54 000000DA5B97F740 00007ff7e1a5f998 apphost!exe_start + 0x878 [D:a_work1ssrcnativecorehostcorehost.cpp:240], calling ntdll!LdrpDispatchUserCallTarget 55 000000DA5B97F780 00007ff7e1a572bf apphost!trace::setup + 0x28f [D:a_work1ssrcnativecorehosthostmisctrace.cpp:76], calling apphost!__security_check_cookie [D:a_work1ssrcvctoolscrtvcstartupsrcgsamd64amdsecgs.asm:45] 56 000000DA5B97F910 00007ff7e1a5fda6 apphost!wmain + 0x146 [D:a_work1ssrcnativecorehostcorehost.cpp:311], calling apphost!exe_start [D:a_work1ssrcnativecorehostcorehost.cpp:101] 57 000000DA5B97F950 00007ff7e1a6100d apphost!__scrt_release_startup_lock + 0xd [D:a_work1ssrcvctoolscrtvcstartupsrcutilityutility.cpp:161], calling apphost!__scrt_is_ucrt_dll_in_use [D:a_work1ssrcvctoolscrtvcstartupsrcutilityucrt_detection.c:22] 58 000000DA5B97F980 00007ff7e1a612e8 apphost!__scrt_common_main_seh + 0x10c [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl:288], calling apphost!wmain [D:a_work1ssrcnativecorehostcorehost.cpp:290] 59 000000DA5B97F9C0 00007ffed5416fd4 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 60 000000DA5B97F9F0 00007ffed6f5cec1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget
【k】命令和【!dumpstack】命令,输出的内容都差不多,但是也有一些区别,【!dumpstack】命令输出的更详细一点,每个调用栈都给出了地址。
如果我们想只输出托管代码的调用栈,可以增加 -ee 命令开关。
1 0:000> !dumpstack -ee 2 OS Thread Id: 0x10d4 (0) 3 Current frame: 4 Child-SP RetAddr Caller, Callee 5 000000DA5B97E390 00007ffec2c37704 (MethodDesc 00007ffd9e17dff8 + 0x84 Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 6 000000DA5B97E3F0 00007ffec2c376eb (MethodDesc 00007ffd9e17dff8 + 0x6b Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 7 000000DA5B97E480 00007ffec2c3c9c0 (MethodDesc 00007ffd9e1a2cc0 + 0x60 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean)) 8 000000DA5B97E4E0 00007ffec2c3c8bb (MethodDesc 00007ffd9e1a2c90 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1)) 9 000000DA5B97E520 00007ffec2c3fb84 (MethodDesc 00007ffd9e1a2968 + 0x74 System.IO.ConsoleStream.Read(Byte[], Int32, Int32)) 10 000000DA5B97E590 00007ffdfcc489c1 (MethodDesc 00007ffd9e1f6080 + 0x41 System.IO.StreamReader.ReadBuffer()) 11 000000DA5B97E5E0 00007ffdfcc490a4 (MethodDesc 00007ffd9e1f60a8 + 0x64 System.IO.StreamReader.ReadLine()) 12 000000DA5B97E690 00007ffec2c4005d (MethodDesc 00007ffd9e1ff650 + 0x3d System.IO.SyncTextReader.ReadLine()) 13 000000DA5B97E6E0 00007ffec2c39319 (MethodDesc 00007ffd9e17a350 + 0x19 System.Console.ReadLine()) 14 000000DA5B97E710 00007ffd9e0c19cf (MethodDesc 00007ffd9e1700c0 + 0x9f ExampleCore_3_1_9.Program.Main(System.String[])) 15 000000DA5B97EAF0 00007ffdfca71ae3 (MethodDesc 00007ffd9e14f460 + 0xa3 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)) 16 000000DA5B97EBF0 00007ffdfca71af0 (MethodDesc 00007ffd9e14f460 + 0xb0 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**))
3.1.4、EEStack
A、基础知识
如果想获取进程中所有托管线程的调用栈,可以使用【!EEStack】命令,【!EEStack】命令会对进程中每个活跃的线程调用【!DumpStack】命令。也可以使用【~*e !dumpstack】,结果是一样的。
该命令有两个命令开关,
-short 这个开关只输出“感兴趣”线程的调用栈。“感兴趣”的线程是指:持有锁的线程、被劫持以执行一个垃圾收集操作的线程或者是正在托管代码中执行的线程。
-EE 这个开关会直接传递给【!dumpstack】命令,只显示托管代码调用栈。
B、眼见为实
调试源码:ExampleCore_3_1_9
调试任务:【!EEStack】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9binDebugnet8.0ExampleCore_3_1_9.exe】,打开调试器。
开启调试器,继续使用【g】命令运行调试器,直到调试器输出如图:
按组合键【ctrl+c】进入调试器的中断模式。我们需要切换到托管线程的上下文中,执行命令【~0s】。
1 0:003> ~0s 2 ntdll!NtDeviceIoControlFile+0x14: 3 00007ff8`f946d0c4 c3 ret
接下来,我们执行【!EEStack】命令,内容太多,省略了一些内容,不影响观看。
1 0:000> !EEStack 2 --------------------------------------------- 3 Thread 0 4 Current frame: ntdll!NtDeviceIoControlFile + 0x14 5 Child-SP RetAddr Caller, Callee 6 000000462657E510 00007ff8f6917861 KERNELBASE!ConsoleCallServerGeneric + 0xe9, calling ntdll!NtDeviceIoControlFile 7 000000462657E5E0 00007ff83f31bbb4 coreclr!PreStubWorker + 0x284 [D:a_work1ssrccoreclrvmprestub.cpp:2444], calling KERNEL32!SetLastErrorStub 8 。。。。。。(省略了) 9 000000462657FBB0 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 10 000000462657FBE0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 11 --------------------------------------------- 12 13 Thread 6 14 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 15 Child-SP RetAddr Caller, Callee 16 0000004626E7F6C0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 17 0000004626E7F710 00007ff8f694b6db KERNELBASE!FlsSetValue + 0xb, calling ntdll!RtlFlsSetValue 18 。。。。。。() 19 0000004626E7FED0 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 20 0000004626E7FF00 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 21 --------------------------------------------- 22 23 Thread 8 24 Current frame: coreclr!JIT_MonExit_Portable + 0xb3 [D:a_work1ssrccoreclrvmjithelpers.cpp:3996] 25 Child-SP RetAddr Caller, Callee 26 000000462717F200 00007ff891580070 (MethodDesc 00007ff7df9c23b8 + 0x50 System.IO.SyncTextReader.ReadLine()), calling 00007ff83f3380c0 (stub for System.Threading.Monitor.Exit(System.Object)) 27 000000462717F250 00007ff891579319 (MethodDesc 00007ff7df92a350 + 0x19 System.Console.ReadLine()) 28 。。。。。。(省略了) 29 000000462717F8A0 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 30 000000462717F8D0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 31 --------------------------------------------- 32 33 Thread 9 34 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 35 Child-SP RetAddr Caller, Callee 36 000000462613F2C0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 37 000000462613F2F0 00007ff8f93f5ba1 ntdll!RtlpFreeHeapInternal + 0x491, calling ntdll!RtlpHpStackLoggingEnabled 38 。。。。。。(省略了) 39 000000462613FD70 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 40 --------------------------------------------- 41 42 Thread 10 43 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 44 Child-SP RetAddr Caller, Callee 45 00000046272FE8F0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 46 00000046272FE910 00007ff83f35da2d coreclr!BucketTable::Add + 0x69 [D:a_work1ssrccoreclrvmvirtualcallstub.cpp:3278], calling ntdll!LdrpDispatchUserCallTarget 47 。。。。。。(省略了) 48 00000046272FF770 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 49 00000046272FF7A0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 50 --------------------------------------------- 51 52 Thread 11 53 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 54 Child-SP RetAddr Caller, Callee 55 000000462747E8B0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 56 000000462747E8D0 00007ff83f284f88 coreclr!CodeFragmentHeap::RealAllocAlignedMem + 0x180 [D:a_work1ssrccoreclrvmcodeman.cpp:2150], calling ntdll!RtlLeaveCriticalSection 57 。。。。。。(省略了) 58 000000462747F730 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 59 000000462747F760 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 60 --------------------------------------------- 61 62 Thread 1(这个线程是调试器本身的,这个可以忽略,在 Windbg Preview 中是没有的) 63 Current frame: ntdll!RtlpEnterCriticalSectionContended + 0xe2 64 Child-SP RetAddr Caller, Callee 65 00000046266FF290 00007ff8f93ffae2 ntdll!RtlEnterCriticalSection + 0x42, calling ntdll!RtlpEnterCriticalSectionContended 66 00000046266FF2C0 00007ff8f94c88b5 ntdll!RtlDebugAllocateHeap + 0xc5, calling ntdll!RtlEnterCriticalSection 67 00000046266FF320 00007ff8f93fd255 ntdll!RtlpAllocateHeap + 0xf5, calling ntdll!RtlDebugAllocateHeap 68 00000046266FF570 00007ff8f93fb44d ntdll!RtlpAllocateHeapInternal + 0xa2d, calling ntdll!RtlpAllocateHeap 69 00000046266FF680 00007ff8f9418269 ntdll!LdrpAllocateTls + 0x109, calling ntdll!RtlAllocateHeap 70 00000046266FF750 00007ff8f93e77a7 ntdll!LdrpInitializeThread + 0x6f, calling ntdll!LdrpAllocateTls 71 00000046266FF830 00007ff8f9445064 ntdll!LdrpInitialize + 0x408, calling ntdll!LdrpInitializeThread 72 00000046266FF8D0 00007ff8f9444c43 ntdll!LdrpInitialize + 0x3b, calling ntdll!LdrpInitialize 73 00000046266FF900 00007ff8f9444bee ntdll!LdrInitializeThunk + 0xe, calling ntdll!LdrpInitialize
这就是【!EEStack】命令的输出,-short 开关加也不加,输出的内容是一样的。红色部分是调试器的,不用在意。线程 ID 不是托管线程的 ID,是调试器自己标识的,它自己使用的,也就是 DBG 的值。
【!EEStack】命令会输出进程中所有托管线程的详细调用栈,而【!t】或者是【!Threads】命令也是输出托管调用栈的简单列表,当然,这个命令也会输出死亡的线程,而【!EEStack】只会输出活跃的线程,这就是区别。我们再看看【!t】的输出,有对比就知道了。
1 0:000> !Threads 2 ThreadCount: 8 3 UnstartedThread: 1 4 BackgroundThread: 5 5 PendingThread: 1 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 1330 000001DC85CE2820 2a020 Preemptive 000001DC8A40C0A8:000001DC8A40C638 000001DC85CD1090 -00001 MTA 11 6 2 1760 000001DC8783DDA0 2b220 Preemptive 0000000000000000:0000000000000000 000001DC85CD1090 -00001 MTA (Finalizer) 12 8 4 3c90 000001DC85CFD1A0 102b220 Cooperative 000001DC8A41F0C0:000001DC8A420FD0 000001DC85CD1090 -00001 MTA (Threadpool Worker) 13 9 5 1dec 0000021D1C7A63C0 302b220 Preemptive 000001DC8A40EA58:000001DC8A410678 000001DC85CD1090 -00001 MTA (Threadpool Worker) 14 10 6 35a8 0000021D1C7AAB70 302b220 Preemptive 000001DC8A4151F0:000001DC8A417030 000001DC85CD1090 -00001 MTA (Threadpool Worker) 15 11 7 4b4 0000021D1C7B1E60 302b220 Preemptive 000001DC8A411848:000001DC8A412FF0 000001DC85CD1090 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 0000021D1C7AC2E0 1039820 Preemptive 0000000000000000:0000000000000000 000001DC85CD1090 -00001 Ukn (Threadpool Worker) 17 1 3 1d70 0000021D1C7B52D0 9600 Preemptive 0000000000000000:0000000000000000 000001DC85CD1090 -00001 Ukn 18 0:000>
DBG 值:XXXX 就是死亡的线程,其他内容输出是一致的,内容很简单,就不多数了。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—【Launch executable】,加载我们的项目文件:ExampleCore_3_1_9.exe,进入到调试器。继续使用【g】命令运行调试器,我们的控制台程序输出如下:
此时,我们的调试器也处于卡住的状态,点击【break】按钮,进入调试器的中断模式,开始我们的调试了。
如果我们要查看调用栈,必须切换到托管线程的上下文中,执行命令【~0s】。
我们直接执行【!EEStack】命令,它会输出所有线程的调用栈,如果说【!t】或者说【!threads】是简化版的,【!EEStack】就是详情版的,内容如下。
1 0:000> !eestack 2 --------------------------------------------- 3 Thread 0 4 Current frame: ntdll!NtDeviceIoControlFile + 0x14 5 Child-SP RetAddr Caller, Callee 6 000000AFC917E780 00007ff8f6917861 KERNELBASE!ConsoleCallServerGeneric + 0xe9, calling ntdll!NtDeviceIoControlFile 7 。。。。。。(省略了) 9 000000AFC917FE20 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 10 000000AFC917FE50 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 11 --------------------------------------------- 12 13 Thread 6 14 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 15 Child-SP RetAddr Caller, Callee 16 000000AFC9A7F5B0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 17 000000AFC9A7F600 00007ff8f694b6db KERNELBASE!FlsSetValue + 0xb, calling ntdll!RtlFlsSetValue 18 。。。。。。(省略了) 19 000000AFC9A7FDC0 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 20 000000AFC9A7FDF0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 21 --------------------------------------------- 22 23 Thread 8 24 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 25 Child-SP RetAddr Caller, Callee 26 000000AFC9D7EA00 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 27 000000AFC9D7EA20 00007ff81efb4f88 coreclr!CodeFragmentHeap::RealAllocAlignedMem + 0x180 [D:a_work1ssrccoreclrvmcodeman.cpp:2150], calling ntdll!RtlLeaveCriticalSection 28 。。。。。。(省略了) 29 000000AFC9D7F880 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 30 000000AFC9D7F8B0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 31 --------------------------------------------- 32 33 Thread 9 34 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 35 Child-SP RetAddr Caller, Callee 36 000000AFC8CEED40 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 37 000000AFC8CEED70 00007ff8f93f5ba1 ntdll!RtlpFreeHeapInternal + 0x491, calling ntdll!RtlpHpStackLoggingEnabled 38 。。。。。。(省略了) 39 000000AFC8CEF760 00007ff81f0673be coreclr!ThreadNative::KickOffThread + 0x7e [D:a_work1ssrccoreclrvmcomsynchronizable.cpp:230], calling coreclr!ManagedThreadBase_DispatchOuter [D:a_work1ssrccoreclrvmthreads.cpp:7377] 40 000000AFC8CEF7C0 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 41 000000AFC8CEF7F0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 42 --------------------------------------------- 43 44 Thread 10 45 Current frame: ntdll!NtWaitForMultipleObjects + 0x14 46 Child-SP RetAddr Caller, Callee 47 000000AFC9EFEEA0 00007ff8f6951d20 KERNELBASE!WaitForMultipleObjectsEx + 0xf0, calling ntdll!NtWaitForMultipleObjects 48 000000AFC9EFEEC0 00007ff81f08da2d coreclr!BucketTable::Add + 0x69 [D:a_work1ssrccoreclrvmvirtualcallstub.cpp:3278], calling ntdll!LdrpDispatchUserCallTarget 49 。。。。。。(省略了) 50 000000AFC9EFFD20 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 51 000000AFC9EFFD50 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget 52 --------------------------------------------- 53 54 Thread 11 55 Current frame: ntdll!NtReadFile + 0x14 56 Child-SP RetAddr Caller, Callee 57 000000AFCA07F260 00007ff8f6935783 KERNELBASE!ReadFile + 0x73, calling ntdll!NtReadFile 58 000000AFCA07F270 00007ff81f0f9da3 coreclr!NDirectImportThunk + 0x43 [D:a_work1ssrccoreclrvmamd64AsmHelpers.asm:185], calling coreclr!NDirectImportWorker [D:a_work1ssrccoreclrvmdllimport.cpp:5834] 59 。。。。。。(省略了) 60 000000AFCA07FC80 00007ff8f9287344 KERNEL32!BaseThreadInitThunk + 0x14, calling ntdll!LdrpDispatchUserCallTarget 61 000000AFCA07FCB0 00007ff8f94226b1 ntdll!RtlUserThreadStart + 0x21, calling ntdll!LdrpDispatchUserCallTarget
这就是【!EEstack】命令的输出,它输出了线程为 0、6、8、9、10、11号的线程内容,当然这个编号不是托管线程的 ID 值,是调试器 DBG 的值。我们在看看【!t】或者【!Threads】命令的输出,大家一对比就知道了。
1 0:000> !t 2 ThreadCount: 7 3 UnstartedThread: 0 4 BackgroundThread: 5 5 PendingThread: 0 6 DeadThread: 1 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3f1c 000001C39D8E9C30 2a020 Preemptive 000001C3A200C0A8:000001C3A200C638 000001c39d929390 -00001 MTA 11 6 2 3d00 000001C39D996C40 2b220 Preemptive 0000000000000000:0000000000000000 000001c39d929390 -00001 MTA (Finalizer) 12 8 4 2c38 000001C39D9062C0 302b220 Preemptive 000001C3A200CEB8:000001C3A200E658 000001c39d929390 -00001 MTA (Threadpool Worker) 13 9 5 1ce4 000001C39F49B930 302b220 Preemptive 000001C3A200EA58:000001C3A2010678 000001c39d929390 -00001 MTA (Threadpool Worker) 14 10 6 3eb0 00000204344A3A80 302b220 Preemptive 000001C3A20151F0:000001C3A2017030 000001c39d929390 -00001 MTA (Threadpool Worker) 15 11 7 3c54 00000204344A69E0 102b220 Preemptive 000001C3A201F0C0:000001C3A2020FD0 000001c39d929390 -00001 MTA (Threadpool Worker) 16 XXXX 8 0 00000204344A61A0 1039820 Preemptive 0000000000000000:0000000000000000 000001c39d929390 -00001 Ukn (Threadpool Worker)
这里还是有一点区别的,【!t】命令还输出了一个死亡的线程,就是 DBG 值:XXXX 的线程,这个线程在【!EEStack】命令是没有输出的。
-short 这个命令开关就是默认值,加上和不加都是一个效果。也就是说【!EEStack】和【!EEStack -short】命令的输出是一样的。
如果我们想输出所有线程的托管线程的调用栈,可以使用【!EEStack -ee|EE】命令。
1 0:000> !EEStack -ee 2 --------------------------------------------- 3 Thread 0 4 Current frame: 5 Child-SP RetAddr Caller, Callee 6 000000AFC917E980 00007ff81e086370 (MethodDesc 00007ff7bf674ed0 + 0x10 System.Runtime.InteropServices.Marshal.SetLastSystemError(Int32)) 7 000000AFC917E9B0 00007ff891727893 (MethodDesc 00007ff7bf64e350 + 0x83 Interop+Kernel32.ReadConsoleInput(IntPtr, INPUT_RECORD ByRef, Int32, Int32 ByRef)) 8 000000AFC917EA08 00007ff89172787a (MethodDesc 00007ff7bf64e350 + 0x6a Interop+Kernel32.ReadConsoleInput(IntPtr, INPUT_RECORD ByRef, Int32, Int32 ByRef)) 9 000000AFC917EAA0 00007ff89172aa0a (MethodDesc 00007ff7bf64b590 + 0xaa System.ConsolePal.ReadKey(Boolean)) 10 000000AFC917EAD0 00007ff7bf591ba9 (MethodDesc 00007ff7bf6400d8 + 0x1b9 ExampleCore_3_1_9.Program.Test(Int32)) 11 000000AFC917EB60 00007ff7bf5919d4 (MethodDesc 00007ff7bf6400c0 + 0xa4 ExampleCore_3_1_9.Program.Main(System.String[])) 12 000000AFC917EF50 00007ff81df31ae3 (MethodDesc 00007ff7bf61f460 + 0xa3 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)) 13 000000AFC917F050 00007ff81df31af0 (MethodDesc 00007ff7bf61f460 + 0xb0 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)) 14 --------------------------------------------- 15 Thread 6 16 Current frame: 17 Child-SP RetAddr Caller, Callee 18 --------------------------------------------- 19 Thread 8 20 Current frame: 21 Child-SP RetAddr Caller, Callee 22 000000AFC9D7EAE0 00007ff81e08634c (MethodDesc 00007ff7bf674eb8 + 0x1c System.Runtime.InteropServices.Marshal.GetLastSystemError()) 23 000000AFC9D7EB10 00007ff891727da7 (MethodDesc 00007ff7bf64e460 + 0xa7 Interop+Kernel32.WriteFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 24 000000AFC9D7EB70 00007ff891727d6b (MethodDesc 00007ff7bf64e460 + 0x6b Interop+Kernel32.WriteFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 25 000000AFC9D7EC00 00007ff89172ca7f (MethodDesc 00007ff7bf672f68 + 0x4f System.ConsolePal+WindowsConsoleStream.WriteFileNative(IntPtr, System.ReadOnlySpan`1, Boolean)) 26 000000AFC9D7EC60 00007ff89172c90b (MethodDesc 00007ff7bf672f30 + 0x1b System.ConsolePal+WindowsConsoleStream.Write(System.ReadOnlySpan`1)) 27 000000AFC9D7EC90 00007ff81e10c6be (MethodDesc 00007ff7bf678cf8 + 0x17e System.IO.StreamWriter.Flush(Boolean, Boolean)) 28 000000AFC9D7F1D0 00007ff891729bba (MethodDesc 00007ff7bf64a6f8 + 0x6a System.Console.g__EnsureInitialized|14_0()) 29 000000AFC9D7F1E0 00007ff891730042 (MethodDesc 00007ff7bf6e23b8 + 0x22 System.IO.SyncTextReader.ReadLine()) 30 000000AFC9D7F200 00007ff891727fca (MethodDesc 00007ff7bf649cd8 + 0xa System.Console.get_In()) 31 000000AFC9D7F230 00007ff891729319 (MethodDesc 00007ff7bf64a350 + 0x19 System.Console.ReadLine()) 32 000000AFC9D7F260 00007ff7bf592600 (MethodDesc 00007ff7bf6400f0 + 0xc0 ExampleCore_3_1_9.Program.Run1()) 33 000000AFC9D7F2F0 00007ff81e026502 (MethodDesc 00007ff7bf69e3f0 + 0x42 System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)) 34 000000AFC9D7F340 00007ff81e040668 (MethodDesc 00007ff7bf67aa10 + 0x98 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)) 35 000000AFC9D7F3E0 00007ff81e02f400 (MethodDesc 00007ff7bf69f738 + 0x210 System.Threading.ThreadPoolWorkQueue.Dispatch()) 36 000000AFC9D7F470 00007ff81e03c1d3 (MethodDesc 00007ff7bf6c4640 + 0x173 System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()) 37 --------------------------------------------- 38 Thread 9 39 Current frame: 40 Child-SP RetAddr Caller, Callee 41 000000AFC8CEF310 00007ff81e020bd4 (MethodDesc 00007ff7bf6c86c8 + 0x64 System.Threading.WaitHandle.WaitOneNoCheck(Int32)) 42 000000AFC8CEF370 00007ff81e038f36 (MethodDesc 00007ff7bf6c5268 + 0x106 System.Threading.PortableThreadPool+GateThread.GateThreadStart()) 43 --------------------------------------------- 44 Thread 10 45 Current frame: 46 Child-SP RetAddr Caller, Callee 47 000000AFC9EFEF80 00007ff81e08634c (MethodDesc 00007ff7bf674eb8 + 0x1c System.Runtime.InteropServices.Marshal.GetLastSystemError()) 48 000000AFC9EFEFB0 00007ff891727da7 (MethodDesc 00007ff7bf64e460 + 0xa7 Interop+Kernel32.WriteFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 49 000000AFC9EFF670 00007ff891729bba (MethodDesc 00007ff7bf64a6f8 + 0x6a System.Console.g__EnsureInitialized|14_0()) 50 000000AFC9EFF680 00007ff891730042 (MethodDesc 00007ff7bf6e23b8 + 0x22 System.IO.SyncTextReader.ReadLine()) 51 000000AFC9EFF6D0 00007ff891729319 (MethodDesc 00007ff7bf64a350 + 0x19 System.Console.ReadLine()) 52 000000AFC9EFF700 00007ff7bf5924a0 (MethodDesc 00007ff7bf640108 + 0xc0 ExampleCore_3_1_9.Program.Run2()) 53 000000AFC9EFF790 00007ff81e026502 (MethodDesc 00007ff7bf69e3f0 + 0x42 System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)) 54 000000AFC9EFF7E0 00007ff81e040668 (MethodDesc 00007ff7bf67aa10 + 0x98 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)) 55 000000AFC9EFF880 00007ff81e02f400 (MethodDesc 00007ff7bf69f738 + 0x210 System.Threading.ThreadPoolWorkQueue.Dispatch()) 56 000000AFC9EFF910 00007ff81e03c1d3 (MethodDesc 00007ff7bf6c4640 + 0x173 System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()) 57 --------------------------------------------- 58 Thread 11 59 Current frame: 60 Child-SP RetAddr Caller, Callee 61 000000AFCA07F2E0 00007ff891727704 (MethodDesc 00007ff7bf64e320 + 0x84 Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 62 000000AFCA07F340 00007ff8917276eb (MethodDesc 00007ff7bf64e320 + 0x6b Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)) 63 000000AFCA07F3D0 00007ff89172c9c0 (MethodDesc 00007ff7bf672f50 + 0x60 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean)) 64 000000AFCA07F430 00007ff89172c8bb (MethodDesc 00007ff7bf672f20 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1)) 65 000000AFCA07F470 00007ff89172fb84 (MethodDesc 00007ff7bf672bf8 + 0x74 System.IO.ConsoleStream.Read(Byte[], Int32, Int32)) 66 000000AFCA07F4E0 00007ff81e1089c1 (MethodDesc 00007ff7bf6e0308 + 0x41 System.IO.StreamReader.ReadBuffer()) 67 000000AFCA07F530 00007ff81e1090a4 (MethodDesc 00007ff7bf6e0330 + 0x64 System.IO.StreamReader.ReadLine()) 68 000000AFCA07F5E0 00007ff89173005d (MethodDesc 00007ff7bf6e23b8 + 0x3d System.IO.SyncTextReader.ReadLine()) 69 000000AFCA07F630 00007ff891729319 (MethodDesc 00007ff7bf64a350 + 0x19 System.Console.ReadLine()) 70 000000AFCA07F660 00007ff7bf592760 (MethodDesc 00007ff7bf640120 + 0xc0 ExampleCore_3_1_9.Program.Run3()) 71 000000AFCA07F6F0 00007ff81e026502 (MethodDesc 00007ff7bf69e3f0 + 0x42 System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)) 72 000000AFCA07F740 00007ff81e040668 (MethodDesc 00007ff7bf67aa10 + 0x98 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)) 73 000000AFCA07F7A0 00007ff81e040563 (MethodDesc 00007ff7bf67a9e0 + 0x43 System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread)) 74 000000AFCA07F7E0 00007ff81e02f400 (MethodDesc 00007ff7bf69f738 + 0x210 System.Threading.ThreadPoolWorkQueue.Dispatch()) 75 000000AFCA07F870 00007ff81e03c1d3 (MethodDesc 00007ff7bf6c4640 + 0x173 System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart())
3.1.5、COMState
A、基础知识
Com 提供了两种套间模型:单线程套间(Single Threaded Apartment,STA)和多线程套间(Multiple Threaded Apartment,MTA)。当一个线程使用一个 COM 对象时,它必须告诉 COM 子系统使用哪种套间模型。在 .NET 互用性层时具体的套间模型对线程的初始化也有区别,我们必须清楚。当我们在调试 COM 互用性问题时,找出线程的套间模型也是一个基本素质要求。
这里有两个命令可以使用,分别是【!ComState】和【!t|Threads】命令。效果是一样的,但是输出的内容会有区别。
B、眼见视为
调试源码:ExampleCore_3_1_9
调试任务:ComState 和 t 命令的使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_9binDebugnet8.0ExampleCore_3_1_9.exe】,打开调试器。
开启调试器,继续使用【g】命令运行调试器,直到调试器输出如图:
按组合键【ctrl+c】进入调试器的中断模式。我们需要切换到托管线程的上下文中,执行命令【~0s】。
1 0:003> ~0s 2 ntdll!NtDeviceIoControlFile+0x14: 3 00007ff8`f946d0c4 c3 ret
我们直接执行【!ComState】命令,看看是什么样子。
1 0:000> !comstate 2 ID TEB APT APTId CallerTID Context 3 0 3dec 0000001082382000 MTA 0 0 0000017EA7F427A8 4 1 2f00 0000001082384000 Ukn 5 2 38ec 0000001082386000 Ukn 6 3 18e0 0000001082388000 Ukn 7 4 3bd0 000000108238A000 Ukn 8 5 39c4 000000108238C000 Ukn 9 6 3c04 000000108238E000 MTA 0 0 0000017EA7F427A8 10 7 3f88 0000001082390000 MTA 0 0 0000017EA7F427A8 11 8 e1c 0000001082394000 MTA 0 0 0000017EA7F427A8 12 9 3f20 0000001082396000 MTA 0 0 0000017EA7F427A8 13 10 10f0 0000001082398000 MTA 0 0 0000017EA7F427A8 14 11 21b8 000000108239A000 MTA 0 0 0000017EA7F427A8 15 12 2664 000000108239C000 MTA 0 0 0000017EA7F427A8 16 13 1ff8 000000108239E000 Ukn 17 14 3f10 00000010823A0000 Ukn
ID 列表示线程的ID,TEB 列表示各个线程的线程环境块,通过【!teb】命令和 TEB 指针获取线程的扩展信息(例如线程最近发生的错误和栈大小的限制)。
我们执行命令【!teb 0000001082382000】命令查看 ID 是 3dec 线程的扩展信息。
1 0:000> !teb 0000001082382000 2 TEB at 0000001082382000 3 ExceptionList: 0000000000000000(没有异常,所以是0) 4 StackBase: 0000001082580000 5 StackLimit: 000000108256f000(栈大小的限制) 6 SubSystemTib: 0000000000000000 7 FiberData: 0000000000001e00 8 ArbitraryUserPointer: 0000000000000000 9 Self: 0000001082382000 10 EnvironmentPointer: 0000000000000000 11 ClientId: 000000000000219c . 0000000000003dec(线程ID) 12 RpcHandle: 0000000000000000 13 Tls Storage: 0000017ea7f2b460 14 PEB Address: 0000001082381000 15 LastErrorValue: 0 16 LastStatusValue: c000007c 17 Count Owned Locks: 0 18 HardErrorMode: 0
APT 列表示的就是线程是在哪一种套间模型下初始化的,MTA表示多线程套间,STA 表示单线程套间,Ukn 通常表示线程此时还没有初始化 COM 。各个列就简介到此。我们在看看【!t】命令,也可以找到线程是哪种套间模型。
1 0:000> !t 2 ThreadCount: 8 3 UnstartedThread: 0 4 BackgroundThread: 7 5 PendingThread: 0 6 DeadThread: 0 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3dec 0000017EA7F32970 2a020 Preemptive 0000017EAC40C0A8:0000017EAC40C638 0000017EA7F21050 -00001 MTA 11 6 2 3c04 000001BF3E4FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000017EA7F21050 -00001 MTA (Finalizer) 12 7 3 3f88 0000017EA7F3E290 2b220 Preemptive 0000000000000000:0000000000000000 0000017EA7F21050 -00001 MTA 13 8 4 e1c 0000017EA7F4C4D0 102b220 Preemptive 0000017EAC40D388:0000017EAC40E658 0000017EA7F21050 -00001 MTA (Threadpool Worker) 14 9 5 3f20 0000017EA7F4FAC0 302b220 Preemptive 0000017EAC40EA58:0000017EAC410678 0000017EA7F21050 -00001 MTA (Threadpool Worker) 15 10 6 10f0 000001BF3E91ACD0 102b220 Preemptive 0000017EAC41E180:0000017EAC41F060 0000017EA7F21050 -00001 MTA (Threadpool Worker) 16 11 7 21b8 000001BF3E91E5B0 302b220 Preemptive 0000017EAC411848:0000017EAC412FF0 0000017EA7F21050 -00001 MTA (Threadpool Worker) 17 12 8 2664 000001BF3E91BDE0 102b220 Preemptive 0000017EAC413330:0000017EAC415010 0000017EA7F21050 -00001 MTA (Threadpool Worker)
红色标注的 Apt 列就是说明线程是属于哪种套件模型。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—【Launch executable】,加载我们的项目文件:ExampleCore_3_1_9.exe,进入到调试器。继续使用【g】命令运行调试器,我们的控制台程序输出如下:
此时,我们的调试器也处于卡住的状态,点击【break】按钮,进入调试器的中断模式,开始我们的调试了。
我们直接执行【!ComState】命令,看结果。
1 0:012> !comstate 2 ID TEB APT APTId CallerTID Context 3 0 3d50 0000005FFA6A4000 MTA 0 0 000002204F75FF18 4 1 1a3c 0000005FFA6A6000 Ukn 5 2 25a4 0000005FFA6A8000 Ukn 6 3 1344 0000005FFA6AA000 Ukn 7 4 1d68 0000005FFA6AC000 Ukn 8 5 1510 0000005FFA6AE000 MTA 0 0 000002204F75FF18 9 6 1e74 0000005FFA6B0000 MTA 0 0 000002204F75FF18 10 7 3344 0000005FFA6B4000 MTA 0 0 000002204F75FF18 11 8 33b4 0000005FFA6B6000 MTA 0 0 000002204F75FF18 12 9 373c 0000005FFA6B8000 MTA 0 0 000002204F75FF18 13 10 3e60 0000005FFA6BA000 MTA 0 0 000002204F75FF18 14 11 1ffc 0000005FFA6BC000 MTA 0 0 000002204F75FF18 15 12 1d04 0000005FFA6BE000 Ukn
ID 列就是线程 ID,TEB 列就是线程的线程环境块,可以使用【!teb】命令和 TEB 指针获取线程的扩展信息。APT 列就是指出线程是在哪一种套间模型中初始化的。MTA表示拖线程套间,STA 表示单线程套间,Ukn 表示线程还没有初始化 COM。
接下来,我们测试一下【!teb】命令。我们查看 ID=3d50 线程的扩展信息。就可以执行命令【!teb 0000005FFA6A4000】,直接看结果。
1 0:012> !teb 0000005FFA6A4000 2 TEB at 0000005ffa6a4000 3 ExceptionList: 0000000000000000 4 StackBase: 0000005ffa5b0000 5 StackLimit: 0000005ffa59f000 6 SubSystemTib: 0000000000000000 7 FiberData: 0000000000001e00 8 ArbitraryUserPointer: 0000000000000000 9 Self: 0000005ffa6a4000 10 EnvironmentPointer: 0000000000000000 11 ClientId: 00000000000033c4 . 0000000000003d50 12 RpcHandle: 0000000000000000 13 Tls Storage: 000002204f7425c0 14 PEB Address: 0000005ffa6a3000 15 LastErrorValue: 0 16 LastStatusValue: c000007c 17 Count Owned Locks: 0 18 HardErrorMode: 0
如果想查看线程属于哪种套间模型,也可以使用【!t】或者【!Threads】命令,输出结果中的 Apt 列就是表示哪种套间模型的。
1 0:012> !threads 2 ThreadCount: 8 3 UnstartedThread: 0 4 BackgroundThread: 7 5 PendingThread: 0 6 DeadThread: 0 7 Hosted Runtime: no 8 Lock 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 10 0 1 3d50 000002204F7499A0 2a020 Preemptive 0000022053C0C0A8:0000022053C0C638 000002204f788fa0 -00001 MTA 11 5 2 1510 000002204F7F6C40 2b220 Preemptive 0000000000000000:0000000000000000 000002204f788fa0 -00001 MTA (Finalizer) 12 6 3 1e74 000002204F756070 2b220 Preemptive 0000000000000000:0000000000000000 000002204f788fa0 -00001 MTA 13 7 4 3344 000002204F768D20 302b220 Preemptive 0000022053C0D388:0000022053C0E658 000002204f788fa0 -00001 MTA (Threadpool Worker) 14 8 5 33b4 00000260E6217740 302b220 Preemptive 0000022053C0EA58:0000022053C10678 000002204f788fa0 -00001 MTA (Threadpool Worker) 15 9 6 373c 00000220512C6440 302b220 Preemptive 0000022053C10EE8:0000022053C10FD0 000002204f788fa0 -00001 MTA (Threadpool Worker) 16 10 7 3e60 00000220512C8870 102b220 Preemptive 0000022053C1D0A0:0000022053C1F060 000002204f788fa0 -00001 MTA (Threadpool Worker) 17 11 8 1ffc 00000260E621DFE0 102b220 Preemptive 0000022053C13330:0000022053C15010 000002204f788fa0 -00001 MTA (Threadpool Worker)
标红的就是 Apt 列,很简单,就不多说了。
3.2、代码审查
3.2.1、反汇编代码
A、基础知识
在我们调试非托管代码的时候,可以使用【u|U】命令,将代码字节流转换为汇编指令。在托管代码调试中,如果我们知道代码的地址,也可以使用【u】命令对代码进行反汇编。这个命令也有弊端,它对 CLR了解不深,所以不能给我太多的信息。SOS 调试器扩展给我们一个更好的用【!u|U】命令,u 字符不区分大小写,由于 SOS 更了解 CLR 内部细节,因此【!u】命令会提供可读性更强的信息。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:u 和 !u 命令的使用
说明一下,在这里用其他的程序都是可以的,只是查看汇编代码,可以查看非托管函数的、也可以查看托管函数的汇编代码。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】,打开调试器。
我们刚进入调试器,可以直接执行【k】命令。
1 0:000> k 2 Child-SP RetAddr Call Site 3 0000004f`6e17f150 00007ff8`f94a3bba ntdll!LdrpDoDebuggerBreak+0x30 4 0000004f`6e17f190 00007ff8`f9444dbb ntdll!LdrpInitializeProcess+0x1eda 5 0000004f`6e17f5b0 00007ff8`f9444c43 ntdll!LdrpInitialize+0x15f 6 0000004f`6e17f650 00007ff8`f9444bee ntdll!LdrpInitialize+0x3b 7 0000004f`6e17f680 00000000`00000000 ntdll!LdrInitializeThunk+0xe
ntdll!LdrpDoDebuggerBreak 我们看看这个方法汇编代码,直接使用【u】命令。
1 0:000> u 00007ff8`f94a3bba 2 ntdll!LdrpInitializeProcess+0x1eda: 3 00007ff8`f94a3bba e8cd9df8ff call ntdll!LdrpDropLastInProgressCount (00007ff8`f942d98c) 4 00007ff8`f94a3bbf 488b052a840900 mov rax,qword ptr [ntdll!Kernel32ThreadInitThunkFunction (00007ff8`f953bff0)] 5 00007ff8`f94a3bc6 4885c0 test rax,rax 6 00007ff8`f94a3bc9 7437 je ntdll!LdrpInitializeProcess+0x1f22 (00007ff8`f94a3c02) 7 00007ff8`f94a3bcb 4533c0 xor r8d,r8d 8 00007ff8`f94a3bce 33d2 xor edx,edx 9 00007ff8`f94a3bd0 8d4a01 lea ecx,[rdx+1] 10 00007ff8`f94a3bd3 ff1527140b00 call qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff8`f9555000)]
这是非托管方法汇编代码的编译过程,我们看看托管代码。继续【g】运行调试器。直到调试器输出如下:
按【ctrl+c】组合键键入调试器中断调试模式。我们执行命令【~0s】命令,切换到托管线程。
1 0:001> ~0s 2 ntdll!LdrpDispatchUserCallTarget+0x12: 3 00007ff8`f945c8d2 4c8bd0 mov r10,rax
现在,我们要查看我们的 Program.Main() 方法的汇编,分别执行【u】和【!u】命令,看看区别吧。
1 0:000> !clrstack 2 OS Thread Id: 0x3e6c (0) 3 Child SP IP Call Site 4 0000004F6E17E4E8 00007ff8f945c8d2 [PrestubMethodFrame: 0000004f6e17e4e8] System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) 5 0000004F6E17E6C0 00007FF813B38A51 System.IO.StreamReader.ReadBuffer() 6 0000004F6E17E710 00007FF813B390A4 System.IO.StreamReader.ReadLine() 7 0000004F6E17E7C0 00007FF8E5E1005D System.IO.SyncTextReader.ReadLine() 8 0000004F6E17E810 00007FF8E5E09319 System.Console.ReadLine() 9 0000004F6E17E840 00007FF7C61A19DB ExampleCore_3_1_10.Program.Main(System.String[])
ExampleCore_3_1_10.Program.Main 方法的地址是00007FF7C61A19DB ,有了地址,我们就可以执行命令。
1 0:000> u 00007FF7C61A19DB 2 00007ff7`c61a19db 488945b8 mov qword ptr [rbp-48h],rax 3 00007ff7`c61a19df 90 nop 4 00007ff7`c61a19e0 90 nop 5 00007ff7`c61a19e1 4883c470 add rsp,70h 6 00007ff7`c61a19e5 5d pop rbp 7 00007ff7`c61a19e6 c3 ret 8 00007ff7`c61a19e7 ba19050200 mov edx,20519h 9 00007ff7`c61a19ec 05d2015000 add eax,5001D2h 10 11 0:000> !u 00007FF7C61A19DB 12 Normal JIT generated code 13 ExampleCore_3_1_10.Program.Main(System.String[]) 14 ilAddr is 0000016EA0AA2050 pImport is 000001C10A940290 15 Begin 00007FF7C61A1930, size b7 16 00007ff7`c61a1930 55 push rbp 17 00007ff7`c61a1931 4883ec70 sub rsp,70h 18 00007ff7`c6服务器托管网1a1935 488d6c2470 lea rbp,[rsp+70h] 19 00007ff7`c61a193a 33c0 xor eax,eax 20 00007ff7`c61a193c 488945b8 mov qword ptr [rbp-48h],rax 21 00007ff7`c61a1940 c5d857e4 vxorps xmm4,xmm4,xmm4 22 00007ff7`c61a1944 c5f97f65c0 vmovdqa xmmword ptr [rbp-40h],xmm4 23 00007ff7`c61a1949 c5f97f65d0 vmovdqa xmmword ptr [rbp-30h],xmm4 24 00007ff7`c61a194e c5f97f65e0 vmovdqa xmmword ptr [rbp-20h],xmm4 25 00007ff7`c61a1953 c5f97f65f0 vmovdqa xmmword ptr [rbp-10h],xmm4 26 00007ff7`c61a1958 48894d10 mov qword ptr [rbp+10h],rcx 27 00007ff7`c61a195c 833dc5c9080000 cmp dword ptr [00007ff7`c622e328],0 28 00007ff7`c61a1963 7405 je 00007ff7`c61a196a 29 00007ff7`c61a1965 e816c1c95f call coreclr!JIT_DbgIsJustMyCode (00007ff8`25e3da80) 30 00007ff7`c61a196a 90 nop 31 00007ff7`c61a196b b90a000000 mov ecx,0Ah 32 00007ff7`c61a1970 ba0b000000 mov edx,0Bh 33 00007ff7`c61a1975 ff154d520a00 call qword ptr [00007ff7`c6246bc8] (ExampleCore_3_1_10.Program.Sum(Int32, Int32), mdToken: 0000000006000002) 34 00007ff7`c61a197b 8945cc mov dword ptr [rbp-34h],eax 35 00007ff7`c61a197e 8b4dcc mov ecx,dword ptr [rbp-34h] 36 00007ff7`c61a1981 894dfc mov dword ptr [rbp-4],ecx 37 00007ff7`c61a1984 488d4dd0 lea rcx,[rbp-30h] 38 00007ff7`c61a1988 ba04000000 mov edx,4 39 00007ff7`c61a198d 41b801000000 mov r8d,1 40 00007ff7`c61a1993 ff15173d0d00 call qword ptr [00007ff7`c62756b0] 41 00007ff7`c61a1999 488d4dd0 lea rcx,[rbp-30h] 42 00007ff7`c61a199d 48baa0047835af010000 mov rdx,1AF357804A0h ("sum=") 43 00007ff7`c61a19a7 ff15db3d0d00 call qword ptr [00007ff7`c6275788] 44 00007ff7`c61a19ad 90 nop 45 00007ff7`c61a19ae 488d4dd0 lea rcx,[rbp-30h] 46 00007ff7`c61a19b2 8b55fc mov edx,dword ptr [rbp-4] 47 00007ff7`c61a19b5 ff15ad3f0d00 call qword ptr [00007ff7`c6275968] 48 00007ff7`c61a19bb 90 nop 49 00007ff7`c61a19bc 488d4dd0 lea rcx,[rbp-30h] 50 00007ff7`c61a19c0 ff157a3d0d00 call qword ptr [00007ff7`c6275740] 51 00007ff7`c61a19c6 488945c0 mov qword ptr [rbp-40h],rax 52 00007ff7`c61a19ca 488b4dc0 mov rcx,qword ptr [rbp-40h] 53 00007ff7`c61a19ce ff1574470d00 call qword ptr [00007ff7`c6276148] 54 00007ff7`c61a19d4 90 nop 55 00007ff7`c61a19d5 ff151d460d00 call qword ptr [00007ff7`c6275ff8] 56 >>> 00007ff7`c61a19db 488945b8 mov qword ptr [rbp-48h],rax 57 00007ff7`c61a19df 90 nop 58 00007ff7`c61a19e0 90 nop 59 00007ff7`c61a19e1 4883c470 add rsp,70h 60 00007ff7`c61a19e5 5d pop rbp 61 00007ff7`c61a19e6 c3 ret 62 0:000>
我们看到了【u】和【!u】命令的区别了,很简单,就不多说了。
2)、Windbg Preview 调试
我们编译项目,打开 Windbg,点击【文件】—-》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。
这个时候,其实,我们不需要执行我们的程序,就可以直接使用【k】命令,查看一下非托管的调用栈,然后从里面随便找一个函数,看看它的汇编代码。
1 0:000> k 2 # Child-SP RetAddr Call Site 3 00 000000f8`6fb7efa0 00007ff8`f94a3bba ntdll!LdrpDoDebuggerBreak+0x30 4 01 000000f8`6fb7efe0 00007ff8`f9444dbb ntdll!LdrpInitializeProcess+0x1eda 5 02 000000f8`6fb7f400 00007ff8`f9444c43 ntdll!LdrpInitialize+0x15f 6 03 000000f8`6fb7f4a0 00007ff8`f9444bee ntdll!LdrpInitialize+0x3b 7 04 000000f8`6fb7f4d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe
00007ff8`f94a3bba 我们查看这个地址的汇编代码,可以使用【u 00007ff8`f94a3bba】指令。
1 0:000> u 00007ff8`f94a3bba 2 ntdll!LdrpInitializeProcess+0x1eda: 3 00007ff8`f94a3bba e8cd9df8ff call ntdll!LdrpDropLastInProgressCount (00007ff8`f942d98c) 4 00007ff8`f94a3bbf 488b052a840900 mov rax,qword ptr [ntdll!Kernel32ThreadInitThunkFunction (00007ff8`f953bff0)] 5 00007ff8`f94a3bc6 4885c0 test rax,rax 6 00007ff8`f94a3bc9 7437 je ntdll!LdrpInitializeProcess+0x1f22 (00007ff8`f94a3c02) 7 00007ff8`f94a3bcb 4533c0 xor r8d,r8d 8 00007ff8`f94a3bce 33d2 xor edx,edx 9 00007ff8`f94a3bd0 8d4a01 lea ecx,[rdx+1] 10 00007ff8`f94a3bd3 ff1527140b00 call qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff8`f9555000)]
我们也可以使用【g】命令运行我们的调试器,控制台输出【sum=21】,调试器卡住状态,点击【break】按钮进入调试状态。切换到托管主线程,执行【~0s】命令。
使用【k】命令,输出调用栈。
1 0:000> k 2 # Child-SP RetAddr Call Site 3 00 000000f8`6fb7e288 00007ff8`f6935783 ntdll!NtReadFile+0x14 4 01 000000f8`6fb7e290 00007ff8`d3b67704 KERNELBASE!ReadFile+0x73 5 02 000000f8`6fb7e310 00007ff8`d3b6c9c0 System_Console!Interop.Kernel32.ReadFile+0x84 [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 6 03 000000f8`6fb7e400 00007ff8`d3b6c8bb System_Console!System.ConsolePal.WindowsConsoleStream.ReadFileNative+0x60 [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 7 04 000000f8`6fb7e460 00007ff8`d3b6fb84 System_Console!System.ConsolePal.WindowsConsoleStream.Read+0x2b [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 8 05 000000f8`6fb7e4a0 00007ff8`0fc189c1 System_Console!System.IO.ConsoleStream.Read+0x74 [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 9 06 000000f8`6fb7e510 00007ff8`0fc190a4 System_Private_CoreLib!System.IO.StreamReader.ReadBuffer+0x41 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 10 07 000000f8`6fb7e560 00007ff8`d3b7005d System_Private_CoreLib!System.IO.StreamReader.ReadLine+0x64 [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 11 08 000000f8`6fb7e610 00007ff8`d3b69319 System_Console!System.IO.SyncTextReader.ReadLine+0x3d [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 12 09 000000f8`6fb7e660 00007ff7`b10b19db System_Console!System.Console.ReadLine+0x19 [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 13 0a 000000f8`6fb7e690 00007ff8`10c0a1a3 ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main+0xab [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9] 14 0b 000000f8`6fb7e710 00007ff8`10b914c9 coreclr!CallDescrWorkerInternal+0x83 [D:a_work1ssrccoreclrvmamd64CallDescrWorkerAMD64.asm @ 100] 15 0c (Inline Function) --------`-------- coreclr!CallDescrWorkerWithHandler+0x56 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 67] 16 0d 000000f8`6fb7e750 00007ff8`10ab75ac coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 570] 17 0e (Inline Function) --------`-------- coreclr!MethodDescCallSite::Call+0xb [D:a_work1ssrccoreclrvmcallhelpers.h @ 458] 18 0f 000000f8`6fb7e890 00007ff8`10ab6f7a coreclr!RunMainInternal+0x11c [D:a_work1ssrccoreclrvmassembly.cpp @ 1304] 19 10 000000f8`6fb7e9b0 00007ff8`10ab6b17 coreclr!RunMain+0xd2 [D:a_work1ssrccoreclrvmassembly.cpp @ 1375] 20 11 000000f8`6fb7ea60 00007ff8`10ab7321 coreclr!Assembly::ExecuteMainMethod+0x1bf [D:a_work1ssrccoreclrvmassembly.cpp @ 1504] 21 12 000000f8`6fb7ed30 00007ff8`10bc7768 coreclr!CorHost2::ExecuteAssembly+0x281 [D:a_work1ssrccoreclrvmcorhost.cpp @ 349] 22 13 000000f8`6fb7eea0 00007ff8`d3be2c36 coreclr!coreclr_execute_assembly+0xd8 [D:a_work1ssrccoreclrdllsmscoreeexports.cpp @ 504] 23 14 (Inline Function) --------`-------- hostpolicy!coreclr_t::execute_assembly+0x2a [D:a_work1ssrcnativecorehosthostpolicycoreclr.cpp @ 109] 24 15 000000f8`6fb7ef40 00007ff8`d3be2f1c hostpolicy!run_app_for_context+0x596 [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 256] 25 16 000000f8`6fb7f0d0 00007ff8`d3be385a hostpolicy!run_app+0x3c [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 285] 26 17 000000f8`6fb7f110 00007ff8`d3c3b5c9 hostpolicy!corehost_main+0x15a [D:a_work1ssrcnativecorehosthostpolicyhostpolicy.cpp @ 426] 27 18 000000f8`6fb7f210 00007ff8`d3c3e066 hostfxr!execute_app+0x2e9 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 145] 28 19 000000f8`6fb7f310 00007ff8`d3c402ec hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 532] 29 1a 000000f8`6fb7f400 00007ff8`d3c3e644 hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 1007] 30 1b 000000f8`6fb7f4b0 00007ff8`d3c385a0 hostfxr!fx_muxer_t::execute+0x494 [D:a_work1ssrcnativecorehostfxrfx_muxer.cpp @ 578] 31 1c 000000f8`6fb7f5f0 00007ff7`bcdef998 hostfxr!hostfxr_main_startupinfo+0xa0 [D:a_work1ssrcnativecorehostfxrhostfxr.cpp @ 62] 32 1d 000000f8`6fb7f6f0 00007ff7`bcdefda6 apphost!exe_start+0x878 [D:a_work1ssrcnativecorehostcorehost.cpp @ 240] 33 1e 000000f8`6fb7f8c0 00007ff7`bcdf12e8 apphost!wmain+0x146 [D:a_work1ssrcnativecorehostcorehost.cpp @ 311] 34 1f (Inline Function) --------`-------- apphost!invoke_main+0x22 [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 90] 35 20 000000f8`6fb7f930 00007ff8`f9287344 apphost!__scrt_common_main_seh+0x10c [D:a_work1ssrcvctoolscrtvcstartupsrcstartupexe_common.inl @ 288] 36 21 000000f8`6fb7f970 00007ff8`f94226b1 KERNEL32!BaseThreadInitThunk+0x14 37 22 000000f8`6fb7f9a0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
coreclr!CallDescrWorkerInternal 我们可以看看这个方法的反汇编。
1 0:000> u 00007ff8`10b914c9 2 coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:a_work1ssrccoreclrvmcallhelpers.cpp @ 570]: 3 00007ff8`10b914c9 488b7520 mov rsi,qword ptr [rbp+20h] 4 00007ff8`10b914cd 4885f6 test rsi,rsi 5 00007ff8`10b914d0 751d jne coreclr!MethodDescCallSite::CallTargetWorker+0x2c7 (00007ff8`10b914ef) 6 00007ff8`10b914d2 488b4d58 mov rcx,qword ptr [rbp+58h] 7 00007ff8`10b914d6 4833cd xor rcx,rbp 8 00007ff8`10b914d9 e832a70700 call coreclr!__security_check_cookie (00007ff8`10c0bc10) 9 00007ff8`10b914de 488d6568 lea rsp,[rbp+68h] 10 00007ff8`10b914e2 415f pop r15
接下来,我们如果想查看 Program.Main() 方法的汇编源码,我们先执行【!clrstack】命令。
1 0:000> !clrstack 2 OS Thread Id: 0x3ad8 (0) 3 Child SP IP Call Site 4 0000000B6B37E3A0 00007ff8f946d0a4 [InlinedCallFrame: 0000000b6b37e3a0] 5 0000000B6B37E3A0 00007ff8916176eb [InlinedCallFrame: 0000000b6b37e3a0] 6 0000000B6B37E370 00007ff8916176eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 7 0000000B6B37E460 00007ff89161c9c0 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 8 0000000B6B37E4C0 00007ff89161c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 9 0000000B6B37E500 00007ff89161fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 10 0000000B6B37E570 00007ff8124389c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 11 0000000B6B37E5C0 00007ff8124390a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 12 0000000B6B37E670 00007ff89162005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 13 0000000B6B37E6C0 00007ff891619319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 14 0000000B6B37E6F0 00007ff7b38e19db ExampleCore_3_1_10.Program.Main(System.String[]) [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9]
ExampleCore_3_1_10.Program.Main 方法的地址是00007ff7b38e19db ,针对这个地址,执行命令【u 00007ff7b38e19db】命令。
1 0:000> u 00007ff7b38e19db 2 ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main+0xab [E:Visual Studio 2022...ExampleCore_3_1_10Program.cs @ 9]: 3 00007ff7`b38e19db 488945b8 mov qword ptr [rbp-48h],rax 4 00007ff7`b38e19df 90 nop 5 00007ff7`b38e19e0 90 nop 6 00007ff7`b38e19e1 4883c470 add rsp,70h 7 00007ff7`b38e19e5 5d pop rbp 8 00007ff7`b38e19e6 c3 ret 9 00007ff7`b38e19e7 ba19050200 mov edx,20519h 10 00007ff7`b38e19ec 05d2015000 add eax,5001D2h
【u】命令给出的内容太少,我们可以使用【!u|U】命令,获取更多的信息。
1 0:000> !u 00007ff7b38e19db 2 Normal JIT generated code 3 ExampleCore_3_1_10.Program.Main(System.String[]) 4 ilAddr is 0000020334F32050 pImport is 000001E665DF3B40 5 Begin 00007FF7B38E1930, size b7 6 7 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 6: 8 00007ff7`b38e1930 55 push rbp 9 00007ff7`b38e1931 4883ec70 sub rsp,70h 10 00007ff7`b38e1935 488d6c2470 lea rbp,[rsp+70h] 11 00007ff7`b38e193a 33c0 xor eax,eax 12 00007ff7`b38e193c 488945b8 mov qword ptr [rbp-48h],rax 13 00007ff7`b38e1940 c5d857e4 vxorps xmm4,xmm4,xmm4 14 00007ff7`b38e1944 c5f97f65c0 vmovdqa xmmword ptr [rbp-40h],xmm4 15 00007ff7`b38e1949 c5f97f65d0 vmovdqa xmmword ptr [rbp-30h],xmm4 16 00007ff7`b38e194e c5f97f65e0 vmovdqa xmmword ptr [rbp-20h],xmm4 17 00007ff7`b38e1953 c5f97f65f0 vmovdqa xmmword ptr [rbp-10h],xmm4 18 00007ff7`b38e1958 48894d10 mov qword ptr [rbp+10h],rcx 19 00007ff7`b38e195c 833dc5c9080000 cmp dword ptr [00007ff7`b396e328],0 20 00007ff7`b38e1963 7405 je ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main+0x3a (00007ff7`b38e196a) 21 00007ff7`b38e1965 e816c1c65f call coreclr!JIT_DbgIsJustMyCode (00007ff8`1354da80) 22 00007ff7`b38e196a 90 nop 23 24 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 7: 25 00007ff7`b38e196b b90a000000 mov ecx,0Ah 26 00007ff7`b38e1970 ba0b000000 mov edx,0Bh 27 00007ff7`b38e1975 ff154d520a00 call qword ptr [00007ff7`b3986bc8] (ExampleCore_3_1_10.Program.Sum(Int32, Int32), mdToken: 0000000006000002) 28 00007ff7`b38e197b 8945cc mov dword ptr [rbp-34h],eax 29 00007ff7`b38e197e 8b4dcc mov ecx,dword ptr [rbp-34h] 30 00007ff7`b38e1981 894dfc mov dword ptr [rbp-4],ecx 31 32 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 8: 33 00007ff7`b38e1984 488d4dd0 lea rcx,[rbp-30h] 34 00007ff7`b38e1988 ba04000000 mov edx,4 35 00007ff7`b38e198d 41b801000000 mov r8d,1 36 00007ff7`b38e1993 ff15173d0d00 call qword ptr [00007ff7`b39b56b0] 37 00007ff7`b38e1999 488d4dd0 lea rcx,[rbp-30h] 38 00007ff7`b38e199d 48baa004cdc943020000 mov rdx,243C9CD04A0h ("sum=") 39 00007ff7`b38e19a7 ff15db3d0d00 call qword ptr [00007ff7`b39b5788] 40 00007ff7`b38e19ad 90 nop 41 00007ff7`b38e19ae 488d4dd0 lea rcx,[rbp-30h] 42 00007ff7`b38e19b2 8b55fc mov edx,dword ptr [rbp-4] 43 00007ff7`b38e19b5 ff15ad3f0d00 call qword ptr [00007ff7`b39b5968] 44 00007ff7`b38e19bb 90 nop 45 00007ff7`b38e19bc 488d4dd0 lea rcx,[rbp-30h] 46 00007ff7`b38e19c0 ff157a3d0d00 call qword ptr [00007ff7`b39b5740] 47 00007ff7`b38e19c6 488945c0 mov qword ptr [rbp-40h],rax 48 00007ff7`b38e19ca 488b4dc0 mov rcx,qword ptr [rbp-40h] 49 00007ff7`b38e19ce ff1574470d00 call qword ptr [00007ff7`b39b6148] 50 00007ff7`b38e19d4 90 nop 51 52 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9: 53 00007ff7`b38e19d5 ff151d460d00 call qword ptr [00007ff7`b39b5ff8] 54 >>> 00007ff7`b38e19db 488945b8 mov qword ptr [rbp-48h],rax 55 00007ff7`b38e19df 90 nop 56 57 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 10: 58 00007ff7`b38e19e0 90 nop 59 00007ff7`b38e19e1 4883c470 add rsp,70h 60 00007ff7`b38e19e5 5d pop rbp 61 00007ff7`b38e19e6 c3 ret
3.2.2、从代码的地址上获取方法描述符
A、基础知识
非托管的【u】命令可以反汇编代码,但是这个命令无法解析托管代码,SOS调试器扩展的【!u】命令可以获取托管代码的更详细信息。如果我们能获取托管代码的地址,然后将这个地址转换为方法描述符(MD),然后就可以使用【!DumpMD】命令获取更详细的信息,【!IP2MD】这个命令就可以完成这个功能。
语法格式:!IP2MD ,这个命令不区分大小写。
>
如果想快速找出汇编代码位于哪个函数中,使用【!IP2MD】命令是很方便的。
这是一种方法,要想查找方法描述符,还有一个命令可以使用,它就是【!name2ee】,该命令的格式:Name2EEmodule name>!type or method name> 或者 Name2EE (name2ee) module name>type or method name>。此命令支持 module!
type> 的 Windows 调试器语法。 类型必须是完全限定的。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:IP2MD 命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】,打开调试器窗口。
继续使用【g】命令运行调试器,知道调试器输出如图:
按组合键【ctrl+c】进入调试器中断模式,由于我们是手动中断,当前线程是调试器的线程,我们需要切换到托管线程上,执行命令【~0s】。
如图:
使用命令【!clrstack】查看一下托管线程的调用栈,找到 Program.Main() 方法的 IP 地址。
1 0:000> !ClrStack 2 OS Thread Id: 0x14fc (0) 3 Child SP IP Call Site 4 000000586F7EDF98 00007ff83cf4de30 [ExternalMethodFrame: 000000586f7edf98] 5 000000586F7EE570 00007FF8F3BDF269 System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) 6 000000586F7EE600 00007FF83C618A51 System.IO.StreamReader.ReadBuffer() 7 000000586F7EE650 00007FF83C6190A4 System.IO.StreamReader.ReadLine() 8 000000586F7EE700 00007FF8F3BE005D System.IO.SyncTextReader.ReadLine() 9 000000586F7EE750 00007FF8F3BD9319 System.Console.ReadLine() 10 000000586F7EE780 00007FF7DD4719DB ExampleCore_3_1_10.Program.Main(System.String[])
ExampleCore_3_1_10.Program.Main 就是我们要找的方法,它的地址就是 IP 列的值:00007FF7DD4719DB ,有了地址,我们使用【!IP2MD 00007FF7DD4719DB】命令找到 Main() 方法的方法描述符。
1 0:000> !IP2MD 00007FF7DD4719DB 2 MethodDesc: 00007ff7dd5200c0 3 Method Name: ExampleCore_3_1_10.Program.Main(System.String[]) 4 Class: 00007ff7dd50fb20 5 MethodTable: 00007ff7dd520100 6 mdToken: 0000000006000001 7 Module: 00007ff7dd4fe0a0 8 IsJitted: yes 9 Current CodeAddr: 00007ff7dd471930 10 Version History: 11 ILCodeVersion: 0000000000000000 12 ReJIT ID: 0 13 IL Addr: 000001e1ebfd2050 14 CodeAddr: 00007ff7dd471930 (MinOptJitted) 15 NativeCodeVersion: 0000000000000000
很简单,我们就知道了 Main() 方法的方法描述符,就是MethodDesc: 00007ff7dd5200c0 。我们也可以使用【!Name2ee】命令通过名称查找制定方法的方法描述符,执行命令【!Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main】。
1 0:000> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ff7dd4fe0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ff7dd5200c0 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ff7dd471930
MethodDesc: 00007ff7dd5200c0 这就是我们找到的方法描述符,两种方法找到的结果是一样的,很简单,话不多说了。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出 sum=21 字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式,由于我们是手动中断,当前线程是调试器的线程,所以需要切换到托管线程,执行命令【~0s】。
1 0:001> ~0s 2 ntdll!NtReadFile+0x14: 3 00007ff9`1134d0a4 c3 ret
执行命令后,提示符就是 0:000>,说明切换到了。继续使用【!ClrStack】命令查看托管线程的调用栈。
1 0:000> !ClrStack 2 OS Thread Id: 0xfbc (0) 3 Child SP IP Call Site 4 000000A01F57E310 00007ff91134d0a4 [InlinedCallFrame: 000000a01f57e310] 5 000000A01F57E310 00007ff835a776eb [InlinedCallFrame: 000000a01f57e310] 6 000000A01F57E2E0 00007ff835a776eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 7 000000A01F57E3D0 00007ff835a7c9c0 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 8 000000A01F57E430 00007ff835a7c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 9 000000A01F57E470 00007ff835a7fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 10 000000A01F57E4E0 00007ff8360689c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 11 000000A01F57E530 00007ff8360690a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 12 000000A01F57E5E0 00007ff835a8005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 13 000000A01F57E630 00007ff835a79319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 14 000000A01F57E660 00007ff7d75119db ExampleCore_3_1_10.Program.Main(System.String[]) [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9]
ExampleCore_3_1_10.Program.Main 这就是我们的主方法,它的地址是00007ff7d75119db ,有了地址,我们就可以使用【!IP2MD】命令找到 Main 方法的方法描述符了。
执行命令【!IP2MD 00007ff7d75119db】。
1 0:000> !IP2MD 00007ff7d75119db 2 MethodDesc: 00007ff7d75c00c0 3 Method Name: ExampleCore_3_1_10.Program.Main(System.String[]) 4 Class: 00007ff7d75afb20 5 MethodTable: 00007ff7d75c0100 6 mdToken: 0000000006000001 7 Module: 00007ff7d759e0a0 8 IsJitted: yes 9 Current CodeAddr: 00007ff7d7511930 10 Version History: 11 ILCodeVersion: 0000000000000000 12 ReJIT ID: 0 13 IL Addr: 000001a4549d2050 14 CodeAddr: 00007ff7d7511930 (MinOptJitted) 15 NativeCodeVersion: 0000000000000000 16 Source file: E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9
MethodDesc: 00007ff7d75c00c0 红色标注的就是方法描述符的地址。
除了以上方法,我们也可以使用【!name2ee】命令达到同样的效果。
1 0:000> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ff7d759e0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ff7d75c00c0 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ff7d7511930
MethodDesc: 00007ff7d75c00c0 这也是找到方法描述符的一种方法。
3.2.3、显示中间语言指令
A、基础知识
.Net 代码由三种形态:机器代码、IL代码、C#代码,我们可以直接查看 IL 代码,当然,查看 IL 代码的方法也有很多种,SOS调试器扩展提供了一个叫 【!dumpil】 的命令用来将托管函数的汇编指令转成可读的 IL 代码,当然,我们也可以使用 ILSpy 或者 DnSpy 等反编译工具查看。我推荐 ILSpy 或者 DnSpy 工具,使用更方便,可读性更强。
这里我们主要介绍命令的用法,【!Dumpil】命令是以方法的方法描述符为参数,所以在使用【!dumpil】命令之前,我们必须找到方法的方法描述,前一节,我们说了,有两种方法可以找到方法的方法描述符,第一个种就是使用【!IP2MD】命令,第二种就是使用【!Name2EE】命令。有了方法描述符的地址,我们就可以直接使用【!dumpil】命令了。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!Dumpil】命令的使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】,打开调试器窗口。
继续使用【g】命令运行调试器,知道调试器输出如图:
按组合键【ctrl+c】进入调试器中断模式,由于我们是手动中断,当前线程是调试器的线程,我们需要切换到托管线程上,执行命令【~0s】。
如图:
使用命令【!clrstack】查看一下托管线程的调用栈,找到 Program.Main() 方法的 IP 地址。
1 0:000> !clrstack 2 OS Thread Id: 0x14fc (0) 3 Child SP IP Call Site 4 000000586F7EDF98 00007ff83cf4de30 [ExternalMethodFrame: 000000586f7edf98] 5 000000586F7EE570 00007FF8F3BDF269 System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) 6 000000586F7EE600 00007FF83C618A51 System.IO.StreamReader.ReadBuffer() 7 000000586F7EE650 00007FF83C6190A4 System.IO.StreamReader.ReadLine() 8 000000586F7EE700 00007FF8F3BE005D System.IO.SyncTextReader.ReadLine() 9 000000586F7EE750 00007FF8F3BD9319 System.Console.ReadLine() 10 000000586F7EE780 00007FF7DD4719DB ExampleCore_3_1_10.Program.Main(System.String[])
ExampleCore_3_1_10.Program.Main 方法的地址就是 00007FF7DD4719DB ,有了这个地址,就可以根据这个地址获取方法的描述符了。
执行命令【!ip2md 00007FF7DD4719DB】。
1 0:000> !ip2md 00007FF7DD4719DB 2 MethodDesc: 00007ff7dd5200c0 3 Method Name: ExampleCore_3_1_10.Program.Main(System.String[]) 4 Class: 00007ff7dd50fb20 5 MethodTable: 00007ff7dd520100 6 mdToken: 0000000006000001 7 Module: 00007ff7dd4fe0a0 8 IsJitted: yes 9 Current CodeAddr: 00007ff7dd471930 10 Version History: 11 ILCodeVersion: 0000000000000000 12 ReJIT ID: 0 13 IL Addr: 000001e1ebfd2050 14 CodeAddr: 00007ff7dd471930 (MinOptJitted) 15 NativeCodeVersion: 0000000000000000
也可以通过【!Name2EE】命令获取方法描述符。
1 0:000> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ff7dd4fe0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ff7dd5200c0 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ff7dd471930
这个两个命令获取同样的方法描述符。继续执行【!DumpIL 00007ff7dd5200c0】获取 IL 代码。
1 0:000> !DumpIL 00007ff7dd5200c0 2 ilAddr is 000001E1EBFD2050 pImport is 0000018FDE7845F0 3 ilAddr = 000001E1EBFD2050 4 IL_0000: nop 5 IL_0001: ldc.i4.s 10 6 IL_0003: ldc.i4.s 11 7 IL_0005: call int32 ExampleCore_3_1_10.Program::Sum(int32,int32) 8 IL_000a: stloc.0 9 IL_000b: ldloca.s VAR OR ARG 1 10 IL_000d: ldc.i4.4 11 IL_000e: ldc.i4.1 12 IL_000f: call void System.Runtime.CompilerServices.DefaultInterpolat::.ctor(int32,int32) 13 IL_0014: ldloca.s VAR OR ARG 1 14 IL_0016: ldstr "sum=" 15 IL_001b: call void System.Runtime.CompilerServices.DefaultInterpolat::AppendLiteral(string) 16 IL_0020: nop 17 IL_0021: ldloca.s VAR OR ARG 1 18 IL_0023: ldloc.0 19 IL_0024: call 20 IL_0029: nop 21 IL_002a: ldloca.s VAR OR ARG 1 22 IL_002c: call string System.Runtime.CompilerServices.DefaultInterpolat::ToStringAndClear() 23 IL_0031: call void System.Console::WriteLine(string) 24 IL_0036: nop 25 IL_0037: call string System.Console::ReadLine() 26 IL_003c: pop 27 IL_003d: ret
内容很简单,就不多说了。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出
sum=21
字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式,由于我们是手动中断,当前线程是调试器的线程,所以需要切换到托管线程,执行命令【~0s】。
1 0:006> ~0s 2 ntdll!NtReadFile+0x14: 3 00007ff9`1134d0a4 c3 ret
我们先执行【!ClrStack】命令查看一下托管调用栈,查找一下 Program.Main() 方法的 IP 地址。
1 0:000> !ClrStack 2 OS Thread Id: 0x1b4c (0) 3 Child SP IP Call Site 4 000000FDCAD7E7A0 00007ff91134d0a4 [InlinedCallFrame: 000000fdcad7e7a0] 5 000000FDCAD7E7A0 00007ff8d9a176eb [InlinedCallFrame: 000000fdcad7e7a0] 6 000000FDCAD7E770 00007ff8d9a176eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 7 000000FDCAD7E860 00007ff8d9a1c9c0 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150] 8 000000FDCAD7E8C0 00007ff8d9a1c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108] 9 000000FDCAD7E900 00007ff8d9a1fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34] 10 000000FDCAD7E970 00007ff834d589c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613] 11 000000FDCAD7E9C0 00007ff834d590a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802] 12 000000FDCAD7EA70 00007ff8d9a2005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 13 000000FDCAD7EAC0 00007ff8d9a19319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752] 14 000000FDCAD7EAF0 00007ff7dcf019db ExampleCore_3_1_10.Program.Main(System.String[]) [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9]
ExampleCore_3_1_10.Program.Main 方法的 IP 地址是00007ff7dcf019db ,有了这个地址,我们先用第一种方式获取方法描述符,执行命令【!IP2MD 00007ff7dcf019db】
1 0:000> !IP2MD 00007ff7dcf019db 2 MethodDesc: 00007ff7dcfb00c0 3 Method Name: ExampleCore_3_1_10.Program.Main(System.String[]) 4 Class: 00007ff7dcf9fb20 5 MethodTable: 00007ff7dcfb0100 6 mdToken: 0000000006000001 7 Module: 00007ff7dcf8e0a0 8 IsJitted: yes 9 Current CodeAddr: 00007ff7dcf01930 10 Version History: 11 ILCodeVersion: 0000000000000000 12 ReJIT ID: 0 13 IL Addr: 000002dddfa32050 14 CodeAddr: 00007ff7dcf01930 (MinOptJitted) 15 NativeCodeVersion: 0000000000000000 16 Source file: E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10Program.cs @ 9
MethodDesc: 00007ff7dcfb00c0 这就是方法描述符的地址,我们在使用第二种方式获取方法描述符,使用【!Name2EE】命令。
1 0:000> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ff7dcf8e0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ff7dcfb00c0 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ff7dcf01930
MethodDesc: 00007ff7dcfb00c0 这也是我们找到的方法描述符,他们的结果都是一样的。有了方法描述符,我们就可以使用【!DumpIL】命令获取 IL 代码了。执行命令【!DumpIL 00007ff7dcfb00c0】。
1 0:000> !DumpIL 00007ff7dcfb00c0 2 ilAddr is 000002DDDFA32050 pImport is 00000230A1AFADC0 3 ilAddr = 000002DDDFA32050 4 IL_0000: nop 5 IL_0001: ldc.i4.s 10 6 IL_0003: ldc.i4.s 11 7 IL_0005: call int32 ExampleCore_3_1_10.Program::Sum(int32,int32) 8 IL_000a: stloc.0 9 IL_000b: ldloca.s VAR OR ARG 1 10 IL_000d: ldc.i4.4 11 IL_000e: ldc.i4.1 12 IL_000f: call void System.Runtime.CompilerServices.DefaultInterpolat::.ctor(int32,int32) 13 IL_0014: ldloca.s VAR OR ARG 1 14 IL_0016: ldstr "sum=" 15 IL_001b: call void System.Runtime.CompilerServices.DefaultInterpolat::AppendLiteral(string) 16 IL_0020: nop 17 IL_0021: ldloca.s VAR OR ARG 1 18 IL_0023: ldloc.0 19 IL_0024: call 20 IL_0029: nop 21 IL_002a: ldloca.s VAR OR ARG 1 22 IL_002c: call string System.Runtime.CompilerServices.DefaultInterpolat::ToStringAndClear() 23 IL_0031: call void System.Console::WriteLine(string) 24 IL_0036: nop 25 IL_0037: call string System.Console::ReadLine() 26 IL_003c: pop 27 IL_003d: ret
很简单,不多说了。
3.3、CLR 内部指令
3.3.1、获得 CLR 的版本
A、基础知识
我们可以使用【!EEVersion】命令获取当前调试回话 CLR 的版本,该命令可以输出使用 CLR 的版本,SOS 调试器扩展的版本,以及 CLR 当前运行的模式(是服务器模式还是工作站模式)。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!EEVersion】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 sum=21。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
此时,我们就可以直接执行【!EEVersion】命令。
1 0:002> !EEVersion 2 8.0.224.6711 free 3 8,0,224,6711 @Commit: 1381d5ebd2ab1f292848d5b19b80cf71ac332508 4 Workstation mode 5 SOS Version: 6.0.5.7301 retail build
效果如图:
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出
sum=21
字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
直接执行命令【!EEVersion】。
1 0:008> !EEVersion 2 8.0.224.6711 free(CLR 版本) 3 8,0,224,6711 @Commit: 1381d5ebd2ab1f292848d5b19b80cf71ac332508 4 Workstation mode(工作站模式) 5 SOS Version: 8.0.10.10501 retail build(SOS 调试器扩展的版本)
很简单,话不多说。
3.3.2、根据名称找到方法的描述符
A、基础知识
当我们知道一个完整的方法名的情况下,可以通过【!Name2ee】命令查找该方法的描述符信息,方法的名称必须是完整的限定名。这个命令不光可以获取方法的详细信息,也可以获取类型的详细信息,只不过参数是类型的完整限定名。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!Name2ee】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 sum=21。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
此时,我们就可以直接执行【!Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main】命令了。
1 0:009> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ffb5bbbe0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ffb5bbe00c0(方法描述符地址) 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ffb5bb31930
效果如图:
该命令,可以针对方法起作用,也可以针对类型起作用。
执行【!Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program】命令查看 Program 类型的详情。
1 0:009> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program 2 Module: 00007ffb5bbbe0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000002000002 5 MethodTable: 00007ffb5bbe0100 6 EEClass: 00007ffb5bbcfb20 7 Name: ExampleCore_3_1_10.Program
效果如图:
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出
sum=21
字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
直接执行【!name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main】命令,查找 Program.Main() 方法的信息。
1 0:008> !name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program.Main 2 Module: 00007ff7dd61e0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000006000001 5 MethodDesc: 00007ff7dd6400c0 6 Name: ExampleCore_3_1_10.Program.Main(System.String[]) 7 JITTED Code Address: 00007ff7dd591930
【!name2ee】命令还可以获取类型的信息。执行命令【!name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program】。
1 0:008> !name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program 2 Module: 00007ff7dd61e0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000002000002 5 MethodTable: 00007ff7dd640100 6 EEClass: 00007ff7dd62fb20 7 Name: ExampleCore_3_1_10.Program
很简单,话不多说。
3.3.3、对象同步块的转储
在这一节中,【!syncblk】命令可以用来获取对象同步块的信息,这个命令在分析死锁问题时非常有用,以后章节详解,这里就不多说了。
3.3.4、对象方法表的转储
A、基础知识
每个托管对象都有一个相应的方法表,它包含了对象的详细信息。我们可以使用【!DumpMT】命令获取指定对象的方法表信息,该命令的参数就是对象方法表的地址。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!DumpMT】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 sum=21。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
此时,我们就可以直接执行【!Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program】命令来查找它的方法表了。
1 0:009> !Name2EE ExampleCore_3_1_10!ExampleCore_3_1_10.Program 2 Module: 00007ffb5bbbe0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000002000002 5 MethodTable: 00007ffb5bbe0100(这就是方法表的地址) 6 EEClass: 00007ffb5bbcfb20 7 Name: ExampleCore_3_1_10.Program
效果如图:
有了方法表的地址,我们就可以使用【!DumpMT】命令,执行命令【!DumpMT 00007ffb5bbe0100】。
1 0:009> !DumpMT 00007ffb5bbe0100 2 EEClass: 00007FFB5BBCFB20 3 Module: 00007FFB5BBBE0A0 4 Name: ExampleCore_3_1_10.Program 5 mdToken: 0000000002000002 6 File: E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 7 BaseSize: 0x18 8 ComponentSize: 0x0 9 DynamicStatics: false 10 ContainsPointers false 11 Slots in VTable: 7 12 Number of IFaces in IFaceMap: 0
效果如图:
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出
sum=21
字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
我们通过【!Name2ee】命令找到类型的信息,执行如下命令【!name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program】
1 0:008> !name2ee ExampleCore_3_1_10!ExampleCore_3_1_10.Program 2 Module: 00007ff7dd61e0a0 3 Assembly: ExampleCore_3_1_10.dll 4 Token: 0000000002000002 5 MethodTable: 00007ff7dd640100 6 EEClass: 00007ff7dd62fb20 7 Name: ExampleCore_3_1_10.Program
MethodTable: 00007ff7dd640100 它就是 Program 类型的方法表地址,继续执行【!DumpMT 00007ff7dd640100】命令。
1 0:008> !DumpMT 00007ff7dd640100 2 EEClass: 00007ff7dd62fb20 3 Module: 00007ff7dd61e0a0 4 Name: ExampleCore_3_1_10.Program 5 mdToken: 0000000002000002 6 File: E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 7 AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet. 8 BaseSize: 0x18 9 ComponentSize: 0x0 10 DynamicStatics: false 11 ContainsPointers: false 12 Slots in VTable: 7 13 Number of IFaces in IFaceMap: 0
内容很简单,就不废话了。
3.3.5、托管堆和垃圾收集器信息的转储
A、基础知识
CLR 垃圾收集器是一种高效的自动内存管理器,它确保内存实现最优的布局和管理。SOS 调试器扩展中提供了一些有关垃圾收集和托管堆的命令,如:DumpHeap(遍历托管堆,收集并输出这个堆以及位于堆上所有对象的信息)、GCRoot(显示对某个对象的根对象的引用信息,当要找出某个对象为什么没有被回收,非常有用)、VerifyHeap(托管堆也可能损坏,这个命令可以验证托管堆的完整性)和 TraverseHeap(遍历托管堆,把结果输出到文件中,有 CLR 分析器分析) 等命令。
我们不会演示所有命令的使用,着重演示 GCRoot 命令的使用。
B、眼见为实
调试源码:ExampleCore_3_1_11
调试任务:【!GCRoot】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_11binDebugnet8.0ExampleCore_3_1_11.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 数据添加完毕!。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
此时,我们就可以直接执行【!DumpHeap -type Byte[]】命令在托管堆上查抄 byte[] 数组了,找到了在分析。
1 0:002> !DumpHeap -type Byte[] 2 Address MT Size 3 0000017882000028 00007ffb5b9fb810 536 4 0000017882000240 00007ffb5b9fb810 1048 5 0000017882000e20 00007ffb5b9f9cc0 34 6 0000017882000e78 00007ffb5b9f9cc0 4120 7 0000017882809650 00007ffb5b9f9e40 32 8 0000017882809798 00007ffb5b9fb810 280 9 00000178019004c8 00007ffb5b9fb810 24 10 00000178019008a0 00007ffb5b9f9cc0 24 11 0000017882c00048 00007ffb5b9f9cc0 100024 12 0000017882c18720 00007ffb5b9f9cc0 100024 13 0000017882c30df8 00007ffb5b9f9cc0 100024 14 0000017882c494d0 00007ffb5b9f9cc0 100024 15 0000017882c61ba8 00007ffb5b9f9cc0 100024 16 0000017882c7a280 00007ffb5b9f9cc0 100024 17 0000017882c92958 00007ffb5b9f9cc0 100024 18 0000017882cab030 00007ffb5b9f9cc0 100024 19 0000017882cc3708 00007ffb5b9f9cc0 100024 20 0000017882cdbde0 00007ffb5b9f9cc0 100024 21 0000017882cf44b8 00007ffb5b9f9cc0 100024 22 0000017882d0cb90 00007ffb5b9f9cc0 100024 23 0000017882d25268 00007ffb5b9f9cc0 100024 24 0000017882d3d940 00007ffb5b9f9cc0 100024 25 0000017882d56018 00007ffb5b9f9cc0 100024 26 0000017882d6e6f0 00007ffb5b9f9cc0 100024 27 0000017882d86dc8 00007ffb5b9f9cc0 100024 28 0000017882d9f4a0 00007ffb5b9f9cc0 100024 29 0000017882db7b78 00007ffb5b9f9cc0 100024 30 0000017882dd0250 00007ffb5b9f9cc0 100024 31 0000017882de8928 00007ffb5b9f9cc0 100024 32 0000017882e01000 00007ffb5b9f9cc0 100024 33 0000017882e196d8 00007ffb5b9f9cc0 100024 34 0000017882e31db0 00007ffb5b9f9cc0 100024 35 0000017882e4a488 00007ffb5b9f9cc0 100024 36 0000017882e62b60 00007ffb5b9f9cc0 100024 37 0000017882e7b238 00007ffb5b9f9cc0 100024 38 0000017882e93910 00007ffb5b9f9cc0 100024 39 0000017882eabfe8 00007ffb5b9f9cc0 100024 40 0000017882ec46c0 00007ffb5b9f9cc0 100024 41 0000017882edcd98 00007ffb5b9f9cc0 100024 42 0000017882ef5470 00007ffb5b9f9cc0 100024 43 0000017882f0db48 00007ffb5b9f9cc0 100024 44 0000017882f26220 00007ffb5b9f9cc0 100024 45 0000017882f3e8f8 00007ffb5b9f9cc0 100024 46 0000017882f56fd0 00007ffb5b9f9cc0 100024 47 0000017882f6f6a8 00007ffb5b9f9cc0 100024 48 0000017882f87d80 00007ffb5b9f9cc0 100024 49 0000017882fa0458 00007ffb5b9f9cc0 100024 50 0000017882fb8b30 00007ffb5b9f9cc0 100024 51 0000017882fd1208 00007ffb5b9f9cc0 100024 52 0000017882fe98e0 00007ffb5b9f9cc0 100024 53 0000017883001fb8 00007ffb5b9f9cc0 100024 54 000001788301a690 00007ffb5b9f9cc0 100024 55 0000017883032d68 00007ffb5b9f9cc0 100024 56 000001788304b440 00007ffb5b9f9cc0 100024 57 0000017883063b18 00007ffb5b9f9cc0 100024 58 000001788307c1f0 00007ffb5b9f9cc0 100024 59 00000178830948c8 00007ffb5b9f9cc0 100024 60 00000178830acfa0 00007ffb5b9f9cc0 100024 61 00000178830c5678 00007ffb5b9f9cc0 100024 62 00000178830ddd50 00007ffb5b9f9cc0 100024 63 00000178830f6428 00007ffb5b9f9cc0 100024 64 000001788310eb00 00007ffb5b9f9cc0 100024 65 00000178831271d8 00007ffb5b9f9cc0 100024 66 000001788313f8b0 00007ffb5b9f9cc0 100024 67 0000017883157f88 00007ffb5b9f9cc0 100024 68 0000017883170660 00007ffb5b9f9cc0 100024 69 0000017883188d38 00007ffb5b9f9cc0 100024 70 00000178831a1410 00007ffb5b9f9cc0 100024 71 00000178831b9ae8 00007ffb5b9f9cc0 100024 72 00000178831d21c0 00007ffb5b9f9cc0 100024 73 00000178831ea898 00007ffb5b9f9cc0 100024 74 0000017883202f70 00007ffb5b9f9cc0 100024 75 000001788321b648 00007ffb5b9f9cc0 100024 76 0000017883233d20 00007ffb5b9f9cc0 100024 77 000001788324c3f8 00007ffb5b9f9cc0 100024 78 0000017883264ad0 00007ffb5b9f9cc0 100024 79 000001788327d1a8 00007ffb5b9f9cc0 100024 80 0000017883295880 00007ffb5b9f9cc0 100024 81 00000178832adf58 00007ffb5b9f9cc0 100024 82 00000178832c6630 00007ffb5b9f9cc0 100024 83 00000178832ded08 00007ffb5b9f9cc0 100024 84 00000178832f73e0 00007ffb5b9f9cc0 100024 85 000001788330fab8 00007ffb5b9f9cc0 100024 86 0000017883328190 00007ffb5b9f9cc0 100024 87 0000017883340868 00007ffb5b9f9cc0 100024 88 0000017883358f40 00007ffb5b9f9cc0 100024 89 0000017883371618 00007ffb5b9f9cc0 100024 90 0000017883389cf0 00007ffb5b9f9cc0 100024 91 00000178833a23c8 00007ffb5b9f9cc0 100024 92 00000178833baaa0 00007ffb5b9f9cc0 100024 93 00000178833d3178 00007ffb5b9f9cc0 100024 94 00000178833eb850 00007ffb5b9f9cc0 100024 95 0000017883403f28 00007ffb5b9f9cc0 100024 96 000001788341c600 00007ffb5b9f9cc0 100024 97 0000017883434cd8 00007ffb5b9f9cc0 100024 98 000001788344d3b0 00007ffb5b9f9cc0 100024 99 0000017883465a88 00007ffb5b9f9cc0 100024 100 000001788347e160 00007ffb5b9f9cc0 100024 101 0000017883496838 00007ffb5b9f9cc0 100024 102 00000178834aef10 00007ffb5b9f9cc0 100024 103 00000178834c75e8 00007ffb5b9f9cc0 100024 104 00000178834dfcc0 00007ffb5b9f9cc0 100024 105 00000178834f8398 00007ffb5b9f9cc0 100024 106 0000017883510a70 00007ffb5b9f9cc0 100024 107 0000017883529148 00007ffb5b9f9cc0 100024 108 0000017883541820 00007ffb5b9f9cc0 100024 109 0000017883559ef8 00007ffb5b9f9cc0 100024 110 00000178835725d0 00007ffb5b9f9cc0 100024 111 112 Statistics: 113 MT Count TotalSize Class Name 114 00007ffb5b9f9e40 1 32 System.Collections.Generic.List`1[[System.Byte[], System.Private.CoreLib]] 115 00007ffb5b9fb810 4 1888 System.Byte[][] 116 00007ffb5b9f9cc0 103 10006578 System.Byte[] 117 Total 108 objects 118 0:002>
我们可以知道数据大小为100024 的项一共有100个,因为我们循环了100次。每一项数据的大小为什么不是100000,而是 100024,因为它是数组,所以它拥有同步块索引、方法表信息、数组长度等附加信息。在32位的机器上是100012,在64位机器是100024。
0000017883559ef8 我随便找了一项,选的是最后一个项,这个就是元素的地址,我们可以使用【!GCRoot 0000017883559ef8】命令,查看它的引用情况。
1 0:009> !gcroot 0000023306d59ef8 2 HandleTable: 3 0000023301B513E8 (strong handle) 4 -> 0000023303800028 System.Object[] 5 -> 0000023306009650 System.Collections.Generic.List`1[[System.Byte[], System.Private.CoreLib]] 6 -> 0000023305800240 System.Byte[][] 7 -> 0000023306D59EF8 System.Byte[] 8 9 Found 1 unique roots (run '!gcroot -all' to see all roots).
【!VerifyHeap】该命令执行会有问题,效果如图:
推荐使用 Windbg,我只是想多熟悉一下。
执行命令【 !TraverseHeap F:booksmyTest.txt】,输出文件。
1 0:009> !TraverseHeap F:booksmyTest.txt 2 Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option 3 Writing CLRProfiler format to file F:booksmyTest.txt 4 Gathering types... 5 tracing roots... 6 7 Walking heap... 8 9 file F:booksmyTest.txt saved
效果如图:
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_11.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出【数据添加完毕!】
字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
第一步,我们先在托管堆中查找 Byte[] 数组对象,执行命令【!dumpheap -type Byte[]】。
1 0:001> !dumpheap -type Byte[] 2 Address MT Size 3 01a606800028 7ff7dd61b810 536 4 01a606800240 7ff7dd61b810 1,048 5 01a606800e20 7ff7dd619cc0 34 6 01a606800e78 7ff7dd619cc0 4,120 7 01a607009650 7ff7dd619e40 32 8 01a607009798 7ff7dd61b810 280 9 01a607400048 7ff7dd619cc0 100,024 10 01a607418720 7ff7dd619cc0 100,024 11 01a607430df8 7ff7dd619cc0 100,024 12 01a6074494d0 7ff7dd619cc0 100,024 13 01a607461ba8 7ff7dd619cc0 100,024 14 01a60747a280 7ff7dd619cc0 100,024 15 01a607492958 7ff7dd619cc0 100,024 16 01a6074ab030 7ff7dd619cc0 100,024 17 01a6074c3708 7ff7dd619cc0 100,024 18 01a6074dbde0 7ff7dd619cc0 100,024 19 01a6074f44b8 7ff7dd619cc0 100,024 20 01a60750cb90 7ff7dd619cc0 100,024 21 01a607525268 7ff7dd619cc0 100,024 22 01a60753d940 7ff7dd619cc0 100,024 23 01a607556018 7ff7dd619cc0 100,024 24 01a60756e6f0 7ff7dd619cc0 100,024 25 01a607586dc8 7ff7dd619cc0 100,024 26 01a60759f4a0 7ff7dd619cc0 100,024 27 01a6075b7b78 7ff7dd619cc0 100,024 28 01a6075d0250 7ff7dd619cc0 100,024 29 01a6075e8928 7ff7dd619cc0 100,024 30 01a607601000 7ff7dd619cc0 100,024 31 01a6076196d8 7ff7dd619cc0 100,024 32 01a607631db0 7ff7dd619cc0 100,024 33 01a60764a488 7ff7dd619cc0 100,024 34 01a607662b60 7ff7dd619cc0 100,024 35 01a60767b238 7ff7dd619cc0 100,024 36 01a607693910 7ff7dd619cc0 100,024 37 01a6076abfe8 7ff7dd619cc0 100,024 38 01a6076c46c0 7ff7dd619cc0 100,024 39 01a6076dcd98 7ff7dd619cc0 100,024 40 01a6076f5470 7ff7dd619cc0 100,024 41 01a60770db48 7ff7dd619cc0 100,024 42 01a607726220 7ff7dd619cc0 100,024 43 01a60773e8f8 7ff7dd619cc0 100,024 44 01a607756fd0 7ff7dd619cc0 100,024 45 01a60776f6a8 7ff7dd619cc0 100,024 46 01a607787d80 7ff7dd619cc0 100,024 47 01a6077a0458 7ff7dd619cc0 100,024 48 01a6077b8b30 7ff7dd619cc0 100,024 49 01a6077d1208 7ff7dd619cc0 100,024 50 01a6077e98e0 7ff7dd619cc0 100,024 51 01a607801fb8 7ff7dd619cc0 100,024 52 01a60781a690 7ff7dd619cc0 100,024 53 01a607832d68 7ff7dd619cc0 100,024 54 01a60784b440 7ff7dd619cc0 100,024 55 01a607863b18 7ff7dd619cc0 100,024 56 01a60787c1f0 7ff7dd619cc0 100,024 57 01a6078948c8 7ff7dd619cc0 100,024 58 01a6078acfa0 7ff7dd619cc0 100,024 59 01a6078c5678 7服务器托管网ff7dd619cc0 100,024 60 01a6078ddd50 7ff7dd619cc0 100,024 61 01a6078f6428 7ff7dd619cc0 100,024 62 01a60790eb00 7ff7dd619cc0 100,024 63 01a6079271d8 7ff7dd619cc0 100,024 64 01a60793f8b0 7ff7dd619cc0 100,024 65 01a607957f88 7ff7dd619cc0 100,024 66 01a607970660 7ff7dd619cc0 100,024 67 01a607988d38 7ff7dd619cc0 100,024 68 01a6079a1410 7ff7dd619cc0 100,024 69 01a6079b9ae8 7ff7dd619cc0 100,024 70 01a6079d21c0 7ff7dd619cc0 100,024 71 01a6079ea898 7ff7dd619cc0 100,024 72 01a607a02f70 7ff7dd619cc0 100,024 73 01a607a1b648 7ff7dd619cc0 100,024 74 01a607a33d20 7ff7dd619cc0 100,024 75 01a607a4c3f8 7ff7dd619cc0 100,024 76 01a607a64ad0 7ff7dd619cc0 100,024 77 01a607a7d1a8 7ff7dd619cc0 100,024 78 01a607a95880 7ff7dd619cc0 100,024 79 01a607aadf58 7ff7dd619cc0 100,024 80 01a607ac6630 7ff7dd619cc0 100,024 81 01a607aded08 7ff7dd619cc0 100,024 82 01a607af73e0 7ff7dd619cc0 100,024 83 01a607b0fab8 7ff7dd619cc0 100,024 84 01a607b28190 7ff7dd619cc0 100,024 85 01a607b40868 7ff7dd619cc0 100,024 86 01a607b58f40 7ff7dd619cc0 100,024 87 01a607b71618 7ff7dd619cc0 100,024 88 01a607b89cf0 7ff7dd619cc0 100,024 89 01a607ba23c8 7ff7dd619cc0 100,024 90 01a607bbaaa0 7ff7dd619cc0 100,024 91 01a607bd3178 7ff7dd619cc0 100,024 92 01a607beb850 7ff7dd619cc0 100,024 93 01a607c03f28 7ff7dd619cc0 100,024 94 01a607c1c600 7ff7dd619cc0 100,024 95 01a607c34cd8 7ff7dd619cc0 100,024 96 01a607c4d3b0 7ff7dd619cc0 100,024 97 01a607c65a88 7ff7dd619cc0 100,024 98 01a607c7e160 7ff7dd619cc0 100,024 99 01a607c96838 7ff7dd619cc0 100,024 100 01a607caef10 7ff7dd619cc0 100,024 101 01a607cc75e8 7ff7dd619cc0 100,024 102 01a607cdfcc0 7ff7dd619cc0 100,024 103 01a607cf8398 7ff7dd619cc0 100,024 104 01a607d10a70 7ff7dd619cc0 100,024 105 01a607d29148 7ff7dd619cc0 100,024 106 01a607d41820 7ff7dd619cc0 100,024 107 01a607d59ef8 7ff7dd619cc0 100,024 108 01a607d725d0 7ff7dd619cc0 100,024 109 01e6991e04c8 7ff7dd61b810 24 110 01e6991e08a0 7ff7dd619cc0 24 111 112 Statistics: 113 MT Count TotalSize Class Name 114 7ff7dd619e40 1 32 System.Collections.Generic.List 115 7ff7dd61b810 4 1,888 System.Byte[][] 116 7ff7dd619cc0 103 10,006,578 System.Byte[] 117 Total 108 objects, 10,008,498 bytes
数据大小为 100,024 的项目,一种有 100 项,因为我们 For 循环了一百次,为什么大小不是 100000,而是 100024 ,因为每个数组对象都有两个附加成员和数组长度,在 32 位机器上是100012,在64位机器上是100024。有了成员列表,我们可以在这个列表中,任意选择一个项,在 Address 列,选一个地址,针对这个地址,我们使用【!gcroot】命令,就能看到我们想要的结果。
01a607d725d0 我选的就是这一项,标红色的。
1 0:001> !GCRoot 01a607d725d0 2 HandleTable: 3 000001a6043213e8 (strong handle)(static 底层是标记了 pinned,也就是这个 handle 持有 System.Object[] 数组) 4 -> 01a604800028 System.Object[] 5 -> 01a607009650 System.Collections.Generic.List 6 -> 01a606800240 System.Byte[][] 7 -> 01a607d725d0 System.Byte[] 8 9 Thread 3028: 10 165219e7c0 7ff7dd5619e4 ExampleCore_3_1_11.Program.Main(System.String[]) [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_11Program.cs @ 14] 11 rbp-18: 000000165219e7e8 12 -> 01a607d725d0 System.Byte[] 13 14 rbp-10: 000000165219e7f0 15 -> 01a607009650 System.Collections.Generic.List 16 -> 01a606800240 System.Byte[][] 17 -> 01a607d725d0 System.Byte[] 18 19 Found 3 unique roots.
执行命令【!VerifyHeap】,看一下托管堆健康的状况。
1 0:001> !VerifyHeap 2 347 objects verified, 0 errors. 3 No heap corruption detected.
执行命令【 !TraverseHeap F:booksmyTest.txt】,输出文件。
1 0:001> !TraverseHeap F:booksmyTest.txt
你去指定目录查找吧,肯定有一个文件【myTest.txt】存在。
3.4、诊断命令
以前的讨论的命令都是调试分析命令,SOS 调试器扩展还提供了一些诊断命令,这些命令在调试会话中提供一些辅助信息。
3.4.1、找出对象的应用程序域
A、基础知识
如果我们想找某个对象属于哪个应用程序域,我们可以使用【!FindAppDomain】命令。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!FindAppDomain】命令的使用。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 sum=21。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
由于我们的操作和具体的堆栈有关,并且由于我们手动中断,必须要转换到托管线程上才可以,执行命令【~0s】。
1 0:009> ~0s 2 ntdll!RtlpEnterCriticalSectionContended+0xe4: 3 00007ffc`82a3fbd4 ebea jmp ntdll!RtlpEnterCriticalSectionContended+0xd0 (00007ffc`82a3fbc0)
效果如图:
继续执行【!dso】或者【!DumpStackObjects】命令查看一下托管线程栈上的对象。
1 0:000> !dso 2 OS Thread Id: 0x3f08 (0) 3 RSP/REG Object Name 4 00000051D657E2D0 000001688d40b648 System.Text.DecoderDBCS 5 00000051D657E930 000001688d40c720 System.Char[] 6 00000051D657E958 000001688d40b6f0 System.Byte[] 7 00000051D657E988 000001688d40b648 System.Text.DecoderDBCS 8 00000051D657E9E0 000001688d40b6f0 System.Byte[] 9 00000051D657E9F0 000001688d40b648 System.Text.DecoderDBCS 10 00000051D657EA10 000001688d40c720 System.Char[] 11 00000051D657EA20 000001688d40b6f0 System.Byte[] 12 00000051D657EA28 000001a91f4810c8 System.String bytes 13 00000051D657EA70 000001688d40b5e8 System.IO.StreamReader 14 00000051D657EAB0 000001688d40b648 System.Text.DecoderDBCS 15 00000051D657EAB8 000001688d40b6f0 System.Byte[] 16 00000051D657EAD0 000001688d40c720 System.Char[] 17 00000051D657EAF0 000001688d40b5e8 System.IO.StreamReader 18 00000051D657EB00 000001688d40b5e8 System.IO.StreamReader 19 00000051D657EB10 000001688d40aeb8 System.Object 20 00000051D657EBB0 000001688d40b5e8 System.IO.StreamReader 21 00000051D657EBC0 000001688d40aeb8 System.Object 22 00000051D657EC00 000001688d414738 System.IO.SyncTextReader 23 00000051D657EC10 000001688d40aeb8 System.Object 24 00000051D657EC48 000001688d40b4b0 System.IO.TextWriter+SyncTextWriter 25 00000051D657EC60 000001688d40ac48 System.String sum=21 26 00000051D657ECB0 000001688d408eb0 System.String[] 27 00000051D657ED58 000001688d408eb0 System.String[] 28 00000051D657EF50 000001688d408eb0 System.String[] 29 00000051D657EF58 000001688d408eb0 System.String[] 30 00000051D657F070 000001688d408eb0 System.String[] 31 00000051D657F0F0 000001688d408ec8 System.String E:Visual Studio 2022...ExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 32 00000051D657F100 000001688d408eb0 System.String[] 33 00000051D657F110 000001688d408e90 System.String[] 34 00000051D657F148 000001688d408ec8 System.String E:Visual Studio 2022...ExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 35 00000051D657F2F8 000001688d408eb0 System.String[]
000001688d40b6f0 我们用它做例子,看看这个 byte 数组属于哪个应用程序域。执行【!FindAppDomain 000001688d40b6f0】命令。
1 0:000> !FindAppDomain 000001688d40b6f0 2 AppDomain: 0000016888e66b60(应用程序域的地址) 3 Name: clrhost 4 ID: 1
AppDomain: 0000016888e66b60 我们有了应用程序域的地址,直接使用【!DumpDomain 0000016888e66b60】命令查看应用程序域的详情。
1 0:000> !DumpDomain 0000016888e66b60 2 -------------------------------------- 3 Domain 1: 0000016888e66b60 4 LowFrequencyHeap: 00007FFBAF5865A8 5 HighFrequencyHeap: 00007FFBAF586638 6 StubHeap: 00007FFBAF5866C8 7 Stage: OPEN 8 Name: clrhost 9 Assembly: 0000016888e79060 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll] 10 ClassLoader: 0000016888E790F0 11 Module 12 00007ffb4f594000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll 13 14 Assembly: 000001688a8c7ac0 [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll] 15 ClassLoader: 000001688A8C7B50 16 Module 17 00007ffb4f77e0a0 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 18 19 Assembly: 0000016888e480b0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll] 20 ClassLoader: 0000016888E48140 21 Module 22 00007ffb4f77fbc8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll 23 24 Assembly: 0000016888eb95c0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll] 25 ClassLoader: 0000016888EB9930 26 Module 27 00007ffb4f7ac3a8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll 28 29 Assembly: 000001688a8ca600 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll] 30 ClassLoader: 000001688A8CA950 31 Module 32 00007ffb4f7d72f0 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll 33 34 Assembly: 0000016888e8f6b0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll] 35 ClassLoader: 0000016888E8F740 36 Module 37 00007ffb4f7dafa8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll 38 39 Assembly: 0000016888e94090 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll] 40 ClassLoader: 0000016888E94120 41 Module 42 00007ffb4f7dd480 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll
当然,如果这个命令不跟任何参数,就会输出当钱的应用程序域的信息。
1 0:000> !dumpdomain 2 -------------------------------------- 3 System Domain: 00007ffbaf5860d0 4 LowFrequencyHeap: 00007FFBAF5865A8 5 HighFrequencyHeap: 00007FFBAF586638 6 StubHeap: 00007FFBAF5866C8 7 Stage: OPEN 8 Name: None 9 -------------------------------------- 10 Domain 1: 0000016888e66b60 11 LowFrequencyHeap: 00007FFBAF5865A8 12 HighFrequencyHeap: 00007FFBAF586638 13 StubHeap: 00007FFBAF5866C8 14 Stage: OPEN 15 Name: clrhost 16 Assembly: 0000016888e79060 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll] 17 ClassLoader: 0000016888E790F0 18 Module 19 00007ffb4f594000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll 20 21 Assembly: 000001688a8c7ac0 [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll] 22 ClassLoader: 000001688A8C7B50 23 Module 24 00007ffb4f77e0a0 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 25 26 Assembly: 0000016888e480b0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll] 27 ClassLoader: 0000016888E48140 28 Module 29 00007ffb4f77fbc8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll 30 31 Assembly: 0000016888eb95c0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll] 32 ClassLoader: 0000016888EB9930 33 Module 34 00007ffb4f7ac3a8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll 35 36 Assembly: 000001688a8ca600 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll] 37 ClassLoader: 000001688A8CA950 38 Module 39 00007ffb4f7d72f0 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll 40 41 Assembly: 0000016888e8f6b0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll] 42 ClassLoader: 0000016888E8F740 43 Module 44 00007ffb4f7dafa8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll 45 46 Assembly: 0000016888e94090 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll] 47 ClassLoader: 0000016888E94120 48 Module 49 00007ffb4f7dd480 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出【sum=21】 字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
由于我们手动切换到中断模式,我们需要切换到托管线程上,执行【~0s】命令。然后,我们执行【!dso】或者【!DumpStackObjects】命令,查找栈上所有的对象。
1 0:000> !DumpStackObjects 2 OS Thread Id: 0x25fc (0) 3 SP/REG Object Name 4 000e6437e6d8 0185c700b580 System.ConsolePal+WindowsConsoleStream 5 000e6437e6e8 0185c700b580 System.ConsolePal+WindowsConsoleStream 6 000e6437e6f8 0185c700b6f0 System.Byte[] 7 000e6437e750 0185c700b580 System.ConsolePal+WindowsConsoleStream 8 000e6437e798 0185c700b5e8 System.IO.StreamReader 9 000e6437e7c0 0185c700b580 System.ConsolePal+WindowsConsoleStream 10 000e6437e7c8 0185c700b6f0 System.Byte[] 11 000e6437e800 0185c700b5e8 System.IO.StreamReader 12 000e6437e810 0185c700b5e8 System.IO.StreamReader 13 000e6437e820 0185c700aeb8 System.Object 14 000e6437e8c0 0185c700b5e8 System.IO.StreamReader 15 000e6437e8d0 0185c700aeb8 System.Object 16 000e6437e910 0185c7014738 System.IO.SyncTextReader 17 000e6437e920 0185c700aeb8 System.Object 18 000e6437e958 0185c700b4b0 System.IO.TextWriter+SyncTextWriter 19 000e6437e970 0185c700ac48 System.String 20 000e6437e9c0 0185c7008eb0 System.String[] 21 000e6437ea68 0185c7008eb0 System.String[] 22 000e6437ec60 0185c7008eb0 System.String[] 23 000e6437ec68 0185c7008eb0 System.String[] 24 000e6437ed80 0185c7008eb0 System.String[] 25 000e6437ee00 0185c7008ec8 System.String 26 000e6437ee10 0185c7008eb0 System.String[] 27 000e6437ee20 0185c7008e90 System.String[] 28 000e6437ee58 0185c7008ec8 System.String 29 000e6437f008 0185c7008eb0 System.String[]
0185c700b6f0 我查找这个数组属于哪个引用程序域,执行命令【!FindAppDomain 0185c700b6f0】。
1 0:000> !FindAppDomain 0185c700b6f0 2 AppDomain: 00000185c2d189a0(引用程序域的地址) 3 Name: clrhost 4 ID: 1
AppDomain: 00000185c2d189a0 有了引用程序域的地址,我们就可以使用【!DumpDomain】命令,查看特定引用程序域的详情了。
执行命令【!DumpDomain 00000185c2d189a0】。
1 0:000> !DumpDomain 00000185c2d189a0 2 -------------------------------------- 3 Domain 1: 00000185c2d189a0 4 LowFrequencyHeap: 00007FFBAA0365A8 5 HighFrequencyHeap: 00007FFBAA036638 6 StubHeap: 00007FFBAA0366C8 7 Stage: OPEN 8 Name: clrhost 9 Assembly: 00000185c2cdf720 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll] 10 ClassLoader: 00000185C2CDF7B0 11 Module 12 00007ffb4a024000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll 13 14 Assembly: 00000185c2cccba0 [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll] 15 ClassLoader: 00000185C2CCCC30 16 Module 17 00007ffb4a20e0a0 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 18 19 Assembly: 00000185c461e520 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll] 20 ClassLoader: 00000185C461E5B0 21 Module 22 00007ffb4a20fbc8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll 23 24 Assembly: 00000185c2cce6c0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll] 25 ClassLoader: 00000185C2CCEA30 26 Module 27 00007ffb4a23c3a8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll 28 29 Assembly: 00000185c2cf1ab0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll] 30 ClassLoader: 00000185C2CF1B40 31 Module 32 00007ffb4a2672f0 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll 33 34 Assembly: 00000185c2cf6480 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll] 35 ClassLoader: 00000185C2CF6510 36 Module 37 00007ffb4a26afa8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll 38 39 Assembly: 00000185c2cf6760 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll] 40 ClassLoader: 00000185C4620050 41 Module 42 00007ffb4a26d480 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll
如果想查看应用程序域的详情,也可以执行命令【!DumpDomain】,无参数。
1 0:000> !DumpDomain 2 -------------------------------------- 3 System Domain: 00007ffbaa0360d0 4 LowFrequencyHeap: 00007FFBAA0365A8 5 HighFrequencyHeap: 00007FFBAA036638 6 StubHeap: 00007FFBAA0366C8 7 Stage: OPEN 8 Name: None 9 -------------------------------------- 10 Domain 1: 00000185c2d189a0 11 LowFrequencyHeap: 00007FFBAA0365A8 12 HighFrequencyHeap: 00007FFBAA036638 13 StubHeap: 00007FFBAA0366C8 14 Stage: OPEN 15 Name: clrhost 16 Assembly: 00000185c2cdf720 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll] 17 ClassLoader: 00000185C2CDF7B0 18 Module 19 00007ffb4a024000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Private.CoreLib.dll 20 21 Assembly: 00000185c2cccba0 [E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll] 22 ClassLoader: 00000185C2CCCC30 23 Module 24 00007ffb4a20e0a0 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.dll 25 26 Assembly: 00000185c461e520 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll] 27 ClassLoader: 00000185C461E5B0 28 Module 29 00007ffb4a20fbc8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.dll 30 31 Assembly: 00000185c2cce6c0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll] 32 ClassLoader: 00000185C2CCEA30 33 Module 34 00007ffb4a23c3a8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Console.dll 35 36 Assembly: 00000185c2cf1ab0 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll] 37 ClassLoader: 00000185C2CF1B40 38 Module 39 00007ffb4a2672f0 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Threading.dll 40 41 Assembly: 00000185c2cf6480 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll] 42 ClassLoader: 00000185C2CF6510 43 Module 44 00007ffb4a26afa8 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Text.Encoding.Extensions.dll 45 46 Assembly: 00000185c2cf6760 [C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll] 47 ClassLoader: 00000185C4620050 48 Module 49 00007ffb4a26d480 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.2System.Runtime.InteropServices.dll
3.4.2、进程信息
A、基础知识
在调试会话中,如果我们想获取内存使用量、环境变量、处理时间等这些信息时,可以使用【!ProcInfo】命令,它有三个命令开关:-env(环境变量),-time(处理时间),-mem(内存使用情况),如果不带任何参数,会输出所有信息。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!ProcInfo】命令的使用。
说明,其实这个项目可以是任意一个,不需要任何代码,所以我继续使用原来的项目。
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_10binDebugnet8.0ExampleCore_3_1_10.exe】命令,打开调试器窗口。
继续使用【g】命令,运行调试器,直到调试器输出 sum=21。此时,调试器也处于卡死的状态,按【ctrl+c】组合键进入调试器中断模式。
我们直接执行【!ProcInfo –env】命令,获取系统的环境变量信息。
1 0:009> !ProcInfo -env 2 --------------------------------------- 3 Environment 4 =::=:: 5 =C:=C:Program Files (x86)Microsoft Visual StudioInstaller 6 =D:=D:Program FilesMicrosoft Visual Studio2022Community 7 =ExitCode=00000000 8 ALLUSERSPROFILE=C:ProgramData 9 APPDATA=C:UsersAdministratorAppDataRoaming 10 CommandPromptType=Native 11 CommonProgramFiles=C:Program FilesCommon Files 12 CommonProgramFiles(x86)=C:Program Files (x86)Common Files 13 CommonProgramW6432=C:Program FilesCommon Files 14 COMPLUS_MDA=1 15 COMPUTERNAME=MS-VMSQSWIJZFNM 16 ComSpec=C:Windowssystem32cmd.exe 17 DevEnvDir=D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDE 18 DriverData=C:WindowsSystem32DriversDriverData 19 ExtensionSdkDir=C:Program Files (x86)Microsoft SDKsWindows Kits10ExtensionSDKs 20 EXTERNAL_INCLUDE=D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519include;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519ATLMFCinclude;D:Program FilesMicrosoft Visual Studio2022CommunityVCAuxiliaryVSinclude;D:Windows Kits10include10.0.22621.0ucrt;D:Windows Kits10include10.0.22621.0um;D:Windows Kits10include10.0.22621.0shared;D:Windows Kits10include10.0.22621.0winrt;D:Windows Kits10include10.0.22621.0cppwinrt;C:Program Files (x86)Windows KitsNETFXSDK4.8includeum 21 FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer 22 FPS_BROWSER_USER_PROFILE_STRING=Default 23 Framework40Version=v4.0 24 FrameworkDir=C:WindowsMicrosoft.NETFramework 25 FrameworkDir32=C:WindowsMicrosoft.NETFramework 26 FrameworkVersion=v4.0.30319 27 FrameworkVersion32=v4.0.30319 28 FSHARPINSTALLDIR=D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftFSharpTools 29 GENICAM_GENTL32_PATH=C:Program Files (x86)Common FilesMVSRuntimeWin32_i86 30 GENICAM_GENTL64_PATH=C:Program Files (x86)Common FilesMVSRuntimeWin64_x64 31 HOMEDRIVE=C: 32 HOMEPATH=UsersAdministrator 33 INCLUDE=D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519include;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC 34 14.39.33519ATLMFCinclude;D:Program FilesMicrosoft Visual Studio2022CommunityVCAuxiliaryVSinclude;D:Windows Kits10include10.0.22621.0ucrt;D:Windows Kits10include10.0.22621.0um;D:Windows Kits10include10.0.22621.0shared;D:Windows Kits10include10.0.22621.0winrt;D:Windows Kits10include10.0.22621.0cppwinrt;C:Program Files (x86)Windows KitsNETFXSDK4.8includeum 35 LIB=D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519ATLMFClibx86;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519libx86;C:Program Files (x86)Windows KitsNETFXSDK4.8libumx86;D:Windows Kits10lib10.0.22621.0ucrtx86;D:Windows Kits10lib10.0.22621.0umx86 36 LIBPATH=D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519ATLMFClibx86;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519libx86;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519libx86storereferences;D:Windows Kits10UnionMetadata10.0.22621.0;D:Windows Kits10References10.0.22621.0;C:WindowsMicrosoft.NETFrameworkv4.0.30319 37 LOCALAPPDATA=C:UsersAdministratorAppDataLocal 38 LOGONSERVER=MS-VMSQSWIJZFNM 39 MVCAM_COMMON_RUNENV=D:Program Files (x86)MVSDevelopment 40 MVCAM_GENICAM_CLPROTOCOL=C:Program Files (x86)Common FilesMVSRuntimeCLProtocol 41 MVCAM_GIGE_DEBUG_HEARTBEAT=60000 42 NETFXSDKDir=C:Program Files (x86)Windows KitsNETFXSDK4.8 43 NUMBER_OF_PROCESSORS=4 44 OneDrive=C:UsersAdministratorOneDrive 45 OS=Windows_NT 46 Path=D:Windows Kits10Debuggersx64;D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519binHostX86x86;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDEVCVCPackages;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftTestWindow;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftTeamFoundationTeam Explorer;D:Program FilesMicr 47 osoft Visual Studio2022CommunityMSBuildCurrentbinRoslyn;D:Program Files (x86)Microsoft Visual StudioSharedCommonVSPerfCollectionToolsvs2019;C:Program Files (x86)Microsoft SDKsWindowsv10.0AbinNETFX 4.8 Tools;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftFSharpTools;D:Program FilesMicrosoft Visual Studio2022CommunityTeam ToolsDiagnosticsHubCollector;D:Windows Kits10bin10.0.22621.0x86;D:Windows Kits10binx86;D:Program FilesMicrosoft Visual Studio2022CommunityMSBuildCurrentBinamd64;C:WindowsMicrosoft.NETFrameworkv4.0.30319;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDE;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7Tools;C:Program Files (x86)Common FilesMVSRuntimeWin32_i86;C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:WindowsSystem32OpenSSH;C:Program FilesMicrosoft SQL Server150ToolsBinn;C:Program FilesMicrosoft SQL ServerClient SDKODBC170ToolsBinn;C:Program Filesdotnet;D:Program Files (x86)Microsoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100DTSBinn;D:Program Files (x86)Microsoft SQL Server100ToolsBinnVSShellCommon7IDE;C:Program Files (x86)Microsoft Visual Studio 9.0Common7IDEPrivateAssemblies;D:Program Files (x86)Microsoft SQL Server100DTSBinn;C:Program FilesMicrosoft SQL Server130ToolsBinn;D:XIMEAAPI;C:XIMEAAPI;D:Program FilesGitcmd;D:Windows Kits10Windows Performance Toolkit;D:Windows Kits10Debuggersx64;C:UsersAdministratorAppDataLocalMicrosoftWindowsApps;C:UsersAdministrator.dotnettools;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftCMakeCMakebin;D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDECommonExtensionsMicrosoftCMakeNinja;D:Program FilesMicrosoft Visual Studio2022CommunityCommo 48 n7IDEVCLinuxbinConnectionManagerExe 49 PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC 50 PROCESSOR_ARCHITECTURE=AMD64 51 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 61 Stepping 4, GenuineIntel 52 PROCESSOR_LEVEL=6 53 PROCESSOR_REVISION=3d04 54 ProgramData=C:ProgramData 55 ProgramFiles=C:Program Files 56 ProgramFiles(x86)=C:Program Files (x86) 57 ProgramW6432=C:Program Files 58 PROMPT=$P$G 59 PSModulePath=C:Program FilesWindowsPowerShellModules;C:Windowssystem32WindowsPowerShellv1.0Modules 60 PUBLIC=C:UsersPublic 61 SESSIONNAME=Console 62 SystemDrive=C: 63 SystemRoot=C:Windows 64 TEMP=C:UsersAdministratorAppDataLocalTemp 65 TMP=C:UsersAdministratorAppDataLocalTemp 66 UCRTVersion=10.0.22621.0 67 UniversalCRTSdkDir=D:Windows Kits10 68 USERDOMAIN=MS-VMSQSWIJZFNM 69 USERDOMAIN_ROAMINGPROFILE=MS-VMSQSWIJZFNM 70 USERNAME=Administrator 71 USERPROFILE=C:UsersAdministrator 72 VCIDEInstallDir=D:Program FilesMicrosoft Visual Studio2022CommunityCommon7IDEVC 73 VCINSTALLDIR=D:Program FilesMicrosoft Visual Studio2022CommunityVC 74 VCToolsInstallDir=D:Program FilesMicrosoft Visual Studio2022CommunityVCToolsMSVC14.39.33519 75 VCToolsRedistDir=D:Program FilesMicrosoft Visual Studio2022CommunityVCRedistMSVC14.38.33135 76 VCToolsVersion=14.39.33519 77 VisualStudioVersion=17.0 78 VS170COMNTOOLS=D:Program FilesMicrosoft Visual Studio2022CommunityCommon7Tools 79 VSCMD_ARG_app_plat=Desktop 80 VSCMD_ARG_HOST_ARCH=x86 81 VSCMD_ARG_TGT_ARCH=x86 82 VSCMD_VER=17.9.2 83 VSINSTALLDIR=D:Program FilesMicrosoft Visual Studio2022Community 84 windir=C:Windows 85 WindowsLibPath=D:Windows Kits10UnionMetadata10.0.22621.0;D:Windows Kits10References10.0.22621.0 86 WindowsSdkBinPath=D:Windows Kits10bin 87 WindowsSdkDir=D:Windows Kits10 88 WindowsSDKLibVersion=10.0.22621.0 89 WindowsSdkVerBinPath=D:Windows Kits10bin10.0.22621.0 90 WindowsSDKVersion=10.0.22621.0 91 WindowsSDK_ExecutablePath_x64=C:Program Files (x86)Microsoft SDKsWindowsv10.0AbinNETFX 4.8 Toolsx64 92 WindowsSDK_ExecutablePath_x86=C:Program Files (x86)Microsoft SDKsWindowsv10.0AbinNETFX 4.8 T 93 ools 94 __DOTNET_ADD_32BIT=1 95 __DOTNET_PREFERRED_BITNESS=32 96 __VSCMD_PREINIT_PATH=C:Program Files (x86)Common FilesMVSRuntimeWin32_i86;C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:WindowsSystem32OpenSSH;C:Program FilesMicrosoft SQL Server150ToolsBinn;C:Program FilesMicrosoft SQL ServerClient SDKODBC170ToolsBinn;C:Program Filesdotnet;D:Program Files (x86)Microsoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100DTSBinn;D:Program Files (x86)Microsoft SQL Server100ToolsBinnVSShellCommon7IDE;C:Program Files (x86)Microsoft Visual Studio 9.0Common7IDEPrivateAssemblies;D:Program Files (x86)Microsoft SQL Server100DTSBinn;C:Program FilesMicrosoft SQL Server130ToolsBinn;D:XIMEAAPI;C:XIMEAAPI;D:Program FilesGitcmd;D:Windows Kits10Windows Performance Toolkit;D:Windows Kits10Debuggersx64;C:UsersAdministratorAppDataLocalMicrosoftWindowsApps;C:UsersAdministrator.dotnettools
我们执行【!ProcInfo -mem】命令,获取系统使用内存使用的信息。
1 0:009> !ProcInfo -mem 2 --------------------------------------- 3 Process Memory 4 WorkingSetSize: 18032 KB PeakWorkingSetSize: 18036 KB 5 VirtualSize: -1872280724 KB PeakVirtualSize: -1872280724 KB 6 PagefileUsage: 5460 KB PeakPagefileUsage: 5476 KB 7 --------------------------------------- 8 58 percent of memory is in use. 9 10 Memory Availability (Numbers in MB) 11 12 Total Avail 13 Physical Memory 16258 6707 14 Page File 18690 7452 15 Virtual Memory 134217727 132115918
我们执行【!ProcInfo -time】命令,获取系统处理时间的信息。
1 0:009> !ProcInfo -time 2 --------------------------------------- 3 Process Times 4 Process Started at: 2024 Mar 20 16:12:22.76 5 Kernel CPU time : 0 days 00:00:00.06 6 User CPU time : 0 days 00:00:00.09 7 Total CPU time : 0 days 00:00:00.15
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—>【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出【sum=21】 字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式。
这里的内容很简单,我不分步骤了,直接输出结果。
1 0:008> !ProcInfo -env 2 --------------------------------------- 3 Environment 4 =::=:: 5 ALLUSERSPROFILE=C:ProgramData 6 APPDATA=C:UsersAdministratorAppDataRoaming 7 CommonProgramFiles=C:Program FilesCommon Files 8 CommonProgramFiles(x86)=C:Program Files (x86)Common Files 9 CommonProgramW6432=C:Program FilesCommon Files 10 COMPLUS_MDA=1 11 COMPUTERNAME=MS-VMSQSWIJZFNM 12 ComSpec=C:Windowssystem32cmd.exe 13 DBGENG_OVERRIDE_DBGSRV_PATH=C:UsersAdministratorAppDataLocalMicrosoftWindowsAppsMicrosoft.WinDbg_8wekyb3d8bbwedbgsrvX64.exe 14 DBGHELP_HOMEDIR=C:ProgramDataDbg 15 DriverData=C:WindowsSystem32DriversDriverData 16 GENICAM_GENTL32_PATH=C:Program Files (x86)Common FilesMVSRuntimeWin32_i86 17 GENICAM_GENTL64_PATH=C:Program Files (x86)Common FilesMVSRuntimeWin64_x64 18 HOMEDRIVE=C: 19 HOMEPATH=UsersAdministrator 20 LOCALAPPDATA=C:UsersAdministratorAppDataLocal 21 LOGONSERVER=MS-VMSQSWIJZFNM 22 MVCAM_COMMON_RUNENV=D:Program Files (x86)MVSDevelopment 23 MVCAM_GENICAM_CLPROTOCOL=C:Program Files (x86)Common FilesMVSRuntimeCLProtocol 24 MVCAM_GIGE_DEBUG_HEARTBEAT=60000 25 NUMBER_OF_PROCESSORS=4 26 OneDrive=C:UsersAdministratorOneDrive 27 OS=Windows_NT 28 Path=C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64;C:Program Files (x86)Common FilesMVSRuntimeWin32_i86;C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:WindowsSystem32OpenSSH;C:Program FilesMicrosoft SQL Server150ToolsBinn;C:Program FilesMicrosoft SQL ServerClient SDKODBC170ToolsBinn;C:Program Filesdotnet;D:Program Files (x86)Microsoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100ToolsBinn;D:Program FilesMicrosoft SQL Server100DTSBinn;D:Program Files (x86)Microsoft SQL Server100ToolsBinnVSShellCommon7IDE;C:Program Files (x86)Microsoft Visual Studio 9.0Common7IDEPrivateAssemblies;D:Program Files (x86)Microsoft SQL Server100DTSBinn;C:Program FilesMicrosoft SQL Server130ToolsBinn;D:XIMEAAPI;C:XIMEAAPI;D:Program FilesGitcmd;D:Windows Kits10Windows Performance Toolkit;D:Wind 29 ows Kits10Debuggersx64;C:UsersAdministratorAppDataLocalMicrosoftWindowsApps;C:UsersAdministrator.dotnettools 30 PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC 31 PROCESSOR_ARCHITECTURE=AMD64 32 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 61 Stepping 4, GenuineIntel 33 PROCESSOR_LEVEL=6 34 PROCESSOR_REVISION=3d04 35 ProgramData=C:ProgramData 36 ProgramFiles=C:Program Files 37 ProgramFiles(x86)=C:Program Files (x86) 38 ProgramW6432=C:Program Files 39 PSModulePath=C:Program FilesWindowsPowerShellModules;C:Windowssystem32WindowsPowerShellv1.0Modules 40 PUBLIC=C:UsersPublic 41 SRCSRV_SHOW_TF_PROMPT=1 42 SystemDrive=C: 43 SystemRoot=C:Windows 44 TEMP=C:UsersAdministratorAppDataLocalTemp 45 TMP=C:UsersAdministratorAppDataLocalTemp 46 USERDOMAIN=MS-VMSQSWIJZFNM 47 USERDOMAIN_ROAMINGPROFILE=MS-VMSQSWIJZFNM 48 USERNAME=Administrator 49 USERPROFILE=C:UsersAdministrator 50 51 windir=C:Windows 52 0:008> !ProcInfo -mem 53 --------------------------------------- 54 Process Memory 55 WorkingSetSize: 17912 KB PeakWorkingSetSize: 17916 KB 56 VirtualSize: -1872282260 KB PeakVirtualSize: -1872282260 KB 57 PagefileUsage: 5380 KB PeakPagefileUsage: 5380 KB 58 --------------------------------------- 59 60 percent of memory is in use. 60 61 Memory Availability (Numbers in MB) 62 63 Total Avail 64 Physical Memory 16258 6410 65 Page File 18690 7124 66 Virtual Memory 134217727 131818889 67 68 0:008> !ProcInfo -time 69 --------------------------------------- 70 Process Times 71 Process Started at: 2024 Mar 20 16:22:16.61 72 Kernel CPU time : 0 days 00:00:00.03 73 User CPU time : 0 days 00:00:00.14 74 Total CPU time : 0 days 00:00:00.17
3.5、崩溃转储文件
A、基础知识
我们以前的分析都是实时调试会话,“实时”意味着我们调试的是一个正在运行的物理进程,可以访问进程的所有状态和控制被调试进程的运行过程。有时候,这种调试方式是不可行的,比如:一些生产的机器,这些机器位于锁定的数据中心,并且进入许可非常严格。当然了,还有其他情况。这时候,我们就需要使用“事后调试”的方法了。
转储崩溃的文件,有很多种方法和工具,我们主要介绍原书上的一些内容和我知道、并熟悉的一些工具,比如:ProcessExplorer。有其他更好的工具大家有也可以拿出来分享。
【.dump】命令的参数是一个文件名,表示要转储的文件。该命令还可以带一系列参数开关,这些参数控制着将哪些进程状态保存到转储文件中。当然,保存进程状态越多,调试成功的几率越大。
我介绍三种方法:
1)、使用非托管调试器元命令【.dump】转储文件。
2)、使用 Windows 系统的任务管理器转储文件。
3)、使用 ProcessExplorer 转储文件。
B、眼见为实
调试源码:ExampleCore_3_1_12
调试任务:转储文件。
1)、使用 【.dump】命令转储文件
1】、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.4】命令行工具,输入命令【NTSD E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_12binDebugnet8.0ExampleCore_3_1_12.exe】,打开调试器窗口。
我们使用【g】命令继续运行调试器,直到调试器输出【请输入一个除数:】字样,我们输入 0 ,回车继续运行。
调试器出现异常,进入中断模式。如图:
此时,我们就可以使用【.dump】命令转储文件了。
1 0:000> .dump F:booksMyTest2.dmp 2 Creating F:booksMyTest2.dmp - mini user dump 3 Dump successfully written
文件保存成功,我们重新打开一个调试器,输入命令【NTSD -z F:booksMyTest2.dmp】,我们开始调试转储文件。需要告诉调试器要调试的是一个快照,需要使用命令开关 -z,紧跟着转储文件的路径。
打开新的调试器窗口。
1 Microsoft (R) Windows Debugger Version 10.0.22621.2428 AMD64 2 Copyright (c) Microsoft Corporation. All rights reserved. 3 4 5 Loading Dump File [F:booksMyTest2.dmp] 6 User Mini Dump File: Only registers, stack and portions of memory are available 7 8 Symbol search path is: srv* 9 Executable search path is: 10 Windows 10 Version 19045 MP (4 procs) Free x64 11 Product: WinNt, suite: SingleUserTS 12 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406 13 Machine Name: 14 Debug session time: Thu Mar 21 13:08:52.000 2024 (UTC + 8:00) 15 System Uptime: not available 16 Process Uptime: 0 days 1:23:35.000 17 .................................. 18 This dump file has an exception of interest stored in it. 19 The stored exception information can be accessed via .ecxr. 20 (3968.2168): Integer divide-by-zero - code c0000094 (first/second chance not available) 21 For analysis of this file, run !analyze -v 22 00007ffc`16d1199c f77de0 idiv eax,dword ptr [rbp-20h] ss:00000092`50b7e510=00000000
在上面的调试输出中,第一部分信息表示调试器加载了一个卫星转储文件。微型转储只是众多转储文件中的一种类型,它包含了有限的进程信息。
接下来,重要的信息,在转储文件中包含了一个异常,并且异常信息可以通过【.ecxr】命令来提取。
1 0:000> .ecxr 2 rax=000000000000000a rbx=0000009250b7e638 rcx=0000000000000000 3 rdx=0000000000000000 rsi=0000009250b7e5e8 rdi=0000009250b7e788 4 rip=00007ffc16d1199c rsp=0000009250b7e4e0 rbp=0000009250b7e530 5 r8=00000193e5813b50 r9=0000009250b7e4c0 r10=00000fff8ed07bf4 6 r11=0000009250b7e330 r12=0000009250b7e5a0 r13=0000000000000004 7 r14=0000000000000001 r15=0000009250b7e700 8 iopl=0 nv up ei pl nz na pe nc 9 cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 10 00007ffc`16d1199c f77de0 idiv eax,dword ptr [rbp-20h] ss:00000092`50b7e510=00000000
执行【.ecxr】命令,可以看到与这个异常相关的信息,例如在发生异常时寄存器的值,通过这些信息就可以获取线程栈信息。
好了,剩下就是使用调试命令进行调试吧。
2】、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】—【Launch executable】,加载我们的项目文件:ExampleCore_3_1_12.exe。进入调试器后,直接【g】命令运行调试器。直到我们的控制台程序输出【请输入一个除数:】,我们输入一个 0,回车,控制台程序继续运行,程序会抛出一个异常,导致调试器进入中断模式,就可以开始我们的调试了。
1 0:000> g 2 ModLoad: 00007ffd`2ec10000 00007ffd`2ec42000 C:WindowsSystem32IMM32.DLL 3 ModLoad: 00007ffc`da2d0000 00007ffc`da329000 C:Program Filesdotnethostfxr8.0.3hostfxr.dll 4 ModLoad: 00007ffc`d63f0000 00007ffc`d6454000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3hostpolicy.dll 5 ModLoad: 00007ffc`6ddb0000 00007ffc`6e296000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3coreclr.dll 6 ModLoad: 00007ffd`2e950000 00007ffd`2ea7b000 C:WindowsSystem32ole32.dll 7 ModLoad: 00007ffd`2e4c0000 00007ffd`2e813000 C:WindowsSystem32combase.dll 8 ModLoad: 00007ffd`2f8a0000 00007ffd`2f96d000 C:WindowsSystem32OLEAUT32.dll 9 ModLoad: 00007ffd`2df10000 00007ffd`2df92000 C:WindowsSystem32bcryptPrimitives.dll 10 (3fdc.c48): Unknown exception - code 04242420 (first chance) 11 ModLoad: 00007ffc`6cb10000 00007ffc`6d79c000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Private.CoreLib.dll 12 ModLoad: 00007ffc`6c950000 00007ffc`6cb09000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3clrjit.dll 13 ModLoad: 00007ffd`2dff0000 00007ffd`2e002000 C:WindowsSystem32kernel.appcore.dll 14 ModLoad: 00000283`bb770000 00000283`bb778000 E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_12binDebugnet8.0ExampleCore_3_1_12.dll 15 ModLoad: 00000283`bb780000 00000283`bb78e000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Runtime.dll 16 ModLoad: 00007ffc`da2a0000 00007ffc`da2c8000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Console.dll 17 ModLoad: 00007ffd`0b5f0000 00007ffd`0b602000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Threading.dll 18 ModLoad: 00000283`bb790000 00000283`bb798000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Text.Encoding.Extensions.dll 19 ModLoad: 00007ffd`06c70000 00007ffd`06c85000 C:Program FilesdotnetsharedMicrosoft.NETCore.App8.0.3System.Runtime.InteropServices.dll 20 ModLoad: 00007ffc`fd310000 00007ffc`fd53e000 C:WindowsSYSTEM32icu.dll 21 (3fdc.c48): Integer divide-by-zero - code c0000094 (first chance)(说明系统抛出了一个除以0的异常) 22 First chance exceptions are reported before any exception handling. 23 This exception may be expected and handled. 24 *** WARNING: Unable to verify checksum for E:Visual Studio 2022SourceProjectsAdvancedDebug.NetFramework.TestExampleCore_3_1_12binDebugnet8.0ExampleCore_3_1_12.dll 25 ExampleCore_3_1_12!ExampleCore_3_1_12.Program.Main+0x6c: 26 00007ffc`0e3a199c f77de0 idiv eax,dword ptr [rbp-20h] ss:00000095`b617ec40=00000000
同时调试器也会打开【Program】窗口,显示我们程序的源码,并在出错的行暂停,如图:
此时,我们就可以使用【.dump】命令转储文件了。
1 0:000> .dump F:booksMyTest.dmp 2 Creating F:booksMyTest.dmp - mini user dump 3 Dump successfully written
dump 命令的参数是一个文件名,表示转储的文件。我们有了转储文件,现在就开始调试这个转储文件。
我们可以打开一个新的【Windbg】,也可以退出当前的重新加载。依次点击【文件】—【Open dump file】,加载我们前面保存的快照文件:MyTest.dmp,点击【open】按钮打开调试器。
1 0:000> .dump F:booksMyTest.dmp 2 Creating F:booksMyTest.dmp - mini user dump 3 Dump successfully written 4 windbg> q 5 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizersatlmfc.natvis' 6 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64VisualizersObjectiveC.natvis' 7 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizersconcurrency.natvis' 8 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizerscpp_rest.natvis' 9 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizersstl.natvis' 10 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64VisualizersWindows.Data.Json.natvis' 11 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64VisualizersWindows.Devices.Geolocation.natvis' 12 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64VisualizersWindows.Devices.Sensors.natvis' 13 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64VisualizersWindows.Media.natvis' 14 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizerswindows.natvis' 15 NatVis script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64Visualizerswinrt.natvis' 16 JavaScript script unloaded from 'C:Program FilesWindowsAppsMicrosoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbweamd64winextApiExtensionCodeFlow.js' 17 18 ************* Preparing the environment for Debugger Extensions Gallery repositories ************** 19 ExtensionRepository : Implicit 20 UseExperimentalFeatureForNugetShare : false 21 AllowNugetExeUpdate : false 22 NonInteractiveNuget : true 23 AllowNugetMSCredentialProviderInstall : false 24 AllowParallelInitializationOfLocalRepositories : true 25 26 EnableRedirectToV8JsProvider : false 27 28 -- Configuring repositories 29 ----> Repository : LocalInstalled, Enabled: true 30 ----> Repository : UserExtensions, Enabled: true 31 32 >>>>>>>>>>>>> Preparing the environment for Debugger Extensions Gallery repositories completed, duration 0.000 seconds 33 34 ************* Waiting for Debugger Extensions Gallery to Initialize ************** 35 36 >>>>>>>>>>>>> Waiting for Debugger Extensions Gallery to Initialize completed, duration 0.032 seconds 37 ----> Repository : UserExtensions, Enabled: true, Packages count: 0 38 ----> Repository : LocalInstalled, Enabled: true, Packages count: 41 39 40 Microsoft (R) Windows Debugger Version 10.0.27553.1004 AMD64 41 Copyright (c) Microsoft Corporation. All rights reserved. 42 43 44 Loading Dump File [F:BooksMyTest.dmp] 45 User Mini Dump File: Only registers, stack and portions of memory are available 46 47 48 ************* Path validation summary ************** 49 Response Time (ms) Location 50 Deferred srv* 51 Symbol search path is: srv* 52 Executable search path is: 53 Windows 10 Version 19045 MP (4 procs) Free x64 54 Product: WinNt, suite: SingleUserTS 55 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406 56 Debug session time: Thu Mar 21 11:27:02.000 2024 (UTC + 8:00) 57 System Uptime: 0 days 1:23:42.004 58 Process Uptime: 0 days 0:08:39.000 59 ....................................................... 60 This dump file has an exception of interest stored in it. 61 The stored exception information can be accessed via .ecxr. 62 (3fdc.c48): Integer divide-by-zero - code c0000094 (first/second chance not available) 63 For analysis of this file, run !analyze -v 64 00007ffc`0e3a199c f77de0 idiv eax,dword ptr [rbp-20h] ss:00000095`b617ec40=00000000
很简单,就不多说了。
2)、使用 Windows 系统的任务管理器转储文件。
这个很简单,直接上图看效果。我们打开【任务管理器】,在【进程】标签里,随便找一个进程,点击左侧 > 小于号图标,显示折叠的内容,我这里选择的是【Visual Studio】。
很简单,话不多说。
3)、使用 ProcessExplorer 转储文件
编译项目,找到程序的可执行文件,我们文件名是:ExampleCore_3_1_12.exe,双击运行。控制台程序输出:请输入一个除数。我们打开【ProcessExplorer】64位版本的。在右侧过滤框中输入 ExampleCore_3_1_12 ,查询出我们程序的进程。
此时,我们在项目进程上点击右键【Create Dump】,该菜单有两个子菜单,分别是:Create Minidump 和 Create full dump,这两个菜单的区别就是,Minidump 的文件包含的进程信息有限,full dump 包含完整进程信息。
四、总结
这篇文章的第三部分终于写完了。做程序员的,不能太懒惰,只有所有代码自己都写了,都测试了,才会明白里面发生的事情。Net 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
本文深入探讨了知识图谱的构建全流程,涵盖了基础理论、数据获取与预处理、知识表示方法、知识图谱构建技术等关键环节。 关注TechLead,分享AI全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器服务器托管网人智能…