Advanced .Net Debugging 4:基本除錯任務(物件檢查:記憶體、值型別、引用型別、陣列和異常的轉儲)

可均可可發表於2024-03-08
一、介紹
    這是我的《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
Advanced .Net Debugging 4:基本除錯任務(物件檢查:記憶體、值型別、引用型別、陣列和異常的轉儲)
  1 using System.Diagnostics;
  2 
  3 namespace ExampleCore_3_1_6
  4 {
  5     public class ObjTypes
  6     {
  7         public struct Coordinate
  8         {
  9             public int xCord;
 10             public int yCord;
 11             public int zCord;
 12 
 13             public Coordinate(int x, int y, int z)
 14             {
 15                 xCord = x;
 16                 yCord = y;
 17                 zCord = z;
 18             }
 19         }
 20 
 21         private Coordinate coordinate;
 22 
 23         int[] intArray = [1, 2, 3, 4, 5];
 24 
 25         string[] strArray = ["Welcome", "to", "Advanced", ".NET", "Debugging"];
 26 
 27         static void Main(string[] args)
 28         {
 29             Coordinate point = new Coordinate(100, 100, 100);
 30             Console.WriteLine("Press any key to continue(AddCoordinate)");
 31             Console.ReadKey();
 32             ObjTypes ob = new ObjTypes();
 33             ob.AddCoordinate(point);
 34 
 35             Console.WriteLine("Press any key to continue(Arrays)");
 36             Console.ReadKey();
 37             ob.PrintArrays();
 38 
 39             Console.WriteLine("Press any key to continue(Generics)");
 40             Console.ReadKey();
 41             Comparer<int> c = new Comparer<int>();
 42             Console.WriteLine("Greater:{0}", c.GreaterThan(5, 10));
 43 
 44             Console.WriteLine("Preaa any key to continue(Exception)");
 45             Console.ReadKey();
 46             ob.ThrowException(null);
 47         }
 48        
 49         public void AddCoordinate(Coordinate coord)
 50         {
 51             coordinate.xCord += coord.xCord;
 52             coordinate.yCord += coord.yCord;
 53             coordinate.zCord += coord.zCord;
 54 
 55             Console.WriteLine("x:{0},y:{1},z:{2}", coordinate.xCord, coordinate.yCord, coordinate.zCord);
 56         }
 57 
 58         public void PrintArrays()
 59         {
 60             foreach (int i in intArray)
 61             {
 62                 Console.WriteLine("Int:{0}", i);
 63             }
 64             foreach (string s in strArray)
 65             {
 66                 Console.WriteLine("Str:{0}", s);
 67             }
 68         }
 69 
 70         public void ThrowException(ObjTypes? obj)
 71         {
 72             if (obj == null)
 73             {
 74                 throw new ArgumentException("Obj cannot be null");
 75             }
 76         }        
 77     }
 78     public class Comparer<T> where T : IComparable
 79     {
 80         public T GreaterThan(T d, T d2)
 81         {
 82             int ret = d.CompareTo(d2);
 83             if (ret > 0)
 84             {
 85                 return d;
 86             }
 87             else
 88             {
 89                 return d2;
 90             }
 91         }
 92 
 93         public T LessThan(T d, T d2)
 94         {
 95             int ret = d.CompareTo(d2);
 96             if (ret < 0)
 97             {
 98                 return d;
 99             }
100             else
101             {
102                 return d2;
103             }
104         }
105     }
106 }
View Code

    2.2、ExampleCore_3_1_7
Advanced .Net Debugging 4:基本除錯任務(物件檢查:記憶體、值型別、引用型別、陣列和異常的轉儲)
 1 namespace ExampleCore_3_1_7
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var person = new Person();
 8             Console.ReadLine();
 9         }
10     }
11     internal class Person
12     {
13         public int Age = 20;
14 
15         public string Name = "jack";
16     }
17 }
View Code

    2.3、ExampleCore_3_1_8
Advanced .Net Debugging 4:基本除錯任務(物件檢查:記憶體、值型別、引用型別、陣列和異常的轉儲)
 1 namespace ExampleCore_3_1_8
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("Welcome to .NET Advanced Debugging!");
 8 
 9             Person person = new Person() { Name = "PatrickLiu", Age = 32, HomeAddress = new Address() { Country = "China", Province = "冀州", City = "直隸總督府", Region = "廣平大街23號", PostalCode = "213339" } };
10 
11             Console.WriteLine($"名稱:{person.Name},地址:{person.HomeAddress}");
12 
13             Console.Read();
14         }
15     }
16 
17     public class Person
18     {
19         public int Age { get; set; }
20         public string? Name { get; set; }
21         public Address? HomeAddress { get; set; }
22     }
23 
24     public class Address
25     {
26         public string? Country { get; set; }
27         public string? Province { get; set; }
28         public string? City { get; set; }
29         public string? Region { get; set; }
30         public string? PostalCode { get; set; }
31         public override string ToString()
32         {
33             return $"{Country}-{Province}-{City}-{Region}-{PostalCode}";
34         }
35     }
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 0:009> ~0s
2 coreclr!GetThreadNULLOk+0x1e [inlined in coreclr!CrstBase::Enter+0x32]:
3 00007ff9`bd119fa2 488b34c8        mov     rsi,qword ptr [rax+rcx*8] ds:0000026c`600cb0b0=0000026c600ba2d0

                    使用【!clrstack -a】檢視託管程式碼的執行緒呼叫棧。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x954 (0)
 3         Child SP               IP Call Site
 4 000000785C77DCE8 00007ff9bd119fa2 [ExternalMethodFrame: 000000785c77dce8]
 5 ......(省略了)
 6 000000785C77E4D0 00007FF95D621987 ExampleCore_3_1_7.Program.Main(System.String[])
 7     PARAMETERS:
 8         args (0x000000785C77E520) = 0x0000026c64808ea0
 9     LOCALS:
10         0x000000785C77E508 = 0x0000026c64809640

                    0x0000026c64809640 紅色標註的就是 Person 型別的區域性變數 person 的地址。我們可以使用【!dumpobj /d 0x0000026c64809640】檢視 person 的詳情。

 1 0:000> !dumpobj /d 0x0000026c64809640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff95d6d93e0(方法表地址)
 4 EEClass:     00007ff95d6e1f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff95d591188  4000001       10         System.Int32  1 instance               20 Age
11 00007ff95d60ec08  4000002        8        System.String  0 instance 000002acf67a04a0 Name
12 0:000>

                    同樣,我們可以使用【dp】命令也能看到 person 的詳情資訊,只是不是很直觀。

1 0:000> dp 0x0000026c64809640
2 0000026c`64809640  00007ff9`5d6d93e0 000002ac`f67a04a0
3 0000026c`64809650  00000000`00000014 00000000`00000000
4 0000026c`64809660  00007ff9`5d555fa8 00000000`00000000
5 0000026c`64809670  00000000`00000000 00007ff9`5d60ec08
6 0000026c`64809680  0065006b`0000000c 006c0065`006e0072
7 0000026c`64809690  0064002e`00320033 00000000`006c006c
8 0000026c`648096a0  00000000`00000000 00007ff9`5d700d68
9 0000026c`648096b0  00000000`00000000 00000000`00000000

                    上面使用【!dumpobj】和【dp】命令我們找到的方法表地址都是一樣的。000002ac`f67a04a0 這個項就是 Name 域的地址,因為 Name 是引用型別,所以這裡是一個地址。我們可以繼續使用【!dumpobj /d 000002ac`f67a04a0】來驗證。

 1 0:000> !dumpobj /d 000002ac`f67a04a0
 2 Name:        System.String
 3 MethodTable: 00007ff95d60ec08
 4 EEClass:     00007ff95d5ea500
 5 Tracked Type: false
 6 Size:        30(0x1e) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 String:      jack(我們賦的值)
 9 Fields:
10               MT    Field   Offset                 Type VT     Attr            Value Name
11 00007ff95d591188  400033b        8         System.Int32  1 instance                4 _stringLength
12 00007ff95d59b538  400033c        c          System.Char  1 instance               6a _firstChar
13 00007ff95d60ec08  400033a       c8        System.String  0   static 000002acf67a0008 Empty

                      我們可以使用【dumpobj】命令,當然也可以使用【dp】命令,都可以看到想看到的資訊,不過是【dp】可讀性差很多。
                      如果我們檢視命令如何使用,可以使用【.hh】命令。

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 0:006> ~0s
2 ntdll!NtReadFile+0x14:
3 00007ffa`9576ae54 c3              ret

                    我們檢視一下當前託管的執行緒棧,執行命令【!clrstack -a】。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x8fc (0)
 3         Child SP               IP Call Site
 4 000000D30A57E230 00007ffa9576ae54 [InlinedCallFrame: 000000d30a57e230] 
 5 000000D30A57E230 00007ff9e4f076eb [InlinedCallFrame: 000000d30a57e230] 
 6 ......(省略了)
 7 
 8 000000D30A57E580 00007ff95e471987 ExampleCore_3_1_7.Program.Main(System.String[]) [E:\Visual Studio\..\ExampleCore_3_1_7\Program.cs @ 8]
 9     PARAMETERS:
10         args (0x000000D30A57E5D0) = 0x0000021dc3808ea0
11     LOCALS:
12         0x000000D30A57E5B8 = 0x0000021dc3809640

                    0x0000021dc3809640 紅色標註的就是 Person 型別的區域性變數 person 的地址。我們直接使用【dp】命令轉儲出 person 的值。

1 0:000> dp 0x0000021dc3809640
2 0000021d`c3809640  00007ff9`5e5293e0 0000025e`558d04a0
3 0000021d`c3809650  00000000`00000014 00000000`00000000
4 0000021d`c3809660  00007ff9`5e3a5fa8 00000000`00000000
5 0000021d`c3809670  00000000`00000000 00007ff9`5e45ec08
6 0000021d`c3809680  0065006b`0000000c 006c0065`006e0072
7 0000021d`c3809690  0064002e`00320033 00000000`006c006c
8 0000021d`c38096a0  00000000`00000000 00007ff9`5e550d68
9 0000021d`c38096b0  00000000`00000000 00000000`00000000

                    最左邊的一列給出了每行記憶體的起始地址,後面是記憶體的內容。00007ff9`5e5293e0 紅色標註的就是方法表。我們可以驗證。執行命令【!dumpheap -type Person】。

1 0:000> !dumpheap -type Person
2          Address               MT           Size
3     021dc3809640     7ff95e5293e0             32 
4 
5 Statistics:
6           MT Count TotalSize Class Name
7 7ff95e5293e0     1        32 ExampleCore_3_1_7.Person
8 Total 1 objects, 32 bytes

                    7ff95e5293e000007ff9`5e5293e0 兩個值是一樣的。
                    
                    雖然直接輸出記憶體的內容很有用,但是閱讀起來就很麻煩。當我們除錯託管程式碼的時候,使用 SOS 擴充套件命令會提供更直接的資訊。
                    如果我們想檢視有關偵錯程式的各種命令。我們可以使用【.hh】幫助檔案。

