翼度科技»论坛 编程开发 .net 查看内容

Advanced .Net Debugging 4:基本调试任务(对象检查:内存、值类型、引用

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
一、介绍
    这是我的《Advanced .Net Debugging》这个系列的第四篇文章。今天这篇文章的标题虽然叫做“基本调试任务”,但是这章的内容还是挺多的。由于内容太多,故原书的第三章内容我分两篇文章来写。上一篇我们了解了一些调试技巧,比如:单步调试、下断点、过程调试等,这篇文章主要涉及的内容是对象的转储,内存的转储,值类型的转储,引用类型的转储、数组的转储、异常的转储等。第一次说到“转储”,可能大家不知道什么意思,其实就是把我们想要的内容输出出来或者说是打印出来,方便我们分析问题。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_6
  1.   1 using System.Diagnostics;
  2.   2
  3.   3 namespace ExampleCore_3_1_6
  4.   4 {
  5.   5     public class ObjTypes
  6.   6     {
  7.   7         public struct Coordinate
  8.   8         {
  9.   9             public int xCord;
  10. 10             public int yCord;
  11. 11             public int zCord;
  12. 12
  13. 13             public Coordinate(int x, int y, int z)
  14. 14             {
  15. 15                 xCord = x;
  16. 16                 yCord = y;
  17. 17                 zCord = z;
  18. 18             }
  19. 19         }
  20. 20
  21. 21         private Coordinate coordinate;
  22. 22
  23. 23         int[] intArray = [1, 2, 3, 4, 5];
  24. 24
  25. 25         string[] strArray = ["Welcome", "to", "Advanced", ".NET", "Debugging"];
  26. 26
  27. 27         static void Main(string[] args)
  28. 28         {
  29. 29             Coordinate point = new Coordinate(100, 100, 100);
  30. 30             Console.WriteLine("Press any key to continue(AddCoordinate)");
  31. 31             Console.ReadKey();
  32. 32             ObjTypes ob = new ObjTypes();
  33. 33             ob.AddCoordinate(point);
  34. 34
  35. 35             Console.WriteLine("Press any key to continue(Arrays)");
  36. 36             Console.ReadKey();
  37. 37             ob.PrintArrays();
  38. 38
  39. 39             Console.WriteLine("Press any key to continue(Generics)");
  40. 40             Console.ReadKey();
  41. 41             Comparer<int> c = new Comparer<int>();
  42. 42             Console.WriteLine("Greater:{0}", c.GreaterThan(5, 10));
  43. 43
  44. 44             Console.WriteLine("Preaa any key to continue(Exception)");
  45. 45             Console.ReadKey();
  46. 46             ob.ThrowException(null);
  47. 47         }
  48. 48        
  49. 49         public void AddCoordinate(Coordinate coord)
  50. 50         {
  51. 51             coordinate.xCord += coord.xCord;
  52. 52             coordinate.yCord += coord.yCord;
  53. 53             coordinate.zCord += coord.zCord;
  54. 54
  55. 55             Console.WriteLine("x:{0},y:{1},z:{2}", coordinate.xCord, coordinate.yCord, coordinate.zCord);
  56. 56         }
  57. 57
  58. 58         public void PrintArrays()
  59. 59         {
  60. 60             foreach (int i in intArray)
  61. 61             {
  62. 62                 Console.WriteLine("Int:{0}", i);
  63. 63             }
  64. 64             foreach (string s in strArray)
  65. 65             {
  66. 66                 Console.WriteLine("Str:{0}", s);
  67. 67             }
  68. 68         }
  69. 69
  70. 70         public void ThrowException(ObjTypes? obj)
  71. 71         {
  72. 72             if (obj == null)
  73. 73             {
  74. 74                 throw new ArgumentException("Obj cannot be null");
  75. 75             }
  76. 76         }        
  77. 77     }
  78. 78     public class Comparer<T> where T : IComparable
  79. 79     {
  80. 80         public T GreaterThan(T d, T d2)
  81. 81         {
  82. 82             int ret = d.CompareTo(d2);
  83. 83             if (ret > 0)
  84. 84             {
  85. 85                 return d;
  86. 86             }
  87. 87             else
  88. 88             {
  89. 89                 return d2;
  90. 90             }
  91. 91         }
  92. 92
  93. 93         public T LessThan(T d, T d2)
  94. 94         {
  95. 95             int ret = d.CompareTo(d2);
  96. 96             if (ret < 0)
  97. 97             {
  98. 98                 return d;
  99. 99             }
  100. 100             else
  101. 101             {
  102. 102                 return d2;
  103. 103             }
  104. 104         }
  105. 105     }
  106. 106 }
复制代码
View Code
    2.2、ExampleCore_3_1_7
  1. 1 namespace ExampleCore_3_1_7
  2. 2 {
  3. 3     internal class Program
  4. 4     {
  5. 5         static void Main(string[] args)
  6. 6         {
  7. 7             var person = new Person();
  8. 8             Console.ReadLine();
  9. 9         }
  10. 10     }
  11. 11     internal class Person
  12. 12     {
  13. 13         public int Age = 20;
  14. 14
  15. 15         public string Name = "jack";
  16. 16     }
  17. 17 }
复制代码
View Code
    2.3、ExampleCore_3_1_8
  1. 1 namespace ExampleCore_3_1_8
  2. 2 {
  3. 3     internal class Program
  4. 4     {
  5. 5         static void Main(string[] args)
  6. 6         {
  7. 7             Console.WriteLine("Welcome to .NET Advanced Debugging!");
  8. 8
  9. 9             Person person = new Person() { Name = "PatrickLiu", Age = 32, HomeAddress = new Address() { Country = "China", Province = "冀州", City = "直隶总督府", Region = "广平大街23号", PostalCode = "213339" } };
  10. 10
  11. 11             Console.WriteLine($"名称:{person.Name},地址:{person.HomeAddress}");
  12. 12
  13. 13             Console.Read();
  14. 14         }
  15. 15     }
  16. 16
  17. 17     public class Person
  18. 18     {
  19. 19         public int Age { get; set; }
  20. 20         public string? Name { get; set; }
  21. 21         public Address? HomeAddress { get; set; }
  22. 22     }
  23. 23
  24. 24     public class Address
  25. 25     {
  26. 26         public string? Country { get; set; }
  27. 27         public string? Province { get; set; }
  28. 28         public string? City { get; set; }
  29. 29         public string? Region { get; set; }
  30. 30         public string? PostalCode { get; set; }
  31. 31         public override string ToString()
  32. 32         {
  33. 33             return $"{Country}-{Province}-{City}-{Region}-{PostalCode}";
  34. 34         }
  35. 35     }
  36. 36 }
复制代码
View Code
三、基础知识
    本节的内容也很多,本来打算这篇文章分为:3.1、3.2、3.3、3.4、3.5、3.6 共 6 节就将原书的第三章剩下的内容全部写完,但是内容太多,就只保留一节了。便于学习和阅读。下一篇,怎么排版再定吧。
    3.1、对象检查
        本节,我们将介绍一些命令用来分析程序的状态,以确定程序的故障。我们先来介绍非托管调试器中一些常用的命令,然后在介绍在 SOS 调试扩展中针对托管代码调试的命令。
        3.1.1、内存转储
            A、基础知识
                在调试器中有很多命令都可以转储内存的内容,这个方式非常底层,从内存地址上观察地址上的内容。最常使用的命令是【d(显示内存)】,比如:【dp】命令。根据转储的数据类型不同,命令【d】也有很多不同的变化,比如:du,dw,db,da 等,如果想了解更多,可以查看 Windbg 的帮助文档,命令是【.hh】。
                其他一些变化形式:
                。du 命令把被转储的内存视作为 Unicode 字符。
                。da 命令把被转储的内存视作为 ASCII字符。
                。dw 命令把被转储的内存视作为字(word)。
                。db 命令把被转储的内存视作为字节值和 ASCII 字符。
                。dq 命令把被转储的内存视作为四字值(quad word)。

            B、眼见为实:
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_7
                    我们编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】,输入命令:NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.EXE。
                    
                    打开【NTSD】调试器窗口。
                    

                    继续使用【g】命令,运行调试器,等调试器卡住后,按【ctrl+c】组合键键入调试器的中断模式。
                    切换到主线程,执行命令【~0s】。
  1. 1 0:009> ~0s
  2. 2 coreclr!GetThreadNULLOk+0x1e [inlined in coreclr!CrstBase::Enter+0x32]:
  3. 3 00007ff9`bd119fa2 488b34c8        mov     rsi,qword ptr [rax+rcx*8] ds:0000026c`600cb0b0=0000026c600ba2d0
复制代码
                    使用【!clrstack -a】查看托管代码的线程调用栈。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x954 (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000785C77DCE8 00007ff9bd119fa2 [ExternalMethodFrame: 000000785c77dce8]
  5. 5 ......(省略了)
  6. 6 000000785C77E4D0 00007FF95D621987 ExampleCore_3_1_7.Program.Main(System.String[])
  7. 7     PARAMETERS:
  8. 8         args (0x000000785C77E520) = 0x0000026c64808ea0
  9. 9     LOCALS:
  10. 10         0x000000785C77E508 = <strong>0x0000026c64809640</strong>
复制代码
                    0x0000026c64809640 红色标注的就是 Person 类型的局部变量 person 的地址。我们可以使用【!dumpobj /d 0x0000026c64809640】查看 person 的详情。
  1. 1 0:000> !dumpobj /d 0x0000026c64809640
  2. 2 Name:        ExampleCore_3_1_7.Person
  3. 3 MethodTable: <strong>00007ff95d6d93e0(方法表地址)
  4. </strong> 4 EEClass:     00007ff95d6e1f18
  5. 5 Tracked Type: false
  6. 6 Size:        32(0x20) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff95d591188  4000001       10         System.Int32  1 instance               20 Age
  11. 11 00007ff95d60ec08  4000002        8        System.String  0 instance 000002acf67a04a0 Name
  12. 12 0:000>
复制代码
                    同样,我们可以使用【dp】命令也能看到 person 的详情信息,只是不是很直观。
  1. 1 0:000> dp 0x0000026c64809640
  2. 2 0000026c`64809640<strong>  00007ff9`5d6d93e0</strong> <strong>000002ac`f67a04a0
  3. </strong>3 0000026c`64809650  00000000`00000014 00000000`00000000
  4. 4 0000026c`64809660  00007ff9`5d555fa8 00000000`00000000
  5. 5 0000026c`64809670  00000000`00000000 00007ff9`5d60ec08
  6. 6 0000026c`64809680  0065006b`0000000c 006c0065`006e0072
  7. 7 0000026c`64809690  0064002e`00320033 00000000`006c006c
  8. 8 0000026c`648096a0  00000000`00000000 00007ff9`5d700d68
  9. 9 0000026c`648096b0  00000000`00000000 00000000`00000000
复制代码
                    上面使用【!dumpobj】和【dp】命令我们找到的方法表地址都是一样的。000002ac`f67a04a0 这个项就是 Name 域的地址,因为 Name 是引用类型,所以这里是一个地址。我们可以继续使用【!dumpobj /d 000002ac`f67a04a0】来验证。
  1. 1 0:000> !dumpobj /d 000002ac`f67a04a0
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ff95d60ec08
  4. 4 EEClass:     00007ff95d5ea500
  5. 5 Tracked Type: false
  6. 6 Size:        30(0x1e) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      <strong>jack(我们赋的值)
  9. </strong> 9 Fields:
  10. 10               MT    Field   Offset                 Type VT     Attr            Value Name
  11. 11 00007ff95d591188  400033b        8         System.Int32  1 instance                4 _stringLength
  12. 12 00007ff95d59b538  400033c        c          System.Char  1 instance               6a _firstChar
  13. 13 00007ff95d60ec08  400033a       c8        System.String  0   static 000002acf67a0008 Empty
复制代码
                      我们可以使用【dumpobj】命令,当然也可以使用【dp】命令,都可以看到想看到的信息,不过是【dp】可读性差很多。
                      如果我们查看命令如何使用,可以使用【.hh】命令。
  1. 1 0:000> .hh
复制代码
                      效果如图:
                      

                      就能打开调试的帮助文件。
                      

                      我们可以打开【索引】选项,查看我们想要查看的命令。
                      


                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_7
                    我们编译项目,打开 Windbg,点击【文件】----》【Launch executable】附加程序 ExampleCore_3_1_7.exe,打开调试器的界面,程序已经处于中断状态。由于显示的内容太多,我们可以使用【.cls】命令清空调试器的界面。我们使用【g】命令继续运行程序,调试器会在【Console.ReadLine()】这行代码处卡住,我们点击【break】按钮,就可以调试程序了。
                    查看是否在主线程,如果不是,切换到主线程,执行命令【~0s】。
  1. 1 0:006> ~0s
  2. 2 ntdll!NtReadFile+0x14:
  3. 3 00007ffa`9576ae54 c3              ret
复制代码
                    我们查看一下当前托管的线程栈,执行命令【!clrstack -a】。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x8fc (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000D30A57E230 00007ffa9576ae54 [InlinedCallFrame: 000000d30a57e230]
  5. 5 000000D30A57E230 00007ff9e4f076eb [InlinedCallFrame: 000000d30a57e230]
  6. 6 ......(省略了)
  7. 7
  8. 8 000000D30A57E580 00007ff95e471987 ExampleCore_3_1_7.Program.Main(System.String[]) [E:\Visual Studio\..\ExampleCore_3_1_7\Program.cs @ 8]
  9. 9     PARAMETERS:
  10. 10         args (0x000000D30A57E5D0) = 0x0000021dc3808ea0
  11. 11     LOCALS:
  12. 12         0x000000D30A57E5B8 = <strong>0x0000021dc3809640</strong>
复制代码
                    0x0000021dc3809640 红色标注的就是 Person 类型的局部变量 person 的地址。我们直接使用【dp】命令转储出 person 的值。
  1. 1 0:000> dp 0x0000021dc3809640
  2. 2 0000021d`c3809640  <strong>00007ff9`5e5293e0</strong> 0000025e`558d04a0
  3. 3 0000021d`c3809650  00000000`00000014 00000000`00000000
  4. 4 0000021d`c3809660  00007ff9`5e3a5fa8 00000000`00000000
  5. 5 0000021d`c3809670  00000000`00000000 00007ff9`5e45ec08
  6. 6 0000021d`c3809680  0065006b`0000000c 006c0065`006e0072
  7. 7 0000021d`c3809690  0064002e`00320033 00000000`006c006c
  8. 8 0000021d`c38096a0  00000000`00000000 00007ff9`5e550d68
  9. 9 0000021d`c38096b0  00000000`00000000 00000000`00000000
复制代码
                    最左边的一列给出了每行内存的起始地址,后面是内存的内容。00007ff9`5e5293e0 红色标注的就是方法表。我们可以验证。执行命令【!dumpheap -type Person】。
  1. 1 0:000> !dumpheap -type Person
  2. 2          Address               MT           Size
  3. 3     021dc3809640     <strong>7ff95e5293e0</strong>             32
  4. 4
  5. 5 Statistics:
  6. 6           MT Count TotalSize Class Name
  7. 7 7ff95e5293e0     1        32 ExampleCore_3_1_7.Person
  8. 8 Total 1 objects, 32 bytes
复制代码
                    7ff95e5293e0 和 00007ff9`5e5293e0 两个值是一样的。
                    
                    虽然直接输出内存的内容很有用,但是阅读起来就很麻烦。当我们调试托管代码的时候,使用 SOS 扩展命令会提供更直接的信息。
                    如果我们想查看有关调试器的各种命令。我们可以使用【.hh】帮助文件。
  1. 1 windbg> .hh
复制代码
                    同样能打开调试器的帮助窗口。
                    

 
        3.1.2、值类型的转储
            A、基础知识
                我们知道.NET 的类型分为值类型和引用类型,那我们如何判断一个指针指向的是否是值类型呢,最佳的方式就是使用【dumpobj】命令,但它只对引用类型有效。【dumpobj】命令的参数是一个指向引用类型的指针,如果指针指向的是值类型,【dumpobj】命令就会输出:Invalid object。
            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_6
                    1.1)、查看独立的值类型
                        编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                        
                        打开【NTSD】调试器窗口。
                        

                        输出内容太多,使用【.cls】命令,清理一下屏幕。然后使用【g】命令,运行调试器。调试器输出:Press any key to continue(AddCoordinate),如图:
                        

                        输入【ctrl+c】组合键进入中断模式。我们现在查看一下托管线程栈,可以使用【!clrstack -a】命令。
  1. 1 0:002> !clrstack -a
  2. 2 OS Thread Id: 0x1764 (2)
  3. 3 Unable to walk the managed stack. The current thread is likely not a
  4. 4 managed thread. You can run !threads to get a list of managed threads in
  5. 5 the process
  6. 6 Failed to start stack walk: 80070057
复制代码
                        如图:
                        

                        该命令执行错误,提示不是一个有效的托管线程,由于我们是手动中断程序的执行,调试器的线程上下文是在调试器线程上,它是一个非托管线程,因此,在执行该命令之前,需要切换到托管线程的上下文中。执行命令【~0s】。
  1. 1 0:002> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffb`2fdaae74 c3              ret
  4. 4 0:000>
复制代码
                        我们再次执行【!clrstack -a】命令。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x2c1c (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000CD41F7E5E8 00007ffb2fdaae74 [InlinedCallFrame: 000000cd41f7e5e8]
  5. 5 000000CD41F7E5E8 00007ffb1b68787a [InlinedCallFrame: 000000cd41f7e5e8]
  6. 6 。。。。。。(省略了)
  7. 7 000000CD41F7E770 00007FF9F55919B6 <strong>ExampleCore_3_1_6.ObjTypes.Main</strong>(System.String[])
  8. 8     PARAMETERS:
  9. 9         args (0x000000CD41F7E840) = 0x000002a492808ea0
  10. 10     LOCALS:
  11. 11         <strong>0x000000CD41F7E820</strong> = 0x0000006400000064
  12. 12         0x000000CD41F7E818 = 0x0000000000000000
  13. 13         0x000000CD41F7E810 = 0x0000000000000000
复制代码
                        这时,【clrstack】命令输出了托管线程的栈回溯,包括每个栈帧的局部变量和参数。我们主要关注【ExampleCore_3_1_6.ObjTypes.Main】栈帧和地址【0x000000CD41F7E820】上的局部变量。【0x000000CD41F7E820】这个地址我们不知道它指向的是一个值类型还是引用类型。我们可以使用【dumpobj】命令做一个测试,因为该命令只对引用类型实例起作用。
  1. 1 0:000> !dumpobj 0x000000CD41F7E820
  2. 2 <Note: this object has an invalid CLASS field>
  3. 3 Invalid object
复制代码
 
                        从这个输出结果可以知道,这个指针指向的肯定不是引用类型。我们可以通过命令【r】验证一下,可以观察【rsp】寄存器,它保存的是当前栈指针。
  1. 1 0:000> r
  2. 2 rax=0000000000000007 rbx=000000cd41f7e550 rcx=00000000000001c8
  3. 3 rdx=0000000000000000 rsi=0000000000000000 rdi=00000000000001c8
  4. 4 rip=00007ffb2fdaae74 <strong>rsp=000000cd41f7e388</strong> rbp=000000cd41f7e490
  5. 5  r8=000000cd41f7e388  r9=000000cd41f7e490 r10=0000000000000000
  6. 6 r11=0000000000000246 r12=000000cd41f7e8a0 r13=0000000000000004
  7. 7 r14=0000000000000003 r15=000000cd41f7ea00
  8. 8 iopl=0         nv up ei pl zr na po nc
  9. 9 cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
  10. 10 ntdll!NtDeviceIoControlFile+0x14:
  11. 11 00007ffb`2fdaae74 c3              ret
复制代码
                        【rsp】寄存器的值是:000000CD41F7E388,我们正在分析的地址是:0x000000CD41F7E820,连个地址非常接近,说明我们分析的地址是栈地址,也就是这个地址存储的是值类型的值。
                        我们可以使用【dp】命令查看一下具体的内容。
  1. 1 0:000> dp 0x000000CD41F7E820
  2. 2 000000cd`41f7e820  <strong>00000064`00000064 00000000`00000064</strong>
  3. 3 000000cd`41f7e830  000000cd`41f7e860 00007ffa`550da1a3
  4. 4 000000cd`41f7e840  000002a4`92808ea0 000000cd`41f7ee88
  5. 5 000000cd`41f7e850  000000cd`41f7ee88 000000cd`41f7ea79
  6. 6 000000cd`41f7e860  000000cd`41f7e910 00000000`0000001d
  7. 7 000000cd`41f7e870  000000cd`41f7ea88 00007ffa`550614c9
  8. 8 000000cd`41f7e880  00000000`00000000 00000000`00000130
  9. 9 000000cd`41f7e890  000000cd`41f7ea88 00007ffa`54f8a456
复制代码
 
                        00000064 的十进制的值就是100。后面有三个域的值是0x64。
 
  1. 1 0:000> ? 00000064
  2. 2 Evaluate expression: 100 = 00000000`00000064                  
复制代码
                    1.2)、查看嵌入在引用类型中的值类型。
                        编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                        
                        打开【NTSD】调试器窗口。
                        

                        输出内容太多,使用【.cls】命令,清理一下屏幕。然后使用【g】命令,运行调试器。调试器输出:Press any key to continue(AddCoordinate),如图:
                        

                        按组合键【ctrl+c】,进入中断模式。我们直接使用【!bpmd ExampleCore_3_1_6 ExampleCore_3_1_6.ObjTypes.AddCoordinate】命令,在 ObjTypes 类型上的 AddCoordinate 的方法上设置断点。
                        
  1. 1 0:009> !bpmd ExampleCore_3_1_6 ExampleCore_3_1_6.ObjTypes.AddCoordinate
  2. 2 MethodDesc = 00007FF9F5630108
  3. 3 Adding pending breakpoints...
复制代码
                        我们继续运行调试器。
  1. 0:000> g
  2. (131c.3ca0): CLR notification exception - code e0444143 (first chance)
  3. JITTED ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)
  4. Setting breakpoint: bp 00007FF9F43B1CD0 [ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)]
  5. 。。。
复制代码
                        重新设置断点,除非并中断执行。我们执行【!clrstack -a】命令查看一下托管线程栈。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3df8 (0)
  3. 3         Child SP               IP Call Site
  4. 4 00000085A277E468 00007ffb2d7f9202 [HelperMethodFrame: 00000085a277e468] System.Diagnostics.Debugger.BreakInternal()
  5. 5 00000085A277E570 00007FFA4FC654DA System.Diagnostics.Debugger.Break()
  6. 6
  7. 7 00000085A277E5A0 00007FF9F0AB1D1A <strong>ExampleCore_3_1_6.ObjTypes.AddCoordinate</strong>(Coordinate)
  8. 8     PARAMETERS:
  9. 9         <strong>this</strong> <strong>(</strong><strong>0x00000085A277E600) = 0x00000264de809ce0</strong>
  10. 10         coord (0x00000085A277E628) = 0x0000006400000064
  11. 11
  12. 12 00000085A277E600 00007FF9F0AB1A11 ExampleCore_3_1_6.ObjTypes.Main(System.String[])
  13. 13     PARAMETERS:
  14. 14         args (0x00000085A277E6D0) = 0x00000264de808ea0
  15. 15     LOCALS:
  16. 16         0x00000085A277E6B0 = 0x0000006400000064
  17. 17         0x00000085A277E6A8 = 0x00000264de809ce0
  18. 18         0x00000085A277E6A0 = 0x0000000000000000
复制代码
                        我们主要关注【ExampleCore_3_1_6.ObjTypes.AddCoordinate】栈帧和【this】指针。【this】指针指向的是当前的引用类型实例。我们可以使用【dumpobj】命令输出看一下。
  1. 1 0:000> !dumpobj /d 0x00000264de809ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9f0b60238
  4. 4 EEClass:     00007ff9f0b4fcb8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 <strong>Fields:
  9. </strong> 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 <strong>00007ff9f0b601c8  4000001       18 ...jTypes+Coordinate  1 instance 00000264de809cf8 coordinate
  11. </strong>11 00007ff9f0a99df8  4000002        8       System.Int32[]  0 instance 00000264de809d10 intArray
  12. 12 00007ff9f0b3fac8  4000003       10      System.String[]  0 instance 00000264de809d80 strArray
复制代码
                        红色标注的【coordinate】就是引用类型包含一个值类型的例子。【MT、Field、Offset、Type、VT、Attr、Value和 Name】这些域具体的意思,可以查看图表。
                        

                        我们有两种方式可以查看【coordinate】变量的具体值。第一种,使用【dp】命令。命令的参数分别是引用类型对象的地址(0x00000264de809ce0)和偏移(0x18)。
  1. 1 0:000> dp 0x00000264de809ce0+0x18
  2. 2 00000264`de809cf8  00000000`00000000 00000000`00000000
  3. 3 00000264`de809d08  00000000`00000000 00007ff9`f0a99df8
  4. 4 00000264`de809d18  00000000`00000005 00000002`00000001
  5. 5 00000264`de809d28  00000004`00000003 00000000`00000005
  6. 6 00000264`de809d38  00000000`00000000 00007ff9`f0bb4468
  7. 7 00000264`de809d48  00000000`00000000 00000000`00000000
  8. 8 00000264`de809d58  00000000`00000000 00000000`00000000
  9. 9 00000264`de809d68  00000000`00000000 00007ff9`f0bb3de0
复制代码
                        第二种就是使用【!dumpvc mt addr】直接查看值类型,更直接。
  1. 1 0:000> !dumpvc 00007ff9f0b601c8  00000264de809cf8
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes+Coordinate
  3. 3 MethodTable: 00007ff9f0b601c8
  4. 4 EEClass:     00007ff9f0b4fd30
  5. 5 Size:        32(0x20) bytes
  6. 6 File:        E:\Visual Studio 2022\Source\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  7. 7 Fields:
  8. 8               MT    Field   Offset                 Type VT     Attr            Value Name
  9. 9 00007ff9f0a21188  4000005        0         System.Int32  1 instance                0 xCord
  10. 10 00007ff9f0a21188  4000006        4         System.Int32  1 instance                0 yCord
  11. 11 00007ff9f0a21188  4000007        8         System.Int32  1 instance                0 zCord
复制代码
                        
                2)、Windbg Preview 调试。
                    调试源码:ExampleCore_3_1_6
                    2.1)、查看独立的值类型
                        编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,控制台程序输出:Press any key to continue(AddCoordinate),我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                        首先,我们希望显示出托管调用栈以及相关的局部变量。使用【!clrstack -a】命令获取这些信息。
  1. 1 0:001> !clrstack -a
  2. 2 OS Thread Id: 0x239c (1)
  3. 3 Unable to walk the managed stack. The current thread is likely not a
  4. 4 managed thread. You can run !clrthreads to get a list of managed threads in
  5. 5 the process
  6. 6 Failed to start stack walk: 80070057
复制代码
                        【clrstack】命令出错了,错误原因是当前的线程上下文并不是一个有效的托管线程。由于我们手动的中断程序的执行,调试器的线程上下文是在调试器线程上,而这个线程是非托管的线程。因此,在执行【clrstack】命令之前,首先必须切换到托管线程上下文。使用【~】命令将上下文切换到线程0。
  1. 1 0:001> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`9576ae74 c3              ret
复制代码
                        然后,再次执行【clrstack】命令。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x584 (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000F06737E948 00007ffa9576ae74 [InlinedCallFrame: 000000f06737e948]
  5. 5 000000F06737E948 00007ffa4f72787a [InlinedCallFrame: 000000f06737e948]
  6. 6 ......(省略了)
  7. 7
  8. 8 000000F06737EAD0 00007ff95dd019b6 ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 29]
  9. 9     PARAMETERS:
  10. 10         args (0x000000F06737EBA0) = 0x0000020f54808ea0
  11. 11     LOCALS:
  12. 12         <strong>0x000000F06737EB80</strong> = <strong>0x0000006400000064</strong>
  13. 13         0x000000F06737EB78 = 0x0000000000000000
  14. 14         0x000000F06737EB70 = 0x0000000000000000
复制代码
                        这就是【clrstack】命令输出的托管线程的栈回溯,包括每个栈帧的局部变量和参数。在调用栈中,我们主要关注的是 Main 栈帧和位于地址 0x000000F06737EB80 上的局部变量(红色标注的)。
                        由于我们不知道这个局部变量指向的是值类型还是引用类型,因此我们可以使用【dumpobj】做一下判断。
  1. 1 0:000> !dumpobj 0x000000F06737EB80
  2. 2 <Note: this object has an invalid CLASS field>
  3. 3 Invalid object
复制代码
                        从命令的输出结果中可以看出,该地址的肯定不是引用类型。我们来验证,值类型被保存在栈上,如果发现某个地址位于当前栈指针的附近,那就可以证明是值类型了。我们使用【r】命令,观察 rsp 寄存器(保存的是当前栈指针)。
  1. 1 0:000> r
  2. 2 rax=0000000000000007 rbx=000000f06737e8b0 rcx=0000000000000058
  3. 3 rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000058
  4. 4 rip=00007ffa9576ae74 <strong>rsp=000000f06737e6e8</strong> rbp=000000f06737e7f0
  5. 5  r8=000000f06737e6e8  r9=000000f06737e7f0 r10=0000000000000000
  6. 6 r11=0000000000000130 r12=000000f06737ec00 r13=0000000000000004
  7. 7 r14=0000000000000003 r15=000000f06737ed60
  8. 8 iopl=0         nv up ei pl zr na po nc
  9. 9 cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
  10. 10 ntdll!NtDeviceIoControlFile+0x14:
  11. 11 00007ffa`9576ae74 c3              ret
复制代码
                        在 rsp 寄存器中包含的值:000000f06737e6e8,它非常接近我们分析的地址:0x000000F06737EB80。说明我们分析的地址就是一个栈地址,我们使用【dp】查看一下详情。
  1. 1 0:000> dp 0x000000F06737EB80
  2. 2 000000f0`6737eb80  <strong>00000064</strong>`<strong>00000064</strong> 00000000`<strong>00000064</strong>
  3. 3 000000f0`6737eb90  000000f0`6737ebc0 00007ff9`bd85a1a3
  4. 4 000000f0`6737eba0  0000020f`54808ea0 000000f0`6737f1e8
  5. 5 000000f0`6737ebb0  000000f0`6737f1e8 000000f0`6737edd9
  6. 6 000000f0`6737ebc0  000000f0`6737ec70 00000000`0000001d
  7. 7 000000f0`6737ebd0  000000f0`6737ede8 00007ff9`bd7e14c9
  8. 8 000000f0`6737ebe0  00000000`00000000 00000000`00000130
  9. 9 000000f0`6737ebf0  000000f0`6737ede8 00007ff9`bd70a456
复制代码
                        00000064 红色标注的十进制的值就是100。
  1. 1 0:000> ? 00000064
  2. 2 Evaluate expression: 100 = 00000000`00000064
复制代码
                        有三个 00000064,分别对应 Coordinate 类型的各个域。                     
                    2.2)、查看引用类型中的值类型
                        【2.1 的例子】我们看到了如何显示在函数内声明的值类型的内容。通常,值类型被嵌入在引用类型中并被保存在托管堆上。在这种情况下,我们不能直接使用内存转储命令,而需要借助一些辅助命令来转储值类型。
                        编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,控制台程序输出:Press any key to continue(AddCoordinate),我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                        由于我们是人工中断程序的执行,当前的线程上下文是调试器的线程上下文不是托管线程上下文,我们必须切换到托管线程的上下文,执行命令【~0s】。
  1. 1 0:007> !bpmd ExampleCore_3_1_6 ExampleCore_3_1_6.ObjTypes.AddCoordinate
  2. 2 MethodDesc = 00007FF9EDAA0108
  3. 3 Adding pending breakpoints...
复制代码
                        断点设置好后,我们继续执行调试器,执行命令【g】。
  1. 1 0:007> g
  2. 2 (3310.3864): CLR notification exception - code e0444143 (first chance)
  3. 3 JITTED ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)
  4. 4 Setting breakpoint: bp 00007FF9ED9F1CE0 [ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)]
  5. 5 Breakpoint 0 hit
  6. 6 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate:
  7. 7 00007ff9`ed9f1ce0 55              push    rbp
复制代码
                        当前我们的调试器线程上下文也自动切换到托管线程的上下文,可以直接执行【!clrstack -a】命令。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3864 (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000DEBDF7E968 00007ff9ed9f1ce0 ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate) [E:\Visual Studio\.\ExampleCore_3_1_6\Program.cs @ 48]
  5. 5     PARAMETERS:
  6. 6         <strong>this (<CLR reg>) =</strong> <strong>0x0000027eb4409ce0</strong>
  7. 7         coord (<CLR reg>) = 0x000000debdf7e998
  8. 8
  9. 9 000000DEBDF7E970 00007ff9ed9f1a11 ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\..\ExampleCore_3_1_6\Program.cs @ 31]
  10. 10     PARAMETERS:
  11. 11         args (0x000000DEBDF7EA40) = 0x0000027eb4408ea0
  12. 12     LOCALS:
  13. 13         0x000000DEBDF7EA20 = 0x0000006400000064
  14. 14         0x000000DEBDF7EA18 = 0x0000027eb4409ce0
  15. 15         0x000000DEBDF7EA10 = 0x0000000000000000
复制代码
                        0x0000027eb4409ce0 红色标注的是 this 指针。this 指针指向当前的对象实例。我们使用【!dumpobj】命令查看一下 this 指针。
  1. 1 0:000> !dumpobj /d 0x0000027eb4409ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9edaa0238
  4. 4 EEClass:     00007ff9eda8fca8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 <strong>Fields:
  9. </strong> 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9edaa01c8  4000001       18 ...jTypes+Coordinate  1<strong> instance 0000027eb4409cf8 coordinate
  11. </strong>11 00007ff9ed9d9df8  4000002        8       System.Int32[]  0 instance 0000027eb4409d10 intArray
  12. 12 00007ff9eda7fac8  4000003       10      System.String[]  0 instance 0000027eb4409d80 strArray
复制代码
                   Fields 是最重要的信息,它包含了这个对象的元数据。各个域的表示信息可以看下表。
                   

                   如果要显示这个域本身的内容,有两种方式。其中一种是使用【dp】命令,参数是引用类型实例的地址(000001d9b1c09ce0)和偏移(0x18)。
  1. 1 0:000> dp 000001d9b1c09ce0+0x18
  2. 2 000001d9`b1c09cf8  00000000`00000000 00000000`00000000
  3. 3 000001d9`b1c09d08  00000000`00000000 00007ff9`ebf49df8
  4. 4 000001d9`b1c09d18  00000000`00000005 00000002`00000001
  5. 5 000001d9`b1c09d28  00000004`00000003 00000000`00000005
  6. 6 000001d9`b1c09d38  00000000`00000000 00007ff9`ec064468
  7. 7 000001d9`b1c09d48  00000000`00000000 00000000`00000000
  8. 8 000001d9`b1c09d58  00000000`00000000 00000000`00000000
  9. 9 000001d9`b1c09d68  00000000`00000000 00007ff9`ec063de0
复制代码
                   如果我们想查看【...jTypes+Coordinate】的具体的值,可以执行命令【!dumpvc mt addr】。
  1. 1 0:000> !dumpvc 00007ff9edaa01c8 0000027eb4409cf8
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes+Coordinate
  3. 3 MethodTable: 00007ff9edaa01c8
  4. 4 EEClass:     00007ff9eda8fd20
  5. 5 Size:        32(0x20) bytes
  6. 6 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  7. 7 Fields:
  8. 8               MT    Field   Offset                 Type VT     Attr            Value Name
  9. 9 00007ff9ed961188  4000005        0         System.Int32  1 instance                0 xCord
  10. 10 00007ff9ed961188  4000006        4         System.Int32  1 instance                0 yCord
  11. 11 00007ff9ed961188  4000007        8         System.Int32  1 instance                0 zCord
复制代码
 
        3.1.3、引用类型的转储
            A、基础知识
                如果我们想将引用类型转储输出,很简单,直接使用【dumpobj】命令就可以了。【dumpobj】命令的参数可以直接跟引用类型的地址,如果不想输出域的内容,可以使用 -nofields 命令开关。这个命令也有一个简写形式就是【do】。
            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_7
                    编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.exe】。
                    
                    打开【NTSD】调试器窗口。
                    

                    我们使用【g】命令,运行调试器,等到调试器暂停,我们按【ctr+c】组合键进入中断模式。
                    

                    由于我们手动中断调试器,需要执行线程上下文的切换,切换到托管线程的上下文,执行命令【~os】。
  1. 1 0:002> ~0s
  2. 2 coreclr!LookupMap<MethodTable *>::GetValueAt+0x3 [inlined in coreclr!ClassLoader::LoadTypeDefThrowing+0x89]:
  3. 3 00007ffa`536a5c49 48f7d2          not     rdx
