|
一、介绍
这是我的《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 <strong> PARAMETERS</strong>:<strong>(这里就是参数所在地)
- </strong>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 <strong> LOCALS:(以下就是局部变量)
- </strong>8 <strong>0x000000F08ABCE81C</strong> <strong>= 0x000000000000000a</strong>
- 9 <strong>0x000000F08ABCE818 = 0x000000000000000b</strong>
复制代码 如果想查看每一栈帧的局部变量和参数,可以使用【!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 <strong> PARAMETERS:(这里是参数区域)
- </strong> 8 <strong>args (0x000000F08ABCE830) = 0x000002664e008ea0</strong>
- 9 <strong> LOCALS:(这里是局部变量区域)
- </strong>10 <strong>0x000000F08ABCE81C = 0x000000000000000a</strong>
- 11 <strong>0x000000F08ABCE818 = 0x000000000000000b</strong>
复制代码 很简单,就不多数了。
还有一个命令,我们要熟悉一下,那就是~*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 <strong>OS Thread Id: 0x3b40 (0)【底层操作系统的ID】
- </strong> 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 <strong>0x000000D3A5F7E8EC</strong> = <strong>0x000000000000000a</strong>
- 53 <strong>0x000000D3A5F7E8E8 = 0x000000000000000b 这就是我们的局部变量</strong>
复制代码 如果我们想查看托管调用栈的参数,可以使用【!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 <strong> PARAMETERS:
- </strong>50 args (0x000000D3A5F7E900) = <strong>0x000001efb4408ea0(这个就是 Main 方法的 args 参数)</strong>
复制代码 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 <strong>args </strong>(<strong>0x000000D3A5F7E900) = 0x000001efb4408ea0 (这是参数)</strong>
- 11 LOCALS:
- 12 <strong>0x000000D3A5F7E8EC</strong> = <strong>0x000000000000000a (这是局部变量)</strong>
- 13 <strong>0x000000D3A5F7E8E8 = 0x000000000000000b (这是局部变量)</strong>
复制代码 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 <strong>00000000`0302b220(线程状态)
- </strong>3 00000210`21f601a0 00000013`9097f108 00000210`21f2fd20
- 4 00000210`21f601b0 baadf00d`<strong>00000004(线程ID)</strong> 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 <strong>m_ThreadId</strong> : <strong>4(托管线程的 ID)</strong>
- 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 <strong>0302b220
- </strong>2 Legal to Join(可以执行 join 操作)
- 3 Background(是后台线程)
- 4 CLR Owns(是CLR 拥有的线程)
- 5 CoInitialized
- 6 In Multi Threaded Apartment(是MTA模式)
- 7 Fully initialized(已经完全初始化)
- 8 Thread Pool Worker Thread(是线程池线程)
- 9 Interruptible(可执行中断操作)
复制代码 第六列 GC Mode:表示当前的线程有没有操作托管堆的权限。
第七列 GC Alloc Context:缓冲区的开始节点和结束节点,每个线程在托管堆中分配一个对象,都有一个缓冲区,这个就是确定了缓冲区起始和结束。
第八列 Domain:表示当前线程所属于的域。
第九列 Lock Count:表示当前的线程有多少个托管锁。
第十列 Apt:表示线程是 STA(线程串行模式,WPF、WinForm) 模式还是 MTA (多线程并行模式)模式。
第十一列 Exception:在当前线程上发生了异常,会把这个异常和这个线程关联起来。
Finalizer 表示当前是终结器线程,Threadpool Worker 表示线程是线程池的线程。
我们先来看看【-live】开关的使用。- 1 0:013> !threads -live
- 2 ThreadCount: 8
- 3 UnstartedThread: 1
- 4 BackgroundThread: 5
- 5 PendingThread: 1
- 6 DeadThread: 1
- 7 Hosted Runtime: no
- 8 Lock
- 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 10 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA
- 11 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer)
- 12 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 13 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 14 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 15 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 16 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
- 17 0:013> !t -live
- 18 ThreadCount: 8
- 19 UnstartedThread: 1
- 20 BackgroundThread: 5
- 21 PendingThread: 1
- 22 DeadThread: 1
- 23 Hosted Runtime: no
- 24 Lock
- 25 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 26 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA
- 27 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer)
- 28 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 29 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 30 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 31 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 32 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
复制代码 我们先来看看【-special】开关的使用。- 1 0:013> !t -special
- 2 ThreadCount: 8
- 3 UnstartedThread: 1
- 4 BackgroundThread: 5
- 5 PendingThread: 1
- 6 DeadThread: 1
- 7 Hosted Runtime: no
- 8 Lock
- 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 10 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA
- 11 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer)
- 12 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 13 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 14 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 15 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 16 XXXX 8 0 00000250B8A2A330 1039820 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn (Threadpool Worker)
- 17 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
- 18 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly.
- 19 For more information see https://go.microsoft.com/fwlink/?linkid=2135652
- 20
- 21
- 22 OSID Special thread type
- 23 5 1464 DbgHelper
- 24 6 2fec Finalizer
- 25
- 26 0:013> !threads -special
- 27 ThreadCount: 8
- 28 UnstartedThread: 1
- 29 BackgroundThread: 5
- 30 PendingThread: 1
- 31 DeadThread: 1
- 32 Hosted Runtime: no
- 33 Lock
- 34 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 35 0 1 40a0 0000021021F41380 2a020 Preemptive 000002102641E0B8:000002102641F060 0000021021F2FD20 -00001 MTA
- 36 6 2 2fec 00000210239FDDA0 2b220 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 MTA (Finalizer)
- 37 8 4 41ec 0000021021F60190 302b220 Preemptive 000002102640D408:000002102640E658 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 38 9 5 37bc 0000021021F623B0 302b220 Preemptive 000002102640EA58:0000021026410678 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 39 10 6 3f0c 00000250B8A1CC50 302b220 Preemptive 0000021026410E68:0000021026410FD0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 40 11 7 3288 00000250B8A23990 302b220 Preemptive 00000210264117C8:0000021026412FF0 0000021021F2FD20 -00001 MTA (Threadpool Worker)
- 41 XXXX 8 0 00000250B8A2A330 1039820 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn (Threadpool Worker)
- 42 7 3 3974 00000250B8A1F8F0 9600 Preemptive 0000000000000000:0000000000000000 0000021021F2FD20 -00001 Ukn
- 43 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly.
- 44 For more information see https://go.microsoft.com/fwlink/?linkid=2135652
- 45
- 46
- 47 OSID Special thread type
- 48 5 1464 DbgHelper
- 49 6 2fec Finalizer
复制代码 上面的参数使用都很简单,就不多数了。
2)、Windbg Preview 调试
编译项目,打开 Windbg Preview 调试器,依次点击【文件】--->【Launch executable】加载我们的项目文件:ExampleCore_3_1_9.exe,进入调试器。【g】继续运行调试器,等我们的控制台程序输出如图:
此时,我们的调试器也处于暂停状态,点击【break】按钮,进入调试器的中断模式。
我们可以使用【!threads】命令或者【!t】命令查看托管线程的内容。- 1 0:001> !t
- 2 ThreadCount: 7
- 3 UnstartedThread: 0
- 4 BackgroundThread: 5
- 5 PendingThread: 0
- 6 DeadThread: 1
- 7 Hosted Runtime: no
- 8 Lock
- 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 16 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker)
- 17
- 18
- 19 0:001> !threads
- 20 ThreadCount: 7
- 21 UnstartedThread: 0
- 22 BackgroundThread: 5
- 23 PendingThread: 0
- 24 DeadThread: 1
- 25 Hosted Runtime: no
- 26 Lock
- 27 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 28 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 29 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 30 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 31 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 32 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 33 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 34 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker)
复制代码 ThreadCount:有多少个托管线程,这个进程里面有7个。
UnstartedThread:已经创建,但是还没有使用的线程,当前数量是0。
BackgroundThread:后台线程的数量,这个进程里面有5个。
PendingThread:阻塞的线程的数量,当前这个进程是0个阻塞的线程。
DeadThread:当一个线程完成任务,但是还没有被回收,这个阶段的线程就是死线程,也就是说这个线程对象底层的数据结构的 OSID 已经销毁。
以上就是介绍的主要名称,下面接着介绍,列表的每一项。我们使用 C# Thread 类型声明一个线程的时候,其实在操作系统和CLR都有一个数据结构相对应,有了 OSID我们才可以在任务管理器中看到线程对象。
第一列 DBG:是 Windbg 调试器自己的线程 ID,也可以区分托管线程和非托管线程。
第二列 ID:是托管线程,也就是我们 Thread 类型的标识符 Id,就是这段代码的值:Environment.CurrentManagedThreadId
第三列 OSID:操作系统线程的 ID。
第四列 ThreadOBJ:指向底层 CLR 线程数据结构的指针,可以使用【dp】命令观察其中的内容,我们查看托管线程 ID=4 的【0000032708325930】内容,红色标注就是托管线程的ID=00000004,就是4。
- 1 0:001> dp 0000032708325930
- 2 00000327`08325930 00000000`00000000 <strong>00000000`0302b220
- </strong>3 00000327`08325940 000000e8`2457f518 000002e6`719d7b00
- 4 00000327`08325950 baadf00d`<strong>00000004</strong> 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 <strong>00000000`0302b220
- </strong>2 Legal to Join(可以执行 join 操作)
- 3 Background(是后台线程)
- 4 CLR Owns(是CLR 拥有的线程)
- 5 CoInitialized(是以单线程方式创建的)
- 6 In Multi Threaded Apartment(是MTA模式)
- 7 Fully initialized(已经完全初始化)
- 8 Thread Pool Worker Thread(是线程池线程)
- 9 Interruptible(可执行中断操作)
复制代码 第六列 GC Mode:表示当前的线程有没有操作托管堆的权限。
第七列 GC Alloc Context:缓冲区的开始节点和结束节点,每个线程在托管堆中分配一个对象,都有一个缓冲区,这个就是确定了缓冲区起始和结束。
第八列 Domain:表示当前线程所属于的域。
第九列 Lock Count:表示当前的线程有多少个托管锁。
第十列 Apt:表示线程是 STA(线程串行模式,WPF、WinForm) 模式还是 MTA (多线程并行模式)模式。
第十一列 Exception:在当前线程上发生了异常,会把这个异常和这个线程关联起来。
Finalizer 表示当前是终结器线程,Threadpool Worker 表示线程是线程池的线程。
我们可以使用【!t -live】或者【!threads -live】输出活跃的线程。
- 1 0:001> !t -live
- 2 ThreadCount: 7
- 3 UnstartedThread: 0
- 4 BackgroundThread: 5
- 5 PendingThread: 0
- 6 DeadThread: 1
- 7 Hosted Runtime: no
- 8 Lock
- 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 16
- 17 0:001> !threads -live
- 18 ThreadCount: 7
- 19 UnstartedThread: 0
- 20 BackgroundThread: 5
- 21 PendingThread: 0
- 22 DeadThread: 1
- 23 Hosted Runtime: no
- 24 Lock
- 25 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 26 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 27 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 28 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 29 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 30 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 31 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
复制代码 这里就没有了 DBG 为 XXXX 的线程了。
接下来,我们在看看【!threads -special】或者【!t -special】命令的区别。效果很明显,就不多说了。- 1 0:001> !t -special
- 2 ThreadCount: 7
- 3 UnstartedThread: 0
- 4 BackgroundThread: 5
- 5 PendingThread: 0
- 6 DeadThread: 1
- 7 Hosted Runtime: no
- 8 Lock
- 9 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 10 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 11 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 12 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 13 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 14 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 15 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 16 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker)
- 17
- 18 <strong> OSID Special thread type
- </strong>19 <strong>5 1220</strong><strong> DbgHelper</strong>
- 20 <strong>6 16f4 Finalizer
- </strong>21
- 22
- 23 0:001> !threads -special
- 24 ThreadCount: 7
- 25 UnstartedThread: 0
- 26 BackgroundThread: 5
- 27 PendingThread: 0
- 28 DeadThread: 1
- 29 Hosted Runtime: no
- 30 Lock
- 31 DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 32 0 1 3968 000002E6719982E0 2a020 Preemptive 000002E675C1E0B8:000002E675C1F060 000002e6719d7b00 -00001 MTA
- 33 6 2 16f4 000002E671A45C40 2b220 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 MTA (Finalizer)
- 34 8 4 1574 0000032708325930 302b220 Preemptive 000002E675C0CEB8:000002E675C0E658 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 35 9 5 3ac8 0000032708328D40 302b220 Preemptive 000002E675C0EA58:000002E675C10678 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 36 10 6 2bf8 0000032707F16910 302b220 Preemptive 000002E675C1F490:000002E675C20FD0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 37 11 7 386c 000003270832E2F0 302b220 Preemptive 000002E675C117C8:000002E675C12FF0 000002e6719d7b00 -00001 MTA (Threadpool Worker)
- 38 XXXX 8 0 0000032707F17880 1039820 Preemptive 0000000000000000:0000000000000000 000002e6719d7b00 -00001 Ukn (Threadpool Worker)
- 39
- 40 <strong> OSID Special thread type
- </strong>41 <strong>5 1220</strong><strong> DbgHelper</strong>
- 42 <strong>6 16f4 Finalizer</strong>
复制代码 红色标注的就是输出的区别信息,很简单,就不多说了。
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 : <strong>00000000`00001000 00007ffd`fdc39da3 00000000`0000005c 000000da`5b97e3d0</strong> : <strong>ntdll!NtReadFile</strong>+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 <strong>00007ffd9e17dff8</strong> + 0x84 Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr))
- 8 000000DA5B97E3F0 00007ffec2c376eb (MethodDesc <strong>00007ffd9e17dff8</strong> + 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 <strong>00007ffd9e1a2cc0</strong> + 0x60 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1<Byte>, Boolean, Int32 ByRef, Boolean)), calling System_Console + 0x10b04
- 10 000000DA5B97E4E0 00007ffec2c3c8bb (MethodDesc <strong>00007ffd9e1a2c90</strong> + 0x2b System.ConsolePal+WindowsConsoleStream.Read(System.Span`1<Byte>)), calling System_Console + 0x10b04
- 11 000000DA5B97E520 00007ffec2c3fb84 (MethodDesc <strong>00007ffd9e1a2968</strong> + 0x74 System.IO.ConsoleStream.Read(Byte[], Int32, Int32))
- 12 000000DA5B97E590 00007ffdfcc489c1 (MethodDesc <strong>00007ffd9e1f6080</strong> + 0x41 System.IO.StreamReader.ReadBuffer())
- 13 000000DA5B97E5E0 00007ffdfcc490a4 (MethodDesc <strong>00007ffd9e1f60a8</strong> + 0x64 System.IO.StreamReader.ReadLine())
- 14 000000DA5B97E690 00007ffec2c4005d (MethodDesc <strong>00007ffd9e1ff650</strong> + 0x3d System.IO.SyncTextReader.ReadLine())
- 15 000000DA5B97E6E0 00007ffec2c39319 (MethodDesc <strong>00007ffd9e17a350</strong> + 0x19 System.Console.ReadLine())
- 16 000000DA5B97E710 00007ffd9e0c19cf (MethodDesc <strong>00007ffd9e1700c0</strong> + 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 <strong>Thread </strong> <strong>0</strong>
- 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 <strong>Thread </strong> <strong>6</strong>
- 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 <strong>Thread 8</strong>
- 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 <strong>Thread 9</strong>
- 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 <strong>Thread </strong> <strong>10</strong>
- 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 <strong>Thread </strong> <strong>11</strong>
- 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 <strong>Thread </strong> <strong>1(这个线程是调试器本身的,这个可以忽略,在 Windbg Preview 中是没有的)</strong>
- 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 <strong>XXXX </strong> <strong>8 0 0000021D1C7AC2E0 1039820 Preemptive 0000000000000000:0000000000000000 000001DC85CD1090 -00001 Ukn (Threadpool Worker)
- </strong>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 <strong>Thread </strong> <strong>0</strong>
- 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 <strong>Thread 6</strong>
- 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 <strong>Thread 8</strong>
- 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 <strong>Thread </strong> <strong>9</strong>
- 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 <strong>Thread </strong> <strong>10</strong>
- 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 <strong>Thread </strong> <strong>11</strong>
- 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 <strong>0</strong> 1 3f1c 000001C39D8E9C30 2a020 Preemptive 000001C3A200C0A8:000001C3A200C638 000001c39d929390 -00001 MTA
- 11 <strong>6</strong> 2 3d00 000001C39D996C40 2b220 Preemptive 0000000000000000:0000000000000000 000001c39d929390 -00001 MTA (Finalizer)
- 12 <strong>8</strong> 4 2c38 000001C39D9062C0 302b220 Preemptive 000001C3A200CEB8:000001C3A200E658 000001c39d929390 -00001 MTA (Threadpool Worker)
- 13 <strong>9</strong> 5 1ce4 000001C39F49B930 302b220 Preemptive 000001C3A200EA58:000001C3A2010678 000001c39d929390 -00001 MTA (Threadpool Worker)
- 14 <strong>10</strong> 6 3eb0 00000204344A3A80 302b220 Preemptive 000001C3A20151F0:000001C3A2017030 000001c39d929390 -00001 MTA (Threadpool Worker)
- 15 <strong>11</strong> 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 <strong>Apt</strong> 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 <strong>Apt</strong> 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 <strong>00007ff8`f94a3bba</strong> <strong>ntdll!LdrpDoDebuggerBreak</strong>+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 <strong>00007FF7C61A19DB</strong> <strong>ExampleCore_3_1_10.Program.Main</strong>(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 <strong>00007ff8`f94a3bba</strong> 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 <strong>coreclr!CallDescrWorkerInternal</strong>+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 <strong>00007ff7b38e19db</strong> <strong>ExampleCore_3_1_10.Program.Main</strong>(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 ,这个命令不区分大小写。
如果想快速找出汇编代码位于哪个函数中,使用【!IP2MD】命令是很方便的。
这是一种方法,要想查找方法描述符,还有一个命令可以使用,它就是【!name2ee】,该命令的格式:Name2EE! 或者 Name2EE (name2ee) 。此命令支持 ! 的 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 <strong>00007FF7DD4719DB ExampleCore_3_1_10.Program.Main</strong>(System.String[])
复制代码 ExampleCore_3_1_10.Program.Main 就是我们要找的方法,它的地址就是 IP 列的值:00007FF7DD4719DB ,有了地址,我们使用【!IP2MD 00007FF7DD4719DB】命令找到 Main() 方法的方法描述符。- 1 0:000> !IP2MD 00007FF7DD4719DB
- 2 <strong>MethodDesc: 00007ff7dd5200c0
- </strong> 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 <strong>MethodDesc: 00007ff7dd5200c0
- </strong>6 Name: ExampleCore_3_1_10.Program.Main(System.String[])
- 7 JITTED Code Address: 00007ff7dd471930
复制代码 MethodDesc: 00007ff7dd5200c0 这就是我们找到的方法描述符,两种方法找到的结果是一样的,很简单,话不多说了。
2)、Windbg Preview 调试
编译项目,打开【Windbg Preview】调试器,依次点击【文件】--->【Launch executable】,加载我们的项目文件:ExampleCore_3_1_10.exe,点击【打开】按钮进入调试器。
【g】继续运行调试器,等我们的控制台程序输出 sum=21 字样后,此时调试器处于卡死状态,我们点击【break】按钮进入调试器的中断模式,由于我们是手动中断,当前线程是调试器的线程,所以需要切换到托管线程,执行命令【~0s】。
- 1 0:001> ~0s
- 2 ntdll!NtReadFile+0x14:
- 3 00007ff9`1134d0a4 c3 ret
复制代码 执行命令后,提示符就是 0:000>,说明切换到了。继续使用【!ClrStack】命令查看托管线程的调用栈。- 1 0:000> !ClrStack
- 2 OS Thread Id: 0xfbc (0)
- 3 Child SP IP Call Site
- 4 000000A01F57E310 00007ff91134d0a4 [InlinedCallFrame: 000000a01f57e310]
- 5 000000A01F57E310 00007ff835a776eb [InlinedCallFrame: 000000a01f57e310]
- 6 000000A01F57E2E0 00007ff835a776eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412]
- 7 000000A01F57E3D0 00007ff835a7c9c0 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, System.Span`1, Boolean, Int32 ByRef, Boolean) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1150]
- 8 000000A01F57E430 00007ff835a7c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1108]
- 9 000000A01F57E470 00007ff835a7fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 34]
- 10 000000A01F57E4E0 00007ff8360689c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 613]
- 11 000000A01F57E530 00007ff8360690a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 802]
- 12 000000A01F57E5E0 00007ff835a8005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77]
- 13 000000A01F57E630 00007ff835a79319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752]
- 14 000000A01F57E660 <strong>00007ff7d75119db</strong> <strong>ExampleCore_3_1_10.Program.Main</strong>(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 <strong>MethodDesc: 00007ff7d75c00c0
- </strong> 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 <strong>MethodDesc: 00007ff7d75c00c0
- </strong>6 Name: ExampleCore_3_1_10.Program.Main(System.String[])
- 7 JITTED Code Address: 00007ff7d7511930
复制代码 MethodDesc: 00007ff7d75c00c0 这也是找到方法描述符的一种方法。
3.2.3、显示中间语言指令
A、基础知识
.Net 代码由三种形态:机器代码、IL代码、C#代码,我们可以直接查看 IL 代码,当然,查看 IL 代码的方法也有很多种,SOS调试器扩展提供了一个叫 【!dumpil】 的命令用来将托管函数的汇编指令转成可读的 IL 代码,当然,我们也可以使用 ILSpy 或者 DnSpy 等反编译工具查看。我推荐 ILSpy 或者 DnSpy 工具,使用更方便,可读性更强。
这里我们主要介绍命令的用法,【!Dumpil】命令是以方法的方法描述符为参数,所以在使用【!dumpil】命令之前,我们必须找到方法的方法描述,前一节,我们说了,有两种方法可以找到方法的方法描述符,第一个种就是使用【!IP2MD】命令,第二种就是使用【!Name2EE】命令。有了方法描述符的地址,我们就可以直接使用【!dumpil】命令了。
B、眼见为实
调试源码:ExampleCore_3_1_10
调试任务:【!Dumpil】命令的使用
1)、NTSD 调试
编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 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 <strong>IP</strong> 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 <strong>00007FF7DD4719DB ExampleCore_3_1_10.Program.Main</strong>(System.String[])
复制代码 ExampleCore_3_1_10.Program.Main 方法的地址就是 00007FF7DD4719DB ,有了这个地址,就可以根据这个地址获取方法的描述符了。
执行命令【!ip2md 00007FF7DD4719DB】。- 1 0:000> !ip2md 00007FF7DD4719DB
- 2 <strong>MethodDesc: 00007ff7dd5200c0
- </strong> 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 <strong>MethodDesc: 00007ff7dd5200c0
- </strong>6 Name: ExampleCore_3_1_10.Program.Main(System.String[])
- 7 JITTED Code Address: 00007ff7dd471930
复制代码 这个两个命令获取同样的方法描述符。继续执行【!DumpIL 00007ff7dd5200c0】获取 IL 代码。- 1 0:000> !DumpIL 00007ff7dd5200c0
- 2 ilAddr is 000001E1EBFD2050 pImport is 0000018FDE7845F0
- 3 ilAddr = 000001E1EBFD2050
- 4 IL_0000: nop
- 5 IL_0001: ldc.i4.s 10
- 6 IL_0003: ldc.i4.s 11
- 7 IL_0005: call int32 ExampleCore_3_1_10.Program::Sum(int32,int32)
- 8 IL_000a: stloc.0
- 9 IL_000b: ldloca.s VAR OR ARG 1
- 10 IL_000d: ldc.i4.4
- 11 IL_000e: ldc.i4.1
- 12 IL_000f: call void System.Runtime.CompilerServices.DefaultInterpolat::.ctor(int32,int32)
- 13 IL_0014: ldloca.s VAR OR ARG 1
- 14 IL_0016: ldstr "sum="
- 15 IL_001b: call void System.Runtime.CompilerServices.DefaultInterpolat::AppendLiteral(string)
- 16 IL_0020: nop
- 17 IL_0021: ldloca.s VAR OR ARG 1
- 18 IL_0023: ldloc.0
- 19 IL_0024: call <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 <strong>00007ff7dcf019db ExampleCore_3_1_10.Program.Main</strong>(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 <strong>MethodDesc: 00007ff7dcfb00c0
- </strong> 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 <strong>MethodDesc: 00007ff7dcfb00c0
- </strong>6 Name: ExampleCore_3_1_10.Program.Main(System.String[])
- 7 JITTED Code Address: 00007ff7dcf01930
复制代码 MethodDesc: 00007ff7dcfb00c0 这也是我们找到的方法描述符,他们的结果都是一样的。有了方法描述符,我们就可以使用【!DumpIL】命令获取 IL 代码了。执行命令【!DumpIL 00007ff7dcfb00c0】。- 1 0:000> !DumpIL 00007ff7dcfb00c0
- 2 ilAddr is 000002DDDFA32050 pImport is 00000230A1AFADC0
- 3 ilAddr = 000002DDDFA32050
- 4 IL_0000: nop
- 5 IL_0001: ldc.i4.s 10
- 6 IL_0003: ldc.i4.s 11
- 7 IL_0005: call int32 ExampleCore_3_1_10.Program::Sum(int32,int32)
- 8 IL_000a: stloc.0
- 9 IL_000b: ldloca.s VAR OR ARG 1
- 10 IL_000d: ldc.i4.4
- 11 IL_000e: ldc.i4.1
- 12 IL_000f: call void System.Runtime.CompilerServices.DefaultInterpolat::.ctor(int32,int32)
- 13 IL_0014: ldloca.s VAR OR ARG 1
- 14 IL_0016: ldstr "sum="
- 15 IL_001b: call void System.Runtime.CompilerServices.DefaultInterpolat::AppendLiteral(string)
- 16 IL_0020: nop
- 17 IL_0021: ldloca.s VAR OR ARG 1
- 18 IL_0023: ldloc.0
- 19 IL_0024: call <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<strong>(CLR 版本)
- </strong>3 8,0,224,6711 @Commit: 1381d5ebd2ab1f292848d5b19b80cf71ac332508
- 4 Workstation mode<strong>(工作站模式)
- </strong>5 SOS Version: 8.0.10.10501 retail build<strong>(SOS 调试器扩展的版本)</strong>
复制代码 很简单,话不多说。
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 <strong>MethodDesc: 00007ffb5bbe00c0(方法描述符地址)
- </strong>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 <strong>MethodTable: 00007ffb5bbe0100(这就是方法表的地址)
- </strong>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 <strong>MethodTable: 00007ff7dd640100
- </strong>6 EEClass: 00007ff7dd62fb20
- 7 Name: ExampleCore_3_1_10.Program
复制代码 MethodTable: 00007ff7dd640100 它就是 Program 类型的方法表地址,继续执行【!DumpMT 00007ff7dd640100】命令。- 1 0:008> !DumpMT 00007ff7dd640100
- 2 EEClass: 00007ff7dd62fb20
- 3 Module: 00007ff7dd61e0a0
- 4 Name: ExampleCore_3_1_10.Program
- 5 mdToken: 0000000002000002
- 6 File: E:\Visual Studio 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[] 数组了,找到了在分析。
我们可以知道数据大小为 100024 的项一共有100个,因为我们循环了100次。每一项数据的大小为什么不是100000,而是 100024,因为它是数组,所以它拥有同步块索引、方法表信息、数组长度等附加信息。在32位的机器上是100012,在64位机器是100024。
0000017883559ef8 我随便找了一项,选的是最后一个项,这个就是元素的地址,我们可以使用【!GCRoot 0000017883559ef8】命令,查看它的引用情况。- 1 0:009> !gcroot 0000023306d59ef8
- 2 <strong>HandleTable:
- </strong>3 0000023301B513E8 (<strong>strong handle</strong>)
- 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 <strong>01a607d725d0 7ff7dd619cc0</strong> 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)<strong>(static 底层是标记了 pinned,也就是这个 handle 持有 System.Object[] 数组)</strong>
- 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 <strong>000001688d40b6f0</strong> 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 <strong>AppDomain: 0000016888e66b60(应用程序域的地址)
- </strong>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 <strong>0185c700b6f0</strong> 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 <strong>AppDomain: 00000185c2d189a0(引用程序域的地址)
- </strong>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): <strong>Integer divide-by-zero - code c0000094 (first/second chance not available)
- </strong>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): <strong>Integer divide-by-zero - code c0000094 (first chance)(说明系统抛出了一个除以0的异常)
- </strong>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 <strong>Loading Dump File [F:\Books\MyTest.dmp]
- </strong>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 <strong>This dump file has an exception of interest stored in it.
- </strong>61 The stored exception information can be accessed via .ecxr.
- 62 (3fdc.c48): <strong>Integer divide-by-zero - code c0000094 (first/second chance not available)
- </strong>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 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。
来源:https://www.cnblogs.com/PatrickLiu/p/18060906
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|