当前位置: 代码网 > it编程>编程语言>Asp.net > Net 高级调试之六:对象检查之值类型、引用类型、数组和异常的转储

Net 高级调试之六:对象检查之值类型、引用类型、数组和异常的转储

2024年05月16日 Asp.net 我要评论
一、简介 今天是《Net 高级调试》的第六篇文章。记得我刚接触 Net 框架的时候,还是挺有信心的,对所谓的值类型和引用类型也能说出自己的见解,毕竟,自己一直在努力。当然这些见解都是书本上的,并没有做到眼见为实,所以总是有些东西说不清楚。今天,我们就好好的说说 C# 的类型,是从内存级别、从底层来说 ...
一、简介
     今天是《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 namespace example_6_1_1
 2 {
 3     internal class program
 4     {
 5         static void main(string[] args)
 6         {
 7             var person = new person();
 8 
 9             console.readline();
10         }
11     }
12 
13     internal class person
14     {
15         public int age = 20;
16 
17         public string name = "jack";
18     }
19 }

        1.2、example_6_1_2
 1 namespace example_6_1_2
 2 {
 3     internal class program
 4     {
 5         static void main(string[] args)
 6         {
 7             debugger.break();
 8 
 9             int a = 10;
10             int b = 11;
11             int c = 12;
12             int d = 13;
13         }
14     }
15 }

        1.3、example_6_1_3
 1 namespace example_6_1_3
 2 {
 3     internal class program
 4     {
 5         static void main(string[] args)
 6         {
 7             int[] intarr = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
 8 
 9             string[] stringarr = { "1", "2", "3", "4", "5" };
10 
11             debugger.break();
12         }
13     }
14 }

        1.4、example_6_1_4
 1 namespace example_6_1_4
 2 {
 3     internal class program
 4     {
 5         static void main(string[] args)
 6         {
 7             console.writeline($"请输入一个能够整除的数字:");
 8             var num = console.readline();
 9 
10             var ret = 10 / convert.toint32(num);
11         }
12     }
13 }

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

        2.1、内存转储
            测试代码:example_6_1_1
            我们首先去托管堆中查找一下 person 对象。【dp】命令的 p 是 pointer,指针的意思。
1 0:006> !dumpheap -type person
2  address       mt     size
3 031424c8 013d4dec       16     
4 
5 statistics:
6       mt    count    totalsize class name
7 013d4dec        1           16 example_6_1_1.person
8 total 1 objects

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

 1 0:006> !dumpobj /d 031424c8
 2 name:        example_6_1_1.person
 3 methodtable: 013d4dec
 4 eeclass:     013d12f0
 5 size:        16(0x10) bytes
 6 file:        e:\visual studio 2022\source\projects\....\example_6_1_1\bin\debug\example_6_1_1.exe
 7 fields:
 8       mt    field   offset                 type vt     attr    value name
 9 706342a8  4000001        8         system.int32  1 instance       20 age
10 706324e4  4000002        4        system.string  0 instance 031424d8 name

 

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

 

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

 

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

 1 0:006> !do 031424d8
 2 name:        system.string
 3 methodtable: 706324e4
 4 eeclass:     70737690
 5 size:        22(0x16) bytes
 6 file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 string:      jack
 8 fields:
 9       mt    field   offset                 type vt     attr    value name
10 706342a8  4000283        4         system.int32  1 instance        4 m_stringlength
11 70632c9c  4000284        8          system.char  1 instance       6a m_firstchar
12 706324e4  4000288       70        system.string  0   shared   static empty
13     >> domain:value  01472260:notinit  <<
14 
15 
16 0:006> ? 00000014
17 evaluate expression: 20 = 00000014

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

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

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

1 0:006> dp 031424d8+0x4 l4
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 0:006> du 031424d8+0x8
2 031424e0  "jack"

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


        2.2、对值类型的转储。
            测试代码:example_6_1_2
            我们首先找到栈地址,可以使用【!clrstack】命令,有了栈地址,我们可以使用【bp】命令在该地址上设置断点。
