Net 高级调试之八:代码审查及杂项命令

  • Net 高级调试之八:代码审查及杂项命令已关闭评论
  • 85 次浏览
  • A+
所属分类:.NET技术
摘要

             代码很清楚,可以自行查看。当然我们也可以在Windbg的【Disassembly】窗口查看汇编代码,效果如图:
                    

一、简介
    今天是《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 的反汇编窗口。我们可以通过点击菜单【调试】--》【窗口】----》【反汇编】,打开反汇编窗口。
                Net 高级调试之八:代码审查及杂项命令

 

            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 

Net 高级调试之八:代码审查及杂项命令Net 高级调试之八:代码审查及杂项命令

 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

Net 高级调试之八:代码审查及杂项命令Net 高级调试之八:代码审查及杂项命令

 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】命令,查看一下非托管的调用栈,然后从里面随便找一个函数,看看它的汇编代码。

Net 高级调试之八:代码审查及杂项命令Net 高级调试之八:代码审查及杂项命令

 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:ddndpclrsrcBCLsystemio__consolestream.cs @ 205]   7 04 00eff0bc 705de6a3     mscorlib_ni!System.IO.__ConsoleStream.Read+0x9f [f:ddndpclrsrcBCLsystemio__consolestream.cs @ 134]   8 05 00eff0d4 705deb5b     mscorlib_ni!System.IO.StreamReader.ReadBuffer+0x33 [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 595]   9 06 00eff0f0 70e73786     mscorlib_ni!System.IO.StreamReader.ReadLine+0xe3 [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 748]  10 07 00eff100 70cd1845     mscorlib_ni!System.IO.TextReader.SyncTextReader.ReadLine+0x1a [f:ddndpclrsrcBCLsystemiotextreader.cs @ 363]  11 08 00eff108 011808d1     mscorlib_ni!System.Console.ReadLine+0x15 [f:ddndpclrsrcBCLsystemconsole.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】窗口查看汇编代码,效果如图:
                    Net 高级调试之八:代码审查及杂项命令

            【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]

             两个命令的效果,如图:
             Net 高级调试之八:代码审查及杂项命令

              如果我们想把一个函数的所有的汇编代码全部显示出来,我们可以使用【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:ddndpclrsrcBCLsystemio__consolestream.cs @ 205]  8 006feecc 70d2b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:ddndpclrsrcBCLsystemio__consolestream.cs @ 134]  9 006feeec 705de6a3 System.IO.StreamReader.ReadBuffer() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 595] 10 006feefc 705deb5b System.IO.StreamReader.ReadLine() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 748] 11 006fef18 70e73786 System.IO.TextReader+SyncTextReader.ReadLine() [f:ddndpclrsrcBCLsystemiotextreader.cs @ 363] 12 006fef28 70cd1845 System.Console.ReadLine() [f:ddndpclrsrcBCLsystemconsole.cs @ 1984] 13 006fef30 025808d1 Example_8_1_1.Program.Main(System.String[]) [E:Visual Studio 2022..Example_8_1_1Program.cs @ 11] 14 006ff0d0 7162f036 [GCFrame: 006ff0d0] 

            025808d1 Main 方法的地址,我们可以使用【!u】查看一下。