1 windbg> .hh

                    同樣能開啟偵錯程式的幫助視窗。
                    

        3.1.2、值型別的轉儲
            A、基礎知識
                我們知道.NET 的型別分為值型別和引用型別,那我們如何判斷一個指標指向的是否是值型別呢,最佳的方式就是使用【dumpobj】命令,但它只對引用型別有效。【dumpobj】命令的引數是一個指向引用型別的指標,如果指標指向的是值型別,【dumpobj】命令就會輸出:<Note:this object has an invalid CLASS field>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 0:002> !clrstack -a
2 OS Thread Id: 0x1764 (2)
3 Unable to walk the managed stack. The current thread is likely not a
4 managed thread. You can run !threads to get a list of managed threads in
5 the process
6 Failed to start stack walk: 80070057

                        如圖:
                        

                        該命令執行錯誤,提示不是一個有效的託管執行緒,由於我們是手動中斷程式的執行,偵錯程式的執行緒上下文是在偵錯程式執行緒上,它是一個非託管執行緒,因此,在執行該命令之前,需要切換到託管執行緒的上下文中。執行命令【~0s】。

1 0:002> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffb`2fdaae74 c3              ret
4 0:000>

                        我們再次執行【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x2c1c (0)
 3         Child SP               IP Call Site
 4 000000CD41F7E5E8 00007ffb2fdaae74 [InlinedCallFrame: 000000cd41f7e5e8]
 5 000000CD41F7E5E8 00007ffb1b68787a [InlinedCallFrame: 000000cd41f7e5e8]
 6 。。。。。。(省略了)
 7 000000CD41F7E770 00007FF9F55919B6 ExampleCore_3_1_6.ObjTypes.Main(System.String[])
 8     PARAMETERS:
 9         args (0x000000CD41F7E840) = 0x000002a492808ea0
10     LOCALS:
11         0x000000CD41F7E820 = 0x0000006400000064
12         0x000000CD41F7E818 = 0x0000000000000000
13         0x000000CD41F7E810 = 0x0000000000000000

                        這時,【clrstack】命令輸出了託管執行緒的棧回溯,包括每個棧幀的區域性變數和引數。我們主要關注【ExampleCore_3_1_6.ObjTypes.Main】棧幀和地址【0x000000CD41F7E820】上的區域性變數。【0x000000CD41F7E820】這個地址我們不知道它指向的是一個值型別還是引用型別。我們可以使用【dumpobj】命令做一個測試,因為該命令只對引用型別例項起作用。

1 0:000> !dumpobj 0x000000CD41F7E820
2 <Note: this object has an invalid CLASS field>
3 Invalid object

                        從這個輸出結果可以知道,這個指標指向的肯定不是引用型別。我們可以透過命令【r】驗證一下,可以觀察【rsp】暫存器,它儲存的是當前棧指標。

 1 0:000> r
 2 rax=0000000000000007 rbx=000000cd41f7e550 rcx=00000000000001c8
 3 rdx=0000000000000000 rsi=0000000000000000 rdi=00000000000001c8
 4 rip=00007ffb2fdaae74 rsp=000000cd41f7e388 rbp=000000cd41f7e490
 5  r8=000000cd41f7e388  r9=000000cd41f7e490 r10=0000000000000000
 6 r11=0000000000000246 r12=000000cd41f7e8a0 r13=0000000000000004
 7 r14=0000000000000003 r15=000000cd41f7ea00
 8 iopl=0         nv up ei pl zr na po nc
 9 cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
10 ntdll!NtDeviceIoControlFile+0x14:
11 00007ffb`2fdaae74 c3              ret

                        【rsp】暫存器的值是:000000CD41F7E388,我們正在分析的地址是:0x000000CD41F7E820,連個地址非常接近,說明我們分析的地址是棧地址,也就是這個地址儲存的是值型別的值。

                        我們可以使用【dp】命令檢視一下具體的內容。

1 0:000> dp 0x000000CD41F7E820
2 000000cd`41f7e820  00000064`00000064 00000000`00000064
3 000000cd`41f7e830  000000cd`41f7e860 00007ffa`550da1a3
4 000000cd`41f7e840  000002a4`92808ea0 000000cd`41f7ee88
5 000000cd`41f7e850  000000cd`41f7ee88 000000cd`41f7ea79
6 000000cd`41f7e860  000000cd`41f7e910 00000000`0000001d
7 000000cd`41f7e870  000000cd`41f7ea88 00007ffa`550614c9
8 000000cd`41f7e880  00000000`00000000 00000000`00000130
9 000000cd`41f7e890  000000cd`41f7ea88 00007ffa`54f8a456

                        00000064 的十進位制的值就是100。後面有三個域的值是0x64。

1 0:000> ? 00000064
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 0:009> !bpmd ExampleCore_3_1_6 ExampleCore_3_1_6.ObjTypes.AddCoordinate
2 MethodDesc = 00007FF9F5630108
3 Adding pending breakpoints...

                        我們繼續執行偵錯程式。

0:000> g
(131c.3ca0): CLR notification exception - code e0444143 (first chance)
JITTED ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)
Setting breakpoint: bp 00007FF9F43B1CD0 [ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)]
。。。

                        重新設定斷點,除非並中斷執行。我們執行【!clrstack -a】命令檢視一下託管執行緒棧。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3df8 (0)
 3         Child SP               IP Call Site
 4 00000085A277E468 00007ffb2d7f9202 [HelperMethodFrame: 00000085a277e468] System.Diagnostics.Debugger.BreakInternal()
 5 00000085A277E570 00007FFA4FC654DA System.Diagnostics.Debugger.Break()
 6 
 7 00000085A277E5A0 00007FF9F0AB1D1A ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)
 8     PARAMETERS:
 9         this (0x00000085A277E600) = 0x00000264de809ce0
10         coord (0x00000085A277E628) = 0x0000006400000064
11 
12 00000085A277E600 00007FF9F0AB1A11 ExampleCore_3_1_6.ObjTypes.Main(System.String[])
13     PARAMETERS:
14         args (0x00000085A277E6D0) = 0x00000264de808ea0
15     LOCALS:
16         0x00000085A277E6B0 = 0x0000006400000064
17         0x00000085A277E6A8 = 0x00000264de809ce0
18         0x00000085A277E6A0 = 0x0000000000000000

                        我們主要關注【ExampleCore_3_1_6.ObjTypes.AddCoordinate】棧幀和【this】指標。【this】指標指向的是當前的引用型別例項。我們可以使用【dumpobj】命令輸出看一下。

 1 0:000> !dumpobj /d 0x00000264de809ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9f0b60238
 4 EEClass:     00007ff9f0b4fcb8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 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 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9f0b601c8  4000001       18 ...jTypes+Coordinate  1 instance 00000264de809cf8 coordinate
11 00007ff9f0a99df8  4000002        8       System.Int32[]  0 instance 00000264de809d10 intArray
12 00007ff9f0b3fac8  4000003       10      System.String[]  0 instance 00000264de809d80 strArray

                        紅色標註的【coordinate】就是引用型別包含一個值型別的例子。【MT、Field、Offset、Type、VT、Attr、Value和 Name】這些域具體的意思,可以檢視圖表。
                        

                        我們有兩種方式可以檢視【coordinate】變數的具體值。第一種,使用【dp】命令。命令的引數分別是引用型別物件的地址(0x00000264de809ce0)和偏移(0x18)。

1 0:000> dp 0x00000264de809ce0+0x18
2 00000264`de809cf8  00000000`00000000 00000000`00000000
3 00000264`de809d08  00000000`00000000 00007ff9`f0a99df8
4 00000264`de809d18  00000000`00000005 00000002`00000001
5 00000264`de809d28  00000004`00000003 00000000`00000005
6 00000264`de809d38  00000000`00000000 00007ff9`f0bb4468
7 00000264`de809d48  00000000`00000000 00000000`00000000
8 00000264`de809d58  00000000`00000000 00000000`00000000
9 00000264`de809d68  00000000`00000000 00007ff9`f0bb3de0

                        第二種就是使用【!dumpvc mt addr】直接檢視值型別,更直接。

 1 0:000> !dumpvc 00007ff9f0b601c8  00000264de809cf8
 2 Name:        ExampleCore_3_1_6.ObjTypes+Coordinate
 3 MethodTable: 00007ff9f0b601c8
 4 EEClass:     00007ff9f0b4fd30
 5 Size:        32(0x20) bytes
 6 File:        E:\Visual Studio 2022\Source\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 7 Fields:
 8               MT    Field   Offset                 Type VT     Attr            Value Name
 9 00007ff9f0a21188  4000005        0         System.Int32  1 instance                0 xCord
10 00007ff9f0a21188  4000006        4         System.Int32  1 instance                0 yCord
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 0:001> !clrstack -a
2 OS Thread Id: 0x239c (1)
3 Unable to walk the managed stack. The current thread is likely not a 
4 managed thread. You can run !clrthreads to get a list of managed threads in
5 the process
6 Failed to start stack walk: 80070057

                        【clrstack】命令出錯了,錯誤原因是當前的執行緒上下文並不是一個有效的託管執行緒。由於我們手動的中斷程式的執行,偵錯程式的執行緒上下文是在偵錯程式執行緒上,而這個執行緒是非託管的執行緒。因此,在執行【clrstack】命令之前,首先必須切換到託管執行緒上下文。使用【~】命令將上下文切換到執行緒0。

1 0:001> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`9576ae74 c3              ret

                        然後,再次執行【clrstack】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x584 (0)
 3         Child SP               IP Call Site
 4 000000F06737E948 00007ffa9576ae74 [InlinedCallFrame: 000000f06737e948] 
 5 000000F06737E948 00007ffa4f72787a [InlinedCallFrame: 000000f06737e948] 
 6 ......(省略了)
 7 
 8 000000F06737EAD0 00007ff95dd019b6 ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 29]
 9     PARAMETERS:
10         args (0x000000F06737EBA0) = 0x0000020f54808ea0
11     LOCALS:
12         0x000000F06737EB80 = 0x0000006400000064
13         0x000000F06737EB78 = 0x0000000000000000
14         0x000000F06737EB70 = 0x0000000000000000

                        這就是【clrstack】命令輸出的託管執行緒的棧回溯,包括每個棧幀的區域性變數和引數。在呼叫棧中,我們主要關注的是 Main 棧幀和位於地址 0x000000F06737EB80 上的區域性變數(紅色標註的)。
                        由於我們不知道這個區域性變數指向的是值型別還是引用型別,因此我們可以使用【dumpobj】做一下判斷。

1 0:000> !dumpobj 0x000000F06737EB80
2 <Note: this object has an invalid CLASS field>
3 Invalid object

                        從命令的輸出結果中可以看出,該地址的肯定不是引用型別。我們來驗證,值型別被儲存在棧上,如果發現某個地址位於當前棧指標的附近,那就可以證明是值型別了。我們使用【r】命令,觀察 rsp 暫存器(儲存的是當前棧指標)。

 1 0:000> r
 2 rax=0000000000000007 rbx=000000f06737e8b0 rcx=0000000000000058
 3 rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000058
 4 rip=00007ffa9576ae74 rsp=000000f06737e6e8 rbp=000000f06737e7f0
 5  r8=000000f06737e6e8  r9=000000f06737e7f0 r10=0000000000000000
 6 r11=0000000000000130 r12=000000f06737ec00 r13=0000000000000004
 7 r14=0000000000000003 r15=000000f06737ed60
 8 iopl=0         nv up ei pl zr na po nc
 9 cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
10 ntdll!NtDeviceIoControlFile+0x14:
11 00007ffa`9576ae74 c3              ret

                        在 rsp 暫存器中包含的值:000000f06737e6e8,它非常接近我們分析的地址:0x000000F06737EB80。說明我們分析的地址就是一個棧地址,我們使用【dp】檢視一下詳情。

