经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
Net 高级调试之六:对象检查之值类型、引用类型、数组和异常的转储
来源:cnblogs  作者:可均可可  时间:2023/11/10 10:07:49  对本文有异议
一、简介
     今天是《Net 高级调试》的第六篇文章。记得我刚接触 Net 框架的时候,还是挺有信心的,对所谓的值类型和引用类型也能说出自己的见解,毕竟,自己一直在努力。当然这些见解都是书本上的,并没有做到眼见为实,所以总是有些东西说不清楚。今天,我们就好好的说说 C# 的类型,是从内存级别、从底层来说一下值类型、引用类型到底是什么,它们在内存中的形态,还有也说说数组的内存形态,如何内部布局的,以及我们如何查找由未捕捉的异常引起的程序崩溃。这些都是基础的,如果这些掌握不好,以后的高级调试的道路,也不好走。自从我过了这一关,很多东西理解起来,比较透彻 了,但是,还必须努力。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现。
     如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。

       调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
          操作系统:Windows Professional 10
          调试工具:Windbg Preview(可以去Microsoft Store 去下载)
          开发工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源码:源码下载

二、基础知识
    1、对象检查
        1.1、简介
            高级调试的目标就是分析应用程序故障所形成的原因,既然是故障,大多数情况下是对象的某种损坏,这就需要我们深入的了解各种对象的审查方法。

    2、各种检查方法
        2.1、内存转储
              这个方式非常底层,从内存地址上观察地址上的内容,常使用【dp】命令。出来【dp】命令,还有其他一些命令,比如:du,dw,db,da 等,如果想了解更多,可以查看 Windbg 的帮助文档,命令是【.hh】。

        2.2、对值类型的转储
              对值类型的转储非常的简单,一般通过【dp】命令观察内存地址,从内存上提取内容,可以在结束处观察 esp 指针,当然,如果知道栈地址,我们也可以使用【dp】命令查看内容,效果和查看 esp 是一样的。

        2.3、对应用类型的转储
              如果我们想查看引用类型的内容,我们可以使用【!do】命令,查看应用类型的内容。

        2.4、对数组的转储
              C# 数组的内存布局,大概有两种形式,值类型和引用类型。
              1)值类型数组。
                  结构:同步块索引,方法表,数组大小,数组元素1,数组元素2......

              2)引用类型数组。
                  结构:同步块索引,方法表,数组大小,数组元素1,数组元素2......
                                      |      |
                               |------------------+      +--------------------|
                               |                       |
                            同步块索引、方法表、内容        同步块索引、方法表、内容

        2.5、对异常的转储
              有时候,程序存在未处理的异常导致的崩溃,在事后分析 dump 中要是能找到这个异常,对我们分析和解决问题会有很大的帮助。如果我们想查看异常的详情,我们可以使用【!pe(print exception)】命令,打印异常的详情,当然也可以使用【!do】命令查看更详细的异常内容。

三、调试过程
    废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
    1、测试源码
        1.1、Example_6_1_1
  1. 1 namespace Example_6_1_1
  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
  9. 9 Console.ReadLine();
  10. 10 }
  11. 11 }
  12. 12
  13. 13 internal class Person
  14. 14 {
  15. 15 public int Age = 20;
  16. 16
  17. 17 public string Name = "jack";
  18. 18 }
  19. 19 }
View Code

        1.2、Example_6_1_2
  1. 1 namespace Example_6_1_2
  2. 2 {
  3. 3 internal class Program
  4. 4 {
  5. 5 static void Main(string[] args)
  6. 6 {
  7. 7 Debugger.Break();
  8. 8
  9. 9 int a = 10;
  10. 10 int b = 11;
  11. 11 int c = 12;
  12. 12 int d = 13;
  13. 13 }
  14. 14 }
  15. 15 }
View Code

        1.3、Example_6_1_3
  1. 1 namespace Example_6_1_3
  2. 2 {
  3. 3 internal class Program
  4. 4 {
  5. 5 static void Main(string[] args)
  6. 6 {
  7. 7 int[] intArr = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
  8. 8
  9. 9 string[] stringArr = { "1", "2", "3", "4", "5" };
  10. 10
  11. 11 Debugger.Break();
  12. 12 }
  13. 13 }
  14. 14 }