复制代码
                    现在就可以使用【!clrstack -a】命令查看托管线程栈了。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x290c (0)
  3. 3         Child SP               IP Call Site
  4. 4 00000068AEBEDE68 00007ffa536a5c49 [ExternalMethodFrame: 00000068aebede68]
  5. 5 。。。。。。(省略了)
  6. 6 00000068AEBEE650 00007FF9F3C51987 ExampleCore_3_1_7.Program.Main(System.String[])
  7. 7     PARAMETERS:
  8. 8         args (0x00000068AEBEE6A0) = 0x000001afc2408ea0
  9. 9     LOCALS:
  10. 10         0x00000068AEBEE688 = <strong>0x000001afc2409640</strong>
复制代码
  1. <strong>                      </strong>0x000001afc2409640 红色标注的就是 Person 类型的局部变量 person。
复制代码
  1.                       废话不多说,直接使用【!dumpobj】或者【!do】命令查看 Person 的详情了。
复制代码
  1. 1 0:000> !dumpobj 0x000001afc2409640
  2. 2 Name:        ExampleCore_3_1_7.Person
  3. 3 MethodTable: 00007ff9f3d093e0
  4. 4 EEClass:     00007ff9f3d11f18
  5. 5 Tracked Type: false
  6. 6 Size:        32(0x20) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9f3bc1188  4000001       10         System.Int32  1 instance               20 Age
  11. 11 00007ff9f3c3ec08  4000002        8        System.String  0 instance 000001f0546704a0 Name
  12. 12
  13. 13
  14. 14 0:000> !do 0x000001afc2409640
  15. 15 Name:        ExampleCore_3_1_7.Person
  16. 16 MethodTable: 00007ff9f3d093e0
  17. 17 EEClass:     00007ff9f3d11f18
  18. 18 Tracked Type: false
  19. 19 Size:        32(0x20) bytes
  20. 20 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  21. 21 Fields:
  22. 22               MT    Field   Offset                 Type VT     Attr            Value Name
  23. 23 00007ff9f3bc1188  4000001       10         System.Int32  1 instance               20 Age
  24. 24 00007ff9f3c3ec08  4000002        8        System.String  0 instance 000001f0546704a0 Name