1 0:000> dp 0x000000F06737EB80
2 000000f0`6737eb80  00000064`00000064 00000000`00000064
3 000000f0`6737eb90  000000f0`6737ebc0 00007ff9`bd85a1a3
4 000000f0`6737eba0  0000020f`54808ea0 000000f0`6737f1e8
5 000000f0`6737ebb0  000000f0`6737f1e8 000000f0`6737edd9
6 000000f0`6737ebc0  000000f0`6737ec70 00000000`0000001d
7 000000f0`6737ebd0  000000f0`6737ede8 00007ff9`bd7e14c9
8 000000f0`6737ebe0  00000000`00000000 00000000`00000130
9 000000f0`6737ebf0  000000f0`6737ede8 00007ff9`bd70a456

                        00000064 紅色標註的十進位制的值就是100。

1 0:000> ? 00000064
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 0:007> !bpmd ExampleCore_3_1_6 ExampleCore_3_1_6.ObjTypes.AddCoordinate
2 MethodDesc = 00007FF9EDAA0108
3 Adding pending breakpoints...

                        斷點設定好後,我們繼續執行偵錯程式,執行命令【g】。

1 0:007> g
2 (3310.3864): CLR notification exception - code e0444143 (first chance)
3 JITTED ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)
4 Setting breakpoint: bp 00007FF9ED9F1CE0 [ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate)]
5 Breakpoint 0 hit
6 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.AddCoordinate:
7 00007ff9`ed9f1ce0 55              push    rbp

                        當前我們的偵錯程式執行緒上下文也自動切換到託管執行緒的上下文,可以直接執行【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3864 (0)
 3         Child SP               IP Call Site
 4 000000DEBDF7E968 00007ff9ed9f1ce0 ExampleCore_3_1_6.ObjTypes.AddCoordinate(Coordinate) [E:\Visual Studio\.\ExampleCore_3_1_6\Program.cs @ 48]
 5     PARAMETERS:
 6         this (<CLR reg>) = 0x0000027eb4409ce0
 7         coord (<CLR reg>) = 0x000000debdf7e998
 8 
 9 000000DEBDF7E970 00007ff9ed9f1a11 ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\..\ExampleCore_3_1_6\Program.cs @ 31]
10     PARAMETERS:
11         args (0x000000DEBDF7EA40) = 0x0000027eb4408ea0
12     LOCALS:
13         0x000000DEBDF7EA20 = 0x0000006400000064
14         0x000000DEBDF7EA18 = 0x0000027eb4409ce0
15         0x000000DEBDF7EA10 = 0x0000000000000000

                        0x0000027eb4409ce0 紅色標註的是 this 指標。this 指標指向當前的物件例項。我們使用【!dumpobj】命令檢視一下 this 指標。

 1 0:000> !dumpobj /d 0x0000027eb4409ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9edaa0238
 4 EEClass:     00007ff9eda8fca8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9edaa01c8  4000001       18 ...jTypes+Coordinate  1 instance 0000027eb4409cf8 coordinate
11 00007ff9ed9d9df8  4000002        8       System.Int32[]  0 instance 0000027eb4409d10 intArray
12 00007ff9eda7fac8  4000003       10      System.String[]  0 instance 0000027eb4409d80 strArray

                   Fields 是最重要的資訊,它包含了這個物件的後設資料。各個域的表示資訊可以看下錶。
                   
                   如果要顯示這個域本身的內容,有兩種方式。其中一種是使用【dp】命令,引數是引用型別例項的地址(000001d9b1c09ce0)和偏移(0x18)。

1 0:000> dp 000001d9b1c09ce0+0x18
2 000001d9`b1c09cf8  00000000`00000000 00000000`00000000
3 000001d9`b1c09d08  00000000`00000000 00007ff9`ebf49df8
4 000001d9`b1c09d18  00000000`00000005 00000002`00000001
5 000001d9`b1c09d28  00000004`00000003 00000000`00000005
6 000001d9`b1c09d38  00000000`00000000 00007ff9`ec064468
7 000001d9`b1c09d48  00000000`00000000 00000000`00000000
8 000001d9`b1c09d58  00000000`00000000 00000000`00000000
9 000001d9`b1c09d68  00000000`00000000 00007ff9`ec063de0

                   如果我們想檢視【...jTypes+Coordinate】的具體的值,可以執行命令【!dumpvc mt addr】。

 1 0:000> !dumpvc 00007ff9edaa01c8 0000027eb4409cf8
 2 Name:        ExampleCore_3_1_6.ObjTypes+Coordinate
 3 MethodTable: 00007ff9edaa01c8
 4 EEClass:     00007ff9eda8fd20
 5 Size:        32(0x20) bytes
 6 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 7 Fields:
 8               MT    Field   Offset                 Type VT     Attr            Value Name
 9 00007ff9ed961188  4000005        0         System.Int32  1 instance                0 xCord
10 00007ff9ed961188  4000006        4         System.Int32  1 instance                0 yCord
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 0:002> ~0s
2 coreclr!LookupMap<MethodTable *>::GetValueAt+0x3 [inlined in coreclr!ClassLoader::LoadTypeDefThrowing+0x89]:
3 00007ffa`536a5c49 48f7d2          not     rdx

                    現在就可以使用【!clrstack -a】命令檢視託管執行緒棧了。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x290c (0)
 3         Child SP               IP Call Site
 4 00000068AEBEDE68 00007ffa536a5c49 [ExternalMethodFrame: 00000068aebede68]
 5 。。。。。。(省略了)
 6 00000068AEBEE650 00007FF9F3C51987 ExampleCore_3_1_7.Program.Main(System.String[])
 7     PARAMETERS:
 8         args (0x00000068AEBEE6A0) = 0x000001afc2408ea0
 9     LOCALS:
10         0x00000068AEBEE688 = 0x000001afc2409640
                      0x000001afc2409640 紅色標註的就是 Person 型別的區域性變數 person。
                      廢話不多說,直接使用【!dumpobj】或者【!do】命令檢視 Person 的詳情了。
 1 0:000> !dumpobj 0x000001afc2409640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff9f3d093e0
 4 EEClass:     00007ff9f3d11f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9f3bc1188  4000001       10         System.Int32  1 instance               20 Age
11 00007ff9f3c3ec08  4000002        8        System.String  0 instance 000001f0546704a0 Name
12 
13 
14 0:000> !do 0x000001afc2409640
15 Name:        ExampleCore_3_1_7.Person
16 MethodTable: 00007ff9f3d093e0
17 EEClass:     00007ff9f3d11f18
18 Tracked Type: false
19 Size:        32(0x20) bytes
20 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
21 Fields:
22               MT    Field   Offset                 Type VT     Attr            Value Name
23 00007ff9f3bc1188  4000001       10         System.Int32  1 instance               20 Age
24 00007ff9f3c3ec08  4000002        8        System.String  0 instance 000001f0546704a0 Name

                    如果不想輸出【Fields】的內容,可以使用 -nofields 命令開關。

 1 0:000> !dumpobj -nofields 0x000001afc2409640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff9f3d093e0
 4 EEClass:     00007ff9f3d11f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 
 9 0:000> !do -nofields 0x000001afc2409640
10 Name:        ExampleCore_3_1_7.Person
11 MethodTable: 00007ff9f3d093e0
12 EEClass:     00007ff9f3d11f18
13 Tracked Type: false
14 Size:        32(0x20) bytes
15 File:        E:\Visual Studio 2022\...ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
16 0:000>

                2)、Windbg Preview 除錯
                    除錯原始碼:ExampleCore_3_1_7
                    編譯專案,開啟 Windbg,點選【檔案】----》【launch executable】附加程式,開啟偵錯程式的介面,程式已經處於中斷狀態。我們使用【g】命令,繼續執行程式,我們的偵錯程式也處於卡住的狀態。此時點選【break】按鈕,就可以除錯程式了。
                    由於我們手動中斷了偵錯程式,當前的執行緒上下文是偵錯程式的,不是託管執行緒的,我們必須使用【~0s】命令,切換到託管執行緒。
1 0:006> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffb`2fdaae74 c3              ret

                    檢視當前的託管執行緒的呼叫棧,使用【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x99c (0)
 3         Child SP               IP Call Site
 4 0000006E2117E200 00007ffb2fdaae54 [InlinedCallFrame: 0000006e2117e200] 
 5 0000006E2117E200 00007ffaeadb76eb [InlinedCallFrame: 0000006e2117e200] 
 6 。。。。。。(省略了)
 7 
 8 0000006E2117E550 00007ff9f3461987 ExampleCore_3_1_7.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_7\Program.cs @ 8]
 9     PARAMETERS:
10         args (0x0000006E2117E5A0) = 0x000001da19808ea0
11     LOCALS:
12         0x0000006E2117E588 = 0x000001da19809640

                    0x000001da19809640 這個地址就是引用型別 Person 的區域性變數 person。我們可以直接使用【!dumpobj】或者【!do】命令。

 1 0:000> !DumpObj /d 000001da19809640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff9f35193e0
 4 EEClass:     00007ff9f3521f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9f33d1188  4000001       10         System.Int32  1 instance               20 Age
11 00007ff9f344ec08  4000002        8        System.String  0 instance 0000021aab9904a0 Name
12 
13 
14 0:000> !do 000001da19809640
15 Name:        ExampleCore_3_1_7.Person
16 MethodTable: 00007ff9f35193e0
17 EEClass:     00007ff9f3521f18
18 Tracked Type: false
19 Size:        32(0x20) bytes
20 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
21 Fields:
22               MT    Field   Offset                 Type VT     Attr            Value Name
23 00007ff9f33d1188  4000001       10         System.Int32  1 instance               20 Age
24 00007ff9f344ec08  4000002        8        System.String  0 instance 0000021aab9904a0 Name

                    我們可以使用 -nofields 命令開關去掉域的內容。

 1 0:000> !DumpObj -nofields 000001da19809640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff9f35193e0
 4 EEClass:     00007ff9f3521f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 
 9 
10 0:000> !do -nofields 000001da19809640
11 Name:        ExampleCore_3_1_7.Person
12 MethodTable: 00007ff9f35193e0
13 EEClass:     00007ff9f3521f18
14 Tracked Type: false
15 Size:        32(0x20) bytes
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 0:002> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`eeaeae74 c3              ret

                        現在,我們就可以檢視託管執行緒棧了,執行命令【!clrstack -a】。