View Code

        1.4、Example_6_1_4
  1. 1 namespace Example_6_1_4
  2. 2 {
  3. 3 internal class Program
  4. 4 {
  5. 5 static void Main(string[] args)
  6. 6 {
  7. 7 Console.WriteLine($"请输入一个能够整除的数字:");
  8. 8 var num = Console.ReadLine();
  9. 9
  10. 10 var ret = 10 / Convert.ToInt32(num);
  11. 11 }
  12. 12 }
  13. 13 }
View Code

    2、眼见为实
        项目的所有操作都是一样的,所以就在这里说明一下,但是每个测试例子,都需要重新启动,并加载相应的应用程序,加载方法都是一样的。流程如下我们编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们需要使用【g】命令,继续运行程序,然后到达指定地点停止后,我们可以点击【break】按钮,就可以调试程序了。有时候可能需要切换到主线程,可以使用【~0s】命令。

        2.1、内存转储
            测试代码:Example_6_1_1
            我们首先去托管堆中查找一下 Person 对象。【dp】命令的 p 是 pointer,指针的意思。
  1. 1 0:006> !dumpheap -type Person
  2. 2 Address MT Size
  3. 3 031424c8 013d4dec 16
  4. 4
  5. 5 Statistics:
  6. 6 MT Count TotalSize Class Name
  7. 7 013d4dec 1 16 Example_6_1_1.Person
  8. 8 Total 1 objects

            031424c8(Person 对象的指针地址) 013d4dec(Person 方法表的地址)

  1. 1 0:006> !dumpobj /d 031424c8
  2. 2 Name: Example_6_1_1.Person
  3. 3 MethodTable: 013d4dec
  4. 4 EEClass: 013d12f0
  5. 5 Size: 16(0x10) bytes
  6. 6 File: E:\Visual Studio 2022\Source\Projects\....\Example_6_1_1\bin\Debug\Example_6_1_1.exe
  7. 7 Fields:
  8. 8 MT Field Offset Type VT Attr Value Name
  9. 9 706342a8 4000001 8 System.Int32 1 instance 20 Age
  10. 10 706324e4 4000002 4 System.String 0 instance 031424d8 Name

 

            上面两段代码,红色标注的就是 Person 对象的方法表的地址,他们是一样的。我们有了对象的地址,对象的地址其实就是类型句柄的地址,也就是知道通过快索引的地址,只要减去 0x4就可以,说明一下,每个引用类型都包含【同步块索引】和【类型句柄】

 

  1. 1 0:006> dp 031424c8-0x4 l4
  2. 2 031424c4 00000000 013d4dec 031424d8 00000014

 

             00000000 就是同步块索引,后面的 013d4dec 就是方法表的地址,最后两项就是 Person 具体的值。00000014 就是十进制的20,也就是 Person 对象的 Age 字段的值。031424d8 就是 Person 对象的 Name 的值。我们来验证。

  1. 1 0:006> !do 031424d8
  2. 2 Name: System.String
  3. 3 MethodTable: 706324e4
  4. 4 EEClass: 70737690
  5. 5 Size: 22(0x16) bytes
  6. 6 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. 7 String: jack
  8. 8 Fields:
  9. 9 MT Field Offset Type VT Attr Value Name
  10. 10 706342a8 4000283 4 System.Int32 1 instance 4 m_stringLength
  11. 11 70632c9c 4000284 8 System.Char 1 instance 6a m_firstChar
  12. 12 706324e4 4000288 70 System.String 0 shared static Empty
  13. 13 >> Domain:Value 01472260:NotInit <<
  14. 14
  15. 15
  16. 16 0:006> ? 00000014
  17. 17 Evaluate expression: 20 = 00000014

              红色标注的就是具体的值。我们可以继续使用【dp】命令,查看 name 的详情。

  1. 1 0:006> dp 031424d8 l4
  2. 2 031424d8 706324e4 00000004 0061006a 006b0063

              031424d8(字符串 Name 的地址) 706324e4(name 的方法表的地址) 00000004(字符串的长度) 0061006a 006b0063(这两项就是具体的值),字符串的长度,我们可以通过 name 的地址加上 0x4,就可以得到字符串的长度。

  1. 1 0:006> dp 031424d8+0x4 l4
  2. 2 031424dc 00000004 0061006a 006b0063 00000000

 

              为什么是加上 0x4,因为我们在使用【!do】命令,查看 name 详情的时候,m_stringLength 的偏移量是4。      
              

              0061006a(看地址,我们是从低地址往高地址看),所以我们先看 006a,然后再查看 0061,006b0063这个地址也是一样的。我们可以使用【du】命令查看它的内容,u就是 Unicode。006a 就是 j,0061就是a,0063就是 c,006b就是k。

  1. 1 0:006> du 031424d8+0x8
  2. 2 031424e0 "jack"

              为什么加 0x8,因为偏移量是8。


        2.2、对值类型的转储。
            测试代码:Example_6_1_2
            我们首先找到栈地址,可以使用【!clrstack】命令,有了栈地址,我们可以使用【bp】命令在该地址上设置断点。
  1. 1 0:000> !clrstack
  2. 2 OS Thread Id: 0x358c (0)
  3. 3 Child SP IP Call Site
  4. 4 007af2e0 7566f262 [HelperMethodFrame: 007af2e0] System.Diagnostics.Debugger.BreakInternal()
  5. 5 007af35c 7146f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
  6. 6 007af384 029e0879 Example_6_1_2.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\...\Example_6_1_2\Program.cs @ 9]
  7. 7 007af504 71daf036 [GCFrame: 007af504]

            红色标注的【029e0879】就是【Program.Main】方法的栈地址,然后我们设置断点,【g】继续运行。

  1. 1 0:000> bp 029e0879
  2. 2 0:000> g
  3. 3 Breakpoint 0 hit
  4. 4 eax=00000000 ebx=007af434 ecx=72227ced edx=00b239b0 esi=00000000 edi=007af3b0
  5. 5 eip=029e0879 esp=007af384 ebp=007af398 iopl=0 nv up ei pl zr na pe nc
  6. 6 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
  7. 7 Example_6_1_2!COM+_Entry_Point <PERF> (Example_6_1_2+0x23d0879):
  8. 8 029e0879 90 nop

            在断点处暂停,如图:
            

            然后,使用【g】命令,继续运行。

  1. 1 0:000> g
  2. 2 Breakpoint 1 hit
  3. 3 eax=00000000 ebx=007af434 ecx=72227ced edx=00b239b0 esi=00000000 edi=007af3b0
  4. 4 eip=029e0896 esp=007af384 ebp=007af398 iopl=0 nv up ei pl zr na pe nc
  5. 5 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
  6. 6 Example_6_1_2!COM+_Entry_Point <PERF> (Example_6_1_2+0x23d0896):
  7. 7 029e0896 90 nop

            然后,我们使用【dp esp】命令查看详细,当然我们可以可以使用【dp 007af384】,dp 命令后跟的是栈地址,都是可以的。

  1. 1 0:000> dp esp
  2. 2 007af384 0000000d 0000000c 0000000b 0000000a
  3. 3 007af394 02a024bc 007af3a4 71daf036 00b239b0
  4. 4 007af3a4 007af3f8 71db22da 007af434 007af3e8
  5. 5 007af3b4 71ea23d0 007af504 71db2284 b7ed3afc
  6. 6 007af3c4 007af544 007af4c8 007af47c 71dbec59
  7. 7 007af3d4 007af434 00000000 b7ed3afc 007af3b0
  8. 8 007af3e4 007af4c8 007af574 71ea09b0 c64cea1c
  9. 9 007af3f4 00000001 007af460 71db859b 00000000

            红色标注的就是变量赋的值。汇编代码如下:

            

            当然,我们也可以通过【!clrstack -l】命令也可以找到所有局部变量。
  1. 1 0:000> !clrstack -l
  2. 2 OS Thread Id: 0x358c (0)
  3. 3 Child SP IP Call Site
  4. 4 007af384 029e0896 Example_6_1_2.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_2\Program.cs @ 15]
  5. 5 LOCALS:
  6. 6 0x007af390 = 0x0000000aa 变量的值,等号前面是栈地址)
  7. 7 0x007af38c = 0x0000000ba 变量的值,等号前面是栈地址,等不 a 的栈地址减去 0x4
  8. 8 0x007af388 = 0x0000000ca 变量的值,等号前面是栈地址,等不 b 的栈地址减去 0x4
  9. 9 0x007af384 = 0x0000000da 变量的值,等号前面是栈地址,等不 c 的栈地址减去 0x4
  10. 10
  11. 11 007af504 71daf036 [GCFrame: 007af504]

            栈地址是由高到低分配的,0x007af384 是变量d 的地址,0x007af384+0x4=0x007af388,这个地址就是c 变量的地址,0x007af388+0x4=0x007af38c,0x007af38c这个地址就是 b 变量的地址,0x007af38c+0x4就是 a 变量的地址,也就是 0x007af390


        2.3、对引用类型的转储。
            测试代码:Example_6_1_1
  1. 1 0:000> !clrstack -l
  2. 2 OS Thread Id: 0x38fc (0)
  3. 3 Child SP IP Call Site
  4. 4 00aff25c 777110fc [InlinedCallFrame: 00aff25c]
  5. 5 00aff258 70d99b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
  6. 6
  7. 7 ...
  8. 8
  9. 9 00aff358 0117088e Example_6_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_1\Program.cs @ 11]
  10. 10 LOCALS:
  11. 11 0x00aff360 = 0x02bb24c8
  12. 12
  13. 13 00aff4dc 71daf036 [GCFrame: 00aff4dc]

            我们查看线程栈了,找到了局部变量。也找到了地址,我们通过【!do】或者【!dumpobj】查看Person 变量。

  1. 1 0:000> !DumpObj /d 02bb24c8
  2. 2 Name: Example_6_1_1.Person
  3. 3 MethodTable: 00d14dec
  4. 4 EEClass: 00d112f0
  5. 5 Size: 16(0x10) bytes
  6. 6 File: E:\Visual Studio 2022\Source\Projects\...\Example_6_1_1\bin\Debug\Example_6_1_1.exe
  7. 7 Fields:
  8. 8 MT Field Offset Type VT Attr Value Name
  9. 9 708f42a8 4000001 8 System.Int32 1 instance 20 Age
  10. 10 708f24e4 4000002 4 System.String 0 instance 02bb24d8 Name(红色又是一个地址)

            红色的可以在【!do】,我们就可以看到详情。

  1. 1 0:000> !DumpObj /d 02bb24d8
  2. 2 Name: System.String
  3. 3 MethodTable: 708f24e4
  4. 4 EEClass: 709f7690
  5. 5 Size: 22(0x16) bytes
  6. 6 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. 7 String: jack(字符串 name 变量的值)
  8. 8 Fields:
  9. 9 MT Field Offset Type VT Attr Value Name
  10. 10 708f42a8 4000283 4 System.Int32 1 instance 4 m_stringLength
  11. 11 708f2c9c 4000284 8 System.Char 1 instance 6a m_firstChar
  12. 12 708f24e4 4000288 70 System.String 0 shared static Empty
  13. 13 >> Domain:Value 00d6d780:NotInit <<
            说明:我们有了对象的地址,也可以使用【dp】命令,只不过不太好看。
  1. 1 0:000> dp 02bb24c8 l4
  2. 2 02bb24c8 00d14dec 02bb24d8 00000014 80000000

            00d14dec 这个地址就是 Person 对象的方法表的地址,02bb24d8 就是name 变量的地址。00000014 就是 age 变量的值。


        2.4、对数组的转储。
            测试代码:Example_6_1_3
           