复制代码
                    如果不想输出【Fields】的内容,可以使用 -nofields 命令开关。
  1. 1 0:000> !dumpobj -nofields 0x000001afc2409640
  2. 2 Name:        ExampleCore_3_1_7.Person
  3. 3 MethodTable: 00007ff9f3d093e0
  4. 4 EEClass:     00007ff9f3d11f18
  5. 5 Tracked Type: false
  6. 6 Size:        32(0x20) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  8. 8
  9. 9 0:000> !do -nofields 0x000001afc2409640
  10. 10 Name:        ExampleCore_3_1_7.Person
  11. 11 MethodTable: 00007ff9f3d093e0
  12. 12 EEClass:     00007ff9f3d11f18
  13. 13 Tracked Type: false
  14. 14 Size:        32(0x20) bytes
  15. 15 File:        E:\Visual Studio 2022\...ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  16. 16 0:000>
复制代码
                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_7
                    编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                    由于我们手动中断了调试器,当前的线程上下文是调试器的,不是托管线程的,我们必须使用【~0s】命令,切换到托管线程。
  1. 1 0:006> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffb`2fdaae74 c3              ret
复制代码
                    查看当前的托管线程的调用栈,使用【!clrstack -a】命令。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x99c (0)
  3. 3         Child SP               IP Call Site
  4. 4 0000006E2117E200 00007ffb2fdaae54 [InlinedCallFrame: 0000006e2117e200]
  5. 5 0000006E2117E200 00007ffaeadb76eb [InlinedCallFrame: 0000006e2117e200]
  6. 6 。。。。。。(省略了)
  7. 7
  8. 8 0000006E2117E550 00007ff9f3461987 ExampleCore_3_1_7.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_7\Program.cs @ 8]
  9. 9     PARAMETERS:
  10. 10         args (0x0000006E2117E5A0) = 0x000001da19808ea0
  11. 11     LOCALS:
  12. 12         0x0000006E2117E588 = <strong>0x000001da19809640</strong>
复制代码
                    0x000001da19809640 这个地址就是引用类型 Person 的局部变量 person。我们可以直接使用【!dumpobj】或者【!do】命令。
  1. 1 0:000> !DumpObj /d 000001da19809640
  2. 2 Name:        ExampleCore_3_1_7.Person
  3. 3 MethodTable: 00007ff9f35193e0
  4. 4 EEClass:     00007ff9f3521f18
  5. 5 Tracked Type: false
  6. 6 Size:        32(0x20) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9f33d1188  4000001       10         System.Int32  1 instance               20 Age
  11. 11 00007ff9f344ec08  4000002        8        System.String  0 instance 0000021aab9904a0 Name
  12. 12
  13. 13
  14. 14 0:000> !do 000001da19809640
  15. 15 Name:        ExampleCore_3_1_7.Person
  16. 16 MethodTable: 00007ff9f35193e0
  17. 17 EEClass:     00007ff9f3521f18
  18. 18 Tracked Type: false
  19. 19 Size:        32(0x20) bytes
  20. 20 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  21. 21 Fields:
  22. 22               MT    Field   Offset                 Type VT     Attr            Value Name
  23. 23 00007ff9f33d1188  4000001       10         System.Int32  1 instance               20 Age
  24. 24 00007ff9f344ec08  4000002        8        System.String  0 instance 0000021aab9904a0 Name
复制代码
                    我们可以使用 -nofields 命令开关去掉域的内容。
  1. 1 0:000> !DumpObj -nofields 000001da19809640
  2. 2 Name:        ExampleCore_3_1_7.Person
  3. 3 MethodTable: 00007ff9f35193e0
  4. 4 EEClass:     00007ff9f3521f18
  5. 5 Tracked Type: false
  6. 6 Size:        32(0x20) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
  8. 8
  9. 9
  10. 10 0:000> !do -nofields 000001da19809640
  11. 11 Name:        ExampleCore_3_1_7.Person
  12. 12 MethodTable: 00007ff9f35193e0
  13. 13 EEClass:     00007ff9f3521f18
  14. 14 Tracked Type: false
  15. 15 Size:        32(0x20) bytes
  16. 16 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
复制代码
                    
        3.1.4、数组的转储
            A、基础知识
                CLR 将数组作为第一级(first class)引用类型(因为所有数组都是从 System.Array 继承下来的),因此我们可以直接使用【!dumpobj】命令或者【!dumparray】命令转储出数组的在内存中的内容。
                数组本身是引用类型,但是它的元素是可以区分为:值类型和引用类型的。由此,我们可以把数组称为值类型的数组和引用类型的数组。虽然有这个区别,但是它们的内存布局是一致的,只不过在查看元素的时候有区别,如果是值类型的元素,可以使用【!dumpvc】命令,如果是引用类型,可以使用【!dumpobj】命令查看。
                数组的内存布局如图:
                
                
                这里的概念不多,但是,要演示的内容很多,而且,内容很简单,就不多说了。

            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_6
                    1.1)、值类型数组
                        编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                        
                        打开【NTSD】调试器窗口。
                        

                        继续使用【g】命令运行调试器,直到调试器输出:Press any key to continue(Arrays),然后,我们手动【ctrl+c】进入调试器的中断模式,就可以调试了。
                        

                        我们进入中断模式后,需要切换线程到托管线程,执行命令【~0s】。
  1. 1 0:002> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                        现在,我们就可以查看托管线程栈了,执行命令【!clrstack -a】。
  1. 0:000> !clrstack -a
  2. OS Thread Id: 0x3394 (0)
  3.         Child SP               IP Call Site
  4. 00000090F577E5E8 00007ffaeeaeae74 [InlinedCallFrame: 00000090f577e5e8]
  5. 00000090F577E5E8 00007ffadb81787a [InlinedCallFrame: 00000090f577e5e8]
  6. 。。。。。。(省略了)
  7. 00000090F577E770 00007FF9BE1E1A2D ExampleCore_3_1_6.ObjTypes.Main(System.String[])
  8.     PARAMETERS:
  9.         args (0x00000090F577E840) = 0x000002282e008ea0
  10.     LOCALS:
  11.         0x00000090F577E820 = 0x0000006400000064
  12.         0x00000090F577E818 = <strong>0x000002282e009ce0</strong>
  13.         0x00000090F577E810 = 0x0000000000000000
复制代码
                        0x000002282e009ce0 红色标注的就是 ObjTypes 类型的指针,我们可以使用【!do】命令查看它。
  1. 1 0:000> !do 0x000002282e009ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9be290238
  4. 4 EEClass:     00007ff9be27fca8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9be2901c8  4000001       18 ...jTypes+Coordinate  1 instance 000002282e009cf8 coordinate
  11. 11 00007ff9be1c9df8  4000002        8       System.Int32[]  0 instance <strong>000002282e009d10 intArray
  12. </strong>12 00007ff9be26fac8  4000003       10      System.String[]  0 instance 000002282e009d80 strArray
  13. 13 0:000>
复制代码
 
                        这里我们开始关注 intArray 值类型数组,也就是红色标注的。intArray 数组的地址是:000002282e009d10,有了地址,我们就可以使用【!dumpobj 000002282e009d10】命令转储出详情。
 
  1. 1 0:000> !dumpobj 000002282e009d10
  2. 2 Name:        System.Int32[]
  3. 3 MethodTable: 00007ff9be1c9df8
  4. 4 EEClass:     00007ff9be1c9d78
  5. 5 Tracked Type: false
  6. 6 Size:        44(0x2c) bytes
  7. 7 Array:       Rank 1, Number of elements 5, Type Int32
  8. 8 Fields:
  9. 9 None
复制代码
 
                        验证了我们的说法,输出了数组的名称,维度(Rank),元素个数(Number of elements)等。我们依然可以使用【dp 000002282e009d10】命令输出详情。
                        

                        我们使用【!dumpmt 00007ff9`be1c9df8】命令是不是数组的方法表。
  1. 1 0:000> !dumpmt 00007ff9`be1c9df8
  2. 2 EEClass:         00007FF9BE1C9D78
  3. 3 Module:          00007FF9BE084000
  4. 4 Name:            System.Int32[]
  5. 5 mdToken:         0000000002000000
  6. 6 File:            C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  7. 7 BaseSize:        0x18
  8. 8 ComponentSize:   0x4
  9. 9 DynamicStatics:  false
  10. 10 ContainsPointers false
  11. 11 Slots in VTable: 28
  12. 12 Number of IFaces in IFaceMap: 6
复制代码
                        当然,我们可以直接使用【!dumparray 000002282e009d10】命令查看 intArray 数组详情,这个命令更直观。
 
  1. 1 0:000> !dumparray 000002282e009d10
  2. 2 Name:        System.Int32[]
  3. 3 MethodTable: 00007ff9be1c9df8
  4. 4 EEClass:     00007ff9be1c9d78
  5. 5 Size:        44(0x2c) bytes
  6. 6 Array:       Rank 1, Number of elements 5, Type Int32
  7. 7 Element Methodtable: 00007ff9be151188
  8. 8 [0] 000002282e009d20
  9. 9 [1] 000002282e009d24
  10. 10 [2] 000002282e009d28
  11. 11 [3] 000002282e009d2c
  12. 12 [4] 000002282e009d30
  13. 13
  14. 14 0:000> !dumpvc 00007ff9be151188 000002282e009d20
  15. 15 Name:        System.Int32
  16. 16 MethodTable: 00007ff9be151188
  17. 17 EEClass:     00007ff9be141e20
  18. 18 Size:        24(0x18) bytes
  19. 19 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  20. 20 Fields:
  21. 21               MT    Field   Offset                 Type VT     Attr            Value Name
  22. 22 00007ff9be151188  400051d        0         System.Int32  1 instance                1 m_value
  23. 23
  24. 24 0:000> !dumpvc 00007ff9be151188 000002282e009d24
  25. 25 Name:        System.Int32
  26. 26 MethodTable: 00007ff9be151188
  27. 27 EEClass:     00007ff9be141e20
  28. 28 Size:        24(0x18) bytes
  29. 29 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  30. 30 Fields:
  31. 31               MT    Field   Offset                 Type VT     Attr            Value Name
  32. 32 00007ff9be151188  400051d        0         System.Int32  1 instance                2 m_value
  33. 33
  34. 34 0:000> !dumpvc 00007ff9be151188 000002282e009d28
  35. 35 Name:        System.Int32
  36. 36 MethodTable: 00007ff9be151188
  37. 37 EEClass:     00007ff9be141e20
  38. 38 Size:        24(0x18) bytes
  39. 39 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  40. 40 Fields:
  41. 41               MT    Field   Offset                 Type VT     Attr            Value Name
  42. 42 00007ff9be151188  400051d        0         System.Int32  1 instance                3 m_value
  43. 43
  44. 44 0:000> !dumpvc 00007ff9be151188 000002282e009d2c
  45. 45 Name:        System.Int32
  46. 46 MethodTable: 00007ff9be151188
  47. 47 EEClass:     00007ff9be141e20
  48. 48 Size:        24(0x18) bytes
  49. 49 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  50. 50 Fields:
  51. 51               MT    Field   Offset                 Type VT     Attr            Value Name
  52. 52 00007ff9be151188  400051d        0         System.Int32  1 instance                4 m_value
  53. 53
  54. 54 0:000> !dumpvc 00007ff9be151188 000002282e009d30
  55. 55 Name:        System.Int32
  56. 56 MethodTable: 00007ff9be151188
  57. 57 EEClass:     00007ff9be141e20
  58. 58 Size:        24(0x18) bytes
  59. 59 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  60. 60 Fields:
  61. 61               MT    Field   Offset                 Type VT     Attr            Value Name
  62. 62 00007ff9be151188  400051d        0         System.Int32  1 instance                5 m_value
  63. 63 0:000>
复制代码
 

                    1.2)、引用类型数组
                        编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                        

                        打开【NTSD】调试器窗口。
                        
                        继续使用【g】命令运行调试器,直到调试器输出:Press any key to continue(Arrays),然后,我们手动【ctrl+c】进入调试器的中断模式,就可以调试了。
                        

                        我们进入中断模式后,需要切换线程到托管线程,执行命令【~0s】。
  1. 1 0:002> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                        现在,我们就可以查看托管线程栈了,执行命令【!clrstack -a】。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3394 (0)
  3. 3         Child SP               IP Call Site
  4. 4 00000090F577E5E8 00007ffaeeaeae74 [InlinedCallFrame: 00000090f577e5e8]
  5. 5 00000090F577E5E8 00007ffadb81787a [InlinedCallFrame: 00000090f577e5e8]
  6. 6 。。。。。。(省略了)
  7. 7
  8. 8 00000090F577E770 00007FF9BE1E1A2D ExampleCore_3_1_6.ObjTypes.Main(System.String[])
  9. 9     PARAMETERS:
  10. 10         args (0x00000090F577E840) = 0x000002282e008ea0
  11. 11     LOCALS:
  12. 12         0x00000090F577E820 = 0x0000006400000064
  13. 13         0x00000090F577E818 = <strong>0x000002282e009ce0</strong>
  14. 14         0x00000090F577E810 = 0x0000000000000000
复制代码
                        0x000002282e009ce0 红色标注的就是 ObjTypes 类型的指针,我们可以使用【!do】命令查看它。
  1. 1 0:000> !do 0x000002282e009ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9be290238
  4. 4 EEClass:     00007ff9be27fca8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9be2901c8  4000001       18 ...jTypes+Coordinate  1 instance 000002282e009cf8 coordinate
  11. 11 00007ff9be1c9df8  4000002        8       System.Int32[]  0 instance 000002282e009d10 intArray
  12. 12 00007ff9be26fac8  4000003       10      System.String[]  0 instance <strong>000002282e009d80 strArray</strong>
复制代码
                        这里我们开始关注 strArray 引用类型数组,也就是红色标注的。我们先使用【dp 000002282e009d80】命令转储一下。
                        

                        执行【!dumpmt 00007ff9`be26fac8】命令查看 strArray 数组的方法表。
  1. 1 0:000> !dumpmt 00007ff9`be26fac8
  2. 2 EEClass:         00007FF9BE11C440
  3. 3 Module:          00007FF9BE084000
  4. 4 Name:            System.String[]
  5. 5 mdToken:         0000000002000000
  6. 6 File:            C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  7. 7 BaseSize:        0x18
  8. 8 ComponentSize:   0x8
  9. 9 DynamicStatics:  false
  10. 10 ContainsPointers true
  11. 11 Slots in VTable: 28
  12. 12 Number of IFaces in IFaceMap: 6
复制代码
                        我们可以使用【!dumpobj】命令查看查看元素的具体信息。
  1. 1 0:000> !do -nofields 00000268`c0280b78
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ff9be1cec08
  4. 4 EEClass:     00007ff9be1aa500
  5. 5 Tracked Type: false
  6. 6 Size:        36(0x24) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      <strong>Welcome
  9. </strong> 9
  10. 10 0:000> !do -nofields 00000268`c0280ba0
  11. 11 Name:        System.String
  12. 12 MethodTable: 00007ff9be1cec08
  13. 13 EEClass:     00007ff9be1aa500
  14. 14 Tracked Type: false
  15. 15 Size:        26(0x1a) bytes
  16. 16 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  17. 17 String:      <strong>to
  18. </strong>18
  19. 19 0:000> !do -nofields 00000268`c0280bc0
  20. 20 Name:        System.String
  21. 21 MethodTable: 00007ff9be1cec08
  22. 22 EEClass:     00007ff9be1aa500
  23. 23 Tracked Type: false
  24. 24 Size:        38(0x26) bytes
  25. 25 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  26. 26 String:      <strong>Advanced
  27. </strong>27
  28. 28 0:000> !do -nofields 00000268`c0280be8
  29. 29 Name:        System.String
  30. 30 MethodTable: 00007ff9be1cec08
  31. 31 EEClass:     00007ff9be1aa500
  32. 32 Tracked Type: false
  33. 33 Size:        30(0x1e) bytes
  34. 34 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  35. 35 String:      <strong>.NET
  36. </strong>36
  37. 37 0:000> !do -nofields 00000268`c0280c08
  38. 38 Name:        System.String
  39. 39 MethodTable: 00007ff9be1cec08
  40. 40 EEClass:     00007ff9be1aa500
  41. 41 Tracked Type: false
  42. 42 Size:        40(0x28) bytes
  43. 43 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  44. 44 String:      <strong>Debugging</strong>