0:000> !clrstack -a
OS Thread Id: 0x3394 (0)
        Child SP               IP Call Site
00000090F577E5E8 00007ffaeeaeae74 [InlinedCallFrame: 00000090f577e5e8]
00000090F577E5E8 00007ffadb81787a [InlinedCallFrame: 00000090f577e5e8]
。。。。。。(省略了)

00000090F577E770 00007FF9BE1E1A2D ExampleCore_3_1_6.ObjTypes.Main(System.String[])
    PARAMETERS:
        args (0x00000090F577E840) = 0x000002282e008ea0
    LOCALS:
        0x00000090F577E820 = 0x0000006400000064
        0x00000090F577E818 = 0x000002282e009ce0
        0x00000090F577E810 = 0x0000000000000000

                        0x000002282e009ce0 紅色標註的就是 ObjTypes 型別的指標,我們可以使用【!do】命令檢視它。

 1 0:000> !do 0x000002282e009ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9be290238
 4 EEClass:     00007ff9be27fca8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9be2901c8  4000001       18 ...jTypes+Coordinate  1 instance 000002282e009cf8 coordinate
11 00007ff9be1c9df8  4000002        8       System.Int32[]  0 instance 000002282e009d10 intArray
12 00007ff9be26fac8  4000003       10      System.String[]  0 instance 000002282e009d80 strArray
13 0:000>

                        這裡我們開始關注 intArray 值型別陣列,也就是紅色標註的。intArray 陣列的地址是:000002282e009d10,有了地址,我們就可以使用【!dumpobj 000002282e009d10】命令轉儲出詳情。

1 0:000> !dumpobj 000002282e009d10
2 Name:        System.Int32[]
3 MethodTable: 00007ff9be1c9df8
4 EEClass:     00007ff9be1c9d78
5 Tracked Type: false
6 Size:        44(0x2c) bytes
7 Array:       Rank 1, Number of elements 5, Type Int32
8 Fields:
9 None

                        驗證了我們的說法,輸出了陣列的名稱,維度(Rank),元素個數(Number of elements)等。我們依然可以使用【dp 000002282e009d10】命令輸出詳情。
                        

                        我們使用【!dumpmt 00007ff9`be1c9df8】命令是不是陣列的方法表。

 1 0:000> !dumpmt 00007ff9`be1c9df8
 2 EEClass:         00007FF9BE1C9D78
 3 Module:          00007FF9BE084000
 4 Name:            System.Int32[]
 5 mdToken:         0000000002000000
 6 File:            C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 7 BaseSize:        0x18
 8 ComponentSize:   0x4
 9 DynamicStatics:  false
10 ContainsPointers false
11 Slots in VTable: 28
12 Number of IFaces in IFaceMap: 6

                        當然,我們可以直接使用【!dumparray 000002282e009d10】命令檢視 intArray 陣列詳情,這個命令更直觀。

 1 0:000> !dumparray 000002282e009d10
 2 Name:        System.Int32[]
 3 MethodTable: 00007ff9be1c9df8
 4 EEClass:     00007ff9be1c9d78
 5 Size:        44(0x2c) bytes
 6 Array:       Rank 1, Number of elements 5, Type Int32
 7 Element Methodtable: 00007ff9be151188
 8 [0] 000002282e009d20
 9 [1] 000002282e009d24
10 [2] 000002282e009d28
11 [3] 000002282e009d2c
12 [4] 000002282e009d30
13 
14 0:000> !dumpvc 00007ff9be151188 000002282e009d20
15 Name:        System.Int32
16 MethodTable: 00007ff9be151188
17 EEClass:     00007ff9be141e20
18 Size:        24(0x18) bytes
19 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
20 Fields:
21               MT    Field   Offset                 Type VT     Attr            Value Name
22 00007ff9be151188  400051d        0         System.Int32  1 instance                1 m_value
23 
24 0:000> !dumpvc 00007ff9be151188 000002282e009d24
25 Name:        System.Int32
26 MethodTable: 00007ff9be151188
27 EEClass:     00007ff9be141e20
28 Size:        24(0x18) bytes
29 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
30 Fields:
31               MT    Field   Offset                 Type VT     Attr            Value Name
32 00007ff9be151188  400051d        0         System.Int32  1 instance                2 m_value
33 
34 0:000> !dumpvc 00007ff9be151188 000002282e009d28
35 Name:        System.Int32
36 MethodTable: 00007ff9be151188
37 EEClass:     00007ff9be141e20
38 Size:        24(0x18) bytes
39 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
40 Fields:
41               MT    Field   Offset                 Type VT     Attr            Value Name
42 00007ff9be151188  400051d        0         System.Int32  1 instance                3 m_value
43 
44 0:000> !dumpvc 00007ff9be151188 000002282e009d2c
45 Name:        System.Int32
46 MethodTable: 00007ff9be151188
47 EEClass:     00007ff9be141e20
48 Size:        24(0x18) bytes
49 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
50 Fields:
51               MT    Field   Offset                 Type VT     Attr            Value Name
52 00007ff9be151188  400051d        0         System.Int32  1 instance                4 m_value
53 
54 0:000> !dumpvc 00007ff9be151188 000002282e009d30
55 Name:        System.Int32
56 MethodTable: 00007ff9be151188
57 EEClass:     00007ff9be141e20
58 Size:        24(0x18) bytes
59 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
60 Fields:
61               MT    Field   Offset                 Type VT     Attr            Value Name
62 00007ff9be151188  400051d        0         System.Int32  1 instance                5 m_value
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 0:002> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
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 
 8 00000090F577E770 00007FF9BE1E1A2D ExampleCore_3_1_6.ObjTypes.Main(System.String[])
 9     PARAMETERS:
10         args (0x00000090F577E840) = 0x000002282e008ea0
11     LOCALS:
12         0x00000090F577E820 = 0x0000006400000064
13         0x00000090F577E818 = 0x000002282e009ce0
14         0x00000090F577E810 = 0x0000000000000000

                        0x000002282e009ce0 紅色標註的就是 ObjTypes 型別的指標,我們可以使用【!do】命令檢視它。

 1 0:000> !do 0x000002282e009ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9be290238
 4 EEClass:     00007ff9be27fca8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 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 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9be2901c8  4000001       18 ...jTypes+Coordinate  1 instance 000002282e009cf8 coordinate
11 00007ff9be1c9df8  4000002        8       System.Int32[]  0 instance 000002282e009d10 intArray
12 00007ff9be26fac8  4000003       10      System.String[]  0 instance 000002282e009d80 strArray

                        這裡我們開始關注 strArray 引用型別陣列,也就是紅色標註的。我們先使用【dp 000002282e009d80】命令轉儲一下。
                        

                        執行【!dumpmt 00007ff9`be26fac8】命令檢視 strArray 陣列的方法表。

 1 0:000> !dumpmt 00007ff9`be26fac8
 2 EEClass:         00007FF9BE11C440
 3 Module:          00007FF9BE084000
 4 Name:            System.String[]
 5 mdToken:         0000000002000000
 6 File:            C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 7 BaseSize:        0x18
 8 ComponentSize:   0x8
 9 DynamicStatics:  false
10 ContainsPointers true
11 Slots in VTable: 28
12 Number of IFaces in IFaceMap: 6

                        我們可以使用【!dumpobj】命令檢視檢視元素的具體資訊。

 1 0:000> !do -nofields 00000268`c0280b78
 2 Name:        System.String
 3 MethodTable: 00007ff9be1cec08
 4 EEClass:     00007ff9be1aa500
 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 String:      Welcome
 9 
10 0:000> !do -nofields 00000268`c0280ba0
11 Name:        System.String
12 MethodTable: 00007ff9be1cec08
13 EEClass:     00007ff9be1aa500
14 Tracked Type: false
15 Size:        26(0x1a) bytes
16 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
17 String:      to
18 
19 0:000> !do -nofields 00000268`c0280bc0
20 Name:        System.String
21 MethodTable: 00007ff9be1cec08
22 EEClass:     00007ff9be1aa500
23 Tracked Type: false
24 Size:        38(0x26) bytes
25 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
26 String:      Advanced
27 
28 0:000> !do -nofields 00000268`c0280be8
29 Name:        System.String
30 MethodTable: 00007ff9be1cec08
31 EEClass:     00007ff9be1aa500
32 Tracked Type: false
33 Size:        30(0x1e) bytes
34 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
35 String:      .NET
36 
37 0:000> !do -nofields 00000268`c0280c08
38 Name:        System.String
39 MethodTable: 00007ff9be1cec08
40 EEClass:     00007ff9be1aa500
41 Tracked Type: false
42 Size:        40(0x28) bytes
43 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
44 String:      Debugging

                        當然,還有一個更容易,更直接的命令,就是【!dumparray】可以輸出所有陣列的資訊。
                        

                        內容很簡單,就不贅述了。

1 0:000> !do -nofields 00000268c0280b78
2 Name:        System.String
3 MethodTable: 00007ff9be1cec08
4 EEClass:     00007ff9be1aa500
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 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 0:001> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`eeaeae74 c3              ret

                        現在我們需要檢視一下託管執行緒的呼叫棧,執行命令【!clrstack -a】。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3144 (0)
 3         Child SP               IP Call Site
 4 0000000C91F7E668 00007ffaeeaeae74 [InlinedCallFrame: 0000000c91f7e668] 
 5 0000000C91F7E668 00007ffa375c787a [InlinedCallFrame: 0000000c91f7e668] 
 6 。。。。。。(省略了)
 7 
 8 0000000C91F7E7F0 00007ff9b6fa1a2d ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 36]
 9     PARAMETERS:
10         args (0x0000000C91F7E8C0) = 0x000001cfe3408ea0
11     LOCALS:
12         0x0000000C91F7E8A0 = 0x0000006400000064
13         0x0000000C91F7E898 = 0x000001cfe3409ce0
14         0x0000000C91F7E890 = 0x0000000000000000

                        0x000001cfe3409ce0 標紅的就是 ObjTypes 型別的指標,執行命令【!do 0x000001cfe3409ce0】確認一下。

 1 0:000> !do 0x000001cfe3409ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9b7050238
 4 EEClass:     00007ff9b703fca8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9b70501c8  4000001       18 ...jTypes+Coordinate  1 instance 000001cfe3409cf8 coordinate
11 00007ff9b6f89df8  4000002        8       System.Int32[]  0 instance 000001cfe3409d10 intArray
12 00007ff9b702fac8  4000003       10      System.String[]  0 instance 000001cfe3409d80 strArray

                        我們看到了 ExampleCore_3_1_6.ObjTypes 型別有兩個陣列的域,分別是:intArray 和 strArray。我們獲取了 intArray陣列的地址:000001cfe3409d10 ,我們使用【!dumpobj 000001cfe3409d10】命令檢視一下 intArray 陣列的詳情。