1 0:000> !clrstack 
2 os thread id: 0x358c (0)
3 child sp       ip call site
4 007af2e0 7566f262 [helpermethodframe: 007af2e0] system.diagnostics.debugger.breakinternal()
5 007af35c 7146f195 system.diagnostics.debugger.break() [f:\dd\ndp\clr\src\bcl\system\diagnostics\debugger.cs @ 91]
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 007af504 71daf036 [gcframe: 007af504] 

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

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

            在断点处暂停,如图:
            

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

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

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

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

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

            

            当然,我们也可以通过【!clrstack -l】命令也可以找到所有局部变量。
 1 0:000> !clrstack -l
 2 os thread id: 0x358c (0)
 3 child sp       ip call site
 4 007af384 029e0896 example_6_1_2.program.main(system.string[]) [e:\visual studio 2022\...\example_6_1_2\program.cs @ 15]
 5     locals:
 6         0x007af390 = 0x0000000a(a 变量的值,等号前面是栈地址)
 7         0x007af38c = 0x0000000b(a 变量的值,等号前面是栈地址,等不 a 的栈地址减去 0x4)
 8         0x007af388 = 0x0000000c(a 变量的值,等号前面是栈地址,等不 b 的栈地址减去 0x4)
 9         0x007af384 = 0x0000000d(a 变量的值,等号前面是栈地址,等不 c 的栈地址减去 0x4)
10 
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 0:000> !clrstack -l
 2 os thread id: 0x38fc (0)
 3 child sp       ip call site
 4 00aff25c 777110fc [inlinedcallframe: 00aff25c] 
 5 00aff258 70d99b71 domainneutralilstubclass.il_stub_pinvoke(microsoft.win32.safehandles.safefilehandle, byte*, int32, int32 byref, intptr)
 6 
 7 ...
 8 
 9 00aff358 0117088e example_6_1_1.program.main(system.string[]) [e:\visual studio 2022\...\example_6_1_1\program.cs @ 11]
10     locals:
11         0x00aff360 = 0x02bb24c8
12 
13 00aff4dc 71daf036 [gcframe: 00aff4dc] 

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

 1 0:000> !dumpobj /d 02bb24c8
 2 name:        example_6_1_1.person
 3 methodtable: 00d14dec
 4 eeclass:     00d112f0
 5 size:        16(0x10) bytes
 6 file:        e:\visual studio 2022\source\projects\...\example_6_1_1\bin\debug\example_6_1_1.exe
 7 fields:
 8       mt    field   offset                 type vt     attr    value name
 9 708f42a8  4000001        8         system.int32  1 instance       20 age
10 708f24e4  4000002        4        system.string  0 instance 02bb24d8 name(红色又是一个地址)

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

 1 0:000> !dumpobj /d 02bb24d8
 2 name:        system.string
 3 methodtable: 708f24e4
 4 eeclass:     709f7690
 5 size:        22(0x16) bytes
 6 file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 string:      jack(字符串 name 变量的值)
 8 fields:
 9       mt    field   offset                 type vt     attr    value name
10 708f42a8  4000283        4         system.int32  1 instance        4 m_stringlength
11 708f2c9c  4000284        8          system.char  1 instance       6a m_firstchar
12 708f24e4  4000288       70        system.string  0   shared   static empty
13     >> domain:value  00d6d780:notinit  <<
            说明:我们有了对象的地址,也可以使用【dp】命令,只不过不太好看。
1 0:000> dp 02bb24c8 l4
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 0:000> !clrstack -l
 2 os thread id: 0x2e54 (0)
 3 child sp       ip call site
 4 00afeeec 7566f262 [helpermethodframe: 00afeeec] system.diagnostics.debugger.breakinternal()
 5 00afef68 7146f195 system.diagnostics.debugger.break() [f:\dd\ndp\clr\src\bcl\system\diagnostics\debugger.cs @ 91]
 6 
 7 00afef90 00ce0920 example_6_1_3.program.main(system.string[]) [e:\visual studio 2022\...\example_6_1_3\program.cs @ 13]
 8     locals:
 9         0x00afef9c = 0x02b82518(int 数组,值类型数组)
10         0x00afef98 = 0x02b82574(string 数组,引用类型的数组)
11 
12 00aff118 71daf036 [gcframe: 00aff118] 

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

 1 0:000> !dumpobj /d 02b82518
 2 name:        system.int32[]
 3 methodtable: 708f426c
 4 eeclass:     709f805c
 5 size:        56(0x38) bytes
 6 array:       rank 1, number of elements 11, type int32 (print array)
 7 fields:
 8 none
 9 0:000> !dumpobj /d 02b82574