复制代码
                        当然,还有一个更容易,更直接的命令,就是【!dumparray】可以输出所有数组的信息。
                        

                        内容很简单,就不赘述了。
  1. 1 0:000> !do -nofields 00000268c0280b78
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ff9be1cec08
  4. 4 EEClass:     00007ff9be1aa500
  5. 5 Tracked Type: false
  6. 6 Size:        36(0x24) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      Welcome
复制代码
                        可以继续使用【!do】命令查看元素的内容,这里就不演示了。

                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_6
                    2.1)、值类型数组
                        编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,直到我们的控制台程序输出:Press any key to continue(Arrays),我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                        由于我们手动中断调试器的执行,所以需要切换到托管线程的上下文中,执行命令【~0s】。
  1. 1 0:001> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                        现在我们需要查看一下托管线程的调用栈,执行命令【!clrstack -a】。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3144 (0)
  3. 3         Child SP               IP Call Site
  4. 4 0000000C91F7E668 00007ffaeeaeae74 [InlinedCallFrame: 0000000c91f7e668]
  5. 5 0000000C91F7E668 00007ffa375c787a [InlinedCallFrame: 0000000c91f7e668]
  6. 6 。。。。。。(省略了)
  7. 7
  8. 8 0000000C91F7E7F0 00007ff9b6fa1a2d ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 36]
  9. 9     PARAMETERS:
  10. 10         args (0x0000000C91F7E8C0) = 0x000001cfe3408ea0
  11. 11     LOCALS:
  12. 12         0x0000000C91F7E8A0 = 0x0000006400000064
  13. 13         0x0000000C91F7E898 = <strong>0x000001cfe3409ce0</strong>
  14. 14         0x0000000C91F7E890 = 0x0000000000000000
复制代码
                        0x000001cfe3409ce0 标红的就是 ObjTypes 类型的指针,执行命令【!do 0x000001cfe3409ce0】确认一下。
  1. 1 0:000> !do 0x000001cfe3409ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9b7050238
  4. 4 EEClass:     00007ff9b703fca8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9b70501c8  4000001       18 ...jTypes+Coordinate  1 instance 000001cfe3409cf8 coordinate
  11. 11 00007ff9b6f89df8  4000002        8       System.Int32[]  0 instance <strong>000001cfe3409d10</strong> <strong>intArray
  12. </strong>12 00007ff9b702fac8  4000003       10      System.String[]  0 instance <strong>000001cfe3409d80</strong> <strong>strArray</strong>
复制代码
                        我们看到了 ExampleCore_3_1_6.ObjTypes 类型有两个数组的域,分别是:intArray 和 strArray。我们获取了 intArray数组的地址:000001cfe3409d10 ,我们使用【!dumpobj 000001cfe3409d10】命令查看一下 intArray 数组的详情。
  1. 1 0:000> !dumpobj 000001cfe3409d10
  2. 2 Name:        System.Int32[]
  3. 3 MethodTable: 00007ff9b6f89df8
  4. 4 EEClass:     00007ff9b6f89d78
  5. 5 Tracked Type: false
  6. 6 Size:        44(0x2c) bytes
  7. 7 Array:       Rank 1, Number of elements 5, Type Int32 (Print Array)
  8. 8 Fields:
  9. 9 None
复制代码
                        【!dumpobj】命令输出了数组的名称(Name),方法表(MethodTable),数组的维度(Rank),数组的类型(Type)和数组的元素个数(Number of elements),但是我们没有看到数组的元素内容。如果想查看数组的内容,我们可以使用【dp】命令。
  1. 1 0:000> dp 000001cfe3409d10
  2. 2 000001cf`e3409d10  <strong>00007ff9`b6f89df8</strong> <strong>00000000</strong><strong>`</strong><strong>00000005</strong>
  3. 3 000001cf`e3409d20  <strong>00000002`00000001</strong> <strong>00000004`00000003</strong>
  4. 4 000001cf`e3409d30  <strong>00000000</strong><strong>`00000005</strong> 00000000`00000000
  5. 5 000001cf`e3409d40  00007ff9`b70a4468 00000000`00000000
  6. 6 000001cf`e3409d50  00000000`00000000 00000000`00000000
  7. 7 000001cf`e3409d60  00000000`00000000 00000000`00000000
  8. 8 000001cf`e3409d70  00007ff9`b70a3de0 00000000`00000000
  9. 9 000001cf`e3409d80  00007ff9`b702fac8 00000000`00000005
复制代码
                        00007ff9`b6f89df8 第一个值就是数组本身的方法表的地址。00000000`00000005 表示的数组元素个数,是 5 个元素。00000002`0000000100000004`0000000300000000`00000005 就是数组的元素内容,分别是:1,2,3,4,5。
                        00007ff9`b6f89df8 这个地址是数组本身的方法表,我们可以执行【!dumpmt 00007ff9`b6f89df8】命令验证一下。
  1. 1 0:000> !dumpmt 00007ff9`b6f89df8
  2. 2 EEClass:             00007ff9b6f89d78
  3. 3 Module:              00007ff9b6e44000
  4. 4 Name:                System.Int32[]
  5. 5 mdToken:             0000000002000000
  6. 6 File:                C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  7. 7 AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
  8. 8 BaseSize:            0x18
  9. 9 ComponentSize:       0x4
  10. 10 DynamicStatics:      false
  11. 11 ContainsPointers:    false
  12. 12 Slots in VTable:     28
  13. 13 Number of IFaces in IFaceMap: 6                     