1 0:000> !dumpobj 000001cfe3409d10
2 Name:        System.Int32[]
3 MethodTable: 00007ff9b6f89df8
4 EEClass:     00007ff9b6f89d78
5 Tracked Type: false
6 Size:        44(0x2c) bytes
7 Array:       Rank 1, Number of elements 5, Type Int32 (Print Array)
8 Fields:
9 None

                        【!dumpobj】命令輸出了陣列的名稱(Name),方法表(MethodTable),陣列的維度(Rank),陣列的型別(Type)和陣列的元素個數(Number of elements),但是我們沒有看到陣列的元素內容。如果想檢視陣列的內容,我們可以使用【dp】命令。

1 0:000> dp 000001cfe3409d10
2 000001cf`e3409d10  00007ff9`b6f89df8 00000000`00000005
3 000001cf`e3409d20  00000002`00000001 00000004`00000003
4 000001cf`e3409d30  00000000`00000005 00000000`00000000
5 000001cf`e3409d40  00007ff9`b70a4468 00000000`00000000
6 000001cf`e3409d50  00000000`00000000 00000000`00000000
7 000001cf`e3409d60  00000000`00000000 00000000`00000000
8 000001cf`e3409d70  00007ff9`b70a3de0 00000000`00000000
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 0:000> !dumpmt 00007ff9`b6f89df8
 2 EEClass:             00007ff9b6f89d78
 3 Module:              00007ff9b6e44000
 4 Name:                System.Int32[]
 5 mdToken:             0000000002000000
 6 File:                C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 7 AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
 8 BaseSize:            0x18
 9 ComponentSize:       0x4
10 DynamicStatics:      false
11 ContainsPointers:    false
12 Slots in VTable:     28
13 Number of IFaces in IFaceMap: 6                     

                    2.2)、引用型別陣列
                        編譯專案,開啟 Windbg,點選【檔案】----》【launch executable】附加程式,開啟偵錯程式的介面,程式已經處於中斷狀態。我們使用【g】命令,繼續執行程式,直到我們的控制檯程式輸出:Press any key to continue(Arrays),我們的偵錯程式也處於卡住的狀態。此時點選【break】按鈕,就可以除錯程式了。
                        由於我們手動中斷偵錯程式的執行,所以需要切換到託管執行緒的上下文中,執行命令【~0s】。
1 0:001> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`eeaeae74 c3              ret

                        現在我們需要檢視一下託管執行緒的呼叫棧,執行命令【!clrstack -a】。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3144 (0)
 3         Child SP               IP Call Site
 4 0000000C91F7E668 00007ffaeeaeae74 [InlinedCallFrame: 0000000c91f7e668] 
 5 0000000C91F7E668 00007ffa375c787a [InlinedCallFrame: 0000000c91f7e668] 
 6 。。。。。。(省略了)
 7 
 8 0000000C91F7E7F0 00007ff9b6fa1a2d ExampleCore_3_1_6.ObjTypes.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_6\Program.cs @ 36]
 9     PARAMETERS:
10         args (0x0000000C91F7E8C0) = 0x000001cfe3408ea0
11     LOCALS:
12         0x0000000C91F7E8A0 = 0x0000006400000064
13         0x0000000C91F7E898 = 0x000001cfe3409ce0
14         0x0000000C91F7E890 = 0x0000000000000000

                        0x000001cfe3409ce0 標紅的就是 ObjTypes 型別的指標,執行命令【!do 0x000001cfe3409ce0】確認一下。

 1 0:000> !do 0x000001cfe3409ce0
 2 Name:        ExampleCore_3_1_6.ObjTypes
 3 MethodTable: 00007ff9b7050238
 4 EEClass:     00007ff9b703fca8
 5 Tracked Type: false
 6 Size:        48(0x30) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff9b70501c8  4000001       18 ...jTypes+Coordinate  1 instance 000001cfe3409cf8 coordinate
11 00007ff9b6f89df8  4000002        8       System.Int32[]  0 instance 000001cfe3409d10 intArray
12 00007ff9b702fac8  4000003       10      System.String[]  0 instance 000001cfe3409d80 strArray
                        我們看到了 ExampleCore_3_1_6.ObjTypes 型別有兩個陣列的域,分別是:intArray 和 strArray。這次我們關注 strArray 字串資料,我們獲取了 strArray 陣列的地址:000001cfe3409d80 ,我們使用【!dumpobj 000001cfe3409d80】命令檢視一下 strArray 陣列的詳情。
1 0:000> !dumpobj /d 000001cfe3409d80
2 Name:        System.String[]
3 MethodTable: 00007ff9b702fac8
4 EEClass:     00007ff9b6edc440
5 Tracked Type: false
6 Size:        64(0x40) bytes
7 Array:       Rank 1, Number of elements 5, Type CLASS (Print Array)
8 Fields:
9 None

                        我們也可以使用【dp 000001cfe3409d80】命令檢視它的顯示。

0:000> dp 000001cfe3409d80
000001cf`e3409d80  00007ff9`b702fac8 00000000`00000005
000001cf`e3409d90  00000210`75660b78 00000210`75660ba0
000001cf`e3409da0  00000210`75660bc0 00000210`75660be8
000001cf`e3409db0  00000210`75660c08 00000000`00000000
000001cf`e3409dc0  00007ff9`b6f11188 00000000`00000064
000001cf`e3409dd0  00000000`00000000 00007ff9`b6f11188
000001cf`e3409de0  00000000`00000064 00000000`00000000
000001cf`e3409df0  00007ff9`b6f11188 00000000`00000064

                        00007ff9`b702fac8 就是 strArray 陣列的方法表,我們可以使用【!dumpmt 00007ff9`b702fac8】驗證這點。

 1 0:000> !dumpmt 00007ff9`b702fac8
 2 EEClass:             00007ff9b6edc440
 3 Module:              00007ff9b6e44000
 4 Name:                System.String[]
 5 mdToken:             0000000002000000
 6 File:                C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 7 AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
 8 BaseSize:            0x18
 9 ComponentSize:       0x8
10 DynamicStatics:      false
11 ContainsPointers:    true
12 Slots in VTable:     28
13 Number of IFaces in IFaceMap: 6

                        【dp 000001cfe3409d80】命令的 00000000`00000005 這個值就是 strArray 陣列的元素個數。【dp 000001cfe3409d80】命令的5個輸出就是陣列元素的值,由於它們是引用型別,我們可以依次使用【!dumpobj】命令輸出它們的內容。

0:000> !dumpobj 00000210`75660b78
Name:        System.String
MethodTable: 00007ff9b6f8ec08
EEClass:     00007ff9b6f6a500
Tracked Type: false
Size:        36(0x24) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
String:      Welcome
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9b6f11188  400033b        8         System.Int32  1 instance                7 _stringLength
00007ff9b6f1b538  400033c        c          System.Char  1 instance               57 _firstChar
00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty

0:000> !dumpobj 00000210`75660ba0
Name:        System.String
MethodTable: 00007ff9b6f8ec08
EEClass:     00007ff9b6f6a500
Tracked Type: false
Size:        26(0x1a) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
String:      to
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9b6f11188  400033b        8         System.Int32  1 instance                2 _stringLength
00007ff9b6f1b538  400033c        c          System.Char  1 instance               74 _firstChar
00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty

0:000> !dumpobj 00000210`75660bc0
Name:        System.String
MethodTable: 00007ff9b6f8ec08
EEClass:     00007ff9b6f6a500
Tracked Type: false
Size:        38(0x26) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
String:      Advanced
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9b6f11188  400033b        8         System.Int32  1 instance                8 _stringLength
00007ff9b6f1b538  400033c        c          System.Char  1 instance               41 _firstChar
00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty

0:000> !dumpobj 00000210`75660be8
Name:        System.String
MethodTable: 00007ff9b6f8ec08
EEClass:     00007ff9b6f6a500
Tracked Type: false
Size:        30(0x1e) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
String:      .NET
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9b6f11188  400033b        8         System.Int32  1 instance                4 _stringLength
00007ff9b6f1b538  400033c        c          System.Char  1 instance               2e _firstChar
00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty

0:000> !dumpobj 00000210`75660c08
Name:        System.String
MethodTable: 00007ff9b6f8ec08
EEClass:     00007ff9b6f6a500
Tracked Type: false
Size:        40(0x28) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
String:      Debugging
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9b6f11188  400033b        8         System.Int32  1 instance                9 _stringLength
00007ff9b6f1b538  400033c        c          System.Char  1 instance               44 _firstChar
00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty

                        紅色標註的就是陣列元素具體的值。
                        上面的命令有些繁瑣,我們可以直接使用【!dumparray】命令獲取我們想要的資料。
                        我們直接輸出 intArray,使用【!dumparray】命令。

 1 0:000> !dumparray 000001cfe3409d10
 2 Name:        System.Int32[]
 3 MethodTable: 00007ff9b6f89df8
 4 EEClass:     00007ff9b6f89d78
 5 Size:        44(0x2c) bytes
 6 Array:       Rank 1, Number of elements 5, Type Int32
 7 Element Methodtable: 00007ff9b6f11188
 8 [0] 000001cfe3409d20
 9 [1] 000001cfe3409d24
10 [2] 000001cfe3409d28
11 [3] 000001cfe3409d2c
12 [4] 000001cfe3409d30
13 
14 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d20
15 Name:        System.Int32
16 MethodTable: 00007ff9b6f11188
17 EEClass:     00007ff9b6f01e20
18 Size:        24(0x18) bytes
19 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
20 Fields:
21               MT    Field   Offset                 Type VT     Attr            Value Name
22 00007ff9b6f11188  400051d        0         System.Int32  1 instance                1 m_value
23 
24 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d24
25 Name:        System.Int32
26 MethodTable: 00007ff9b6f11188
27 EEClass:     00007ff9b6f01e20
28 Size:        24(0x18) bytes
29 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
30 Fields:
31               MT    Field   Offset                 Type VT     Attr            Value Name
32 00007ff9b6f11188  400051d        0         System.Int32  1 instance                2 m_value
33 
34 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d28
35 Name:        System.Int32
36 MethodTable: 00007ff9b6f11188
37 EEClass:     00007ff9b6f01e20
38 Size:        24(0x18) bytes
39 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
40 Fields:
41               MT    Field   Offset                 Type VT     Attr            Value Name
42 00007ff9b6f11188  400051d        0         System.Int32  1 instance                3 m_value
43 
44 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d2c
45 Name:        System.Int32
46 MethodTable: 00007ff9b6f11188
47 EEClass:     00007ff9b6f01e20
48 Size:        24(0x18) bytes
49 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
50 Fields:
51               MT    Field   Offset                 Type VT     Attr            Value Name
52 00007ff9b6f11188  400051d        0         System.Int32  1 instance                4 m_value
53 
54 0:000> !DumpVC /d 00007ff9b6f11188 000001cfe3409d30
55 Name:        System.Int32
56 MethodTable: 00007ff9b6f11188
57 EEClass:     00007ff9b6f01e20
58 Size:        24(0x18) bytes
59 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
60 Fields:
61               MT    Field   Offset                 Type VT     Attr            Value Name
62 00007ff9b6f11188  400051d        0         System.Int32  1 instance                5 m_value

                        我們直接輸出strArray,使用【!dumparray】命令。

 1 0:000> !dumparray 000001cfe3409d80
 2 Name:        System.String[]
 3 MethodTable: 00007ff9b702fac8
 4 EEClass:     00007ff9b6edc440
 5 Size:        64(0x40) bytes
 6 Array:       Rank 1, Number of elements 5, Type CLASS
 7 Element Methodtable: 00007ff9b6f8ec08
 8 [0] 0000021075660b78
 9 [1] 0000021075660ba0