1)值类型数组
              
我们定义的数组:int[] intArr = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
              
我们先打印出线程栈。
  1. 1 0:000> !clrstack -l
  2. 2 OS Thread Id: 0x2e54 (0)
  3. 3 Child SP IP Call Site
  4. 4 00afeeec 7566f262 [HelperMethodFrame: 00afeeec] System.Diagnostics.Debugger.BreakInternal()
  5. 5 00afef68 7146f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
  6. 6
  7. 7 00afef90 00ce0920 Example_6_1_3.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_3\Program.cs @ 13]
  8. 8 LOCALS:
  9. 9 0x00afef9c = 0x02b82518(int 数组,值类型数组)
  10. 10 0x00afef98 = 0x02b82574string 数组,引用类型的数组)
  11. 11
  12. 12 00aff118 71daf036 [GCFrame: 00aff118]

                我们可以点击栈地址后面的地址,就可以查看详情,相当于【!do】命令。

  1. 1 0:000> !DumpObj /d 02b82518
  2. 2 Name: System.Int32[]
  3. 3 MethodTable: 708f426c
  4. 4 EEClass: 709f805c
  5. 5 Size: 56(0x38) bytes
  6. 6 Array: Rank 1, Number of elements 11, Type Int32 (Print Array)
  7. 7 Fields:
  8. 8 None
  9. 9 0:000> !DumpObj /d 02b82574
  10. 10 Name: System.String[]
  11. 11 MethodTable: 708f2d74
  12. 12 EEClass: 709f7820
  13. 13 Size: 32(0x20) bytes
  14. 14 Array: Rank 1, Number of elements 5, Type CLASS (Print Array)
  15. 15 Fields:
  16. 16 None

                使用【dp】命令,查看结果。                

  1. 1 0:000> dp 0x02b82518-0x4 L20(为什么要减去 0x4,因为当前对象的指针指向方法表,每个域是占用4个字节,L20 l 是不区分大小写的)
  2. 2 02b82514 00000000 708f426c 0000000b 0000000a
  3. 3 02b82524 0000000b 0000000c 0000000d 0000000e
  4. 4 02b82534 0000000f 00000010 00000011 00000012
  5. 5 02b82544 00000013 00000014 00000000 708f4354
  6. 6 02b82554 00000000 00000000 00000000 00000000
  7. 7 02b82564 00000000 00000000 00c44dd8 00000000
  8. 8 02b82574 708f2d74 00000005 02b824c8 02b824d8
  9. 9 02b82584 02b824e8 02b824f8 02b82508 00000000

                00000000 表示的同步块所有,为0就是什么数据都没有,708f426c 就是方法表的地址。0000000b 表示的数组长度,十进制是11,表示有11个元素。此项后面的就是数组元素了。
                  如果想可视化的查看,可以使用命令【!da -details】,内容太多,折叠了。

  1. 1 0:000> !da -details 0x02b82518
  2. 2 Name: System.Int32[]
  3. 3 MethodTable: 708f426c
  4. 4 EEClass: 709f805c
  5. 5 Size: 56(0x38) bytes
  6. 6 Array: Rank 1, Number of elements 11, Type Int32
  7. 7 Element Methodtable: 708f42a8
  8. 8 [0] 02b82520
  9. 9 Name: System.Int32
  10. 10 MethodTable: 708f42a8
  11. 11 EEClass: 709f8014
  12. 12 Size: 12(0xc) bytes
  13. 13 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  14. 14 Fields:
  15. 15 MT Field Offset Type VT Attr Value Name
  16. 16 708f42a8 40005a2 0 System.Int32 1 instance 10 m_value
  17. 17 [1] 02b82524
  18. 18 Name: System.Int32
  19. 19 MethodTable: 708f42a8
  20. 20 EEClass: 709f8014
  21. 21 Size: 12(0xc) bytes
  22. 22 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  23. 23 Fields:
  24. 24 MT Field Offset Type VT Attr Value Name
  25. 25 708f42a8 40005a2 0 System.Int32 1 instance 11 m_value
  26. 26 [2] 02b82528
  27. 27 Name: System.Int32
  28. 28 MethodTable: 708f42a8
  29. 29 EEClass: 709f8014
  30. 30 Size: 12(0xc) bytes
  31. 31 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  32. 32 Fields:
  33. 33 MT Field Offset Type VT Attr Value Name
  34. 34 708f42a8 40005a2 0 System.Int32 1 instance 12 m_value
  35. 35 [3] 02b8252c
  36. 36 Name: System.Int32
  37. 37 MethodTable: 708f42a8
  38. 38 EEClass: 709f8014
  39. 39 Size: 12(0xc) bytes
  40. 40 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  41. 41 Fields:
  42. 42 MT Field Offset Type VT Attr Value Name
  43. 43 708f42a8 40005a2 0 System.Int32 1 instance 13 m_value
  44. 44 [4] 02b82530
  45. 45 Name: System.Int32
  46. 46 MethodTable: 708f42a8
  47. 47 EEClass: 709f8014
  48. 48 Size: 12(0xc) bytes
  49. 49 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  50. 50 Fields:
  51. 51 MT Field Offset Type VT Attr Value Name
  52. 52 708f42a8 40005a2 0 System.Int32 1 instance 14 m_value
  53. 53 [5] 02b82534
  54. 54 Name: System.Int32
  55. 55 MethodTable: 708f42a8
  56. 56 EEClass: 709f8014
  57. 57 Size: 12(0xc) bytes
  58. 58 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  59. 59 Fields:
  60. 60 MT Field Offset Type VT Attr Value Name
  61. 61 708f42a8 40005a2 0 System.Int32 1 instance 15 m_value
  62. 62 [6] 02b82538
  63. 63 Name: System.Int32
  64. 64 MethodTable: 708f42a8
  65. 65 EEClass: 709f8014
  66. 66 Size: 12(0xc) bytes
  67. 67 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  68. 68 Fields:
  69. 69 MT Field Offset Type VT Attr Value Name
  70. 70 708f42a8 40005a2 0 System.Int32 1 instance 16 m_value
  71. 71 [7] 02b8253c
  72. 72 Name: System.Int32
  73. 73 MethodTable: 708f42a8
  74. 74 EEClass: 709f8014
  75. 75 Size: 12(0xc) bytes
  76. 76 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  77. 77 Fields:
  78. 78 MT Field Offset Type VT Attr Value Name
  79. 79 708f42a8 40005a2 0 System.Int32 1 instance 17 m_value
  80. 80 [8] 02b82540
  81. 81 Name: System.Int32
  82. 82 MethodTable: 708f42a8
  83. 83 EEClass: 709f8014
  84. 84 Size: 12(0xc) bytes
  85. 85 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  86. 86 Fields:
  87. 87 MT Field Offset Type VT Attr Value Name
  88. 88 708f42a8 40005a2 0 System.Int32 1 instance 18 m_value
  89. 89 [9] 02b82544
  90. 90 Name: System.Int32
  91. 91 MethodTable: 708f42a8
  92. 92 EEClass: 709f8014
  93. 93 Size: 12(0xc) bytes
  94. 94 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  95. 95 Fields:
  96. 96 MT Field Offset Type VT Attr Value Name
  97. 97 708f42a8 40005a2 0 System.Int32 1 instance 19 m_value
  98. 98 [10] 02b82548
  99. 99 Name: System.Int32
  100. 100 MethodTable: 708f42a8
  101. 101 EEClass: 709f8014
  102. 102 Size: 12(0xc) bytes
  103. 103 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  104. 104 Fields:
  105. 105 MT Field Offset Type VT Attr Value Name
  106. 106 708f42a8 40005a2 0 System.Int32 1 instance 20 m_value