复制代码
                    2.2)、引用类型数组
                        编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,直到我们的控制台程序输出:Press any key to continue(Arrays),我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                        由于我们手动中断调试器的执行,所以需要切换到托管线程的上下文中,执行命令【~0s】。
  1. 1 0:001> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                        现在我们需要查看一下托管线程的调用栈,执行命令【!clrstack -a】。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3144 (0)
  3. 3         Child SP               IP Call Site
  4. 4 0000000C91F7E668 00007ffaeeaeae74 [InlinedCallFrame: 0000000c91f7e668]
  5. 5 0000000C91F7E668 00007ffa375c787a [InlinedCallFrame: 0000000c91f7e668]
  6. 6 。。。。。。(省略了)
  7. 7
  8. 8 0000000C91F7E7F0 00007ff9b6fa1a2d ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 36]
  9. 9     PARAMETERS:
  10. 10         args (0x0000000C91F7E8C0) = 0x000001cfe3408ea0
  11. 11     LOCALS:
  12. 12         0x0000000C91F7E8A0 = 0x0000006400000064
  13. 13         0x0000000C91F7E898 = <strong>0x000001cfe3409ce0</strong>
  14. 14         0x0000000C91F7E890 = 0x0000000000000000
复制代码
                        0x000001cfe3409ce0 标红的就是 ObjTypes 类型的指针,执行命令【!do 0x000001cfe3409ce0】确认一下。
  1. 1 0:000> !do 0x000001cfe3409ce0
  2. 2 Name:        ExampleCore_3_1_6.ObjTypes
  3. 3 MethodTable: 00007ff9b7050238
  4. 4 EEClass:     00007ff9b703fca8
  5. 5 Tracked Type: false
  6. 6 Size:        48(0x30) bytes
  7. 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff9b70501c8  4000001       18 ...jTypes+Coordinate  1 instance 000001cfe3409cf8 coordinate
  11. 11 00007ff9b6f89df8  4000002        8       System.Int32[]  0 instance <strong>000001cfe3409d10</strong> <strong>intArray
  12. </strong>12 00007ff9b702fac8  4000003       10      System.String[]  0 instance <strong>000001cfe3409d80</strong> <strong>strArray</strong>
复制代码
                        我们看到了 ExampleCore_3_1_6.ObjTypes 类型有两个数组的域,分别是:intArray 和 strArray。这次我们关注 strArray 字符串数据,我们获取了 strArray 数组的地址:000001cfe3409d80 ,我们使用【!dumpobj 000001cfe3409d80】命令查看一下 strArray 数组的详情。
  1. 1 0:000> !dumpobj /d 000001cfe3409d80
  2. 2 Name:        System.String[]
  3. 3 MethodTable: 00007ff9b702fac8
  4. 4 EEClass:     00007ff9b6edc440
  5. 5 Tracked Type: false
  6. 6 Size:        64(0x40) bytes
  7. 7 Array:       Rank 1, Number of elements 5, Type CLASS (Print Array)
  8. 8 Fields:
  9. 9 None
复制代码
                        我们也可以使用【dp 000001cfe3409d80】命令查看它的显示。
  1. 0:000> dp 000001cfe3409d80
  2. 000001cf`e3409d80  <strong>00007ff9`b702fac8 </strong>00000000`00000005
  3. 000001cf`e3409d90  00000210`75660b78 00000210`75660ba0
  4. 000001cf`e3409da0  00000210`75660bc0 00000210`75660be8
  5. 000001cf`e3409db0  00000210`75660c08 00000000`00000000
  6. 000001cf`e3409dc0  00007ff9`b6f11188 00000000`00000064
  7. 000001cf`e3409dd0  00000000`00000000 00007ff9`b6f11188
  8. 000001cf`e3409de0  00000000`00000064 00000000`00000000
  9. 000001cf`e3409df0  00007ff9`b6f11188 00000000`00000064
复制代码
                        00007ff9`b702fac8 就是 strArray 数组的方法表,我们可以使用【!dumpmt 00007ff9`b702fac8】验证这点。
  1. 1 0:000> !dumpmt 00007ff9`b702fac8
  2. 2 EEClass:             00007ff9b6edc440
  3. 3 Module:              00007ff9b6e44000
  4. 4 Name:                System.String[]
  5. 5 mdToken:             0000000002000000
  6. 6 File:                C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  7. 7 AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
  8. 8 BaseSize:            0x18
  9. 9 ComponentSize:       0x8
  10. 10 DynamicStatics:      false
  11. 11 ContainsPointers:    true
  12. 12 Slots in VTable:     28
  13. 13 Number of IFaces in IFaceMap: 6
复制代码
                        【dp 000001cfe3409d80】命令的 00000000`00000005 这个值就是 strArray 数组的元素个数。【dp 000001cfe3409d80】命令的5个输出就是数组元素的值,由于它们是引用类型,我们可以依次使用【!dumpobj】命令输出它们的内容。
  1. 0:000> !dumpobj 00000210`75660b78
  2. Name:        System.String
  3. MethodTable: 00007ff9b6f8ec08
  4. EEClass:     00007ff9b6f6a500
  5. Tracked Type: false
  6. Size:        36(0x24) bytes
  7. File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. <strong>String:      Welcome</strong>
  9. Fields:
  10.               MT    Field   Offset                 Type VT     Attr            Value Name
  11. 00007ff9b6f11188  400033b        8         System.Int32  1 instance                7 _stringLength
  12. 00007ff9b6f1b538  400033c        c          System.Char  1 instance               57 _firstChar
  13. 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  14. 0:000> !dumpobj 00000210`75660ba0
  15. Name:        System.String
  16. MethodTable: 00007ff9b6f8ec08
  17. EEClass:     00007ff9b6f6a500
  18. Tracked Type: false
  19. Size:        26(0x1a) bytes
  20. File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  21. <strong>String:      to</strong>
  22. Fields:
  23.               MT    Field   Offset                 Type VT     Attr            Value Name
  24. 00007ff9b6f11188  400033b        8         System.Int32  1 instance                2 _stringLength
  25. 00007ff9b6f1b538  400033c        c          System.Char  1 instance               74 _firstChar
  26. 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  27. 0:000> !dumpobj 00000210`75660bc0
  28. Name:        System.String
  29. MethodTable: 00007ff9b6f8ec08
  30. EEClass:     00007ff9b6f6a500
  31. Tracked Type: false
  32. Size:        38(0x26) bytes
  33. File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  34. <strong>String:      Advanced</strong>
  35. Fields:
  36.               MT    Field   Offset                 Type VT     Attr            Value Name
  37. 00007ff9b6f11188  400033b        8         System.Int32  1 instance                8 _stringLength
  38. 00007ff9b6f1b538  400033c        c          System.Char  1 instance               41 _firstChar
  39. 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  40. 0:000> !dumpobj 00000210`75660be8
  41. Name:        System.String
  42. MethodTable: 00007ff9b6f8ec08
  43. EEClass:     00007ff9b6f6a500
  44. Tracked Type: false
  45. Size:        30(0x1e) bytes
  46. File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  47. <strong>String:      .NET</strong>
  48. Fields:
  49.               MT    Field   Offset                 Type VT     Attr            Value Name
  50. 00007ff9b6f11188  400033b        8         System.Int32  1 instance                4 _stringLength
  51. 00007ff9b6f1b538  400033c        c          System.Char  1 instance               2e _firstChar
  52. 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  53. 0:000> !dumpobj 00000210`75660c08
  54. Name:        System.String
  55. MethodTable: 00007ff9b6f8ec08
  56. EEClass:     00007ff9b6f6a500
  57. Tracked Type: false
  58. Size:        40(0x28) bytes
  59. File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  60. <strong>String:      Debugging</strong>
  61. Fields:
  62.               MT    Field   Offset                 Type VT     Attr            Value Name
  63. 00007ff9b6f11188  400033b        8         System.Int32  1 instance                9 _stringLength
  64. 00007ff9b6f1b538  400033c        c          System.Char  1 instance               44 _firstChar
  65. 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
复制代码
                        红色标注的就是数组元素具体的值。
                        上面的命令有些繁琐,我们可以直接使用【!dumparray】命令获取我们想要的数据。
                        我们直接输出 intArray,使用【!dumparray】命令。
  1. 1 0:000> !dumparray 000001cfe3409d10
  2. 2 Name:        System.Int32[]
  3. 3 MethodTable: 00007ff9b6f89df8
  4. 4 EEClass:     00007ff9b6f89d78
  5. 5 Size:        44(0x2c) bytes
  6. 6 Array:       Rank 1, Number of elements 5, Type Int32
  7. 7 Element Methodtable: 00007ff9b6f11188
  8. 8 [0] 000001cfe3409d20
  9. 9 [1] 000001cfe3409d24
  10. 10 [2] 000001cfe3409d28
  11. 11 [3] 000001cfe3409d2c
  12. 12 [4] 000001cfe3409d30
  13. 13
  14. 14 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d20
  15. 15 Name:        System.Int32
  16. 16 MethodTable: 00007ff9b6f11188
  17. 17 EEClass:     00007ff9b6f01e20
  18. 18 Size:        24(0x18) bytes
  19. 19 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  20. 20 Fields:
  21. 21               MT    Field   Offset                 Type VT     Attr            Value Name
  22. 22 00007ff9b6f11188  400051d        0         System.Int32  1 instance                1 m_value
  23. 23
  24. 24 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d24
  25. 25 Name:        System.Int32
  26. 26 MethodTable: 00007ff9b6f11188
  27. 27 EEClass:     00007ff9b6f01e20
  28. 28 Size:        24(0x18) bytes
  29. 29 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  30. 30 Fields:
  31. 31               MT    Field   Offset                 Type VT     Attr            Value Name
  32. 32 00007ff9b6f11188  400051d        0         System.Int32  1 instance                2 m_value
  33. 33
  34. 34 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d28
  35. 35 Name:        System.Int32
  36. 36 MethodTable: 00007ff9b6f11188
  37. 37 EEClass:     00007ff9b6f01e20
  38. 38 Size:        24(0x18) bytes
  39. 39 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  40. 40 Fields:
  41. 41               MT    Field   Offset                 Type VT     Attr            Value Name
  42. 42 00007ff9b6f11188  400051d        0         System.Int32  1 instance                3 m_value
  43. 43
  44. 44 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d2c
  45. 45 Name:        System.Int32
  46. 46 MethodTable: 00007ff9b6f11188
  47. 47 EEClass:     00007ff9b6f01e20
  48. 48 Size:        24(0x18) bytes
  49. 49 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  50. 50 Fields:
  51. 51               MT    Field   Offset                 Type VT     Attr            Value Name
  52. 52 00007ff9b6f11188  400051d        0         System.Int32  1 instance                4 m_value
  53. 53
  54. 54 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d30
  55. 55 Name:        System.Int32
  56. 56 MethodTable: 00007ff9b6f11188
  57. 57 EEClass:     00007ff9b6f01e20
  58. 58 Size:        24(0x18) bytes
  59. 59 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  60. 60 Fields:
  61. 61               MT    Field   Offset                 Type VT     Attr            Value Name
  62. 62 00007ff9b6f11188  400051d        0         System.Int32  1 instance                5 m_value
复制代码
                        我们直接输出strArray,使用【!dumparray】命令。
  1. 1 0:000> !dumparray 000001cfe3409d80
  2. 2 Name:        System.String[]
  3. 3 MethodTable: 00007ff9b702fac8
  4. 4 EEClass:     00007ff9b6edc440
  5. 5 Size:        64(0x40) bytes
  6. 6 Array:       Rank 1, Number of elements 5, Type CLASS
  7. 7 Element Methodtable: 00007ff9b6f8ec08
  8. 8 [0] 0000021075660b78
  9. 9 [1] 0000021075660ba0
  10. 10 [2] 0000021075660bc0
  11. 11 [3] 0000021075660be8
  12. 12 [4] 0000021075660c08
  13. 13
  14. 14 0:000> !DumpObj /d 0000021075660b78
  15. 15 Name:        System.String
  16. 16 MethodTable: 00007ff9b6f8ec08
  17. 17 EEClass:     00007ff9b6f6a500
  18. 18 Tracked Type: false
  19. 19 Size:        36(0x24) bytes
  20. 20 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  21. 21 String:      Welcome
  22. 22 Fields:
  23. 23               MT    Field   Offset                 Type VT     Attr            Value Name
  24. 24 00007ff9b6f11188  400033b        8         System.Int32  1 instance                7 _stringLength
  25. 25 00007ff9b6f1b538  400033c        c          System.Char  1 instance               57 _firstChar
  26. 26 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  27. 27
  28. 28 0:000> !DumpObj /d 0000021075660ba0
  29. 29 Name:        System.String
  30. 30 MethodTable: 00007ff9b6f8ec08
  31. 31 EEClass:     00007ff9b6f6a500
  32. 32 Tracked Type: false
  33. 33 Size:        26(0x1a) bytes
  34. 34 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  35. 35 String:      to
  36. 36 Fields:
  37. 37               MT    Field   Offset                 Type VT     Attr            Value Name
  38. 38 00007ff9b6f11188  400033b        8         System.Int32  1 instance                2 _stringLength
  39. 39 00007ff9b6f1b538  400033c        c          System.Char  1 instance               74 _firstChar
  40. 40 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  41. 41
  42. 42 0:000> !DumpObj /d 0000021075660bc0
  43. 43 Name:        System.String
  44. 44 MethodTable: 00007ff9b6f8ec08
  45. 45 EEClass:     00007ff9b6f6a500
  46. 46 Tracked Type: false
  47. 47 Size:        38(0x26) bytes
  48. 48 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  49. 49 String:      Advanced
  50. 50 Fields:
  51. 51               MT    Field   Offset                 Type VT     Attr            Value Name
  52. 52 00007ff9b6f11188  400033b        8         System.Int32  1 instance                8 _stringLength
  53. 53 00007ff9b6f1b538  400033c        c          System.Char  1 instance               41 _firstChar
  54. 54 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  55. 55
  56. 56 0:000> !DumpObj /d 0000021075660be8
  57. 57 Name:        System.String
  58. 58 MethodTable: 00007ff9b6f8ec08
  59. 59 EEClass:     00007ff9b6f6a500
  60. 60 Tracked Type: false
  61. 61 Size:        30(0x1e) bytes
  62. 62 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  63. 63 String:      .NET
  64. 64 Fields:
  65. 65               MT    Field   Offset                 Type VT     Attr            Value Name
  66. 66 00007ff9b6f11188  400033b        8         System.Int32  1 instance                4 _stringLength
  67. 67 00007ff9b6f1b538  400033c        c          System.Char  1 instance               2e _firstChar
  68. 68 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
  69. 69
  70. 70 0:000> !DumpObj /d 0000021075660c08
  71. 71 Name:        System.String
  72. 72 MethodTable: 00007ff9b6f8ec08
  73. 73 EEClass:     00007ff9b6f6a500
  74. 74 Tracked Type: false
  75. 75 Size:        40(0x28) bytes
  76. 76 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  77. 77 String:      Debugging
  78. 78 Fields:
  79. 79               MT    Field   Offset                 Type VT     Attr            Value Name
  80. 80 00007ff9b6f11188  400033b        8         System.Int32  1 instance                9 _stringLength
  81. 81 00007ff9b6f1b538  400033c        c          System.Char  1 instance               44 _firstChar
  82. 82 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
复制代码

        3.1.5、栈上对象的转储
            A、基础知识
                我们可以使用【!clrstack】命令找出每个栈帧的局部变量和参数,但是有时候需要对栈进行更深入的分析,就可以使用【!dumpstackobjects】命令。它能对栈进行遍历,并输出栈上所有的托管对象。
                命令格式:!DumpStackObjects [-verify] [top stack [bottom stack]],verify 选项表示对找到的每个托管对象进行一个验证过程,这对对象是否被破坏来说非常有用。如果想对这个命令的输出进行限制,需要指定一个范围(栈顶[top stack]和栈低[bottom stack])。
                这个命令确实有点长,当然也有一个缩写形式:dso。

            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_6
                    编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                    

                    打开【NTSD】调试器窗口。
                    
                    继续使用【g】命令运行调试器,直到调试器输出:Press any key to continue(Generics),然后,我们手动【ctrl+c】进入调试器的中断模式,就可以调试了。
                    

                    我们进入中断模式后,需要切换线程到托管线程,执行命令【~0s】。
  1. 1 0:002> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                    我们直接执行【!dso】或者【!dumpstackobjects】命令。
  1. 1 0:000> !dso
  2. 2 OS Thread Id: 0x1d8c (0)
  3. 3 RSP/REG          Object           Name
  4. 4 000000F45E99E8A8 00000235eb220b30 Interop+INPUT_RECORD
  5. 5 000000F45E99E918 00000235eb220b30 Interop+INPUT_RECORD
  6. 6 000000F45E99E920 000001f559009c98 System.Object
  7. 7 000000F45E99E950 00000235eb220560 System.String    Press any key to continue(Generics)
  8. 8 000000F45E99E9B8 000001f559009c38 System.IO.TextWriter+SyncTextWriter
  9. 9 000000F45E99E9C0 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
  10. 10 000000F45E99EA28 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
  11. 11 000000F45E99EA48 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
  12. 12 000000F45E99EA70 000001f559008ea0 System.String[]
  13. 13 000000F45E99EB18 000001f559008ea0 System.String[]
  14. 14 000000F45E99ED10 000001f559008ea0 System.String[]
  15. 15 000000F45E99ED18 000001f559008ea0 System.String[]
  16. 16 000000F45E99EE30 000001f559008ea0 System.String[]
  17. 17 000000F45E99EEB0 000001f559008eb8 System.String    E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  18. 18 000000F45E99EEC0 000001f559008ea0 System.String[]
  19. 19 000000F45E99EED0 000001f559008e80 System.String[]
  20. 20 000000F45E99EF08 000001f559008eb8 System.String    E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  21. 21 000000F45E99F0B8 000001f559008ea0 System.String[]
复制代码
                    在输出结果中有 3 列:
                    SP/REG:该列表式托管对象所在的栈地址。
                    Object:是托管对象的地址。我们可以使用【!dumpobj】命令查看对象的详情。
                    Name:该列表示托管对象的名称。
                    在输出的内容中,我们看到有几行是完全相同的,这也在预料之中,因为对象可能从一个函数传递另一个函数,其中每个栈帧都包含了对同一个对象的引用。
                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_6
                    编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,直到我们的控制台程序输出:Press any key to continue(Generics),我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                    由于我们手动中断调试器的执行,所以需要切换到托管线程的上下文中,执行命令【~0s】。
  1. 1 0:007> ~0s
  2. 2 ntdll!NtDeviceIoControlFile+0x14:
  3. 3 00007ffa`eeaeae74 c3              ret