10 [2] 0000021075660bc0
11 [3] 0000021075660be8
12 [4] 0000021075660c08
13 
14 0:000> !DumpObj /d 0000021075660b78
15 Name:        System.String
16 MethodTable: 00007ff9b6f8ec08
17 EEClass:     00007ff9b6f6a500
18 Tracked Type: false
19 Size:        36(0x24) bytes
20 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
21 String:      Welcome
22 Fields:
23               MT    Field   Offset                 Type VT     Attr            Value Name
24 00007ff9b6f11188  400033b        8         System.Int32  1 instance                7 _stringLength
25 00007ff9b6f1b538  400033c        c          System.Char  1 instance               57 _firstChar
26 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
27 
28 0:000> !DumpObj /d 0000021075660ba0
29 Name:        System.String
30 MethodTable: 00007ff9b6f8ec08
31 EEClass:     00007ff9b6f6a500
32 Tracked Type: false
33 Size:        26(0x1a) bytes
34 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
35 String:      to
36 Fields:
37               MT    Field   Offset                 Type VT     Attr            Value Name
38 00007ff9b6f11188  400033b        8         System.Int32  1 instance                2 _stringLength
39 00007ff9b6f1b538  400033c        c          System.Char  1 instance               74 _firstChar
40 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
41 
42 0:000> !DumpObj /d 0000021075660bc0
43 Name:        System.String
44 MethodTable: 00007ff9b6f8ec08
45 EEClass:     00007ff9b6f6a500
46 Tracked Type: false
47 Size:        38(0x26) bytes
48 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
49 String:      Advanced
50 Fields:
51               MT    Field   Offset                 Type VT     Attr            Value Name
52 00007ff9b6f11188  400033b        8         System.Int32  1 instance                8 _stringLength
53 00007ff9b6f1b538  400033c        c          System.Char  1 instance               41 _firstChar
54 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
55 
56 0:000> !DumpObj /d 0000021075660be8
57 Name:        System.String
58 MethodTable: 00007ff9b6f8ec08
59 EEClass:     00007ff9b6f6a500
60 Tracked Type: false
61 Size:        30(0x1e) bytes
62 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
63 String:      .NET
64 Fields:
65               MT    Field   Offset                 Type VT     Attr            Value Name
66 00007ff9b6f11188  400033b        8         System.Int32  1 instance                4 _stringLength
67 00007ff9b6f1b538  400033c        c          System.Char  1 instance               2e _firstChar
68 00007ff9b6f8ec08  400033a       c8        System.String  0   static 0000021075660008 Empty
69 
70 0:000> !DumpObj /d 0000021075660c08
71 Name:        System.String
72 MethodTable: 00007ff9b6f8ec08
73 EEClass:     00007ff9b6f6a500
74 Tracked Type: false
75 Size:        40(0x28) bytes
76 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
77 String:      Debugging
78 Fields:
79               MT    Field   Offset                 Type VT     Attr            Value Name
80 00007ff9b6f11188  400033b        8         System.Int32  1 instance                9 _stringLength
81 00007ff9b6f1b538  400033c        c          System.Char  1 instance               44 _firstChar
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 0:002> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`eeaeae74 c3              ret

                    我們直接執行【!dso】或者【!dumpstackobjects】命令。

 1 0:000> !dso
 2 OS Thread Id: 0x1d8c (0)
 3 RSP/REG          Object           Name
 4 000000F45E99E8A8 00000235eb220b30 Interop+INPUT_RECORD
 5 000000F45E99E918 00000235eb220b30 Interop+INPUT_RECORD
 6 000000F45E99E920 000001f559009c98 System.Object
 7 000000F45E99E950 00000235eb220560 System.String    Press any key to continue(Generics)
 8 000000F45E99E9B8 000001f559009c38 System.IO.TextWriter+SyncTextWriter
 9 000000F45E99E9C0 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
10 000000F45E99EA28 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
11 000000F45E99EA48 000001f559009ce0 ExampleCore_3_1_6.ObjTypes
12 000000F45E99EA70 000001f559008ea0 System.String[]
13 000000F45E99EB18 000001f559008ea0 System.String[]
14 000000F45E99ED10 000001f559008ea0 System.String[]
15 000000F45E99ED18 000001f559008ea0 System.String[]
16 000000F45E99EE30 000001f559008ea0 System.String[]
17 000000F45E99EEB0 000001f559008eb8 System.String    E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
18 000000F45E99EEC0 000001f559008ea0 System.String[]
19 000000F45E99EED0 000001f559008e80 System.String[]
20 000000F45E99EF08 000001f559008eb8 System.String    E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
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 0:007> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffa`eeaeae74 c3              ret

                    直接執行命令【!DumpStackObjects】。

 1 0:000> !DumpStackObjects
 2 OS Thread Id: 0x3a04 (0)
 3           SP/REG           Object Name
 4     00c21e57e918     026cd5cb1378 System.String
 5     00c21e57ea28     026cd5cb0b30 Interop+INPUT_RECORD
 6     00c21e57ea98     026cd5cb0b30 Interop+INPUT_RECORD
 7     00c21e57eaa0     022c43c09c98 System.Object
 8     00c21e57ead0     026cd5cb0560 System.String
 9     00c21e57eb38     022c43c09c38 System.IO.TextWriter+SyncTextWriter
10     00c21e57eb40     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
11     00c21e57eba8     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
12     00c21e57ebc8     022c43c09ce0 ExampleCore_3_1_6.ObjTypes
13     00c21e57ebf0     022c43c08ea0 System.String[]
14     00c21e57ec98     022c43c08ea0 System.String[]
15     00c21e57ee90     022c43c08ea0 System.String[]
16     00c21e57ee98     022c43c08ea0 System.String[]
17     00c21e57efb0     022c43c08ea0 System.String[]
18     00c21e57f030     022c43c08eb8 System.String
19     00c21e57f040     022c43c08ea0 System.String[]
20     00c21e57f050     022c43c08e80 System.String[]
21     00c21e57f088     022c43c08eb8 System.String
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 0:001> ~0s
2 ucrtbase!strcmp+0x49:
3 00007ffa`ec654e69 4e8d0c10        lea     r9,[rax+r10]

                    我們使用【!clrstack -a】命令檢視一下託管執行緒的呼叫棧。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x2798 (0)
 3         Child SP               IP Call Site
 4 0000006CF777E238 00007ffaec654e69 [ExternalMethodFrame: 0000006cf777e238]
 5 。。。。。。(省略了)
 6 0000006CF777E9A0 00007FF996FC1B34 ExampleCore_3_1_8.Program.Main(System.String[])
 7     PARAMETERS:
 8         args (0x0000006CF777EA40) = 0x0000023e84c08ea0
 9     LOCALS:
10         0x0000006CF777EA28 = 0x0000023e84c09c98
11         0x0000006CF777EA00 = 0x0000000000000000

                    0x0000023e84c09c98 紅色標註的就是 Person 型別的區域性變數 person 的地址,我們使用【!dumpobj 0x0000023e84c09c98】檢視一下它的詳情。

 1 0:000> !dumpobj 0x0000023e84c09c98
 2 Name:        ExampleCore_3_1_8.Person
 3 MethodTable: 00007ff997079480
 4 EEClass:     00007ff997082008
 5 Tracked Type: false
 6 Size:        40(0x28) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_8\bin\Debug\net8.0\ExampleCore_3_1_8.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff996f31188  4000001       18         System.Int32  1 instance               32 <Age>k__BackingField
11 00007ff996faec08  4000002        8        System.String  0 instance 0000027f16be0500 <Name>k__BackingField
12 00007ff99707d9a0  4000003       10 ...ore_3_1_8.Address  0 instance 0000023e84c09cc0 <HomeAddress>k__BackingField

                      紅色標註就是它的大小。我們再使用【!ObjSize 0x0000023e84c09c98】命令檢視一下它的大小。

1 0:000> !ObjSize 0x0000023e84c09c98
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 0:000> !ObjSize
 2 Thread 2798 ([ExternalMethodFrame: 0000006cf777e238] ): 0000006cf777e810 -> 0000023e84c0b148: 4120 (0x1018) bytes (System.Byte[])
 3 Thread 2798 ([ExternalMethodFrame: 0000006cf777e238] ): 0000006cf777e818 -> 0000027f16be0a90: 32 (0x20) bytes (System.String)
 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Failed to enumerate GC references.
20 Failed to walk thread 31d4
21 Handle (strong): 0000023E804A1378 -> 0000023E84C0A2D8: 1024 (0x400) bytes (System.Object[])
22 Handle (strong): 0000023E804A1380 -> 0000023E84C0A228: 176 (0xb0) bytes (System.Object[])
23 Handle (strong): 0000023E804A1388 -> 0000023E84C0A180: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
24 Handle (strong): 0000023E804A1390 -> 0000023E84C0A0D0: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
25 Handle (strong): 0000023E804A1398 -> 0000023E82402020: 8232 (0x2028) bytes (System.Object[])
26 Handle (strong): 0000023E804A13A0 -> 0000023E84C092F0: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
27 Handle (strong): 0000023E804A13A8 -> 0000023E84C095B8: 88 (0x58) bytes (System.Diagnostics.Tracing.EventPipeEventProvider)
28 Handle (strong): 0000023E804A13B0 -> 0000023E84C09508: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
29 Handle (strong): 0000023E804A13B8 -> 0000023E84C09240: 112 (0x70) bytes (System.Diagnostics.Tracing.EtwEventProvider)
30 Handle (strong): 0000023E804A13C8 -> 0000023E84C00188: 128 (0x80) bytes (System.ExecutionEngineException)
31 Handle (strong): 0000023E804A13D0 -> 0000023E84C00108: 128 (0x80) bytes (System.StackOverflowException)
32 Handle (strong): 0000023E804A13D8 -> 0000023E84C00088: 128 (0x80) bytes (System.OutOfMemoryException)
33 Handle (strong): 0000023E804A13E0 -> 0000023E84C00028: 96 (0x60) bytes (System.Int32[])
34 Handle (strong): 0000023E804A13E8 -> 0000023E82400028: 87704 (0x15698) bytes (System.Object[])
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 0:007> ~0s
2 ntdll!NtReadFile+0x14:
3 00007ffa`eeaeae54 c3              ret

                    我們執行命令【!clrstack -a】檢視託管執行緒棧。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3cf8 (0)
 3         Child SP               IP Call Site
 4 000000CD8377E2C0 00007ffaeeaeae54 [InlinedCallFrame: 000000cd8377e2c0] 
 5 000000CD8377E2C0 00007ffa2d8276eb [InlinedCallFrame: 000000cd8377e2c0] 
 6 。。。。。。(省略了)
 7 
 8 000000CD8377E590 00007ff997991b34 ExampleCore_3_1_8.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_8\Program.cs @ 13]
 9     PARAMETERS:
