Advanced .Net Debugging 5:基本调试任务(线程的操作、代码审查、CLR内部的命令、诊断命令和崩溃转储文件)

一、介绍
    这是我的《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 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。

相关推荐
&梧桐树夏20 小时前
JavaEE 线程安全
java-ee·多线程
程序猿小D1 天前
第三百三十节 Java网络教程 - Java网络UDP服务器
java·开发语言·网络·网络协议·udp·多线程
robin_suli2 天前
Java多线程八股(二),CAS详解,ReentrantLock和Synchronized的区别
java·开发语言·多线程·八股
蓑衣夜行2 天前
捉虫笔记(五)-WinDbg调试3个时机
windbg·捉虫笔记
zhuqiyua3 天前
深入解析Kernel32.dll与Msvcrt.dll
汇编·microsoft·windbg·二进制·dll
雪碧聊技术3 天前
多线程4:线程池、并发、并行、综合案例-抢红包游戏
java·线程池·多线程·并发·并行·复用线程
且听真言3 天前
Flutter实现绝对定位学习
top·绝对定位·stack·positioned·mediaquery·left
琪露诺大湿4 天前
JavaEE-多线程初阶(5)
java·开发语言·jvm·java-ee·idea·多线程·基础
SlothLu4 天前
Debezium-MySqlConnectorTask
java·大数据·数据库·多线程·数据库开发·debezium·数据迁移
SlothLu4 天前
Debezium-EmbeddedEngine
java·数据库·设计模式·database·多线程·debezium·数据迁移