复制代码
                    直接执行命令【!DumpStackObjects】。
  1. 1 0:000> !DumpStackObjects
  2. 2 OS Thread Id: 0x3a04 (0)
  3. 3           SP/REG           Object Name
  4. 4     00c21e57e918     026cd5cb1378 System.String
  5. 5     00c21e57ea28     026cd5cb0b30 Interop+INPUT_RECORD
  6. 6     00c21e57ea98     026cd5cb0b30 Interop+INPUT_RECORD
  7. 7     00c21e57eaa0     022c43c09c98 System.Object
  8. 8     00c21e57ead0     026cd5cb0560 System.String
  9. 9     00c21e57eb38     022c43c09c38 System.IO.TextWriter+SyncTextWriter
  10. 10     00c21e57eb40     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
  11. 11     00c21e57eba8     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
  12. 12     00c21e57ebc8     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
  13. 13     00c21e57ebf0     022c43c08ea0 System.String[]
  14. 14     00c21e57ec98     022c43c08ea0 System.String[]
  15. 15     00c21e57ee90     022c43c08ea0 System.String[]
  16. 16     00c21e57ee98     022c43c08ea0 System.String[]
  17. 17     00c21e57efb0     022c43c08ea0 System.String[]
  18. 18     00c21e57f030     022c43c08eb8 System.String
  19. 19     00c21e57f040     022c43c08ea0 System.String[]
  20. 20     00c21e57f050     022c43c08e80 System.String[]
  21. 21     00c21e57f088     022c43c08eb8 System.String
  22. 22     00c21e57f238     022c43c08ea0 System.String[]
复制代码
                    在输出结果中有 3 列:
                    SP/REG:该列表式托管对象所在的栈地址。
                    Object:是托管对象的地址。我们可以使用【!dumpobj】命令查看对象的详情。
                    Name:该列表示托管对象的名称。
                    在输出的内容中,我们看到有几行是完全相同的,这也在预料之中,因为对象可能从一个函数传递另一个函数,其中每个栈帧都包含了对同一个对象的引用。
                    
        3.1.6、找出对象的大小
            A、基础知识
                我们能找到一个对象的准确大小,对于我们排查一些问题是很有用的。我们知道【!dumpobj】命令可以输出一个对象的大小。如图:
                
                这种方法对我们找到单个对象的确切大小是非常有用的。但是,对象有时候会引用其他对象,而这些其他对象有可能引用另外的对象,以此类推。此时,知道对象的总体大小(包括遍历每个类型域的大小)能够有助于了解一些非常大和复杂的对象。我们可以使用【!objsize】命令。

            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_8
                    编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_8\bin\Debug\net8.0\ExampleCore_3_1_8.exe】。
                    
                    打开【NTSD】调试器窗口。
                    

                    我们通过【g】命令直接运行调试器,知道调试器输出:名称:PatrickLiu,地址:China-冀州-直隶总督府-广平大街23号-213339。此时,调试器卡住,我们按【ctrl+c】进入调试器中断模式。
                    

                    我们切换到托管线程0下,执行命令【~0s】。
  1. 1 0:001> ~0s
  2. 2 ucrtbase!strcmp+0x49:
  3. 3 00007ffa`ec654e69 4e8d0c10        lea     r9,[rax+r10]
复制代码
                    我们使用【!clrstack -a】命令查看一下托管线程的调用栈。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x2798 (0)
  3. 3         Child SP               IP Call Site
  4. 4 0000006CF777E238 00007ffaec654e69 [ExternalMethodFrame: 0000006cf777e238]
  5. 5 。。。。。。(省略了)
  6. 6 0000006CF777E9A0 00007FF996FC1B34 ExampleCore_3_1_8.Program.Main(System.String[])
  7. 7     PARAMETERS:
  8. 8         args (0x0000006CF777EA40) = 0x0000023e84c08ea0
  9. 9     LOCALS:
  10. 10         0x0000006CF777EA28 = <strong>0x0000023e84c09c98</strong>
  11. 11         0x0000006CF777EA00 = 0x0000000000000000
复制代码
                    0x0000023e84c09c98 红色标注的就是 Person 类型的局部变量 person 的地址,我们使用【!dumpobj 0x0000023e84c09c98】查看一下它的详情。
  1. 1 0:000> !dumpobj 0x0000023e84c09c98
  2. 2 Name:        ExampleCore_3_1_8.Person
  3. 3 MethodTable: 00007ff997079480
  4. 4 EEClass:     00007ff997082008
  5. 5 Tracked Type: false
  6. 6 Size:        <strong>40(0x28) bytes
  7. </strong> 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_8\bin\Debug\net8.0\ExampleCore_3_1_8.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff996f31188  4000001       18         System.Int32  1 instance               32 <Age>k__BackingField
  11. 11 00007ff996faec08  4000002        8        System.String  0 instance 0000027f16be0500 <Name>k__BackingField
  12. 12 00007ff99707d9a0  4000003       10 ...ore_3_1_8.Address  0 instance 0000023e84c09cc0 <HomeAddress>k__BackingField
复制代码
                      红色标注就是它的大小。我们再使用【!ObjSize 0x0000023e84c09c98】命令查看一下它的大小。
  1. 1 0:000> !ObjSize 0x0000023e84c09c98
  2. 2 sizeof(0000023E84C09C98) = 320 (0x140) bytes (ExampleCore_3_1_8.Person)
复制代码
                      【!objsize】命令和【!dumpobj】命令输出的大小区别还是挺大的。
                      【!objsize】命令在【NTSD】调试器模式下,如果没有指定任何地址,它会输出当前进程中所有托管线程上的所有托管对象的大小。Windbg Preview 执行错误:ystem.ArgumentException: Could not parse target object address: 0。
  1. 1 0:000> !ObjSize
  2. 2 Thread 2798 ([ExternalMethodFrame: 0000006cf777e238] ): 0000006cf777e810 -> 0000023e84c0b148: 4120 (0x1018) bytes (System.Byte[])
  3. 3 Thread 2798 ([ExternalMethodFrame: 0000006cf777e238] ): 0000006cf777e818 -> 0000027f16be0a90: 32 (0x20) bytes (System.String)
  4. 4 Thread 2798 (System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) [/_/src/libraries/Common/src/System/Text/DBCSDecoder.cs @ 130]): rbp: 0000023e84c0b0b8: 232 (0xe8) bytes (System.Text.DecoderDBCS)
  5. 5 Thread 2798 (System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) [/_/src/libraries/Common/src/System/Text/DBCSDecoder.cs @ 130]): rsi: 0000023e84c0b148: 4120 (0x1018) bytes (System.Byte[])
  6. 6 Thread 2798 (System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean) [/_/src/libraries/Common/src/System/Text/DBCSDecoder.cs @ 130]): r15: 0000023e84c0c178: 32792 (0x8018) bytes (System.Char[])
  7. 7 Thread 2798 (System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 604]): rbx: 0000023e84c0b058: 37416 (0x9228) bytes (System.IO.StreamReader)
  8. 8 Thread 2798 (System.IO.StreamReader.Read() [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 338]): rbx: 0000023e84c0b058: 37416 (0x9228) bytes (System.IO.StreamReader)
  9. 9 Thread 2798 (System.IO.SyncTextReader.Read() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 53]): rbp+10: 0000006cf777e970 -> 0000023e84c14190: 37440 (0x9240) bytes (System.IO.SyncTextReader)
  10. 10 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-58: 0000006cf777e9d8 -> 0000006cf777ea00 (interior): 0 (0x0) bytes (unknown type)
  11. 11 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-48: 0000006cf777e9e8 -> 0000006cf777ea00 (interior): 0 (0x0) bytes (unknown type)
  12. 12 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-68: 0000006cf777e9c8 -> 0000023e84c0a9d8: 120 (0x78) bytes (System.String)
  13. 13 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-60: 0000006cf777e9d0 -> 0000023e84c09cc0: 232 (0xe8) bytes (ExampleCore_3_1_8.Address)
  14. 14 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-50: 0000006cf777e9e0 -> 0000027f16be0500: 48 (0x30) bytes (System.String)
  15. 15 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-40: 0000006cf777e9f0 -> 0000023e84c09cc0: 232 (0xe8) bytes (ExampleCore_3_1_8.Address)
  16. 16 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-38: 0000006cf777e9f8 -> 0000023e84c09c98: 320 (0x140) bytes (ExampleCore_3_1_8.Person)
  17. 17 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp-8: 0000006cf777ea28 -> 0000023e84c09c98: 320 (0x140) bytes (ExampleCore_3_1_8.Person)
  18. 18 Thread 2798 (ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio 2022\...\ExampleCore_3_1_8\Program.cs @ 13]): rbp+10: 0000006cf777ea40 -> 0000023e84c08ea0: 24 (0x18) bytes (System.String[])
  19. 19 Failed to enumerate GC references.
  20. 20 Failed to walk thread 31d4
  21. 21 Handle (strong): 0000023E804A1378 -> 0000023E84C0A2D8: 1024 (0x400) bytes (System.Object[])
  22. 22 Handle (strong): 0000023E804A1380 -> 0000023E84C0A228: 176 (0xb0) bytes (System.Object[])
  23. 23 Handle (strong): 0000023E804A1388 -> 0000023E84C0A180: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
  24. 24 Handle (strong): 0000023E804A1390 -> 0000023E84C0A0D0: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
  25. 25 Handle (strong): 0000023E804A1398 -> 0000023E82402020: 8232 (0x2028) bytes (System.Object[])
  26. 26 Handle (strong): 0000023E804A13A0 -> 0000023E84C092F0: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
  27. 27 Handle (strong): 0000023E804A13A8 -> 0000023E84C095B8: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
  28. 28 Handle (strong): 0000023E804A13B0 -> 0000023E84C09508: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
  29. 29 Handle (strong): 0000023E804A13B8 -> 0000023E84C09240: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
  30. 30 Handle (strong): 0000023E804A13C8 -> 0000023E84C00188: 128 (0x80) bytes (System.ExecutionEngineException)
  31. 31 Handle (strong): 0000023E804A13D0 -> 0000023E84C00108: 128 (0x80) bytes (System.StackOverflowException)
  32. 32 Handle (strong): 0000023E804A13D8 -> 0000023E84C00088: 128 (0x80) bytes (System.OutOfMemoryException)
  33. 33 Handle (strong): 0000023E804A13E0 -> 0000023E84C00028: 96 (0x60) bytes (System.Int32[])
  34. 34 Handle (strong): 0000023E804A13E8 -> 0000023E82400028: 87704 (0x15698) bytes (System.Object[])
  35. 35 Handle (pinned): 0000023E804A15F8 -> 0000023E84C00208: 24 (0x18) bytes (System.Object)
复制代码
                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_8
                    编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,直到我们的控制台程序输出:名称:PatrickLiu,地址:China-冀州-直隶总督府-广平大街23号-213339,我们的调试器也处于卡住的状态。此时点击【break】按钮,就可以调试程序了。
                    切换到托管线程,执行命令【~0s】。
  1. 1 0:007> ~0s
  2. 2 ntdll!NtReadFile+0x14:
  3. 3 00007ffa`eeaeae54 c3              ret