View Code

           
2)引用类型数组
              
我们定义的数组:string[] stringArr = { "1", "2", "3", "4", "5" };
  1. 1 0:000> !clrstack -l
  2. 2 OS Thread Id: 0x2e54 (0)
  3. 3 Child SP IP Call Site
  4. 4 00afeeec 7566f262 [HelperMethodFrame: 00afeeec] System.Diagnostics.Debugger.BreakInternal()
  5. 5 00afef68 7146f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
  6. 6
  7. 7 00afef90 00ce0920 Example_6_1_3.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_3\Program.cs @ 13]
  8. 8 LOCALS:
  9. 9 0x00afef9c = 0x02b82518
  10. 10 0x00afef98 = 0x02b82574(引用类型的数组)
  11. 11
  12. 12 00aff118 71daf036 [GCFrame: 00aff118]
  13. 13 0:000> !DumpObj /d 02b82574
  14. 14 Name: System.String[]
  15. 15 MethodTable: 708f2d74
  16. 16 EEClass: 709f7820
  17. 17 Size: 32(0x20) bytes
  18. 18 Array: Rank 1, Number of elements 5, Type CLASS (Print Array)
  19. 19 Fields:
  20. 20 None

              我们同样使用【dp】命令,查看详情。

  1. 1 0:000> dp 02b82574-0x4 l20
  2. 2 02b82570 00000000(同步块索引) 708f2d74(方法表) 00000005(数组的长度是5 02b824c8(具体的元素了,是一个地址,可以使用【!do】命令)
  3. 3 02b82580 02b824d8 02b824e8 02b824f8 02b82508
  4. 4 02b82590 00000000 7093dbc4 00000002 00000000
  5. 5 02b825a0 7093dbc4 00000001 00000000 709444c4
  6. 6 02b825b0 00000000 02b82768 00000000 00000018
  7. 7 02b825c0 00000008 00000001 00000006 00000000
  8. 8 02b825d0 70944500 00000000 02b82640 02b826d4
  9. 9 02b825e0 02b825f0 00000004 00000011 00000000

              我们使用【!do】命令,查看元素详情。

  1. 1 0:000> !do 02b824c8
  2. 2 Name: System.String
  3. 3 MethodTable: 708f24e4
  4. 4 EEClass: 709f7690
  5. 5 Size: 16(0x10) bytes
  6. 6 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. 7 String: 1
  8. 8 Fields:
  9. 9 MT Field Offset Type VT Attr Value Name
  10. 10 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  11. 11 708f2c9c 4000284 8 System.Char 1 instance 31 m_firstChar
  12. 12 708f24e4 4000288 70 System.String 0 shared static Empty
  13. 13 >> Domain:Value 00dbd7b8:NotInit <<
  14. 14 0:000> !do 02b824d8
  15. 15 Name: System.String
  16. 16 MethodTable: 708f24e4
  17. 17 EEClass: 709f7690
  18. 18 Size: 16(0x10) bytes
  19. 19 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  20. 20 String: 2
  21. 21 Fields:
  22. 22 MT Field Offset Type VT Attr Value Name
  23. 23 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  24. 24 708f2c9c 4000284 8 System.Char 1 instance 32 m_firstChar
  25. 25 708f24e4 4000288 70 System.String 0 shared static Empty
  26. 26 >> Domain:Value 00dbd7b8:NotInit <<
View Code

              当然,我们也可以使用【!da -details】命令。

  1. 1 0:000> !da -details 02b82574
  2. 2 Name: System.String[]
  3. 3 MethodTable: 708f2d74
  4. 4 EEClass: 709f7820
  5. 5 Size: 32(0x20) bytes
  6. 6 Array: Rank 1, Number of elements 5, Type CLASS
  7. 7 Element Methodtable: 708f24e4
  8. 8 [0] 02b824c8
  9. 9 Name: System.String
  10. 10 MethodTable: 708f24e4
  11. 11 EEClass: 709f7690
  12. 12 Size: 16(0x10) bytes
  13. 13 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  14. 14 String: 1
  15. 15 Fields:
  16. 16 MT Field Offset Type VT Attr Value Name
  17. 17 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  18. 18 708f2c9c 4000284 8 System.Char 1 instance 31 m_firstChar
  19. 19 708f24e4 4000288 70 System.String 0 shared static Empty
  20. 20 >> Domain:Value 00dbd7b8:NotInit <<
  21. 21 [1] 02b824d8
  22. 22 Name: System.String
  23. 23 MethodTable: 708f24e4
  24. 24 EEClass: 709f7690
  25. 25 Size: 16(0x10) bytes
  26. 26 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  27. 27 String: 2
  28. 28 Fields:
  29. 29 MT Field Offset Type VT Attr Value Name
  30. 30 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  31. 31 708f2c9c 4000284 8 System.Char 1 instance 32 m_firstChar
  32. 32 708f24e4 4000288 70 System.String 0 shared static Empty
  33. 33 >> Domain:Value 00dbd7b8:NotInit <<
  34. 34 [2] 02b824e8
  35. 35 Name: System.String
  36. 36 MethodTable: 708f24e4
  37. 37 EEClass: 709f7690
  38. 38 Size: 16(0x10) bytes
  39. 39 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  40. 40 String: 3
  41. 41 Fields:
  42. 42 MT Field Offset Type VT Attr Value Name
  43. 43 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  44. 44 708f2c9c 4000284 8 System.Char 1 instance 33 m_firstChar
  45. 45 708f24e4 4000288 70 System.String 0 shared static Empty
  46. 46 >> Domain:Value 00dbd7b8:NotInit <<
  47. 47 [3] 02b824f8
  48. 48 Name: System.String
  49. 49 MethodTable: 708f24e4
  50. 50 EEClass: 709f7690
  51. 51 Size: 16(0x10) bytes
  52. 52 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  53. 53 String: 4
  54. 54 Fields:
  55. 55 MT Field Offset Type VT Attr Value Name
  56. 56 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  57. 57 708f2c9c 4000284 8 System.Char 1 instance 34 m_firstChar
  58. 58 708f24e4 4000288 70 System.String 0 shared static Empty
  59. 59 >> Domain:Value 00dbd7b8:NotInit <<
  60. 60 [4] 02b82508
  61. 61 Name: System.String
  62. 62 MethodTable: 708f24e4
  63. 63 EEClass: 709f7690
  64. 64 Size: 16(0x10) bytes
  65. 65 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  66. 66 String: 5
  67. 67 Fields:
  68. 68 MT Field Offset Type VT Attr Value Name
  69. 69 708f42a8 4000283 4 System.Int32 1 instance 1 m_stringLength
  70. 70 708f2c9c 4000284 8 System.Char 1 instance 35 m_firstChar
  71. 71 708f24e4 4000288 70 System.String 0 shared static Empty
  72. 72 >> Domain:Value 00dbd7b8:NotInit <<
View Code

       
2.5、异常的转储
            测试代码:Example_4_1_2
          
我们必须先要下载 procdump 程序,这个程序分两个版本,32 位和64 位的,使用它注册 aedebug。打开 procdump 文件所在的目录,就可以执行下面的命令了。
            操作步骤:
            1)下载 procdump
                下载地址:https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump
            2)创建保存dump的目录,不创建会出错
                F:\Test\TestDump
            3)注册 Aedbug。
                打开 cmd 窗口,执行如下的命令:procdump -ma -i F:\Test\TestDump
              

            4)运行程序,保存dump
                故意输入0,系统崩溃。
                


                 5)运行 windbg

              我们打开 windbg Preview,点击【文件】菜单,然后选择【Open dump file】,打开文件。
              我们输出所有线程,使用【!t】命令。
  1. 1 0:000> !t
  2. 2 ThreadCount: 2
  3. 3 UnstartedThread: 0
  4. 4 BackgroundThread: 1
  5. 5 PendingThread: 0
  6. 6 DeadThread: 0
  7. 7 Hosted Runtime: no
  8. 8 Lock
  9. 9 ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
  10. 10 0 1 2048 0081da18 2a020 Preemptive 0284F400:00000000 00817c30 0 MTA System.DivideByZeroException 028450c0
  11. 11 5 2 334c 0082db60 2b220 Preemptive 00000000:00000000 00817c30 0 MTA (Finalizer)

                红色标注,说明主线程发生了异常。继续使用【!pe[exception]】命令查看异常详情。

  1. 1 :000> !pe
  2. 2 Exception object: 028450c0
  3. 3 Exception type: System.DivideByZeroException
  4. 4 Message: 尝试除以零。
  5. 5 InnerException: <none>
  6. 6 StackTrace (generated):
  7. 7 SP IP Function
  8. 8 006FEFE0 026408A5 Example_6_1_4!Example_6_1_4.Program.Main(System.String[])+0x5d
  9. 9
  10. 10 StackTraceString: <none>
  11. 11 HResult: 80020012

                我们可以继续使用【!do】命令查看异常对象。

  1. 1 0:000> !DumpObj /d 028450c0
  2. 2 Name: System.DivideByZeroException
  3. 3 MethodTable: 708fd01c
  4. 4 EEClass: 70ad84fc
  5. 5 Size: 84(0x54) bytes
  6. 6 File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
  7. 7 Fields:
  8. 8 MT Field Offset Type VT Attr Value Name
  9. 9 708f24e4 40002a4 4 System.String 0 instance 0284ce64 _className
  10. 10 708f6f28 40002a5 8 ...ection.MethodBase 0 instance 00000000 _exceptionMethod
  11. 11 708f24e4 40002a6 c System.String 0 instance 00000000 _exceptionMethodString
  12. 12 708f24e4 40002a7 10 System.String 0 instance 0284b5d8 _message
  13. 13 7093f8fc 40002a8 14 ...tions.IDictionary 0 instance 00000000 _data
  14. 14 708f25d4 40002a9 18 System.Exception 0 instance 00000000 _innerException
  15. 15 708f24e4 40002aa 1c System.String 0 instance 00000000 _helpURL
  16. 16 708f2734 40002ab 20 System.Object 0 instance 0284b670 _stackTrace
  17. 17 708f2734 40002ac 24 System.Object 0 instance 0284b6a0 _watsonBuckets
  18. 18 708f24e4 40002ad 28 System.String 0 instance 00000000 _stackTraceString
  19. 19 708f24e4 40002ae 2c System.String 0 instance 00000000 _remoteStackTraceString
  20. 20 708f42a8 40002af 3c System.Int32 1 instance 0 _remoteStackIndex
  21. 21 708f2734 40002b0 30 System.Object 0 instance 00000000 _dynamicMethods
  22. 22 708f42a8 40002b1 40 System.Int32 1 instance -2147352558 _HResult
  23. 23 708f24e4 40002b2 34 System.String 0 instance 00000000 _source
  24. 24 708f7b18 40002b3 44 System.IntPtr 1 instance 6fe8a4 _xptrs
  25. 25 708f42a8 40002b4 48 System.Int32 1 instance -1073741676 _xcode
  26. 26 70903adc 40002b5 4c System.UIntPtr 1 instance 0 _ipForWatsonBuckets
  27. 27 709362d0 40002b6 38 ...ializationManager 0 instance 0284b654 _safeSerializationManager
  28. 28 708f2734 40002a3 84 System.Object 0 shared static s_EDILock
  29. 29 >> Domain:Value 00817c30:NotInit <<


四、总结
    
终于写完了,为什么说是终于,因为写这一篇文章,不是一天完成的。今天介绍的是值类型、应用类型的区别,另外在加上数组和异常崩溃的解决思路,现在终于做到知其一也知其二了,但是这个过程不好熬。为什么不好熬呢,基础差,没有汇编基础,什么栈帧,栈地址,都没有什么概念,没有别的办法,努力补充起来,底层的东西,一遍肯定是不行的。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。

原文链接:https://www.cnblogs.com/PatrickLiu/p/17812156.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号