Net 高级调试之八:代码审查及杂项命令Net 高级调试之八:代码审查及杂项命令

 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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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:ddndpclrsrcBCLsystemio__consolestream.cs @ 205]  8 006feecc 70d2b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:ddndpclrsrcBCLsystemio__consolestream.cs @ 134]  9 006feeec 705de6a3 System.IO.StreamReader.ReadBuffer() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 595] 10 006feefc 705deb5b System.IO.StreamReader.ReadLine() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 748] 11 006fef18 70e73786 System.IO.TextReader+SyncTextReader.ReadLine() [f:ddndpclrsrcBCLsystemiotextreader.cs @ 363] 12 006fef28 70cd1845 System.Console.ReadLine() [f:ddndpclrsrcBCLsystemconsole.cs @ 1984] 13 006fef30 025808d1 Example_8_1_1.Program.Main(System.String[]) [E:Visual Studio 2022...Example_8_1_1Program.cs @ 11] 14 006ff0d0 7162f036 [GCFrame: 006ff0d0] 

            025808d1 红色标注的就是 Main 方法的IP,我们使用【!ip2md】命令,就可以找到方法描述符Md了。

 1 0:000> !ip2md 025808d1  2 MethodDesc:   00c44d58  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 2022SourceProjectsAdvancedDebug.NetFramework.TestExample_8_1_1Program.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 MethodDesc:  00c44d64 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:ddndpclrsrcBCLsystemio__consolestream.cs @ 205]  8 00cff02c 6df9b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:ddndpclrsrcBCLsystemio__consolestream.cs @ 134]  9 00cff04c 6d84e6a3 System.IO.StreamReader.ReadBuffer() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 595] 10 00cff05c 6d84eb5b System.IO.StreamReader.ReadLine() [f:ddndpclrsrcBCLsystemiostreamreader.cs @ 748] 11 00cff078 6e0e3786 System.IO.TextReader+SyncTextReader.ReadLine() [f:ddndpclrsrcBCLsystemiotextreader.cs @ 363] 12 00cff088 6df41845 System.Console.ReadLine() [f:ddndpclrsrcBCLsystemconsole.cs @ 1984] 13 00cff090 013e08d1 Example_8_1_1.Program.Main(System.String[]) [E:Visual Studio 2022...Example_8_1_1Program.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 mdToken:      06000001  7 Module:       00f14044  8 IsJitted:     yes  9 CodeAddr:     013e0848 10 Transparency: Critical 11 Source file:  E:Visual Studio 2022...Example_8_1_1Program.cs

             Module: 00f14044 这个信息就是模块信息,我们就可以使用【!savemodule】命令,保存文件了。

1 0:000> !savemodule 00f14044 F:TestSaveDumptest.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

            文件就保存下来了,截图效果:
            Net 高级调试之八:代码审查及杂项命令

            有了这个文件,我们就可以使用 ILSpy 反编译工具查看源码了,很简单,就不写了。

        2.5、如何查看 CLR 版本和 GC 模式。
            测试源码:Example_8_1_1

1 0:000> !eeversion 2 4.8.4300.0 retail 3 Workstation mode(工作站模式,一般指:Winform,WPF,Console等桌面应用) 4 SOS Version: 4.8.4300.0 retail build

            还有一种方式查看 GC 模式,我们可以查看 托管堆的个数,一个堆的就是工作站模式,其他就是服务器模式。

 1 0:000> !eeheap -gc  2 Number of GC Heaps: 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[]】,代码如下:

Net 高级调试之八:代码审查及杂项命令Net 高级调试之八:代码审查及杂项命令

  1 0:006> !dumpheap -type Byte[]   2  Address       MT     Size   3 029724c8 6d3f49d8       24        4 029724e0 6d4216e4       12        5 0297259c 6d4216e4      140        6 02972634 6d4216e4      268        7 02972740 6d4216e4      524        8 02974438 6d3c5c40      526        9 02974eec 6d3c5c40      268       10 02975208 6d3c5c40       12       11 03975568 6d3c5c40   100012       12 0398dc28 6d3c5c40   100012       13 039a62e8 6d3c5c40   100012       14 039be9a8 6d3c5c40   100012       15 039d7068 6d3c5c40   100012       16 039ef728 6d3c5c40   100012       17 03a07de8 6d3c5c40   100012       18 03a204a8 6d3c5c40   100012       19 03a38b68 6d3c5c40   100012       20 03a51228 6d3c5c40   100012       21 03a698e8 6d3c5c40   100012       22 03a81fa8 6d3c5c40   100012       23 03a9a668 6d3c5c40   100012       24 03ab2d28 6d3c5c40   100012       25 03acb3e8 6d3c5c40   100012       26 03ae3aa8 6d3c5c40   100012       27 03afc168 6d3c5c40   100012       28 03b14828 6d3c5c40   100012       29 03b2cee8 6d3c5c40   100012       30 03b455a8 6d3c5c40   100012       31 03b5dc68 6d3c5c40   100012       32 03b76328 6d3c5c40   100012       33 03b8e9e8 6d3c5c40   100012       34 03ba70a8 6d3c5c40   100012       35 03bbf768 6d3c5c40   100012       36 03bd7e28 6d3c5c40   100012       37 03bf04e8 6d3c5c40   100012       38 03c08ba8 6d3c5c40   100012       39 03c21268 6d3c5c40   100012       40 03c39928 6d3c5c40   100012       41 03c51fe8 6d3c5c40   100012       42 03c6a6a8 6d3c5c40   100012       43 03c82d68 6d3c5c40   100012       44 03c9b428 6d3c5c40   100012       45 03cb3ae8 6d3c5c40   100012       46 03ccc1a8 6d3c5c40   100012       47 03ce4868 6d3c5c40   100012       48 03cfcf28 6d3c5c40   100012       49 03d155e8 6d3c5c40   100012       50 03d2dca8 6d3c5c40   100012       51 03d46368 6d3c5c40   100012       52 03d5ea28 6d3c5c40   100012       53 03d770e8 6d3c5c40   100012       54 03d8f7a8 6d3c5c40   100012       55 03da7e68 6d3c5c40   100012       56 03dc0528 6d3c5c40   100012       57 03dd8be8 6d3c5c40   100012       58 03df12a8 6d3c5c40   100012       59 03e09968 6d3c5c40   100012       60 03e22028 6d3c5c40   100012       61 03e3a6e8 6d3c5c40   100012       62 03e52da8 6d3c5c40   100012       63 03e6b468 6d3c5c40   100012       64 03e83b28 6d3c5c40   100012       65 03e9c1e8 6d3c5c40   100012       66 03eb48a8 6d3c5c40   100012       67 03eccf68 6d3c5c40   100012       68 03ee5628 6d3c5c40   100012       69 03efdce8 6d3c5c40   100012       70 03f163a8 6d3c5c40   100012       71 03f2ea68 6d3c5c40   100012       72 03f47128 6d3c5c40   100012       73 03f5f7e8 6d3c5c40   100012       74 03f77ea8 6d3c5c40   100012       75 03f90568 6d3c5c40   100012       76 03fa8c28 6d3c5c40   100012       77 03fc12e8 6d3c5c40   100012       78 03fd99a8 6d3c5c40   100012       79 03ff2068 6d3c5c40   100012       80 0400a728 6d3c5c40   100012       81 04022de8 6d3c5c40   100012       82 0403b4a8 6d3c5c40   100012       83 04053b68 6d3c5c40   100012       84 0406c228 6d3c5c40   100012       85 040848e8 6d3c5c40   100012       86 0409cfa8 6d3c5c40   100012       87 040b5668 6d3c5c40   100012       88 040cdd28 6d3c5c40   100012       89 040e63e8 6d3c5c40   100012       90 040feaa8 6d3c5c40   100012       91 04117168 6d3c5c40   100012       92 0412f828 6d3c5c40   100012       93 04147ee8 6d3c5c40   100012       94 041605a8 6d3c5c40   100012       95 04178c68 6d3c5c40   100012       96 04191328 6d3c5c40   100012       97 041a99e8 6d3c5c40   100012       98 041c20a8 6d3c5c40   100012       99 041da768 6d3c5c40   100012      100 041f2e28 6d3c5c40   100012      101 0420b4e8 6d3c5c40   100012      102 04223ba8 6d3c5c40   100012      103 0423c268 6d3c5c40   100012      104 04254928 6d3c5c40   100012      105 0426cfe8 6d3c5c40   100012      106 042856a8 6d3c5c40   100012      107 0429dd68 6d3c5c40   100012      108 042b6428 6d3c5c40   100012      109 042ceae8 6d3c5c40   100012      110 042e71a8 6d3c5c40   100012      111  112 Statistics: 113       MT    Count    TotalSize Class Name 114 6d3f49d8        1           24 System.Collections.Generic.List`1[[System.Byte[], mscorlib]] 115 6d4216e4        4          944 System.Byte[][] 116 6d3c5c40      103     10002006 System.Byte[] 117 Total 108 objects

View Code

            如果想看代码详情,点开看吧,这里就不展示了。
            数据大小为 100012 的项目,一种有 100 项,因为我们 For 循环了一百次,为什么大小不是 100000,而是 100012 ,因为每个对象都有两个附加成员。有了成员列表,我们可以在这个列表中,任意选择一个项,在 Address 列,选一个地址,针对这个地址,我们使用【!gcroot】命令,就能看到我们想要的结果。
            我们选择这个列表中最后一项,地址是:042e71a8

1 0:006> !gcroot 042e71a8 2 HandleTable: 3     00bc13ec (pinned handle)(static 底层是标记了 pinned,也就是这个 handle 持有 System.Object[] 数组) 4     -> 03973568 System.Object[](这个对象是 CLR 创建的,持有这个 List 对象。) 5     -> 029724c8 System.Collections.Generic.List`1[[System.Byte[], mscorlib]](这个就是我们声明的 list 类型,底层是一个 Byte 二维数组) 6     -> 02972740 System.Byte[][](这个数组持有 042e71a8 这个元素) 7     -> 042e71a8 System.Byte[](这个是我选择的最后一个元素值) 8  9 Found 1 unique roots (run '!GCRoot -all' to see all roots).

四、总结
    终于写完了,写作的过程是累并快乐着。学习过程真的没那么轻松,还好是自己比较喜欢这一行,否则真不知道自己能不能坚持下来。老话重谈,《高级调试》的这本书第一遍看,真的很晕,第二遍稍微好点,不学不知道,一学吓一跳,自己欠缺的很多。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。