10 name:        system.string[]
11 methodtable: 708f2d74
12 eeclass:     709f7820
13 size:        32(0x20) bytes
14 array:       rank 1, number of elements 5, type class (print array)
15 fields:
16 none

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

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

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

  1 0:000> !da -details 0x02b82518
  2 name:        system.int32[]
  3 methodtable: 708f426c
  4 eeclass:     709f805c
  5 size:        56(0x38) bytes
  6 array:       rank 1, number of elements 11, type int32
  7 element methodtable: 708f42a8
  8 [0] 02b82520
  9     name:        system.int32
 10     methodtable: 708f42a8
 11     eeclass:     709f8014
 12     size:        12(0xc) bytes
 13     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 14     fields:
 15               mt    field   offset                 type vt     attr    value name
 16         708f42a8  40005a2        0             system.int32      1     instance           10     m_value
 17 [1] 02b82524
 18     name:        system.int32
 19     methodtable: 708f42a8
 20     eeclass:     709f8014
 21     size:        12(0xc) bytes
 22     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 23     fields:
 24               mt    field   offset                 type vt     attr    value name
 25         708f42a8  40005a2        0             system.int32      1     instance           11     m_value
 26 [2] 02b82528
 27     name:        system.int32
 28     methodtable: 708f42a8
 29     eeclass:     709f8014
 30     size:        12(0xc) bytes
 31     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 32     fields:
 33               mt    field   offset                 type vt     attr    value name
 34         708f42a8  40005a2        0             system.int32      1     instance           12     m_value
 35 [3] 02b8252c
 36     name:        system.int32
 37     methodtable: 708f42a8
 38     eeclass:     709f8014
 39     size:        12(0xc) bytes
 40     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 41     fields:
 42               mt    field   offset                 type vt     attr    value name
 43         708f42a8  40005a2        0             system.int32      1     instance           13     m_value
 44 [4] 02b82530
 45     name:        system.int32
 46     methodtable: 708f42a8
 47     eeclass:     709f8014
 48     size:        12(0xc) bytes
 49     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 50     fields:
 51               mt    field   offset                 type vt     attr    value name
 52         708f42a8  40005a2        0             system.int32      1     instance           14     m_value
 53 [5] 02b82534
 54     name:        system.int32
 55     methodtable: 708f42a8
 56     eeclass:     709f8014
 57     size:        12(0xc) bytes
 58     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 59     fields:
 60               mt    field   offset                 type vt     attr    value name
 61         708f42a8  40005a2        0             system.int32      1     instance           15     m_value
 62 [6] 02b82538
 63     name:        system.int32
 64     methodtable: 708f42a8
 65     eeclass:     709f8014
 66     size:        12(0xc) bytes
 67     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 68     fields:
 69               mt    field   offset                 type vt     attr    value name
 70         708f42a8  40005a2        0             system.int32      1     instance           16     m_value
 71 [7] 02b8253c
 72     name:        system.int32
 73     methodtable: 708f42a8
 74     eeclass:     709f8014
 75     size:        12(0xc) bytes
 76     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 77     fields:
 78               mt    field   offset                 type vt     attr    value name
 79         708f42a8  40005a2        0             system.int32      1     instance           17     m_value
 80 [8] 02b82540
 81     name:        system.int32
 82     methodtable: 708f42a8
 83     eeclass:     709f8014
 84     size:        12(0xc) bytes
 85     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 86     fields:
 87               mt    field   offset                 type vt     attr    value name
 88         708f42a8  40005a2        0             system.int32      1     instance           18     m_value
 89 [9] 02b82544
 90     name:        system.int32
 91     methodtable: 708f42a8
 92     eeclass:     709f8014
 93     size:        12(0xc) bytes
 94     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 95     fields:
 96               mt    field   offset                 type vt     attr    value name
 97         708f42a8  40005a2        0             system.int32      1     instance           19     m_value
 98 [10] 02b82548
 99     name:        system.int32
100     methodtable: 708f42a8
101     eeclass:     709f8014
102     size:        12(0xc) bytes
103     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
104     fields:
105               mt    field   offset                 type vt     attr    value name
106         708f42a8  40005a2        0             system.int32      1     instance           20     m_value

           
2)引用类型数组
              