复制代码
                    我们执行命令【!clrstack -a】查看托管线程栈。
  1. 1 0:000> !clrstack -a
  2. 2 OS Thread Id: 0x3cf8 (0)
  3. 3         Child SP               IP Call Site
  4. 4 000000CD8377E2C0 00007ffaeeaeae54 [InlinedCallFrame: 000000cd8377e2c0]
  5. 5 000000CD8377E2C0 00007ffa2d8276eb [InlinedCallFrame: 000000cd8377e2c0]
  6. 6 。。。。。。(省略了)
  7. 7
  8. 8 000000CD8377E590 00007ff997991b34 ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_8\Program.cs @ 13]
  9. 9     PARAMETERS:
  10. 10         args (0x000000CD8377E630) = 0x0000024827408ea0
  11. 11     LOCALS:
  12. 12         0x000000CD8377E618 = <strong>0x0000024827409c98</strong>
  13. 13         0x000000CD8377E5F0 = 0x0000000000000000
复制代码
                    0x0000024827409c98 红色标注的就是 Person 类型的局部变量 person 的地址。我们可以使用【!dumpobj 0x0000024827409c98】命令查看这个对象的大小。
  1. 1 0:000> !dumpobj 0x0000024827409c98
  2. 2 Name:        ExampleCore_3_1_8.Person
  3. 3 MethodTable: 00007ff997a49480
  4. 4 EEClass:     00007ff997a52008
  5. 5 Tracked Type: false
  6. 6 Size:        <strong>40(0x28) bytes(这里就是大小)
  7. </strong> 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_8\bin\Debug\net8.0\ExampleCore_3_1_8.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 00007ff997901188  4000001       18         System.Int32  1 instance               32 <Age>k__BackingField
  11. 11 00007ff99797ec08  4000002        8        System.String  0 instance 00000288b93b0500 <Name>k__BackingField
  12. 12 00007ff997a4d9a0  4000003       10 ...ore_3_1_8.Address  0 instance 0000024827409cc0 <HomeAddress>k__BackingField
复制代码
                    我们使用【!objsize 0x0000024827409c98】这个命令看看它的大小。
  1. 1 0:000> !objsize 0x0000024827409c98
  2. 2 Objects which 24827409c98(ExampleCore_3_1_8.Person) transitively keep alive:
  3. 3
  4. 4          Address               MT           Size
  5. 5     024827409c98     7ff997a49480             40
  6. 6     0288b93b0500     7ff99797ec08             42
  7. 7     024827409cc0     7ff997a4d9a0             56
  8. 8     0288b93b0530     7ff99797ec08             32
  9. 9     0288b93b0550     7ff99797ec08             26
  10. 10     0288b93b0570     7ff99797ec08             32
  11. 11     0288b93b0590     7ff99797ec08             36
  12. 12     0288b93b05b8     7ff99797ec08             34
  13. 13
  14. 14 Statistics:
  15. 15           MT Count TotalSize Class Name
  16. 16 7ff997a49480     1        40 ExampleCore_3_1_8.Person
  17. 17 7ff997a4d9a0     1        56 ExampleCore_3_1_8.Address
  18. 18 7ff99797ec08     6       202 System.String
  19. 19 Total 8 objects, 298 bytes
复制代码
                    这个结果还是很详细的。我们看到了【!objsize】命令得到的是 298 bytes,而【!dumpobj】得到的大小是 40 bytes,这个差别还是挺大的。


        3.1.7、异常的转储
            A、基础知识
                Windows 的异常模型采用的是结构化异常处理(Structured Exception Handling,SEH)。同样,.NET异常模型也是构建在 Windows SEH模型之上的,并提供了基于对象的异常模型。CLR 在每个异常内携带的额外信息都被保存到托管堆上。所有 CLR 异常都以 SEH 异常的形式出现,错误码是:0xe0434352。既然所有的异常的错误码都是 0xe0434352,我们是如何区分异常的不同的呢?答案就是依靠异常中保存的扩展信息。(说明,原书错误码是:0xe0434f4d,平台是 .NET Framework,我的错误码是:0xe0434352,平台是.NET 8.0)
                

                在 CLR 看来,异常也是一种引用类型,所以也可以使用【!dumpobj】命令输出它的信息。这个命令有些繁琐,如果我们只希望显示异常的类型、栈回溯(包括内部栈回溯)以及信息,我们就可以使用【printexception】命令。这个命令的参数托管异常的地址。当然,我们在分析异常时,【threads】命令也会经常用得到,它能显示出系统中各个托管线程的信息,包括该线程抛出的最后一个异常。
                【StopOnException】命令是在抛出特定异常时设置一个断点,和异常信息的转储关系不大。
                命令的格式:
                !StopOnException [-derived] [-create|-create2]  [],create 和 create2 这两个开关控制着断点是在第一次出现指定的异常时触发,还是第二次出现时触发。derived 开关会增加断点的范围,不仅包含指定的异常,而且还包括从指定异常派生的任意异常。pseudo-register 是可选的,表示命令将使用哪个伪寄存器设置断点,如果没有指定伪寄存器,默认是 $t1。
                例子:
                  !StopOnException -create System.ArgumentException,表示当第一次抛出 System.ArgumentException 异常时设置断点。
                  !StopOnException -derived System.Exception ,表示对从 System.Exception 派生下来的任意异常都设置一个断点。

            B、眼见为实
                1)、NTSD 调试
                    调试源码:ExampleCore_3_1_6
                    编译项目,打开【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,输入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                    
                    打开【NTSD】调试器。
                    

                    使用【g】命令运行调试器,直到调试器抛出异常,调试器中断执行。
                    

                    我们使用【kb】命令,查看托管调用栈的栈回溯。
  1. 0:000> kb
  2. RetAddr               : Args to Child                                                           : Call Site
  3. 00007ffb`00a9ca6f     : <strong>000001b8`72c0aca8</strong> 0000002c`9517e710 0000002c`9517ea00 00007ffb`00a7bbb4 : <strong>KERNELBASE</strong><strong>!RaiseException</strong>+0x69
  4. 00007ffb`00a9c129     : 00000000`70000185 00007ffb`00d961c8 0000002c`9517eb88 0000002c`9517ed28 : coreclr!RaiseTheExceptionInternalOnly+0x26b
  5. 00007ffa`a0fc2416     : <strong>000001b8`72c0aca8</strong> 000001f9`04c913c8 000001b8`700fa1e8 00000000`00000040 : <strong>coreclr!IL_Throw</strong>+0xb9
  6. 00007ffa`a0fc1afd     : 000001b8`72c09ce0 00000000`00000000 000001b8`6e7af410 000001b8`72c09c38 : 0x00007ffa`a0fc2416
  7. 00007ffb`00b2a1a3     : 000001b8`72c08ea0 0000002c`9517f128 0000002c`9517f128 0000002c`9517ed19 : 0x00007ffa`a0fc1afd
  8. 00007ffb`00ab14c9     : 00000000`00000000 00000000`00000130 0000002c`9517ed28 00007ffb`009da456 : coreclr!CallDescrWorkerInternal+0x83
  9. (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56
  10. 00007ffb`009d75ac     : 0000002c`9517eda8 00000000`00000000 00000000`00000048 00007ffb`00ac28a6 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1
  11. (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb
  12. 00007ffb`009d6f7a     : 000001b8`72c08ea0 000001b8`72c08ea0 00000000`00000000 0000002c`9517f128 : coreclr!RunMainInternal+0x11c
  13. 00007ffb`009d6b17     : 000001b8`6e7af410 000001b8`00000000 000001b8`6e7af410 00000000`00000000 : coreclr!RunMain+0xd2
  14. 00007ffb`009d7321     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf
  15. 00007ffb`00ae7768     : 00000000`00000001 0000002c`9517f301 0000002c`9517f350 00007ffb`86ff23ea : coreclr!CorHost2::ExecuteAssembly+0x281
  16. 00007ffb`87012c36     : 000001b8`6e780fd0 000001b8`6e780310 00000000`00000000 000001b8`6e780310 : coreclr!coreclr_execute_assembly+0xd8
  17. (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a
  18. 00007ffb`87012f1c     : 000001b8`6e76bcc8 0000002c`9517f579 00007ffb`8704c9c0 000001b8`6e76bcc8 : hostpolicy!run_app_for_context+0x596
  19. 00007ffb`8701385a     : 00000000`00000000 000001b8`6e76bcc0 000001b8`6e76bcc0 00000000`00000000 : hostpolicy!run_app+0x3c
  20. 00007ffb`b3e2b5c9     : 000001b8`6e77f438 000001b8`6e77f320 00000000`00000000 0000002c`9517f679 : hostpolicy!corehost_main+0x15a
  21. 00007ffb`b3e2e066     : 000001b8`6e77e330 0000002c`9517fa00 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9
  22. 00007ffb`b3e302ec     : 00007ffb`b3e625f8 000001b8`6e77cb40 0000002c`9517f940 0000002c`9517f8f0 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6
  23. 00007ffb`b3e2e644     : 0000002c`9517fa00 0000002c`9517fa20 0000002c`9517f971 000001b8`6e77cf60 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c
  24. 00007ffb`b3e285a0     : 0000002c`9517fa20 000001b8`6e77c440 00000000`00000001 000001b8`6e760000 : hostfxr!fx_muxer_t::execute+0x494
  25. *** WARNING: Unable to verify checksum for apphost.exe
  26. 00007ff6`5faef998     : 00007ffb`d241f4e8 00007ffb`b3e29b10 0000002c`9517fbc0 000001b8`6e77c130 : hostfxr!hostfxr_main_startupinfo+0xa0
  27. 00007ff6`5faefda6     : 00007ff6`5fafb6c0 00000000`00000007 000001b8`6e76bcc0 00000000`0000005e : apphost!exe_start+0x878
  28. 00007ff6`5faf12e8     : 00000000`00000000 00000000`00000000 000001b8`6e76bcc0 00000000`00000000 : apphost!wmain+0x146
  29. (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22
  30. 00007ffb`d33f6fd4     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : apphost!__scrt_common_main_seh+0x10c
  31. 00007ffb`d4b7cec1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
  32. 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
  33. 0:000>
复制代码
                     接下来,我们找到 KERNELBASE!RaiseException 函数(000001b8`72c0aca8)的第一个参数,在该地址上执行【!dumpobj】命令,查看异常详情。
  1. 1 0:000> !DumpObj 000001b8`72c0aca8
  2. 2 Name:        System.ArgumentException
  3. 3 MethodTable: 00007ffaa10467b0
  4. 4 EEClass:     00007ffaa0f8cdf8
  5. 5 Tracked Type: false
  6. 6 Size:        136(0x88) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 0000000000000000  4000264        8 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
  11. 11 00007ffaa0faec08  4000265       10        System.String  0 instance <strong>000001f904c913c8</strong> <strong>_message
  12. </strong>12 00007ffaa1043060  4000266       18 ...tions.IDictionary  0 instance 0000000000000000 _data
  13. 13 00007ffaa1040820  4000267       20     System.Exception  0 instance 0000000000000000 _innerException
  14. 14 00007ffaa0faec08  4000268       28        System.String  0 instance 0000000000000000 _helpURL
  15. 15 00007ffaa10aa858  4000269       30        System.Byte[]  0 instance 000001b872c0ad78 _stackTrace
  16. 16 00007ffaa10aa858  400026a       38        System.Byte[]  0 instance 0000000000000000 _watsonBuckets
  17. 17 00007ffaa0faec08  400026b       40        System.String  0 instance 0000000000000000 _stackTraceString
  18. 18 00007ffaa0faec08  400026c       48        System.String  0 instance 0000000000000000 _remoteStackTraceString
  19. 19 00007ffaa0efc4d8  400026d       50      System.Object[]  0 instance 0000000000000000 _dynamicMethods
  20. 20 00007ffaa0faec08  400026e       58        System.String  0 instance 0000000000000000 _source
  21. 21 00007ffaa0fa8b78  400026f       60       System.UIntPtr  1 instance 00007FFAA0FC2415 _ipForWatsonBuckets
  22. 22 00007ffaa0fa70a0  4000270       68        System.IntPtr  1 instance 0000000000000000 _xptrs
  23. 23 00007ffaa0f31188  4000271       70         System.Int32  1 instance       -532462766 _xcode
  24. 24 00007ffaa0f31188  4000272       74         System.Int32  1 instance      -2147024809 _HResult
  25. 25 00007ffaa0faec08  4000383       78        System.String  0 instance 0000000000000000 _paramName
  26. 26 0:000>
复制代码
                    以上就是异常的详情,如果我们想查看具体提示消息,可以针对 _message 域的地址 000001f904c913c8 再次执行【!DumpObj】命令。
  1. 1 0:000> !dumpobj 000001f904c913c8
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ffaa0faec08
  4. 4 EEClass:     00007ffaa0f8a500
  5. 5 Tracked Type: false
  6. 6 Size:        58(0x3a) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      <strong>Obj cannot be</strong> <strong>null</strong>
  9. 9 Fields:
  10. 10               MT    Field   Offset                 Type VT     Attr            Value Name
  11. 11 00007ffaa0f31188  400033b        8         System.Int32  1 instance               18 _stringLength
  12. 12 00007ffaa0f3b538  400033c        c          System.Char  1 instance               4f _firstChar
  13. 13 00007ffaa0faec08  400033a       c8        System.String  0   static 000001f904c90008 Empty
复制代码
                    【!dumpobj】命令有些繁琐,我们可以直接使用【!PrintException】命令输出异常信息。
  1. 1 0:000> !PrintException 000001b8`72c0aca8
  2. 2 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly.
  3. 3 For more information see https://go.microsoft.com/fwlink/?linkid=2135652
  4. 4
  5. 5 Exception object: 000001b872c0aca8
  6. 6 Exception type:   System.ArgumentException
  7. 7 Message:          <strong>Obj cannot be</strong> <strong>null</strong>
  8. 8 InnerException:   <none>
  9. 9 StackTrace (generated):
  10. 10     SP               IP               Function
  11. 11     0000002C9517E9C0 00007FFAA0FC2415 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException(ExampleCore_3_1_6.ObjTypes)+0x85
  12. 12     0000002C9517EA10 00007FFAA0FC1AFC ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.Main(System.String[])+0x1cc
  13. 13
  14. 14 StackTraceString: <none>
  15. 15 HResult: 80070057
复制代码
                    在这里看的很清楚,包括异常的类型(Exception type:System.ArgumentException),异常的地址(Exception object: 000001b872c0aca8),异常抛出的方法(ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException)等。
                    当然,我们可以使用【!threads】或者【!t】命令输出所有托管线程的信息,包括托管线程抛出的最有一个异常。
                    

                    【!threads】命令执行的效果。
  1. 1 0:000> !threads
  2. 2 ThreadCount:      3
  3. 3 UnstartedThread:  0
  4. 4 BackgroundThread: 2
  5. 5 PendingThread:    0
  6. 6 DeadThread:       0
  7. 7 Hosted Runtime:   no
  8. 8                                                                                                             Lock
  9. 9  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
  10. 10    0    1     1190 000001B86E7AF410    2a020 Preemptive  000001B872C0ADF0:000001B872C0C638 000001B86E7F1530 -00001 MTA <strong>System.ArgumentException</strong> <strong>000001b872c0aca8
  11. </strong>11    6    2     3b64 000001B8700EDDA0    21220 Preemptive  0000000000000000:0000000000000000 000001B86E7F1530 -00001 Ukn (Finalizer)
  12. 12    7    3     1630 000001B86E7BBF00    2b220 Preemptive  0000000000000000:0000000000000000 000001B86E7F1530 -00001 MTA
复制代码
                    红色标注的就是 ID 为 1 的托管线程抛出的异常(System.ArgumentException),异常的地址:000001b872c0aca8

                2)、Windbg Preview 调试
                    调试源码:ExampleCore_3_1_6
                    编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行程序,直到我们的控制台程序抛出一个异常,调试器停止执行。
  1. 1 0:000> g
  2. 2 ModLoad: 00007ffb`d4ac0000 00007ffb`d4af0000   C:\Windows\System32\IMM32.DLL
  3. 3 ModLoad: 00007ffb`c8d40000 00007ffb`c8d99000   C:\Program Files\dotnet\host\fxr\8.0.2\hostfxr.dll
  4. 4 ModLoad: 00007ffb`c8cd0000 00007ffb`c8d34000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\hostpolicy.dll
  5. 5 ModLoad: 00007ffa`fcab0000 00007ffa`fcf98000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\coreclr.dll
  6. 6 ModLoad: 00007ffb`d3b60000 00007ffb`d3c89000   C:\Windows\System32\ole32.dll
  7. 7 ModLoad: 00007ffb`d34a0000 00007ffb`d37f4000   C:\Windows\System32\combase.dll
  8. 8 ModLoad: 00007ffb`d3200000 00007ffb`d32d5000   C:\Windows\System32\OLEAUT32.dll
  9. 9 ModLoad: 00007ffb`d1fc0000 00007ffb`d203f000   C:\Windows\System32\bcryptPrimitives.dll
  10. 10 (2e10.1988): Unknown exception - code 04242420 (first chance)
  11. 11 ModLoad: 00007ffa`fb810000 00007ffa`fc49c000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  12. 12 ModLoad: 00007ffa`fb650000 00007ffa`fb809000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\clrjit.dll
  13. 13 ModLoad: 00007ffb`d1f00000 00007ffb`d1f13000   C:\Windows\System32\kernel.appcore.dll
  14. 14 ModLoad: 00000293`f6f10000 00000293`f6f18000   E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
  15. 15 ModLoad: 00000293`f6f30000 00000293`f6f3e000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
  16. 16 ModLoad: 00007ffb`c8c70000 00007ffb`c8c98000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
  17. 17 ModLoad: 00007ffb`79470000 00007ffb`79482000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
  18. 18 ModLoad: 00000293`f6f40000 00000293`f6f48000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
  19. 19 ModLoad: 00007ffb`5dc90000 00007ffb`5dca5000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll
  20. 20 <strong>(2e10.1988): CLR exception - code e0434352 (first chance)
  21. </strong>21 <strong>(2e10.1988): CLR exception - code e0434352 (!!! second chance !!!)
  22. </strong>22 KERNELBASE!RaiseException+0x69:
  23. 23 00007ffb`d2563e49 0f1f440000      nop     dword ptr [rax+rax]
复制代码
                    我们使用【kb】命令查看调用栈。
  1. 1 0:000> kb
  2. 2  # RetAddr               : Args to Child                                                           : Call Site
  3. 3 00 00007ffa`fcb7ca6f     : <strong>00000293`fb40aca8</strong> 000000d7`0477e1f0 000000d7`0477e4e0 00007ffa`fcb5bbb4 : KERNELBASE!RaiseException+0x69
  4. 4 01 00007ffa`fcb7c129     : 00000000`70000185 00007ffa`fce761c8 000000d7`0477e668 000000d7`0477e808 : coreclr!RaiseTheExceptionInternalOnly+0x26b [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 2795]
  5. 5 02 00007ffa`9d0a24b6     : <strong>00000293`fb40aca8</strong> 000002d4`8d5813c8 00000293`f6fbe1e8 00000000`00000040 : coreclr!IL_Throw+0xb9 [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 4247]
  6. 6 03 00007ffa`9d0a1afd     : 00000293`fb409ce0 00000000`00000000 00000293`f6fa6280 00000293`fb409c38 : ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException+0x86 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\Program.cs @ 76]
  7. 7 04 00007ffa`fcc0a1a3     : 00000293`fb408ea0 000000d7`0477ec08 000000d7`0477ec08 000000d7`0477e7f9 : ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.Main+0x1cd [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\Program.cs @ 46]
  8. 8 05 00007ffa`fcb914c9     : 00000000`00000000 00000000`00000130 000000d7`0477e808 00007ffa`fcaba456 : coreclr!CallDescrWorkerInternal+0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm @ 100]
  9. 9 06 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67]
  10. 10 07 00007ffa`fcab75ac     : 000000d7`0477e888 00000000`00000000 00000000`00000048 00007ffa`fcba28a6 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 570]
  11. 11 08 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458]
  12. 12 09 00007ffa`fcab6f7a     : 00000293`fb408ea0 00000293`fb408ea0 00000000`00000000 000000d7`0477ec08 : coreclr!RunMainInternal+0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1304]
  13. 13 0a 00007ffa`fcab6b17     : 00000293`f6fa6280 00000293`00000000 00000293`f6fa6280 00000000`00000000 : coreclr!RunMain+0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1375]
  14. 14 0b 00007ffa`fcab7321     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1504]
  15. 15 0c 00007ffa`fcbc7768     : 00000000`00000001 000000d7`0477ee01 000000d7`0477ee30 00007ffb`c8cd23ea : coreclr!CorHost2::ExecuteAssembly+0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp @ 349]
  16. 16 0d 00007ffb`c8cf2c36     : 00000293`f6f7a900 00000293`f6f7a660 00000000`00000000 00000293`f6f7a660 : coreclr!coreclr_execute_assembly+0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp @ 504]
  17. 17 0e (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109]
  18. 18 0f 00007ffb`c8cf2f1c     : 00000293`f6f67da8 000000d7`0477f059 00007ffb`c8d2c9c0 00000293`f6f67da8 : hostpolicy!run_app_for_context+0x596 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 256]
  19. 19 10 00007ffb`c8cf385a     : 00000000`00000000 00000293`f6f67da0 00000293`f6f67da0 00000000`00000000 : hostpolicy!run_app+0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 285]
  20. 20 11 00007ffb`c8d4b5c9     : 00000293`f6f769e8 00000293`f6f768d0 00000000`00000000 000000d7`0477f159 : hostpolicy!corehost_main+0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 426]
  21. 21 12 00007ffb`c8d4e066     : 00000293`f6f77540 000000d7`0477f4e0 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 145]
  22. 22 13 00007ffb`c8d502ec     : 00007ffb`c8d825f8 00000293`f6f780d0 000000d7`0477f420 000000d7`0477f3d0 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 532]
  23. 23 14 00007ffb`c8d4e644     : 000000d7`0477f4e0 000000d7`0477f500 000000d7`0477f451 00000293`f6f78401 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 1007]
  24. 24 15 00007ffb`c8d485a0     : 000000d7`0477f500 00000293`f6f76690 00000000`00000001 00000293`f6f60000 : hostfxr!fx_muxer_t::execute+0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 578]
  25. 25 16 00007ff7`6c9ff998     : 00007ffb`d241f4e8 00007ffb`c8d49b10 000000d7`0477f6a0 00000293`f6f76380 : hostfxr!hostfxr_main_startupinfo+0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp @ 62]
  26. 26 17 00007ff7`6c9ffda6     : 00007ff7`6ca0b6c0 00000000`00000007 00000293`f6f67da0 00000000`0000005e : apphost!exe_start+0x878 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 240]
  27. 27 18 00007ff7`6ca012e8     : 00000000`00000000 00000000`00000000 00000293`f6f67da0 00000000`00000000 : apphost!wmain+0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 311]
  28. 28 19 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90]
  29. 29 1a 00007ffb`d33f6fd4     : 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]
  30. 30 1b 00007ffb`d4b7cec1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
  31. 31 1c 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
