|
一、简介
今天是《Net 高级调试》的第八篇文章。这篇文章设计的内容挺多的,比如:如何查看方法的汇编代码,如何获取方法的描述符,对象同步块的转储,对象方法表的转储,托管堆和垃圾回收器信息的转储,CLR 的版本,GC 模式,等等,内容挺多的。内容虽然挺多,但是这些都是高级调试的基础。虽然这些都是基础,如果这些掌握不好,以后的高级调试的道路,也不好走。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现。
如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
操作系统:Windows Professional 10
调试工具:Windbg Preview(可以去Microsoft Store 去下载)
开发工具:Visual Studio 2022
Net 版本:Net Framework 4.8
CoreCLR源码:源码下载
二、基础知识
1、代码审查
1.1、简介
代码审查就是观察代码,代码由三种心态:机器代码、IL代码、C#代码。高级调试属于逆向分析,更多的是以 汇编代码 为主,如果对汇编无感,想学好 Net 高级调试,也是比较困难的。
1.2、观察汇编代码
1)u命令
这个命令用来将非托管函数的机器代码转成汇编代码,当然,我们想要查看汇编代码,不一定非要使用 Windbg,我们也可以使用 Visual Studio IDE 的反汇编窗口。我们可以通过点击菜单【调试】--》【窗口】----》【反汇编】,打开反汇编窗口。
2)!u命令
这个命令是由 SOS 提供的,专门用于观察 托管函数 的汇编表示,接下来,我们查看一下 Main 方法的汇编代码。
1.3、观察 IL 代码
SOS 提供了一个叫 !dumpil 的命令用来将托管函数的汇编指令转成可读的 IL 代码。
1.4、观察 C# 代码
要观察 C# 代码,需要将内存中的 module 给剥离出来,然后使用 ILSpy 或者 DnSpy 等反编译工具反转即可。这就需要使用 Windbg 的【!savemodule】命令。大家也要防止 Dump 泄露,这个是严重的,别人就可以看到你的完整代码。
2、杂项命令
2.1、获取 CLR 版本、GC模式
如果想获取 CLR 的版本,可以使用【!eeversion】命令,当然,我们也可以通过查看堆的数量,来了解 GC 模式,使用【!eeheap -gc】命令。
2.2、查看线程栈对象
如果我们想查看线程栈上的对象,一般都会使用【!clrstack -a】命令,但是这个命令只是看表面,不能映射到对象,那么我们可以使用【!dso】命令,这个命令可以把线程栈中的所有对象全部显示出来。
2.3、观察对象引用根
我们知道了如何查看对象引用根,我们就知道了为什么没有被 GC 回收,很轻松找到问题的所在,这个命令就是【!gcroot】。
三、调试过程
废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
1、测试源码
1.1、Example_8_1_1
- 1 namespace Example_8_1_1
- 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
1.2、Example_8_1_2
- 1 namespace Example_8_1_2
- 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、眼见为实
项目的所有操作都是一样的,所以就在这里说明一下,但是每个测试例子,都需要重新启动,并加载相应的应用程序,加载方法都是一样的。流程如下:我们编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们需要使用【g】命令,继续运行程序,然后到达指定地点停止后,我们可以点击【break】按钮,就可以调试程序了。有时候可能需要切换到主线程,可以使用【~0s】命令。
2.1、查看 clr!CallDescrWorkerInternal+0x34 处的汇编代码。
测试源码:Example_8_1_1
说明一下,在这里用其他的程序都是可以的,只是查看汇编代码,可以查看非托管函数的、也可以查看托管函数的汇编代码。
接下来,我们先找一个非托管函数的,看看它们的汇编代码,到底是哪个函数,是随机的。
我们使用【k】命令,查看一下非托管的调用栈,然后从里面随便找一个函数,看看它的汇编代码。
- 1 0:000> k
- 2 # ChildEBP RetAddr
- 3 00 00efeff4 7533f25c ntdll!NtReadFile+0xc
- 4 01 00efeff4 705f9b71 KERNELBASE!ReadFile+0xec
- 5 02 00eff064 70d2b275 mscorlib_ni+0x4b9b71
- 6 03 00eff090 70d2b17b mscorlib_ni!System.IO.__ConsoleStream.ReadFileNative+0x89 [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
- 7 04 00eff0bc 705de6a3 mscorlib_ni!System.IO.__ConsoleStream.Read+0x9f [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
- 8 05 00eff0d4 705deb5b mscorlib_ni!System.IO.StreamReader.ReadBuffer+0x33 [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
- 9 06 00eff0f0 70e73786 mscorlib_ni!System.IO.StreamReader.ReadLine+0xe3 [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
- 10 07 00eff100 70cd1845 mscorlib_ni!System.IO.TextReader.SyncTextReader.ReadLine+0x1a [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
- 11 08 00eff108 011808d1 mscorlib_ni!System.Console.ReadLine+0x15 [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
- 12 WARNING: Frame IP not in any known module. Following frames may be wrong.
- 13 09 00eff138 7162f036 0x11808d1
- 14 0a 00eff144 716322da clr!CallDescrWorkerInternal+0x34
- 15 0b 00eff198 7163859b clr!CallDescrWorkerWithHandler+0x6b
- 16 0c 00eff204 717db11b clr!MethodDescCallSite::CallTargetWorker+0x16a
- 17 0d 00eff328 717db7fa clr!RunMain+0x1b3
- 18 0e 00eff594 717db727 clr!Assembly::ExecuteMainMethod+0xf7
- 19 0f 00effa78 717db8a8 clr!SystemDomain::ExecuteMainMethod+0x5ef
- 20 10 00effad0 717db9ce clr!ExecuteEXE+0x4c
- 21 11 00effb10 717d7305 clr!_CorExeMainInternal+0xdc
- 22 12 00effb4c 71ddfa84 clr!_CorExeMain+0x4d
- 23 13 00effb84 71ede81e mscoreei!_CorExeMain+0xd6
- 24 14 00effb94 71ee4338 MSCOREE!ShellShim__CorExeMain+0x9e
- 25 15 00effbac 7515f989 MSCOREE!_CorExeMain_Exported+0x8
- 26 16 00effbac 772a7084 KERNEL32!BaseThreadInitThunk+0x19
- 27 17 00effc08 772a7054 ntdll!__RtlUserThreadStart+0x2f
- 28 18 00effc18 00000000 ntdll!_RtlUserThreadStart+0x1b
复制代码 View Code 调用栈还是很多的,列表里有:00eff144 716322da clr!CallDescrWorkerInternal+0x34,这样一行代码,我们使用【u】命令查看一下。- 1 0:000> u clr!CallDescrWorkerInternal+0x34
- 2 clr!CallDescrWorkerInternal+0x34:
- 3 7162f036 8b4b0c mov ecx,dword ptr [ebx+0Ch]
- 4 7162f039 83f900 cmp ecx,0
- 5 7162f03c 740c je clr!CallDescrWorkerInternal+0x48 (7162f04a)
- 6 7162f03e 83f904 cmp ecx,4
- 7 7162f041 7412 je clr!CallDescrWorkerInternal+0x53 (7162f055)
- 8 7162f043 83f908 cmp ecx,8
- 9 7162f046 7412 je clr!CallDescrWorkerInternal+0x58 (7162f05a)
- 10 7162f048 eb06 jmp clr!CallDescrWorkerInternal+0x4e (7162f050)
复制代码 代码很清楚,可以自行查看。当然我们也可以在Windbg的【Disassembly】窗口查看汇编代码,效果如图:
【u】命令是从上向下看汇编代码,我们也可以使用【ub】命令,从下向上查看汇编代码。- 1 0:000> ub clr!CallDescrWorkerInternal+0x34
- 2 clr!CallDescrWorkerInternal+0x21:
- 3 7162f023 83e804 sub eax,4
- 4 7162f026 ff30 push dword ptr [eax]
- 5 7162f028 49 dec ecx
- 6 7162f029 75f8 jne clr!CallDescrWorkerInternal+0x21 (7162f023)
- 7 7162f02b 8b4308 mov eax,dword ptr [ebx+8]
- 8 7162f02e 8b10 mov edx,dword ptr [eax]
- 9 7162f030 8b4804 mov ecx,dword ptr [eax+4]
- 10 7162f033 ff5310 call dword ptr [ebx+10h]
复制代码 两个命令的效果,如图:
如果我们想把一个函数的所有的汇编代码全部显示出来,我们可以使用【uf】命令,我们看看 clr!_CorExeMain 函数的汇编代码吧。- 1 0:000> uf clr!_CorExeMain
- 2 clr!_CorExeMain:
- 3 717d72e0 6a14 push 14h
- 4 717d72e2 6818737d71 push offset clr!`dynamic atexit destructor for 'AppDataPathHolder''+0x27e0 (717d7318)
- 5 717d72e7 e8349de4ff call clr!_SEH_prolog4 (71621020)
- 6 717d72ec 33c0 xor eax,eax
- 7 717d72ee 8985e0ffffff mov dword ptr [ebp-20h],eax
- 8 717d72f4 8985e4ffffff mov dword ptr [ebp-1Ch],eax
- 9 717d72fa 8985fcffffff mov dword ptr [ebp-4],eax
- 10 717d7300 e81b460000 call clr!_CorExeMainInternal (717db920)
- 11 717d7305 c785fcfffffffeffffff mov dword ptr [ebp-4],0FFFFFFFEh
- 12 717d730f 33c0 xor eax,eax
- 13 717d7311 e8509de4ff call clr!_SEH_epilog4 (71621066)
- 14 717d7316 c3 ret
复制代码 2.2、我们查看 Program 类型的 Main 方法的汇编代码。
测试源码:Example_8_1_1
- 1 0:000> !clrstack
- 2 OS Thread Id: 0x4164 (0)
- 3 Child SP IP Call Site
- 4 006fee34 772b10fc [InlinedCallFrame: 006fee34]
- 5 006fee30 705f9b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
- 6 006fee34 70d2b275 [InlinedCallFrame: 006fee34] Microsoft.Win32.Win32Native.ReadFile(.....) 7 006fee98 70d2b275 System.IO.__ConsoleStream.ReadFileNative(oolean, Int32 ByRef) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
- 8 006feecc 70d2b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
- 9 006feeec 705de6a3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
- 10 006feefc 705deb5b System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
- 11 006fef18 70e73786 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
- 12 006fef28 70cd1845 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
- 13 006fef30 <strong>025808d1</strong> Example_8_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\..\Example_8_1_1\Program.cs @ 11]
- 14 006ff0d0 7162f036 [GCFrame: 006ff0d0]
复制代码 025808d1 Main 方法的地址,我们可以使用【!u】查看一下。
- 1 0:000> !u 025808d1
- 2 Normal JIT generated code
- 3 Example_8_1_1.Program.Main(System.String[])
- 4 Begin 02580848, size 95
- 5
- 6 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 8:
- 7 02580848 55 push ebp
- 8 02580849 8bec mov ebp,esp
- 9 0258084b 57 push edi
- 10 0258084c 56 push esi
- 11 0258084d 83ec20 sub esp,20h
- 12 02580850 8bf1 mov esi,ecx
- 13 02580852 8d7dd8 lea edi,[ebp-28h]
- 14 02580855 b906000000 mov ecx,6
- 15 0258085a 33c0 xor eax,eax
- 16 0258085c f3ab rep stos dword ptr es:[edi]
- 17 0258085e 8bce mov ecx,esi
- 18 02580860 894df4 mov dword ptr [ebp-0Ch],ecx
- 19 02580863 833df042c40000 cmp dword ptr ds:[0C442F0h],0
- 20 0258086a 7405 je 02580871
- 21 0258086c e80ff5446f call clr!JIT_DbgIsJustMyCode (719cfd80)
- 22 02580871 33d2 xor edx,edx
- 23 02580873 8955f0 mov dword ptr [ebp-10h],edx
- 24 02580876 90 nop
- 25
- 26 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 9:
- 27 02580877 b90a000000 mov ecx,0Ah
- 28 0258087c ba0b000000 mov edx,0Bh
- 29 02580881 ff156c4dc400 call dword ptr ds:[0C44D6Ch] (Example_8_1_1.Program.Sum(Int32, Int32), mdToken: 06000002)
- 30 02580887 8945ec mov dword ptr [ebp-14h],eax
- 31 0258088a 8b45ec mov eax,dword ptr [ebp-14h]
- 32 0258088d 8945f0 mov dword ptr [ebp-10h],eax
- 33
- 34 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 10:
- 35 02580890 b9a8421570 mov ecx,offset mscorlib_ni!GetObjectData+0x102 (701542a8) (MT: System.Int32)
- 36 02580895 e85a286bfe call 00c330f4 (JitHelp: CORINFO_HELP_NEWSFAST)
- 37 0258089a 8945e8 mov dword ptr [ebp-18h],eax
- 38 0258089d 8b0544237403 mov eax,dword ptr ds:[3742344h] ("sum={0}")
- 39 025808a3 8945dc mov dword ptr [ebp-24h],eax
- 40 025808a6 8b45e8 mov eax,dword ptr [ebp-18h]
- 41 025808a9 8b55f0 mov edx,dword ptr [ebp-10h]
- 42 025808ac 895004 mov dword ptr [eax+4],edx
- 43 025808af 8b45e8 mov eax,dword ptr [ebp-18h]
- 44 025808b2 8945d8 mov dword ptr [ebp-28h],eax
- 45 025808b5 8b4ddc mov ecx,dword ptr [ebp-24h]
- 46 025808b8 8b55d8 mov edx,dword ptr [ebp-28h]
- 47 025808bb e890d7f86d call mscorlib_ni!System.String.Format (7050e050)
- 48 025808c0 8945e4 mov dword ptr [ebp-1Ch],eax
- 49 025808c3 8b4de4 mov ecx,dword ptr [ebp-1Ch]
- 50 025808c6 e89946076e call mscorlib_ni!System.Console.WriteLine (705f4f64)
- 51 025808cb 90 nop
- 52
- 53 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 11:
- 54 025808cc e85f0f756e call mscorlib_ni!System.Console.ReadLine (70cd1830)
- 55 >>> 025808d1 8945e0 mov dword ptr [ebp-20h],eax
- 56 025808d4 90 nop
- 57
- 58 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 12:
- 59 025808d5 90 nop
- 60 025808d6 8d65f8 lea esp,[ebp-8]
- 61 025808d9 5e pop esi
- 62 025808da 5f pop edi
- 63 025808db 5d pop ebp
- 64 025808dc c3 ret
复制代码 View Code 代码太多,不变展示。>>> 025808d1 8945e0 mov dword ptr [ebp-20h],eax,三个小于号,表示当前执行到的位置。
2.3、使用【!dumpil】命令查看 IL 代码。
测试源码:Example_8_1_1
我们要想查看一个方法的 IL 代码,必须先找到 这个方法的 MD,也就是方法描述符。
我们可以使用【!clrstack】命令找到 Main 方法的 IP,根据 IP 再找到 MD。- 1 0:000> !clrstack
- 2 OS Thread Id: 0x4164 (0)
- 3 Child SP IP Call Site
- 4 006fee34 772b10fc [InlinedCallFrame: 006fee34]
- 5 006fee30 705f9b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
- 6 006fee34 70d2b275 [InlinedCallFrame: 006fee34] Microsoft.Win32.Win32Native.ReadFile(... Int32 ByRef, IntPtr)
- 7 006fee98 70d2b275 System.IO.__ConsoleStream.ReadFileNative(...) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
- 8 006feecc 70d2b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
- 9 006feeec 705de6a3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
- 10 006feefc 705deb5b System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
- 11 006fef18 70e73786 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
- 12 006fef28 70cd1845 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
- 13 006fef30 <strong>025808d1</strong> Example_8_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_8_1_1\Program.cs @ 11]
- 14 006ff0d0 7162f036 [GCFrame: 006ff0d0]
复制代码 025808d1 红色标注的就是 Main 方法的IP,我们使用【!ip2md】命令,就可以找到方法描述符Md了。- 1 0:000> !ip2md 025808d1
- 2 <strong>MethodDesc: 00c44d58
- </strong> 3 Method Name: Example_8_1_1.Program.Main(System.String[])
- 4 Class: 00c41290
- 5 MethodTable: 00c44d78
- 6 mdToken: 06000001
- 7 Module: 00c44044
- 8 IsJitted: yes
- 9 CodeAddr: 02580848
- 10 Transparency: Critical
- 11 Source file: E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_8_1_1\Program.cs @ 11
复制代码 MethodDesc: 00c44d58 红色标注的就是方法描述符的而地址。有了方法描述(MD),我们就可以使用【!dumpil】命令查看 IL 代码了。- 1 0:000> !dumpil 00c44d58
- 2 ilAddr = 00362050
- 3 IL_0000: nop
- 4 IL_0001: ldc.i4.s 10
- 5 IL_0003: ldc.i4.s 11
- 6 IL_0005: call Example_8_1_1.Program::Sum
- 7 IL_000a: stloc.0
- 8 IL_000b: ldstr "sum={0}"
- 9 IL_0010: ldloc.0
- 10 IL_0011: box System.Int32
- 11 IL_0016: call System.String::Format
- 12 IL_001b: call System.Console::WriteLine
- 13 IL_0020: nop
- 14 IL_0021: call System.Console::ReadLine
- 15 IL_0026: pop
- 16 IL_0027: ret
复制代码 除了以上方法,我们也可以使用【!name2ee】命令达到同样的效果。那我么查看一下 Sum 方法 IL 代码。- 1 0:000> !name2ee Example_8_1_1!Example_8_1_1.Program.Sum
- 2 Module: 00c44044
- 3 Assembly: Example_8_1_1.exe
- 4 Token: 06000002
- 5 <strong>MethodDesc: 00c44d64
- </strong>6 Name: Example_8_1_1.Program.Sum(Int32, Int32)
- 7 JITTED Code Address: 025808f0
复制代码 MethodDesc: 00c44d64 红色标注的就是方法描述符的地址,有了这个地址,我们就可以使用【!dumpil】命令了。- 1 0:000> !dumpil 00c44d64
- 2 ilAddr = 00362084
- 3 IL_0000: nop
- 4 IL_0001: ldarg.0
- 5 IL_0002: stloc.0
- 6 IL_0003: ldarg.1
- 7 IL_0004: stloc.1
- 8 IL_0005: ldloc.0
- 9 IL_0006: ldloc.1
- 10 IL_0007: add
- 11 IL_0008: stloc.2
- 12 IL_0009: ldloc.2
- 13 IL_000a: stloc.3
- 14 IL_000b: br.s IL_000d
- 15 IL_000d: ldloc.3
- 16 IL_000e: ret
复制代码 2.4、通过 Dump 查看 C# 源码。
测试源码:Example_8_1_1
我们想要查看 Example_8_1_1项目的源码,必须找到这个项目的【模块】,也就是 module,我们切换到主线程,也就是 0 号线程,使用命令【~0s】,使用命令【!clrstack】找到调用栈。- 1 0:000> !clrstack
- 2 OS Thread Id: 0x3438 (0)
- 3 Child SP IP Call Site
- 4 00cfef94 772910fc [InlinedCallFrame: 00cfef94]
- 5 00cfef90 6d869b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
- 6 00cfef94 6df9b275 [InlinedCallFrame: 00cfef94] Microsoft.Win32.Win32Native.ReadFile(... Int32 ByRef, IntPtr)
- 7 00cfeff8 6df9b275 System.IO.__ConsoleStream.ReadFileNative(.. Int32 ByRef) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
- 8 00cff02c 6df9b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
- 9 00cff04c 6d84e6a3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
- 10 00cff05c 6d84eb5b System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
- 11 00cff078 6e0e3786 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
- 12 00cff088 6df41845 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
- 13 00cff090 <strong>013e08d1</strong> Example_8_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_8_1_1\Program.cs @ 11]
- 14 00cff228 6fdbf036 [GCFrame: 00cff228]
复制代码 013e08d1 红色标注的地址就是 Main 方法的 IP,有了 IP,我们可以通过 IP 找到 MD(方法描述符),在方法描述符里就包含了module 信息。- 1 0:000> !ip2md 013e08d1
- 2 MethodDesc: 00f14d58
- 3 Method Name: Example_8_1_1.Program.Main(System.String[])
- 4 Class: 00f11290
- 5 MethodTable: 00f14d78
- 6 <strong>mdToken: </strong> 06000001
- 7 <strong>Module: 00f14044
- </strong> 8 IsJitted: yes
- 9 CodeAddr: 013e0848
- 10 Transparency: Critical
- 11 Source file: E:\Visual Studio 2022\...\Example_8_1_1\Program.cs
复制代码 Module: 00f14044 这个信息就是模块信息,我们就可以使用【!savemodule】命令,保存文件了。- 1 0:000> !savemodule 00f14044 F:\Test\SaveDump\test.dll
- 2 3 sections in file
- 3 section 0 - VA=2000, VASize=818, FileAddr=200, FileSize=a00
- 4 section 1 - VA=4000, VASize=5f0, FileAddr=c00, FileSize=600
- 5 section 2 - VA=6000, VASize=c, FileAddr=1200, FileSize=200
复制代码 文件就保存下来了,截图效果:
有了这个文件,我们就可以使用 ILSpy 反编译工具查看源码了,很简单,就不写了。
2.5、如何查看 CLR 版本和 GC 模式。
测试源码:Example_8_1_1- 1 0:000> !eeversion
- 2 4.8.4300.0 retail
- 3 <strong>Workstation mode</strong>(工作站模式,一般指:Winform,WPF,Console等桌面应用)
- 4 SOS Version: 4.8.4300.0 retail build
复制代码 还有一种方式查看 GC 模式,我们可以查看 托管堆的个数,一个堆的就是工作站模式,其他就是服务器模式。- 1 0:000> !eeheap -gc
- 2 <strong>Number of GC Heaps: </strong>1(这里就表示是工作站模式)
- 3 generation 0 starts at 0x02d91018
- 4 generation 1 starts at 0x02d9100c
- 5 generation 2 starts at 0x02d91000
- 6 ephemeral segment allocation context: none
- 7 segment begin allocated size
- 8 02d90000 02d91000 02d95ff4 0x4ff4(20468)
- 9 Large object heap starts at 0x03d91000
- 10 segment begin allocated size
- 11 03d90000 03d91000 03d95558 0x4558(17752)
- 12 Total Size: Size: 0x954c (38220) bytes.
- 13 ------------------------------
- 14 GC Heap Size: Size: 0x954c (38220) bytes.
复制代码
2.6、查看线程栈对象。
测试源码:Example_8_1_1- 1 0:000> !dso
- 2 OS Thread Id: 0x3438 (0)
- 3 ESP/REG Object Name
- 4 00CFEFB0 02d94d64 Microsoft.Win32.SafeHandles.SafeFileHandle
- 5 00CFEFD4 02d94d64 Microsoft.Win32.SafeHandles.SafeFileHandle
- 6 00CFEFE0 02d94d64 Microsoft.Win32.SafeHandles.SafeFileHandle
- 7 00CFF008 02d94d64 Microsoft.Win32.SafeHandles.SafeFileHandle
- 8 00CFF030 02d94da0 System.IO.StreamReader
- 9 00CFF034 02d94da0 System.IO.StreamReader
- 10 00CFF04C 02d95124 System.IO.TextReader+SyncTextReader
- 11 00CFF068 02d95124 System.IO.TextReader+SyncTextReader
- 12 00CFF078 02d924bc System.String[]
- 13 00CFF090 02d924e4 System.Int32
- 14 00CFF094 02d924c8 System.String sum={0}
- 15 00CFF09C 02d93a4c System.String sum=21
- 16 00CFF0A0 02d924e4 System.Int32
- 17 00CFF0AC 02d924bc System.String[]
- 18 00CFF124 02d924bc System.String[]
- 19 00CFF260 02d924bc System.String[]
- 20 00CFF28C 02d924bc System.String[]
- 21 00CFF358 02d9141c System.AppDomain
- 22 00CFF674 03d92338 System.Object[] (System.Object[])
- 23 00CFF6B8 03d92338 System.Object[] (System.Object[])
- 24 00CFF6BC 03d92338 System.Object[] (System.Object[])
- 25 00CFF7CC 02d91238 System.SharedStatics
复制代码 2.7、如何查看对象的引用根。
测试源码:Example_8_1_2
第一步,我们先在托管堆中查找 Byte[] 数组对象,执行命令【!dumpheap -type Byte[]】,代码如下:
View Code 如果想看代码详情,点开看吧,这里就不展示了。
数据大小为 100012 的项目,一种有 100 项,因为我们 For 循环了一百次,为什么大小不是 100000,而是 100012 ,因为每个对象都有两个附加成员。有了成员列表,我们可以在这个列表中,任意选择一个项,在 Address 列,选一个地址,针对这个地址,我们使用【!gcroot】命令,就能看到我们想要的结果。
我们选择这个列表中最后一项,地址是:042e71a8- 1 0:006> !gcroot 042e71a8
- 2 HandleTable:
- 3 <strong> 00bc13ec (pinned handle)(static 底层是标记了 pinned,也就是这个 handle 持有 System.Object[] 数组)
- </strong>4 -> 03973568 System.Object[](这个对象是 CLR 创建的,持有这个 List 对象。)
- 5 -> 029724c8 System.Collections.Generic.List`1[[System.Byte[], mscorlib]](这个就是我们声明的 list 类型,底层是一个 Byte 二维数组)
- 6 -> 02972740 System.Byte[][](这个数组持有 042e71a8 这个元素)
- 7 -><strong> 042e71a8 System.Byte[](这个是我选择的最后一个元素值)
- </strong>8
- 9 Found 1 unique roots (run '!GCRoot -all' to see all roots).
复制代码 四、总结
终于写完了,写作的过程是累并快乐着。学习过程真的没那么轻松,还好是自己比较喜欢这一行,否则真不知道自己能不能坚持下来。老话重谈,《高级调试》的这本书第一遍看,真的很晕,第二遍稍微好点,不学不知道,一学吓一跳,自己欠缺的很多。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
来源:https://www.cnblogs.com/PatrickLiu/archive/2023/11/14/17828613.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|