我们定义的数组:string[] stringarr = { "1", "2", "3", "4", "5" };
 1 0:000> !clrstack -l
 2 os thread id: 0x2e54 (0)
 3 child sp       ip call site
 4 00afeeec 7566f262 [helpermethodframe: 00afeeec] system.diagnostics.debugger.breakinternal()
 5 00afef68 7146f195 system.diagnostics.debugger.break() [f:\dd\ndp\clr\src\bcl\system\diagnostics\debugger.cs @ 91]
 6 
 7 00afef90 00ce0920 example_6_1_3.program.main(system.string[]) [e:\visual studio 2022\...\example_6_1_3\program.cs @ 13]
 8     locals:
 9         0x00afef9c = 0x02b82518
10         0x00afef98 = 0x02b82574(引用类型的数组)
11 
12 00aff118 71daf036 [gcframe: 00aff118] 
13 0:000> !dumpobj /d 02b82574
14 name:        system.string[]
15 methodtable: 708f2d74
16 eeclass:     709f7820
17 size:        32(0x20) bytes
18 array:       rank 1, number of elements 5, type class (print array)
19 fields:
20 none

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

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

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

 1 0:000> !do 02b824c8
 2 name:        system.string
 3 methodtable: 708f24e4
 4 eeclass:     709f7690
 5 size:        16(0x10) bytes
 6 file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 string:      1
 8 fields:
 9       mt    field   offset                 type vt     attr    value name
10 708f42a8  4000283        4         system.int32  1 instance        1 m_stringlength
11 708f2c9c  4000284        8          system.char  1 instance       31 m_firstchar
12 708f24e4  4000288       70        system.string  0   shared   static empty
13     >> domain:value  00dbd7b8:notinit  <<
14 0:000> !do 02b824d8
15 name:        system.string
16 methodtable: 708f24e4
17 eeclass:     709f7690
18 size:        16(0x10) bytes
19 file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
20 string:      2
21 fields:
22       mt    field   offset                 type vt     attr    value name
23 708f42a8  4000283        4         system.int32  1 instance        1 m_stringlength
24 708f2c9c  4000284        8          system.char  1 instance       32 m_firstchar
25 708f24e4  4000288       70        system.string  0   shared   static empty
26     >> domain:value  00dbd7b8:notinit  <<

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

 1 0:000> !da -details 02b82574
 2 name:        system.string[]
 3 methodtable: 708f2d74
 4 eeclass:     709f7820
 5 size:        32(0x20) bytes
 6 array:       rank 1, number of elements 5, type class
 7 element methodtable: 708f24e4
 8 [0] 02b824c8
 9     name:        system.string
10     methodtable: 708f24e4
11     eeclass:     709f7690
12     size:        16(0x10) bytes
13     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
14     string:          1    
15     fields:
16               mt    field   offset                 type vt     attr    value name
17         708f42a8  4000283        4             system.int32      1     instance            1     m_stringlength
18         708f2c9c  4000284        8              system.char      1     instance           31     m_firstchar
19         708f24e4  4000288       70            system.string      0       shared   static     empty
20         >> domain:value      00dbd7b8:notinit      <<
21 [1] 02b824d8
22     name:        system.string
23     methodtable: 708f24e4
24     eeclass:     709f7690
25     size:        16(0x10) bytes
26     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
27     string:          2    
28     fields:
29               mt    field   offset                 type vt     attr    value name
30         708f42a8  4000283        4             system.int32      1     instance            1     m_stringlength
31         708f2c9c  4000284        8              system.char      1     instance           32     m_firstchar
32         708f24e4  4000288       70            system.string      0       shared   static     empty
33         >> domain:value      00dbd7b8:notinit      <<
34 [2] 02b824e8
35     name:        system.string
36     methodtable: 708f24e4
37     eeclass:     709f7690
38     size:        16(0x10) bytes
39     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
40     string:          3    
41     fields:
42               mt    field   offset                 type vt     attr    value name
43         708f42a8  4000283        4             system.int32      1     instance            1     m_stringlength
44         708f2c9c  4000284        8              system.char      1     instance           33     m_firstchar
45         708f24e4  4000288       70            system.string      0       shared   static     empty
46         >> domain:value      00dbd7b8:notinit      <<
47 [3] 02b824f8
48     name:        system.string
49     methodtable: 708f24e4
50     eeclass:     709f7690
51     size:        16(0x10) bytes
52     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
53     string:          4    
54     fields:
55               mt    field   offset                 type vt     attr    value name
56         708f42a8  4000283        4             system.int32      1     instance            1     m_stringlength
57         708f2c9c  4000284        8              system.char      1     instance           34     m_firstchar
58         708f24e4  4000288       70            system.string      0       shared   static     empty
59         >> domain:value      00dbd7b8:notinit      <<
60 [4] 02b82508
61     name:        system.string
62     methodtable: 708f24e4
63     eeclass:     709f7690
64     size:        16(0x10) bytes
65     file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
66     string:          5    
67     fields:
68               mt    field   offset                 type vt     attr    value name
69         708f42a8  4000283        4             system.int32      1     instance            1     m_stringlength
70         708f2c9c  4000284        8              system.char      1     instance           35     m_firstchar
71         708f24e4  4000288       70            system.string      0       shared   static     empty
72         >> domain:value      00dbd7b8:notinit      <<

       
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 0:000> !t
 2 threadcount:      2
 3 unstartedthread:  0
 4 backgroundthread: 1
 5 pendingthread:    0
 6 deadthread:       0
 7 hosted runtime:   no
 8                                                                          lock  
 9        id osid threadobj    state gc mode     gc alloc context  domain   count apt exception