复制代码
                    接下来,找出 KERNELBASE!RaiseException 函数的(000001f8`4c80aca8)第一个参数,并在这个地址上执行【!dumpobj 000001f8`4c80aca8】命令。
  1. 1 0:000> !DumpObj 000001f8`4c80aca8
  2. 2 Name:        System.ArgumentException
  3. 3 MethodTable: 00007ffaa06e67b0
  4. 4 EEClass:     00007ffaa062cdf8
  5. 5 Tracked Type: false
  6. 6 Size:        136(0x88) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 Fields:
  9. 9               MT    Field   Offset                 Type VT     Attr            Value Name
  10. 10 0000000000000000  4000264        8 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
  11. 11 00007ffaa064ec08  4000265       10        System.String  0 instance 00000238deaf13c8 <strong>_message
  12. </strong>12 00007ffaa06e3060  4000266       18 ...tions.IDictionary  0 instance 0000000000000000 _data
  13. 13 00007ffaa06e0820  4000267       20     System.Exception  0 instance 0000000000000000 _innerException
  14. 14 00007ffaa064ec08  4000268       28        System.String  0 instance 0000000000000000 _helpURL
  15. 15 00007ffaa074a858  4000269       30        System.Byte[]  0 instance 000001f84c80ad78 _stackTrace
  16. 16 00007ffaa074a858  400026a       38        System.Byte[]  0 instance 0000000000000000 _watsonBuckets
  17. 17 00007ffaa064ec08  400026b       40        System.String  0 instance 0000000000000000 _stackTraceString
  18. 18 00007ffaa064ec08  400026c       48        System.String  0 instance 0000000000000000 _remoteStackTraceString
  19. 19 00007ffaa059c4d8  400026d       50      System.Object[]  0 instance 0000000000000000 _dynamicMethods
  20. 20 00007ffaa064ec08  400026e       58        System.String  0 instance 0000000000000000 _source
  21. 21 00007ffaa0648b78  400026f       60       System.UIntPtr  1 instance 00007FFAA0662415 _ipForWatsonBuckets
  22. 22 00007ffaa06470a0  4000270       68        System.IntPtr  1 instance 0000000000000000 _xptrs
  23. 23 00007ffaa05d1188  4000271       70         System.Int32  1 instance       -532462766 _xcode
  24. 24 00007ffaa05d1188  4000272       74         System.Int32  1 instance      -2147024809 _HResult
  25. 25 00007ffaa064ec08  4000383       78        System.String  0 instance 0000000000000000 _paramName
复制代码
                    从【!dumpobj】命令的输出中,我们可以看到托管代码异常的所有信息,包括异常的类型(System.ArgumentException)以及异常相关的所有域。我们可以针对标红的 _message 再次执行【!dumpobj 00000238deaf13c8】命令,查看异常的具体提示信息。
  1. 1 0:000> !dumpobj 00000238deaf13c8
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ffaa064ec08
  4. 4 EEClass:     00007ffaa062a500
  5. 5 Tracked Type: false
  6. 6 Size:        58(0x3a) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      <strong>Obj cannot be null</strong>
  9. 9 Fields:
  10. 10               MT    Field   Offset                 Type VT     Attr            Value Name
  11. 11 00007ffaa05d1188  400033b        8         System.Int32  1 instance               18 _stringLength
  12. 12 00007ffaa05db538  400033c        c          System.Char  1 instance               4f _firstChar
  13. 13 00007ffaa064ec08  400033a       c8        System.String  0   static 00000238deaf0008 Empty
复制代码
                    简写方式输出。
  1. 1 0:000> !DumpObj -nofields 00000238deaf13c8
  2. 2 Name:        System.String
  3. 3 MethodTable: 00007ffaa064ec08
  4. 4 EEClass:     00007ffaa062a500
  5. 5 Tracked Type: false
  6. 6 Size:        58(0x3a) bytes
  7. 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
  8. 8 String:      <strong>Obj cannot be null</strong>
复制代码
                    上面我们使用【!dumpobj】命令可以查看异常信息,但是比较繁琐。我们可以使用【!PrintException】命令输出异常信息。
  1. 1 0:000> !PrintException 000001f8`4c80aca8
  2. 2 Exception object: 000001f84c80aca8
  3. 3 Exception type:   System.ArgumentException
  4. 4 Message:          Obj cannot be null
  5. 5 InnerException:   <none>
  6. 6 StackTrace (generated):
  7. 7     SP               IP               Function
  8. 8     0000007BA577E6D0 00007FFAA0662415 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException(ExampleCore_3_1_6.ObjTypes)+0x85
  9. 9     0000007BA577E720 00007FFAA0661AFC ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.Main(System.String[])+0x1cc
  10. 10
  11. 11 StackTraceString: <none>
  12. 12 HResult: 80070057
复制代码
                    这里输出的内容更清晰,是哪个方法抛出的异常都可以看到(ThrowException)。我们分析异常的时候,可以使用【!threads】或者【!t】命令查看所有托管线程信息,包括该线程抛出的最后一个异常。
  1. 1 0:000> !threads
  2. 2 ThreadCount:      3
  3. 3 UnstartedThread:  0
  4. 4 BackgroundThread: 2
  5. 5 PendingThread:    0
  6. 6 DeadThread:       0
  7. 7 Hosted Runtime:   no
  8. 8                                                                                                             Lock  
  9. 9  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
  10. 10    0    1     379c 000001F8485263D0    2a020 Preemptive  000001F84C80ADF0:000001F84C80C638 000001f848568920 -00001 MTA System.ArgumentException <strong>000001f84c80aca8
  11. </strong>11    6    2     10d4 000001F8485D7C40    21220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 Ukn (Finalizer)
  12. 12    7    3     3204 000001F8485346F0    2b220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 MTA
  13. 13
  14. 14
  15. 15 0:000> !t
  16. 16 ThreadCount:      3
  17. 17 UnstartedThread:  0
  18. 18 BackgroundThread: 2
  19. 19 PendingThread:    0
  20. 20 DeadThread:       0
  21. 21 Hosted Runtime:   no
  22. 22                                                                                                             Lock  
  23. 23  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
  24. 24    0    1     379c 000001F8485263D0    2a020 Preemptive  000001F84C80ADF0:000001F84C80C638 000001f848568920 -00001 MTA System.ArgumentException <strong>000001f84c80aca8
  25. </strong>25    6    2     10d4 000001F8485D7C40    21220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 Ukn (Finalizer)
  26. 26    7    3     3204 000001F8485346F0    2b220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 MTA
复制代码
                    我们看到了 ID 为 1 的线程抛出一个类型为 System.ArgumentException 异常,异常的地址:000001f84c80aca8 ,我们可以针对这个地址使用【!DumpObj】或者【!PrintException】命令查看异常的详情。

四、总结
    这篇文章的第二部分终于写完了,考虑到内容太多,分多篇写作了。只有所有代码自己都写了,都测试了,才会明白里面发生的事情。Net 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。
来源:https://www.cnblogs.com/PatrickLiu/p/18052105
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具