一、介绍
这是我的《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 List<byte[]> list = new List<byte[]>();
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_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_9\Program.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_9\Program.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_9\Program.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_9\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_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 : Volatile<enum Thread::ThreadState>
6 +0x00c m_fPreemptiveGCDisabled : Volatile<unsigned long>
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<DeadlockAwareLock,DeadlockAwareLock *>
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 : Volatile<long>
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 : Volatile<long>
60 +0x3b0 m_hCurrNotification : 0xbaadf00d`baadf00d OBJECTHANDLE__
61 +0x3b8 m_fInteropDebuggingHijacked : 0n0
62 +0x3bc m_profilerCallbackState : 0
63 +0x3c0 m_dwProfilerEvacuationCounters : [33] Volatile<unsigned long>
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<DebuggerPatchSkip,DebuggerPatchSkip *>
75 +0x4b0 m_fAllowProfilerCallbacks : 0n1
76 +0x4b4 m_dwThreadHandleBeingUsed : Volatile<long>
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 : Volatile<unsigned long>
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 0302b2202 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\`0302b2203 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\`0302b2202 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 type19 5 1220DbgHelper
20 6 16f4 Finalizer21
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 type41 5 1220DbgHelper
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\Program.cs @ 11]
14 0b 000000da`5b97e770 00007ffd`fdbc14c9 coreclr!CallDescrWorkerInternal+0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm @ 100]
15 0c (Inline Function) --------`-------- coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67]
16 0d 000000da`5b97e7b0 00007ffd`fdae75ac coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 570]
17 0e (Inline Function) --------`-------- coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458]
18 0f 000000da`5b97e8f0 00007ffd`fdae6f7a coreclr!RunMainInternal+0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1304]
19 10 000000da`5b97ea10 00007ffd`fdae6b17 coreclr!RunMain+0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1375]
20 11 000000da`5b97eac0 00007ffd`fdae7321 coreclr!Assembly::ExecuteMainMethod+0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1504]
21 12 000000da`5b97ed90 00007ffd`fdbf7768 coreclr!CorHost2::ExecuteAssembly+0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp @ 349]
22 13 000000da`5b97ef00 00007ffe`c2c82c36 coreclr!coreclr_execute_assembly+0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp @ 504]
23 14 (Inline Function) --------`-------- hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109]
24 15 000000da`5b97efa0 00007ffe`c2c82f1c hostpolicy!run_app_for_context+0x596 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 256]
25 16 000000da`5b97f130 00007ffe`c2c8385a hostpolicy!run_app+0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 285]
26 17 000000da`5b97f170 00007ffe`c2cdb5c9 hostpolicy!corehost_main+0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 426]
27 18 000000da`5b97f270 00007ffe`c2cde066 hostfxr!execute_app+0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 145]
28 19 000000da`5b97f370 00007ffe`c2ce02ec hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 532]
29 1a 000000da`5b97f460 00007ffe`c2cde644 hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 1007]
30 1b 000000da`5b97f510 00007ffe`c2cd85a0 hostfxr!fx_muxer_t::execute+0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 578]
31 1c 000000da`5b97f650 00007ff7`e1a5f998 hostfxr!hostfxr_main_startupinfo+0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp @ 62]
32 1d 000000da`5b97f750 00007ff7`e1a5fda6 apphost!exe_start+0x878 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 240]
33 1e 000000da`5b97f920 00007ff7`e1a612e8 apphost!wmain+0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 311]
34 1f (Inline Function) --------`-------- apphost!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90]
35 20 000000da`5b97f990 00007ffe`d5416fd4 apphost!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\Program.cs @ 11]
14 0b 00007ffd`fdbc14c9 : 00000000`00000000 00000000`00000130 000000da`5b97e9b8 00007ffd`fdaea456 : coreclr!CallDescrWorkerInternal+0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm @ 100]
15 0c (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67]
16 0d 00007ffd`fdae75ac : 000000da`5b97ea38 00000000`00000000 00000000`00000048 00007ffd`fdbd28a6 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 570]
17 0e (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458]
18 0f 00007ffd`fdae6f7a : 00000251`29808ea0 00000251`29808ea0 00000000`00000000 000000da`5b97edb8 : coreclr!RunMainInternal+0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1304]
19 10 00007ffd`fdae6b17 : 00000251`25198390 00000251`00000000 00000251`25198390 00000000`00000000 : coreclr!RunMain+0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1375]
20 11 00007ffd`fdae7321 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1504]
21 12 00007ffd`fdbf7768 : 00000000`00000001 000000da`5b97ef01 000000da`5b97efe0 00007ffe`c2c623ea : coreclr!CorHost2::ExecuteAssembly+0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp @ 349]
22 13 00007ffe`c2c82c36 : 00000251`2516a900 00000251`2516a660 00000000`00000000 00000251`2516a660 : coreclr!coreclr_execute_assembly+0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp @ 504]
23 14 (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109]
24 15 00007ffe`c2c82f1c : 00000251`25158228 000000da`5b97f209 00007ffe`c2cbc9c0 00000251`25158228 : hostpolicy!run_app_for_context+0x596 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 256]
25 16 00007ffe`c2c8385a : 00000000`00000000 00000251`25158220 00000251`25158220 00000000`00000000 : hostpolicy!run_app+0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 285]
26 17 00007ffe`c2cdb5c9 : 00000251`251669e8 00000251`251668d0 00000000`00000000 000000da`5b97f309 : hostpolicy!corehost_main+0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 426]
27 18 00007ffe`c2cde066 : 00000251`25167540 000000da`5b97f690 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_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\_work\1\s\src\native\corehost\fxr\fx_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\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 1007]
30 1b 00007ffe`c2cd85a0 : 000000da`5b97f6b0 00000251`25166690 00000000`00000001 00000251`25150000 : hostfxr!fx_muxer_t::execute+0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 578]
31 1c 00007ff7`e1a5f998 : 00007ffe`d48ef4e8 00007ffe`c2cd9b10 000000da`5b97f850 00000251`25166380 : hostfxr!hostfxr_main_startupinfo+0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp @ 62]
32 1d 00007ff7`e1a5fda6 : 00007ff7`e1a6b6c0 00000000`00000007 00000251`25158220 00000000`0000005e : apphost!exe_start+0x878 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 240]
33 1e 00007ff7`e1a612e8 : 00000000`00000000 00000000`00000000 00000251`25158220 00000000`00000000 : apphost!wmain+0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 311]
34 1f (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90]
35 20 00007ffe`d5416fd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : apphost!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_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\_work\1\s\src\coreclr\vm\amd64\AsmHelpers.asm:185], calling coreclr!NDirectImportWorker [D:\a\_work\1\s\src\coreclr\vm\dllimport.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\_work\1\s\src\coreclr\vm\amd64\PInvokeStubs.asm:142]
9 000000DA5B97E480 00007ffec2c3c9c0 (MethodDesc 00007ffd9e1a2cc0 + 0x60 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1<Byte>, Boolean, Int32 ByRef, Boolean)), calling System_Console + 0x10b04
10 000000DA5B97E4E0 00007ffec2c3c8bb (MethodDesc 00007ffd9e1a2c90 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1<Byte>)), 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\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm:100]
18 000000DA5B97E7A0 00007ffdfdbc14c9 coreclr!MethodDescCallSite::CallTargetWorker + 0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp:570], calling coreclr!CallDescrWorkerInternal [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm:37]
19 000000DA5B97E7C0 00007ffdfdaea456 coreclr!AppDomain::LoadDomainAssembly + 0xee [D:\a\_work\1\s\src\coreclr\vm\appdomain.cpp:2596], calling coreclr!AppDomain::LoadDomainAssembly [D:\a\_work\1\s\src\coreclr\vm\appdomain.cpp:2809]
20 000000DA5B97E830 00007ffdfdae708b coreclr!MethodDesc::IsVoid + 0x2f [D:\a\_work\1\s\src\coreclr\vm\method.cpp:987], calling coreclr!SigPointer::PeekElemTypeClosed [D:\a\_work\1\s\src\coreclr\vm\siginfo.cpp:2429]
21 000000DA5B97E8E0 00007ffdfdae75ac coreclr!RunMainInternal + 0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp:1304], calling coreclr!MethodDescCallSite::CallTargetWorker [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp:260]
22 000000DA5B97E900 00007ffdfdbd28a6 coreclr!MDInternalRO::GetSigOfMethodDef + 0x86 [D:\a\_work\1\s\src\coreclr\md\runtime\mdinternalro.cpp:1403], calling ntdll!LdrpDispatchUserCallTarget
23 000000DA5B97EA00 00007ffdfdae6f7a coreclr!RunMain + 0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp:1375], calling coreclr!RunMainInternal [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp:1275]
24 000000DA5B97EAB0 00007ffdfdae6b17 coreclr!Assembly::ExecuteMainMethod + 0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp:1504], calling coreclr!RunMain [D:\a\_work\1\s\src\coreclr\vm\assembly.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\_work\1\s\src\coreclr\vm\amd64\ExternalMethodFixupThunk.asm:61], calling coreclr!DynamicHelperWorker [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp:3776]
27 000000DA5B97EBF0 00007ffdfca71af0 (MethodDesc 00007ffd9e14f460 + 0xb0 System.Environment.InitializeCommandLineArgs(Char*, Int32, Char**)), calling 00007ffd9df50010
28 000000DA5B97EC50 00007ffdfdc3a1a3 coreclr!CallDescrWorkerInternal + 0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm:100]
29 000000DA5B97EC90 00007ffdfdb2389e coreclr!DispatchCallSimple + 0x72 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp:222], calling coreclr!__security_check_cookie [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\gs\amd64\amdsecgs.asm:45]
30 000000DA5B97ECB0 00007ffdfdbc1abf coreclr!MethodDesc::TryGetMultiCallableAddrOfCode + 0x87 [D:\a\_work\1\s\src\coreclr\vm\method.cpp:2071], calling coreclr!MethodDesc::GetMethodEntryPoint [D:\a\_work\1\s\src\coreclr\vm\method.cpp:477]
31 000000DA5B97ECF0 00007ffdfdbf9eb2 coreclr!`anonymous namespace'::GetConfigDWORD + 0x32 [D:\a\_work\1\s\src\coreclr\utilcode\clrconfig.cpp:247], calling coreclr!`anonymous namespace'::EnvGetString [D:\a\_work\1\s\src\coreclr\utilcode\clrconfig.cpp:134]
32 000000DA5B97ED80 00007ffdfdae7321 coreclr!CorHost2::ExecuteAssembly + 0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp:349], calling coreclr!Assembly::ExecuteMainMethod [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp:1440]
33 000000DA5B97EDE0 00007ffed44ce8e6 KERNELBASE!MultiByteToWideChar + 0x186, calling KERNELBASE!_security_check_cookie
34 000000DA5B97EEB0 00007ffdfdbf7ad4 coreclr!StringToUnicode + 0x94 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp:87], calling KERNEL32!MultiByteToWideCharStub
35 000000DA5B97EEF0 00007ffdfdbf7768 coreclr!coreclr_execute_assembly + 0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp:504], calling ntdll!LdrpDispatchUserCallTarget
36 000000DA5B97EF10 00007ffec2c623ea hostpolicy!breadcrumb_writer_t::begin_write + 0x1ba [D:\a\_work\1\s\src\native\corehost\hostpolicy\breadcrumbs.cpp:39], calling hostpolicy!trace::verbose [D:\a\_work\1\s\src\native\corehost\hostmisc\trace.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\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:256], calling ntdll!LdrpDispatchUserCallTarget
40 000000DA5B97EFD0 00007ffed6f295f1 ntdll!RtlpFreeHeapInternal + 0x491, calling ntdll!RtlpHpStackLoggingEnabled
41 000000DA5B97F070 00007ffec2c9bd64 hostpolicy!mtx_do_lock + 0x11c [D:\a\_work\1\s\src\vctools\crt\github\stl\src\mutex.cpp:146], calling hostpolicy!__security_check_cookie [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\gs\amd64\amdsecgs.asm:45]
42 000000DA5B97F090 00007ffec2c9bc40 hostpolicy!_Mtx_unlock + 0x18 [D:\a\_work\1\s\src\vctools\crt\github\stl\src\mutex.cpp:159], calling ntdll!RtlReleaseSRWLockExclusive
43 000000DA5B97F0C0 00007ffec2c82316 hostpolicy!`anonymous namespace'::get_hostpolicy_context + 0x116 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:168], calling hostpolicy!_Mtx_unlock [D:\a\_work\1\s\src\vctools\crt\github\stl\src\mutex.cpp:148]
44 000000DA5B97F120 00007ffec2c82f1c hostpolicy!run_app + 0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:285], calling hostpolicy!run_app_for_context [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:208]
45 000000DA5B97F160 00007ffec2c8385a hostpolicy!corehost_main + 0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:426], calling hostpolicy!run_app [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp:280]
46 000000DA5B97F260 00007ffec2cdb5c9 hostfxr!execute_app + 0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:145], calling ntdll!LdrpDispatchUserCallTarget
47 000000DA5B97F360 00007ffec2cde066 hostfxr!`anonymous namespace'::read_config_and_execute + 0xa6 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:532], calling hostfxr!execute_app [D:\a\_work\1\s\src\native\corehost\fxr\fx_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\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:1007], calling hostfxr!`anonymous namespace'::read_config_and_execute [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:515]
50 000000DA5B97F500 00007ffec2cde644 hostfxr!fx_muxer_t::execute + 0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:578], calling hostfxr!fx_muxer_t::handle_exec_host_command [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp:990]
51 000000DA5B97F640 00007ffec2cd85a0 hostfxr!hostfxr_main_startupinfo + 0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp:62], calling hostfxr!fx_muxer_t::execute [D:\a\_work\1\s\src\native\corehost\fxr\fx_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\_work\1\s\src\native\corehost\corehost.cpp:240], calling ntdll!LdrpDispatchUserCallTarget
55 000000DA5B97F780 00007ff7e1a572bf apphost!trace::setup + 0x28f [D:\a\_work\1\s\src\native\corehost\hostmisc\trace.cpp:76], calling apphost!__security_check_cookie [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\gs\amd64\amdsecgs.asm:45]
56 000000DA5B97F910 00007ff7e1a5fda6 apphost!wmain + 0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp:311], calling apphost!exe_start [D:\a\_work\1\s\src\native\corehost\corehost.cpp:101]
57 000000DA5B97F950 00007ff7e1a6100d apphost!__scrt_release_startup_lock + 0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility.cpp:161], calling apphost!__scrt_is_ucrt_dll_in_use [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\utility\ucrt_detection.c:22]
58 000000DA5B97F980 00007ff7e1a612e8 apphost!__scrt_common_main_seh + 0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288], calling apphost!wmain [D:\a\_work\1\s\src\native\corehost\corehost.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<Byte>, Boolean, Int32 ByRef, Boolean))
8 000000DA5B97E4E0 00007ffec2c3c8bb (MethodDesc 00007ffd9e1a2c90 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1<Byte>))
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_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\_work\1\s\src\coreclr\vm\prestub.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\_work\1\s\src\coreclr\vm\jithelpers.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\_work\1\s\src\coreclr\vm\virtualcallstub.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\_work\1\s\src\coreclr\vm\codeman.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\_work\1\s\src\coreclr\vm\codeman.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\_work\1\s\src\coreclr\vm\comsynchronizable.cpp:230], calling coreclr!ManagedThreadBase_DispatchOuter [D:\a\_work\1\s\src\coreclr\vm\threads.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\_work\1\s\src\coreclr\vm\virtualcallstub.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\_work\1\s\src\coreclr\vm\amd64\AsmHelpers.asm:185], calling coreclr!NDirectImportWorker [D:\a\_work\1\s\src\coreclr\vm\dllimport.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<Byte>, Boolean))
26 000000AFC9D7EC60 00007ff89172c90b (MethodDesc 00007ff7bf672f30 + 0x1b System.ConsolePal+WindowsConsoleStream.Write(System.ReadOnlySpan`1<Byte>))
27 000000AFC9D7EC90 00007ff81e10c6be (MethodDesc 00007ff7bf678cf8 + 0x17e System.IO.StreamWriter.Flush(Boolean, Boolean))
28 000000AFC9D7F1D0 00007ff891729bba (MethodDesc 00007ff7bf64a6f8 + 0x6a System.Console.<get_In>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.<get_In>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<Byte>, Boolean, Int32 ByRef, Boolean))
64 000000AFCA07F430 00007ff89172c8bb (MethodDesc 00007ff7bf672f20 + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1<Byte>))
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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`c61a1935 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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.cs @ 9]
14 0b 000000f8`6fb7e710 00007ff8`10b914c9 coreclr!CallDescrWorkerInternal+0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm @ 100]
15 0c (Inline Function) --------`-------- coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67]
16 0d 000000f8`6fb7e750 00007ff8`10ab75ac coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 570]
17 0e (Inline Function) --------`-------- coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458]
18 0f 000000f8`6fb7e890 00007ff8`10ab6f7a coreclr!RunMainInternal+0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1304]
19 10 000000f8`6fb7e9b0 00007ff8`10ab6b17 coreclr!RunMain+0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1375]
20 11 000000f8`6fb7ea60 00007ff8`10ab7321 coreclr!Assembly::ExecuteMainMethod+0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1504]
21 12 000000f8`6fb7ed30 00007ff8`10bc7768 coreclr!CorHost2::ExecuteAssembly+0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp @ 349]
22 13 000000f8`6fb7eea0 00007ff8`d3be2c36 coreclr!coreclr_execute_assembly+0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp @ 504]
23 14 (Inline Function) --------`-------- hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109]
24 15 000000f8`6fb7ef40 00007ff8`d3be2f1c hostpolicy!run_app_for_context+0x596 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 256]
25 16 000000f8`6fb7f0d0 00007ff8`d3be385a hostpolicy!run_app+0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 285]
26 17 000000f8`6fb7f110 00007ff8`d3c3b5c9 hostpolicy!corehost_main+0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 426]
27 18 000000f8`6fb7f210 00007ff8`d3c3e066 hostfxr!execute_app+0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 145]
28 19 000000f8`6fb7f310 00007ff8`d3c402ec hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 532]
29 1a 000000f8`6fb7f400 00007ff8`d3c3e644 hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 1007]
30 1b 000000f8`6fb7f4b0 00007ff8`d3c385a0 hostfxr!fx_muxer_t::execute+0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 578]
31 1c 000000f8`6fb7f5f0 00007ff7`bcdef998 hostfxr!hostfxr_main_startupinfo+0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp @ 62]
32 1d 000000f8`6fb7f6f0 00007ff7`bcdefda6 apphost!exe_start+0x878 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 240]
33 1e 000000f8`6fb7f8c0 00007ff7`bcdf12e8 apphost!wmain+0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 311]
34 1f (Inline Function) --------`-------- apphost!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90]
35 20 000000f8`6fb7f930 00007ff8`f9287344 apphost!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_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\_work\1\s\src\coreclr\vm\callhelpers.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 <Code Address>,这个命令不区分大小写。
如果想快速找出汇编代码位于哪个函数中,使用【!IP2MD】命令是很方便的。
这是一种方法,要想查找方法描述符,还有一个命令可以使用,它就是【!name2ee】,该命令的格式:Name2EE <module 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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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: 00007ff7dd5200c06 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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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: 00007ff7d75c00c06 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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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: 00007ff7dd5200c06 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 <unknown token type 2b000000>
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\Program.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: 00007ff7dcfb00c06 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 <unknown token type 2b000000>
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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: 00007ff7dd6401006 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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_11\bin\Debug\net8.0\ExampleCore_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:\books\myTest.txt】,输出文件。
1 0:009> !TraverseHeap F:\books\myTest.txt
2 Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option
3 Writing CLRProfiler format to file F:\books\myTest.txt
4 Gathering types...
5 tracing roots...
6
7 Walking heap...
8
9 file F:\books\myTest.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 7ff7dd619cc0 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<System.Byte[]>
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<System.Byte[]>
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_11\Program.cs @ 14]
11 rbp-18: 000000165219e7e8
12 -> 01a607d725d0 System.Byte[]
13
14 rbp-10: 000000165219e7f0
15 -> 01a607009650 System.Collections.Generic.List<System.Byte[]>
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:\books\myTest.txt】,输出文件。
1 0:001> !TraverseHeap F:\books\myTest.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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_10\bin\Debug\net8.0\ExampleCore_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_10\bin\Debug\net8.0\ExampleCore_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 Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll]
10 ClassLoader: 0000016888E790F0
11 Module
12 00007ffb4f594000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
13
14 Assembly: 000001688a8c7ac0 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll]
15 ClassLoader: 000001688A8C7B50
16 Module
17 00007ffb4f77e0a0 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll
18
19 Assembly: 0000016888e480b0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll]
20 ClassLoader: 0000016888E48140
21 Module
22 00007ffb4f77fbc8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
23
24 Assembly: 0000016888eb95c0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll]
25 ClassLoader: 0000016888EB9930
26 Module
27 00007ffb4f7ac3a8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
28
29 Assembly: 000001688a8ca600 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll]
30 ClassLoader: 000001688A8CA950
31 Module
32 00007ffb4f7d72f0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
33
34 Assembly: 0000016888e8f6b0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll]
35 ClassLoader: 0000016888E8F740
36 Module
37 00007ffb4f7dafa8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
38
39 Assembly: 0000016888e94090 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll]
40 ClassLoader: 0000016888E94120
41 Module
42 00007ffb4f7dd480 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.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 Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll]
17 ClassLoader: 0000016888E790F0
18 Module
19 00007ffb4f594000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
20
21 Assembly: 000001688a8c7ac0 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll]
22 ClassLoader: 000001688A8C7B50
23 Module
24 00007ffb4f77e0a0 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll
25
26 Assembly: 0000016888e480b0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll]
27 ClassLoader: 0000016888E48140
28 Module
29 00007ffb4f77fbc8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
30
31 Assembly: 0000016888eb95c0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll]
32 ClassLoader: 0000016888EB9930
33 Module
34 00007ffb4f7ac3a8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
35
36 Assembly: 000001688a8ca600 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll]
37 ClassLoader: 000001688A8CA950
38 Module
39 00007ffb4f7d72f0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
40
41 Assembly: 0000016888e8f6b0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll]
42 ClassLoader: 0000016888E8F740
43 Module
44 00007ffb4f7dafa8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
45
46 Assembly: 0000016888e94090 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll]
47 ClassLoader: 0000016888E94120
48 Module
49 00007ffb4f7dd480 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.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 Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll]
10 ClassLoader: 00000185C2CDF7B0
11 Module
12 00007ffb4a024000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
13
14 Assembly: 00000185c2cccba0 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll]
15 ClassLoader: 00000185C2CCCC30
16 Module
17 00007ffb4a20e0a0 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll
18
19 Assembly: 00000185c461e520 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll]
20 ClassLoader: 00000185C461E5B0
21 Module
22 00007ffb4a20fbc8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
23
24 Assembly: 00000185c2cce6c0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll]
25 ClassLoader: 00000185C2CCEA30
26 Module
27 00007ffb4a23c3a8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
28
29 Assembly: 00000185c2cf1ab0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll]
30 ClassLoader: 00000185C2CF1B40
31 Module
32 00007ffb4a2672f0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
33
34 Assembly: 00000185c2cf6480 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll]
35 ClassLoader: 00000185C2CF6510
36 Module
37 00007ffb4a26afa8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
38
39 Assembly: 00000185c2cf6760 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll]
40 ClassLoader: 00000185C4620050
41 Module
42 00007ffb4a26d480 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.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 Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll]
17 ClassLoader: 00000185C2CDF7B0
18 Module
19 00007ffb4a024000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
20
21 Assembly: 00000185c2cccba0 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll]
22 ClassLoader: 00000185C2CCCC30
23 Module
24 00007ffb4a20e0a0 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_3_1_10.dll
25
26 Assembly: 00000185c461e520 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll]
27 ClassLoader: 00000185C461E5B0
28 Module
29 00007ffb4a20fbc8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
30
31 Assembly: 00000185c2cce6c0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll]
32 ClassLoader: 00000185C2CCEA30
33 Module
34 00007ffb4a23c3a8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
35
36 Assembly: 00000185c2cf1ab0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll]
37 ClassLoader: 00000185C2CF1B40
38 Module
39 00007ffb4a2672f0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
40
41 Assembly: 00000185c2cf6480 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll]
42 ClassLoader: 00000185C2CF6510
43 Module
44 00007ffb4a26afa8 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
45
46 Assembly: 00000185c2cf6760 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll]
47 ClassLoader: 00000185C4620050
48 Module
49 00007ffb4a26d480 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_10\bin\Debug\net8.0\ExampleCore_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 Studio\Installer
6 =D:=D:\Program Files\Microsoft Visual Studio\2022\Community
7 =ExitCode=00000000
8 ALLUSERSPROFILE=C:\ProgramData
9 APPDATA=C:\Users\Administrator\AppData\Roaming
10 CommandPromptType=Native
11 CommonProgramFiles=C:\Program Files\Common Files
12 CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
13 CommonProgramW6432=C:\Program Files\Common Files
14 COMPLUS_MDA=1
15 COMPUTERNAME=MS-VMSQSWIJZFNM
16 ComSpec=C:\Windows\system32\cmd.exe
17 DevEnvDir=D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\
18 DriverData=C:\Windows\System32\Drivers\DriverData
19 ExtensionSdkDir=C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs
20 EXTERNAL_INCLUDE=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\ATLMFC\include;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include;D:\Windows Kits\10\include\10.0.22621.0\ucrt;D:\Windows Kits\10\\include\10.0.22621.0\\um;D:\Windows Kits\10\\include\10.0.22621.0\\shared;D:\Windows Kits\10\\include\10.0.22621.0\\winrt;D:\Windows Kits\10\\include\10.0.22621.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um
21 FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer
22 FPS_BROWSER_USER_PROFILE_STRING=Default
23 Framework40Version=v4.0
24 FrameworkDir=C:\Windows\Microsoft.NET\Framework\
25 FrameworkDir32=C:\Windows\Microsoft.NET\Framework\
26 FrameworkVersion=v4.0.30319
27 FrameworkVersion32=v4.0.30319
28 FSHARPINSTALLDIR=D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\FSharp\Tools
29 GENICAM_GENTL32_PATH=C:\Program Files (x86)\Common Files\MVS\Runtime\Win32_i86
30 GENICAM_GENTL64_PATH=C:\Program Files (x86)\Common Files\MVS\Runtime\Win64_x64
31 HOMEDRIVE=C:
32 HOMEPATH=\Users\Administrator
33 INCLUDE=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\
34 14.39.33519\ATLMFC\include;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include;D:\Windows Kits\10\include\10.0.22621.0\ucrt;D:\Windows Kits\10\\include\10.0.22621.0\\um;D:\Windows Kits\10\\include\10.0.22621.0\\shared;D:\Windows Kits\10\\include\10.0.22621.0\\winrt;D:\Windows Kits\10\\include\10.0.22621.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um
35 LIB=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\ATLMFC\lib\x86;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\lib\x86;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x86;D:\Windows Kits\10\lib\10.0.22621.0\ucrt\x86;D:\Windows Kits\10\\lib\10.0.22621.0\\um\x86
36 LIBPATH=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\ATLMFC\lib\x86;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\lib\x86;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\lib\x86\store\references;D:\Windows Kits\10\UnionMetadata\10.0.22621.0;D:\Windows Kits\10\References\10.0.22621.0;C:\Windows\Microsoft.NET\Framework\v4.0.30319
37 LOCALAPPDATA=C:\Users\Administrator\AppData\Local
38 LOGONSERVER=\\MS-VMSQSWIJZFNM
39 MVCAM_COMMON_RUNENV=D:\Program Files (x86)\MVS\Development
40 MVCAM_GENICAM_CLPROTOCOL=C:\Program Files (x86)\Common Files\MVS\Runtime\CLProtocol
41 MVCAM_GIGE_DEBUG_HEARTBEAT=60000
42 NETFXSDKDir=C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\
43 NUMBER_OF_PROCESSORS=4
44 OneDrive=C:\Users\Administrator\OneDrive
45 OS=Windows_NT
46 Path=D:\Windows Kits\10\Debuggers\x64;D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\HostX86\x86;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VC\VCPackages;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer;D:\Program Files\Micr
47 osoft Visual Studio\2022\Community\MSBuild\Current\bin\Roslyn;D:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\FSharp\Tools;D:\Program Files\Microsoft Visual Studio\2022\Community\Team Tools\DiagnosticsHub\Collector;D:\Windows Kits\10\bin\10.0.22621.0\\x86;D:\Windows Kits\10\bin\\x86;D:\Program Files\Microsoft Visual Studio\2022\Community\\MSBuild\Current\Bin\amd64;C:\Windows\Microsoft.NET\Framework\v4.0.30319;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\;C:\Program Files (x86)\Common Files\MVS\Runtime\Win32_i86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\dotnet\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\DTS\Binn\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\;D:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;D:\XIMEA\API;C:\XIMEA\API;D:\Program Files\Git\cmd;D:\Windows Kits\10\Windows Performance Toolkit\;D:\Windows Kits\10\Debuggers\x64;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Users\Administrator\.dotnet\tools;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin;D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;D:\Program Files\Microsoft Visual Studio\2022\Community\Commo
48 n7\IDE\VC\Linux\bin\ConnectionManagerExe
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 Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
60 PUBLIC=C:\Users\Public
61 SESSIONNAME=Console
62 SystemDrive=C:
63 SystemRoot=C:\Windows
64 TEMP=C:\Users\Administrator\AppData\Local\Temp
65 TMP=C:\Users\Administrator\AppData\Local\Temp
66 UCRTVersion=10.0.22621.0
67 UniversalCRTSdkDir=D:\Windows Kits\10\
68 USERDOMAIN=MS-VMSQSWIJZFNM
69 USERDOMAIN_ROAMINGPROFILE=MS-VMSQSWIJZFNM
70 USERNAME=Administrator
71 USERPROFILE=C:\Users\Administrator
72 VCIDEInstallDir=D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VC\
73 VCINSTALLDIR=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\
74 VCToolsInstallDir=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\
75 VCToolsRedistDir=D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.38.33135\
76 VCToolsVersion=14.39.33519
77 VisualStudioVersion=17.0
78 VS170COMNTOOLS=D:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\
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 Files\Microsoft Visual Studio\2022\Community\
84 windir=C:\Windows
85 WindowsLibPath=D:\Windows Kits\10\UnionMetadata\10.0.22621.0;D:\Windows Kits\10\References\10.0.22621.0
86 WindowsSdkBinPath=D:\Windows Kits\10\bin\
87 WindowsSdkDir=D:\Windows Kits\10\
88 WindowsSDKLibVersion=10.0.22621.0\
89 WindowsSdkVerBinPath=D:\Windows Kits\10\bin\10.0.22621.0\
90 WindowsSDKVersion=10.0.22621.0\
91 WindowsSDK_ExecutablePath_x64=C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\
92 WindowsSDK_ExecutablePath_x86=C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 T
93 ools\
94 __DOTNET_ADD_32BIT=1
95 __DOTNET_PREFERRED_BITNESS=32
96 __VSCMD_PREINIT_PATH=C:\Program Files (x86)\Common Files\MVS\Runtime\Win32_i86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\dotnet\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\DTS\Binn\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\;D:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;D:\XIMEA\API;C:\XIMEA\API;D:\Program Files\Git\cmd;D:\Windows Kits\10\Windows Performance Toolkit\;D:\Windows Kits\10\Debuggers\x64;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Users\Administrator\.dotnet\tools
我们执行【!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:\Users\Administrator\AppData\Roaming
7 CommonProgramFiles=C:\Program Files\Common Files
8 CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
9 CommonProgramW6432=C:\Program Files\Common Files
10 COMPLUS_MDA=1
11 COMPUTERNAME=MS-VMSQSWIJZFNM
12 ComSpec=C:\Windows\system32\cmd.exe
13 DBGENG_OVERRIDE_DBGSRV_PATH=C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps\Microsoft.WinDbg_8wekyb3d8bbwe\dbgsrvX64.exe
14 DBGHELP_HOMEDIR=C:\ProgramData\Dbg
15 DriverData=C:\Windows\System32\Drivers\DriverData
16 GENICAM_GENTL32_PATH=C:\Program Files (x86)\Common Files\MVS\Runtime\Win32_i86
17 GENICAM_GENTL64_PATH=C:\Program Files (x86)\Common Files\MVS\Runtime\Win64_x64
18 HOMEDRIVE=C:
19 HOMEPATH=\Users\Administrator
20 LOCALAPPDATA=C:\Users\Administrator\AppData\Local
21 LOGONSERVER=\\MS-VMSQSWIJZFNM
22 MVCAM_COMMON_RUNENV=D:\Program Files (x86)\MVS\Development
23 MVCAM_GENICAM_CLPROTOCOL=C:\Program Files (x86)\Common Files\MVS\Runtime\CLProtocol
24 MVCAM_GIGE_DEBUG_HEARTBEAT=60000
25 NUMBER_OF_PROCESSORS=4
26 OneDrive=C:\Users\Administrator\OneDrive
27 OS=Windows_NT
28 Path=C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64;C:\Program Files (x86)\Common Files\MVS\Runtime\Win32_i86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\dotnet\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\DTS\Binn\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\;D:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;D:\XIMEA\API;C:\XIMEA\API;D:\Program Files\Git\cmd;D:\Windows Kits\10\Windows Performance Toolkit\;D:\Wind
29 ows Kits\10\Debuggers\x64;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Users\Administrator\.dotnet\tools
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 Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
40 PUBLIC=C:\Users\Public
41 SRCSRV_SHOW_TF_PROMPT=1
42 SystemDrive=C:
43 SystemRoot=C:\Windows
44 TEMP=C:\Users\Administrator\AppData\Local\Temp
45 TMP=C:\Users\Administrator\AppData\Local\Temp
46 USERDOMAIN=MS-VMSQSWIJZFNM
47 USERDOMAIN_ROAMINGPROFILE=MS-VMSQSWIJZFNM
48 USERNAME=Administrator
49 USERPROFILE=C:\Users\Administrator
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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_12\bin\Debug\net8.0\ExampleCore_3_1_12.exe】,打开调试器窗口。
我们使用【g】命令继续运行调试器,直到调试器输出【请输入一个除数:】字样,我们输入 0 ,回车继续运行。
调试器出现异常,进入中断模式。如图:
此时,我们就可以使用【.dump】命令转储文件了。
1 0:000> .dump F:\books\MyTest2.dmp
2 Creating F:\books\MyTest2.dmp - mini user dump
3 Dump successfully written
文件保存成功,我们重新打开一个调试器,输入命令【NTSD -z F:\books\MyTest2.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:\books\MyTest2.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:\Windows\System32\IMM32.DLL
3 ModLoad: 00007ffc`da2d0000 00007ffc`da329000 C:\Program Files\dotnet\host\fxr\8.0.3\hostfxr.dll
4 ModLoad: 00007ffc`d63f0000 00007ffc`d6454000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\hostpolicy.dll
5 ModLoad: 00007ffc`6ddb0000 00007ffc`6e296000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\coreclr.dll
6 ModLoad: 00007ffd`2e950000 00007ffd`2ea7b000 C:\Windows\System32\ole32.dll
7 ModLoad: 00007ffd`2e4c0000 00007ffd`2e813000 C:\Windows\System32\combase.dll
8 ModLoad: 00007ffd`2f8a0000 00007ffd`2f96d000 C:\Windows\System32\OLEAUT32.dll
9 ModLoad: 00007ffd`2df10000 00007ffd`2df92000 C:\Windows\System32\bcryptPrimitives.dll
10 (3fdc.c48): Unknown exception - code 04242420 (first chance)
11 ModLoad: 00007ffc`6cb10000 00007ffc`6d79c000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Private.CoreLib.dll
12 ModLoad: 00007ffc`6c950000 00007ffc`6cb09000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\clrjit.dll
13 ModLoad: 00007ffd`2dff0000 00007ffd`2e002000 C:\Windows\System32\kernel.appcore.dll
14 ModLoad: 00000283`bb770000 00000283`bb778000 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_12\bin\Debug\net8.0\ExampleCore_3_1_12.dll
15 ModLoad: 00000283`bb780000 00000283`bb78e000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Runtime.dll
16 ModLoad: 00007ffc`da2a0000 00007ffc`da2c8000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Console.dll
17 ModLoad: 00007ffd`0b5f0000 00007ffd`0b602000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Threading.dll
18 ModLoad: 00000283`bb790000 00000283`bb798000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Text.Encoding.Extensions.dll
19 ModLoad: 00007ffd`06c70000 00007ffd`06c85000 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.3\System.Runtime.InteropServices.dll
20 ModLoad: 00007ffc`fd310000 00007ffc`fd53e000 C:\Windows\SYSTEM32\icu.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 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_12\bin\Debug\net8.0\ExampleCore_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:\books\MyTest.dmp
2 Creating F:\books\MyTest.dmp - mini user dump
3 Dump successfully written
dump 命令的参数是一个文件名,表示转储的文件。我们有了转储文件,现在就开始调试这个转储文件。
我们可以打开一个新的【Windbg】,也可以退出当前的重新加载。依次点击【文件】---【Open dump file】,加载我们前面保存的快照文件:MyTest.dmp,点击【open】按钮打开调试器。
1 0:000> .dump F:\books\MyTest.dmp
2 Creating F:\books\MyTest.dmp - mini user dump
3 Dump successfully written
4 windbg> q
5 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\atlmfc.natvis'
6 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\ObjectiveC.natvis'
7 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\concurrency.natvis'
8 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\cpp_rest.natvis'
9 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\stl.natvis'
10 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\Windows.Data.Json.natvis'
11 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\Windows.Devices.Geolocation.natvis'
12 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\Windows.Devices.Sensors.natvis'
13 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\Windows.Media.natvis'
14 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\windows.natvis'
15 NatVis script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\Visualizers\winrt.natvis'
16 JavaScript script unloaded from 'C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\ApiExtension\CodeFlow.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:\\Books\\MyTest.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 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。