10    0    1 2048 0081da18     2a020 preemptive  0284f400:00000000 00817c30 0     mta system.dividebyzeroexception 028450c0
11    5    2 334c 0082db60     2b220 preemptive  00000000:00000000 00817c30 0     mta (finalizer) 

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

 1 :000> !pe
 2 exception object: 028450c0
 3 exception type:   system.dividebyzeroexception
 4 message:          尝试除以零。
 5 innerexception:   <none>
 6 stacktrace (generated):
 7     sp       ip       function
 8     006fefe0 026408a5 example_6_1_4!example_6_1_4.program.main(system.string[])+0x5d
 9 
10 stacktracestring: <none>
11 hresult: 80020012

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

 1 0:000> !dumpobj /d 028450c0
 2 name:        system.dividebyzeroexception
 3 methodtable: 708fd01c
 4 eeclass:     70ad84fc
 5 size:        84(0x54) bytes
 6 file:        c:\windows\microsoft.net\assembly\gac_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 fields:
 8       mt    field   offset                 type vt     attr    value name
 9 708f24e4  40002a4        4        system.string  0 instance 0284ce64 _classname
10 708f6f28  40002a5        8 ...ection.methodbase  0 instance 00000000 _exceptionmethod
11 708f24e4  40002a6        c        system.string  0 instance 00000000 _exceptionmethodstring
12 708f24e4  40002a7       10        system.string  0 instance 0284b5d8 _message
13 7093f8fc  40002a8       14 ...tions.idictionary  0 instance 00000000 _data
14 708f25d4  40002a9       18     system.exception  0 instance 00000000 _innerexception
15 708f24e4  40002aa       1c        system.string  0 instance 00000000 _helpurl
16 708f2734  40002ab       20        system.object  0 instance 0284b670 _stacktrace
17 708f2734  40002ac       24        system.object  0 instance 0284b6a0 _watsonbuckets
18 708f24e4  40002ad       28        system.string  0 instance 00000000 _stacktracestring
19 708f24e4  40002ae       2c        system.string  0 instance 00000000 _remotestacktracestring
20 708f42a8  40002af       3c         system.int32  1 instance        0 _remotestackindex
21 708f2734  40002b0       30        system.object  0 instance 00000000 _dynamicmethods
22 708f42a8  40002b1       40         system.int32  1 instance -2147352558 _hresult
23 708f24e4  40002b2       34        system.string  0 instance 00000000 _source
24 708f7b18  40002b3       44        system.intptr  1 instance   6fe8a4 _xptrs
25 708f42a8  40002b4       48         system.int32  1 instance -1073741676 _xcode
26 70903adc  40002b5       4c       system.uintptr  1 instance        0 _ipforwatsonbuckets
27 709362d0  40002b6       38 ...ializationmanager  0 instance 0284b654 _safeserializationmanager
28 708f2734  40002a3       84        system.object  0   shared   static s_edilock
29     >> domain:value  00817c30:notinit  <<


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

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com