10         args (0x000000CD8377E630) = 0x0000024827408ea0
11     LOCALS:
12         0x000000CD8377E618 = 0x0000024827409c98
13         0x000000CD8377E5F0 = 0x0000000000000000

                    0x0000024827409c98 紅色標註的就是 Person 型別的區域性變數 person 的地址。我們可以使用【!dumpobj 0x0000024827409c98】命令檢視這個物件的大小。

 1 0:000> !dumpobj 0x0000024827409c98
 2 Name:        ExampleCore_3_1_8.Person
 3 MethodTable: 00007ff997a49480
 4 EEClass:     00007ff997a52008
 5 Tracked Type: false
 6 Size:        40(0x28) bytes(這裡就是大小)
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_8\bin\Debug\net8.0\ExampleCore_3_1_8.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff997901188  4000001       18         System.Int32  1 instance               32 <Age>k__BackingField
11 00007ff99797ec08  4000002        8        System.String  0 instance 00000288b93b0500 <Name>k__BackingField
12 00007ff997a4d9a0  4000003       10 ...ore_3_1_8.Address  0 instance 0000024827409cc0 <HomeAddress>k__BackingField

                    我們使用【!objsize 0x0000024827409c98】這個命令看看它的大小。

 1 0:000> !objsize 0x0000024827409c98
 2 Objects which 24827409c98(ExampleCore_3_1_8.Person) transitively keep alive:
 3 
 4          Address               MT           Size
 5     024827409c98     7ff997a49480             40 
 6     0288b93b0500     7ff99797ec08             42 
 7     024827409cc0     7ff997a4d9a0             56 
 8     0288b93b0530     7ff99797ec08             32 
 9     0288b93b0550     7ff99797ec08             26 
10     0288b93b0570     7ff99797ec08             32 
11     0288b93b0590     7ff99797ec08             36 
12     0288b93b05b8     7ff99797ec08             34 
13 
14 Statistics:
15           MT Count TotalSize Class Name
16 7ff997a49480     1        40 ExampleCore_3_1_8.Person
17 7ff997a4d9a0     1        56 ExampleCore_3_1_8.Address
18 7ff99797ec08     6       202 System.String
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] <Exception> [<Pseudo-register number>],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】命令,檢視託管呼叫棧的棧回溯。

0:000> kb
RetAddr               : Args to Child                                                           : Call Site
00007ffb`00a9ca6f     : 000001b8`72c0aca8 0000002c`9517e710 0000002c`9517ea00 00007ffb`00a7bbb4 : KERNELBASE!RaiseException+0x69
00007ffb`00a9c129     : 00000000`70000185 00007ffb`00d961c8 0000002c`9517eb88 0000002c`9517ed28 : coreclr!RaiseTheExceptionInternalOnly+0x26b
00007ffa`a0fc2416     : 000001b8`72c0aca8 000001f9`04c913c8 000001b8`700fa1e8 00000000`00000040 : coreclr!IL_Throw+0xb9
00007ffa`a0fc1afd     : 000001b8`72c09ce0 00000000`00000000 000001b8`6e7af410 000001b8`72c09c38 : 0x00007ffa`a0fc2416
00007ffb`00b2a1a3     : 000001b8`72c08ea0 0000002c`9517f128 0000002c`9517f128 0000002c`9517ed19 : 0x00007ffa`a0fc1afd
00007ffb`00ab14c9     : 00000000`00000000 00000000`00000130 0000002c`9517ed28 00007ffb`009da456 : coreclr!CallDescrWorkerInternal+0x83
(Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56
00007ffb`009d75ac     : 0000002c`9517eda8 00000000`00000000 00000000`00000048 00007ffb`00ac28a6 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1
(Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb
00007ffb`009d6f7a     : 000001b8`72c08ea0 000001b8`72c08ea0 00000000`00000000 0000002c`9517f128 : coreclr!RunMainInternal+0x11c
00007ffb`009d6b17     : 000001b8`6e7af410 000001b8`00000000 000001b8`6e7af410 00000000`00000000 : coreclr!RunMain+0xd2
00007ffb`009d7321     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf
00007ffb`00ae7768     : 00000000`00000001 0000002c`9517f301 0000002c`9517f350 00007ffb`86ff23ea : coreclr!CorHost2::ExecuteAssembly+0x281
00007ffb`87012c36     : 000001b8`6e780fd0 000001b8`6e780310 00000000`00000000 000001b8`6e780310 : coreclr!coreclr_execute_assembly+0xd8
(Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a
00007ffb`87012f1c     : 000001b8`6e76bcc8 0000002c`9517f579 00007ffb`8704c9c0 000001b8`6e76bcc8 : hostpolicy!run_app_for_context+0x596
00007ffb`8701385a     : 00000000`00000000 000001b8`6e76bcc0 000001b8`6e76bcc0 00000000`00000000 : hostpolicy!run_app+0x3c
00007ffb`b3e2b5c9     : 000001b8`6e77f438 000001b8`6e77f320 00000000`00000000 0000002c`9517f679 : hostpolicy!corehost_main+0x15a
00007ffb`b3e2e066     : 000001b8`6e77e330 0000002c`9517fa00 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9
00007ffb`b3e302ec     : 00007ffb`b3e625f8 000001b8`6e77cb40 0000002c`9517f940 0000002c`9517f8f0 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6
00007ffb`b3e2e644     : 0000002c`9517fa00 0000002c`9517fa20 0000002c`9517f971 000001b8`6e77cf60 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c
00007ffb`b3e285a0     : 0000002c`9517fa20 000001b8`6e77c440 00000000`00000001 000001b8`6e760000 : hostfxr!fx_muxer_t::execute+0x494
*** WARNING: Unable to verify checksum for apphost.exe
00007ff6`5faef998     : 00007ffb`d241f4e8 00007ffb`b3e29b10 0000002c`9517fbc0 000001b8`6e77c130 : hostfxr!hostfxr_main_startupinfo+0xa0
00007ff6`5faefda6     : 00007ff6`5fafb6c0 00000000`00000007 000001b8`6e76bcc0 00000000`0000005e : apphost!exe_start+0x878
00007ff6`5faf12e8     : 00000000`00000000 00000000`00000000 000001b8`6e76bcc0 00000000`00000000 : apphost!wmain+0x146
(Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22
00007ffb`d33f6fd4     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : apphost!__scrt_common_main_seh+0x10c
00007ffb`d4b7cec1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
0:000>

                     接下來,我們找到 KERNELBASE!RaiseException 函式(000001b8`72c0aca8)的第一個引數,在該地址上執行【!dumpobj】命令,檢視異常詳情。

 1 0:000> !DumpObj 000001b8`72c0aca8
 2 Name:        System.ArgumentException
 3 MethodTable: 00007ffaa10467b0
 4 EEClass:     00007ffaa0f8cdf8
 5 Tracked Type: false
 6 Size:        136(0x88) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 0000000000000000  4000264        8 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
11 00007ffaa0faec08  4000265       10        System.String  0 instance 000001f904c913c8 _message
12 00007ffaa1043060  4000266       18 ...tions.IDictionary  0 instance 0000000000000000 _data
13 00007ffaa1040820  4000267       20     System.Exception  0 instance 0000000000000000 _innerException
14 00007ffaa0faec08  4000268       28        System.String  0 instance 0000000000000000 _helpURL
15 00007ffaa10aa858  4000269       30        System.Byte[]  0 instance 000001b872c0ad78 _stackTrace
16 00007ffaa10aa858  400026a       38        System.Byte[]  0 instance 0000000000000000 _watsonBuckets
17 00007ffaa0faec08  400026b       40        System.String  0 instance 0000000000000000 _stackTraceString
18 00007ffaa0faec08  400026c       48        System.String  0 instance 0000000000000000 _remoteStackTraceString
19 00007ffaa0efc4d8  400026d       50      System.Object[]  0 instance 0000000000000000 _dynamicMethods
20 00007ffaa0faec08  400026e       58        System.String  0 instance 0000000000000000 _source
21 00007ffaa0fa8b78  400026f       60       System.UIntPtr  1 instance 00007FFAA0FC2415 _ipForWatsonBuckets
22 00007ffaa0fa70a0  4000270       68        System.IntPtr  1 instance 0000000000000000 _xptrs
23 00007ffaa0f31188  4000271       70         System.Int32  1 instance       -532462766 _xcode
24 00007ffaa0f31188  4000272       74         System.Int32  1 instance      -2147024809 _HResult
25 00007ffaa0faec08  4000383       78        System.String  0 instance 0000000000000000 _paramName
26 0:000>

                    以上就是異常的詳情,如果我們想檢視具體提示訊息,可以針對 _message 域的地址 000001f904c913c8 再次執行【!DumpObj】命令。

 1 0:000> !dumpobj 000001f904c913c8
 2 Name:        System.String
 3 MethodTable: 00007ffaa0faec08
 4 EEClass:     00007ffaa0f8a500
 5 Tracked Type: false
 6 Size:        58(0x3a) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 String:      Obj cannot be null
 9 Fields:
10               MT    Field   Offset                 Type VT     Attr            Value Name
11 00007ffaa0f31188  400033b        8         System.Int32  1 instance               18 _stringLength
12 00007ffaa0f3b538  400033c        c          System.Char  1 instance               4f _firstChar
13 00007ffaa0faec08  400033a       c8        System.String  0   static 000001f904c90008 Empty

                    【!dumpobj】命令有些繁瑣,我們可以直接使用【!PrintException】命令輸出異常資訊。

 1 0:000> !PrintException 000001b8`72c0aca8
 2 WARNING: SOS needs to be upgraded for this version of the runtime. Some commands may not work correctly.
 3 For more information see https://go.microsoft.com/fwlink/?linkid=2135652
 4 
 5 Exception object: 000001b872c0aca8
 6 Exception type:   System.ArgumentException
 7 Message:          Obj cannot be null
 8 InnerException:   <none>
 9 StackTrace (generated):
10     SP               IP               Function
11     0000002C9517E9C0 00007FFAA0FC2415 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException(ExampleCore_3_1_6.ObjTypes)+0x85
12     0000002C9517EA10 00007FFAA0FC1AFC ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.Main(System.String[])+0x1cc
13 
14 StackTraceString: <none>
15 HResult: 80070057

                    在這裡看的很清楚,包括異常的型別(Exception typeSystem.ArgumentException),異常的地址(Exception object: 000001b872c0aca8),異常丟擲的方法(ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException)等。
                    當然,我們可以使用【!threads】或者【!t】命令輸出所有託管執行緒的資訊,包括託管執行緒丟擲的最有一個異常。
                    

                    【!threads】命令執行的效果。

 1 0:000> !threads
 2 ThreadCount:      3
 3 UnstartedThread:  0
 4 BackgroundThread: 2
 5 PendingThread:    0
 6 DeadThread:       0
 7 Hosted Runtime:   no
 8                                                                                                             Lock
 9  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
10    0    1     1190 000001B86E7AF410    2a020 Preemptive  000001B872C0ADF0:000001B872C0C638 000001B86E7F1530 -00001 MTA System.ArgumentException 000001b872c0aca8
11    6    2     3b64 000001B8700EDDA0    21220 Preemptive  0000000000000000:0000000000000000 000001B86E7F1530 -00001 Ukn (Finalizer)
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 0:000> g
 2 ModLoad: 00007ffb`d4ac0000 00007ffb`d4af0000   C:\Windows\System32\IMM32.DLL
 3 ModLoad: 00007ffb`c8d40000 00007ffb`c8d99000   C:\Program Files\dotnet\host\fxr\8.0.2\hostfxr.dll
 4 ModLoad: 00007ffb`c8cd0000 00007ffb`c8d34000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\hostpolicy.dll
 5 ModLoad: 00007ffa`fcab0000 00007ffa`fcf98000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\coreclr.dll
 6 ModLoad: 00007ffb`d3b60000 00007ffb`d3c89000   C:\Windows\System32\ole32.dll
 7 ModLoad: 00007ffb`d34a0000 00007ffb`d37f4000   C:\Windows\System32\combase.dll
 8 ModLoad: 00007ffb`d3200000 00007ffb`d32d5000   C:\Windows\System32\OLEAUT32.dll
 9 ModLoad: 00007ffb`d1fc0000 00007ffb`d203f000   C:\Windows\System32\bcryptPrimitives.dll
10 (2e10.1988): Unknown exception - code 04242420 (first chance)
11 ModLoad: 00007ffa`fb810000 00007ffa`fc49c000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
12 ModLoad: 00007ffa`fb650000 00007ffa`fb809000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\clrjit.dll
13 ModLoad: 00007ffb`d1f00000 00007ffb`d1f13000   C:\Windows\System32\kernel.appcore.dll
14 ModLoad: 00000293`f6f10000 00000293`f6f18000   E:\Visual Studio 2022\...\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.dll
15 ModLoad: 00000293`f6f30000 00000293`f6f3e000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.dll
16 ModLoad: 00007ffb`c8c70000 00007ffb`c8c98000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Console.dll
17 ModLoad: 00007ffb`79470000 00007ffb`79482000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Threading.dll
18 ModLoad: 00000293`f6f40000 00000293`f6f48000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Text.Encoding.Extensions.dll
19 ModLoad: 00007ffb`5dc90000 00007ffb`5dca5000   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Runtime.InteropServices.dll
20 (2e10.1988): CLR exception - code e0434352 (first chance)
21 (2e10.1988): CLR exception - code e0434352 (!!! second chance !!!)
22 KERNELBASE!RaiseException+0x69:
23 00007ffb`d2563e49 0f1f440000      nop     dword ptr [rax+rax]

                    我們使用【kb】命令檢視呼叫棧。

 1 0:000> kb
 2  # RetAddr               : Args to Child                                                           : Call Site
 3 00 00007ffa`fcb7ca6f     : 00000293`fb40aca8 000000d7`0477e1f0 000000d7`0477e4e0 00007ffa`fcb5bbb4 : KERNELBASE!RaiseException+0x69
 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 02 00007ffa`9d0a24b6     : 00000293`fb40aca8 000002d4`8d5813c8 00000293`f6fbe1e8 00000000`00000040 : coreclr!IL_Throw+0xb9 [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 4247] 
 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 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 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 06 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67] 
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 08 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458] 
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 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 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 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 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 0e (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109] 
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 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 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 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 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 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 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 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 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 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 19 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : apphost!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90] 
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 1b 00007ffb`d4b7cec1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
31 1c 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

                    接下來,找出 KERNELBASE!RaiseException 函式的(000001f8`4c80aca8)第一個引數,並在這個地址上執行【!dumpobj 000001f8`4c80aca8】命令。

 1 0:000> !DumpObj 000001f8`4c80aca8
 2 Name:        System.ArgumentException
 3 MethodTable: 00007ffaa06e67b0
 4 EEClass:     00007ffaa062cdf8
 5 Tracked Type: false
 6 Size:        136(0x88) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 0000000000000000  4000264        8 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
11 00007ffaa064ec08  4000265       10        System.String  0 instance 00000238deaf13c8 _message
12 00007ffaa06e3060  4000266       18 ...tions.IDictionary  0 instance 0000000000000000 _data
13 00007ffaa06e0820  4000267       20     System.Exception  0 instance 0000000000000000 _innerException
14 00007ffaa064ec08  4000268       28        System.String  0 instance 0000000000000000 _helpURL
15 00007ffaa074a858  4000269       30        System.Byte[]  0 instance 000001f84c80ad78 _stackTrace
16 00007ffaa074a858  400026a       38        System.Byte[]  0 instance 0000000000000000 _watsonBuckets
17 00007ffaa064ec08  400026b       40        System.String  0 instance 0000000000000000 _stackTraceString
18 00007ffaa064ec08  400026c       48        System.String  0 instance 0000000000000000 _remoteStackTraceString
19 00007ffaa059c4d8  400026d       50      System.Object[]  0 instance 0000000000000000 _dynamicMethods
20 00007ffaa064ec08  400026e       58        System.String  0 instance 0000000000000000 _source
21 00007ffaa0648b78  400026f       60       System.UIntPtr  1 instance 00007FFAA0662415 _ipForWatsonBuckets
22 00007ffaa06470a0  4000270       68        System.IntPtr  1 instance 0000000000000000 _xptrs
23 00007ffaa05d1188  4000271       70         System.Int32  1 instance       -532462766 _xcode
24 00007ffaa05d1188  4000272       74         System.Int32  1 instance      -2147024809 _HResult
25 00007ffaa064ec08  4000383       78        System.String  0 instance 0000000000000000 _paramName

                    從【!dumpobj】命令的輸出中,我們可以看到託管程式碼異常的所有資訊,包括異常的型別(System.ArgumentException)以及異常相關的所有域。我們可以針對標紅的 _message 再次執行【!dumpobj 00000238deaf13c8】命令,檢視異常的具體提示資訊。

 1 0:000> !dumpobj 00000238deaf13c8
 2 Name:        System.String
 3 MethodTable: 00007ffaa064ec08
 4 EEClass:     00007ffaa062a500
 5 Tracked Type: false
 6 Size:        58(0x3a) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 String:      Obj cannot be null
 9 Fields:
10               MT    Field   Offset                 Type VT     Attr            Value Name
11 00007ffaa05d1188  400033b        8         System.Int32  1 instance               18 _stringLength
12 00007ffaa05db538  400033c        c          System.Char  1 instance               4f _firstChar
13 00007ffaa064ec08  400033a       c8        System.String  0   static 00000238deaf0008 Empty

                    簡寫方式輸出。

1 0:000> !DumpObj -nofields 00000238deaf13c8
2 Name:        System.String
3 MethodTable: 00007ffaa064ec08
4 EEClass:     00007ffaa062a500
5 Tracked Type: false
6 Size:        58(0x3a) bytes
7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
8 String:      Obj cannot be null

                    上面我們使用【!dumpobj】命令可以檢視異常資訊,但是比較繁瑣。我們可以使用【!PrintException】命令輸出異常資訊。

 1 0:000> !PrintException 000001f8`4c80aca8
 2 Exception object: 000001f84c80aca8
 3 Exception type:   System.ArgumentException
 4 Message:          Obj cannot be null
 5 InnerException:   <none>
 6 StackTrace (generated):
 7     SP               IP               Function
 8     0000007BA577E6D0 00007FFAA0662415 ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.ThrowException(ExampleCore_3_1_6.ObjTypes)+0x85
 9     0000007BA577E720 00007FFAA0661AFC ExampleCore_3_1_6!ExampleCore_3_1_6.ObjTypes.Main(System.String[])+0x1cc
10 
11 StackTraceString: <none>
12 HResult: 80070057

                    這裡輸出的內容更清晰,是哪個方法丟擲的異常都可以看到(ThrowException)。我們分析異常的時候,可以使用【!threads】或者【!t】命令檢視所有託管執行緒資訊,包括該執行緒丟擲的最後一個異常。

 1 0:000> !threads
 2 ThreadCount:      3
 3 UnstartedThread:  0
 4 BackgroundThread: 2
 5 PendingThread:    0
 6 DeadThread:       0
 7 Hosted Runtime:   no
 8                                                                                                             Lock  
 9  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
10    0    1     379c 000001F8485263D0    2a020 Preemptive  000001F84C80ADF0:000001F84C80C638 000001f848568920 -00001 MTA System.ArgumentException 000001f84c80aca8
11    6    2     10d4 000001F8485D7C40    21220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 Ukn (Finalizer) 
12    7    3     3204 000001F8485346F0    2b220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 MTA 
13 
14 
15 0:000> !t
16 ThreadCount:      3
17 UnstartedThread:  0
18 BackgroundThread: 2
19 PendingThread:    0
20 DeadThread:       0
21 Hosted Runtime:   no
22                                                                                                             Lock  
23  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
24    0    1     379c 000001F8485263D0    2a020 Preemptive  000001F84C80ADF0:000001F84C80C638 000001f848568920 -00001 MTA System.ArgumentException 000001f84c80aca8
25    6    2     10d4 000001F8485D7C40    21220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 Ukn (Finalizer) 
26    7    3     3204 000001F8485346F0    2b220 Preemptive  0000000000000000:0000000000000000 000001f848568920 -00001 MTA 

                    我們看到了 ID 為 1 的執行緒丟擲一個型別為 System.ArgumentException 異常,異常的地址:000001f84c80aca8 ,我們可以針對這個地址使用【!DumpObj】或者【!PrintException】命令檢視異常的詳情。


四、總結
    這篇文章的第二部分終於寫完了,考慮到內容太多,分多篇寫作了。只有所有程式碼自己都寫了,都測試了,才會明白裡面發生的事情。Net 高階除錯這條路,也剛剛起步,還有很多要學的地方。皇天不負有心人,努力,不辜負自己,我相信付出就有回報,再者說,學習的過程,有時候,雖然很痛苦,但是,學有所成,學有所懂,這個開心的感覺還是不可言喻的。不忘初心,繼續努力。做自己喜歡做的,開心就好。

相關文章