Advanced .Net Debugging 10:事後除錯

可均可可發表於2024-07-04

一、介紹
    這是我的《Advanced .Net Debugging》這個系列的第十篇文章。這篇文章的內容是原書的第三部分的【高階主題】的第八章【事後除錯】。前面幾篇文章,我們介紹了很多工具,可以幫助大家找出問題的所在。但是,有一類問題我們是沒辦法使用這些工具來解決的,那就是已經發布的程式。在程式釋出後,總是會出現一些問題,並且這些問題出現的時機是不確定的,大多數出現在使用者在使用軟體的過程中。想要解決這樣的問題,我們當然會想到遠端除錯,有時候可以,有時候是不可以的,當然,不可以的理由會有很多,比如:安全的原因等類似的。
    如果使用軟體的客戶拒絕對出現問題的軟體進行實時除錯,並且本地又無法重現問題,那我們該怎麼辦呢?當然是有辦法的,那就是【事後除錯】。
    【事後除錯】的步驟:
      1)、觸發故障的發生。
      2)、抓取系統在發生故障時的狀態快照(根據不同的故障型別,在某些情況下還需要抓取故障發生前後的狀態快照)。
      3)、將快照傳送給工程師作進一步的分析。
    在這篇文章中,我們將討論抓取快照的各種不同方式,不同型別的轉儲資訊以及如何分析它們。
    當然,高階除錯會涉及很多方面的內容,你對 .NET 基礎知識掌握越全面、細節越底層,除錯成功的機率越大,當我們遇到各種奇葩問題的時候才不會手足無措。
    如果在沒有說明的情況下,所有程式碼的測試環境都是 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原始碼:原始碼下載

    在此說明:我使用了兩種除錯工具,第一種:Windbg Preivew,圖形介面,使用方便,操作順手,不用擔心干擾;第二種是:NTSD,是命令列形式的偵錯程式,在命令使用上和 Windbg 沒有任何區別,之所以增加了它的除錯過程,不過是我的個人愛好,想多瞭解一些,看看他們有什麼區別,為了學習而使用的。如果在工作中,我推薦使用 Windbg Preview,更好用,更方便,也不會出現奇怪問題(我在使用 NTSD 除錯斷點的時候,不能斷住,提示記憶體不可讀,Windbg preview 就沒有任何問題)。
    如果大家想了解除錯過程,二選一即可,當然,我推薦檢視【Windbg Preview 除錯】。

二、目錄結構
    為了讓大家看的更清楚,也為了自己方便查詢,我做了一個目錄結構,可以直觀的檢視文章的佈局、內容,可以有針對性檢視。

    2.1、轉儲檔案基本知識
        2.1.1、透過偵錯程式來生成轉儲檔案
            A、基礎知識
            B、眼見為實
                1)、NTSD 除錯
                2)、Windbg Preview 除錯
        2.1.2、透過 ADPlus 生成轉儲檔案
            A、基礎知識
            B、眼見為實
                1)、崩潰模式
        2.1.3、轉儲檔案的除錯
        2.1.4、資料訪問層
        2.1.5、轉儲檔案分析:未處理的 .NET 異常
            1)、NTSD 除錯
            2)、Windbg Preview 除錯
    2.2、Windows 錯誤報告
        
三、除錯原始碼
    廢話不多說,本節是除錯的原始碼部分,沒有程式碼,當然就談不上測試了,除錯必須有載體。
    1、ExampleCore_8_01

Advanced .Net Debugging 10:事後除錯
 1 namespace ExampleCore_8_01
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Program program = new Program();
 8             program.Run();
 9         }
10 
11         public void Run()
12         {
13             Console.WriteLine("Press any key to start");
14             Console.ReadKey();
15             ProcessData(null);
16         }
17 
18         public void ProcessData(string? data)
19         {
20             if (data == null)
21             {
22                 throw new ArgumentNullException("Argument is Null");
23             }
24             string s = "Hello:" + data;
25         }
26     }
27 }
View Code


四、基礎知識
    在這一段內容中,有的小節可能會包含兩個部分,分別是 A 和 B,也有可能只包含 A,如果只包含 A 部分,A 字母會省略。A 是【基礎知識】,講解必要的知識點,B 是【眼見為實】,透過除錯證明講解的知識點。

    4.1、轉儲檔案的基本知識
        轉儲檔案是程序狀態的外在表示。生成轉儲檔案的目的:在不需要對出問題的計算機進行實時訪問的情況下,就可以對程式故障進行分析。有了轉儲檔案,除錯人員就可以使用偵錯程式的事後除錯功能來分析故障。
        共有兩種型別的轉儲檔案:
          1)、完全轉儲檔案(Full Dump)
          2)、微型轉儲檔案(Mini Dump)
        在完全轉儲檔案中包含了程序的整個記憶體空間、可執行影響、控制代碼表和偵錯程式需要使用的其他資訊。當使用完全轉儲檔案時不能指定所要收集的資料量。但是,我們可以使用偵錯程式將完全轉儲檔案轉換為微型轉儲檔案。
        微型轉儲檔案的內容是可變的,並且能根據使用的轉儲檔案生成器進行定製。在微型轉儲檔案中可以只包含某個執行緒的資訊,也可以包含被轉儲程序的詳細資訊。需要注意的是,在最大的微型轉儲檔案中包含的內容要多於在完全轉儲檔案中包含的內容。因此,我們這裡只介紹微型轉儲檔案的結構。

        以下是能夠生成轉儲檔案的工具列表:
          1)、Windows Debuggers(Windows 偵錯程式):Windows 偵錯程式可以生成各種不同大小的轉儲檔案,並且能夠完全控制轉儲檔案的生成過程。
          2)、ADPlus:ADPlus 是 Windows 除錯工具集中的一個。它的作用相當於一個程序監視器,每當發生崩潰或者掛起時,都能生成轉儲檔案。此外,它還能將崩潰事件通知給使用者。
          3)、Windows 錯誤報告:Windows錯誤報告是Microsoft提供的一種服務,使用者透過這種服務註冊到一個實時的錯誤報告站點。每當使用者的某個應用程式發生錯誤時,都會將一個錯誤報告從發生崩潰的機器傳送到Windows錯誤報告站點。然後,在進行事後分析時可以從WER服務中提取崩潰資訊(包括轉儲檔案)。        

        以上都是書裡介紹的內容,由於書寫的比較早,到現在還有很多其他工具可以生成轉儲檔案,比如:工作管理員,Windbg 偵錯程式,Process Explorer,PCHunter 等,使用起來也很方便,網上學習資料很多,我就不多說了。
        接下來,我們就針對這三種工具分別介紹如何生成轉儲檔案。
        
        4.1.1、透過偵錯程式生成轉儲檔案
            A、基礎知識
                如果我們想使用偵錯程式生成 DUMP 檔案,可以使用【.dump】命令,【.dump /m】表示偵錯程式將生成一個微型轉儲檔案。當然【.dump】命令還有其他的選項,如下:
                  a、生成一個完整的微型轉儲檔案,啟動所有選項。在這個檔案中將包含完整的記憶體資料、控制代碼資訊、模組資訊、基本的記憶體資訊和執行緒資訊等。相當於使用/mfFhut。
                  f、生成一個微型轉儲檔案,其中包含程序內所有可訪問和已提交的記憶體頁。
                  F、生成一個微型轉儲檔案,其中包含偵錯程式在重構整個虛擬記憶體地址空間時需要的所有基本記憶體資訊。
                  h、生成一個微型轉儲檔案,其中包含控制代碼資訊。
                  u、生成一個微型轉儲檔案,其中包含未解除安裝模組的資訊。注意,這個選項只能在Windows Server 2003上使用。
                  t、生成一個微型轉儲檔案,其中包含執行緒時間的資訊。線上程時間資訊中包括建立時間,以及在使用者態和核心態中執行的時間。
                  i、生成一個微型轉儲檔案,其中包含輔助記憶體資訊。輔助記憶體是指由棧指標或者後臺儲存使用的記憶體(及其周圍的一小塊記憶體)。
                  p、生成一個微型轉儲檔案,其中包含程序環境塊和執行緒環境塊。
                  w、生成一個微型轉儲檔案,其中包含所有已提交的讀-寫私有記憶體頁。
                  d、生成一個微型轉儲檔案,其中包含映像中的所有資料段。
                  c、生成一個微型轉儲檔案,其中包含映像中的所有程式碼段。
                  r、生成一個微型轉儲檔案,適合於在需要保護隱私的情況中使用。這個選項將刪除在重建棧時不需要的任何資訊(將這些資訊替換為0,包括區域性變數)。
                  R、生成一個微型轉儲檔案,適合在需要保護隱私的情況下使用。這個選項將從微型轉儲檔案中刪除完整的模組路徑,因此將確保使用者目錄結構的隱私性。
                舉個例子:我們在偵錯程式中執行【.dump /ma /u F:\MyDump.dmp】命令,可以在F盤看到生成 dmp 檔案到MyDump_1c90_2024-06-27_15-13-24-466_3660.dmp檔案。 .dump命令引數比較多,常用的組合就是/ma,/m 表示生成minidump,/a 表示dmp包含所有資訊,/u 引數就是上面說的附加時間和PID資訊到檔名。

1 0:000> .dump /ma /u F:\Test\TestDump\MyDump.dmp
2 Creating F:\Test\TestDump\MyDump_1c90_2024-06-27_15-13-24-466_3660.dmp - mini user dump
3 Dump successfully written

                當我們在生成一個轉儲檔案的時候,有一個經驗法則,在轉儲檔案中包含的狀態越多,在進行事後除錯的時候就能獲取更多的資訊。當然,最大的限制因素就是轉儲檔案的大小。有時候你會發現無法從一個高安全的伺服器上獲取很大的轉儲檔案,而只能對一個刪除了某些敏感資訊之後的轉儲檔案進行分析。
                如果我們使用命令列偵錯程式,當我們需要載入 dump 檔案時,必須使用【ntsd -z dumpFileName】命令才可以,我使用的是【ntsd】偵錯程式。dumpFileName:必須包含 dump 檔案的完整路徑和字尾名。
                透過偵錯程式生成轉儲檔案的一個難點就是偵錯程式必須在合適的時候被附加到故障程序上。一般來說,還好,但是對於崩潰情況是不定時的,是沒有規律的,這樣就很容易錯失附加偵錯程式的機會。當我們的程式發生崩潰的時候 Windows 可以自動的生成轉儲檔案就好了,這種機制是有的。我們可以使用“事後偵錯程式設定(Postmortem Debugger Setup)”。我們可以使用以下命令來修改事後偵錯程式:windbg -I、cdb -iae、ntsd -iae、drwtsn32 -i
                效果如圖:
                想要執行以上命令,直接開啟【cmd】命令列工具,輸入命令【windbg -I】、【cdb -iae】、【ntsd -iae】、【drwtsn32 -i】就可以了。
                修改登錄檔的值:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug
                效果如圖:
                

                這個結果是我執行【cdb -iae】命令的結果。命令視窗如圖:
                

                我們可以直接執行我們的例項程式,控制檯程式一旦丟擲異常,立刻就能開啟我們註冊偵錯程式。
                  1)先演示執行【Windbg -I】命令後的效果。
                    雙擊我們的控制檯程式,看到“Press any key to start”字樣,然後點選Enter鍵,就會開啟指定的偵錯程式。
                    效果如圖:
                    

                  2)、先演示執行【cdb -iae】命令後的效果。
                    雙擊我們的控制檯程式,看到“Press any key to start”字樣,然後點選Enter鍵,就會開啟指定的偵錯程式。
                    效果如圖:
                    

                轉儲檔案生成注意事項:
                  在 Windows Vista 中修改了錯誤報告技術在本地機器上儲存轉儲檔案的方式。在 Windows Vista 之前,Dr.Watson 預設將生成的轉儲檔案儲存在本地機器上。這些轉儲檔案可以由任何一個想要除錯轉儲檔案的使用者訪問。在 Windows Vista 中去掉了 Dr.Watson,而是引入了一種更為可靠和穩定的錯誤報告機制。其中的修改之一就是,生成的轉儲檔案(在預設情況下)不會被儲存到本地機器上。要改變這種預設行為,可以將登錄檔 ForceQueue 設定為1,這將使所有轉儲檔案在上傳到 Microsoft 之前就在本地機器上排隊。ForceQueue 註冊鍵值位於以下注冊路徑:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting          
                  在註冊鍵值 ForceQueue 被設定為1後,生成的所有轉儲檔案都將被儲存到以下位置:
                  對於在系統上下文中執行或者被提升到系統上下文中執行的程序:
                  %ALLUSERSPROFILE %\Microsoft\Windows\WER\[ReportQueue|ReportArchive]
                  對於所有其他的程序:
                  %LOCALAPPDATA%\Microsoft\Windows\WER\[ReportQueue|ReportArchive]

                當除錯非託管程式碼程式時,會用到 AeDebug 鍵值。然而,對於託管程式碼除錯,可以使用【DbgManagedDebugger】和【DbgJITDebugLaunchSetting】這兩個鍵值控制器除錯。該鍵值位置:計算機\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework,效果如圖:
                

                  1)DbgManagedDebugger:該註冊鍵值會指定當遇到未處理的異常時應該啟動哪一個偵錯程式。當遇到未處理的異常時,該註冊鍵值指定的偵錯程式並不會立即呼叫,而是顯示一個訊息框,由使用者選擇是除錯程式還是結束程式。在事後除錯中,一個很常見的問題就是,如何在程式發生故障的時候自動生成轉儲檔案。
                    我們可以執行以下命令【ntsd -pv -p %ld -c ",dump /u /ma <path to dump file>; .kill; qd】,該命令的意思是,當一個故障發生時,啟動【ntsd】偵錯程式,並且執行【.dump】命令生成一個轉儲檔案,然後退出除錯回話。

                  2)、DbgJITDebugLaunchSetting:該註冊鍵值表示發生未處理異常時的行為。如果這個值被設定為 0,那麼將顯示一個訊息框,並由使用者選擇對故障採用何種處理方式。請注意,只有在互動程序的情況下才會顯示這個訊息框,而對於其他程序(例如服務)則是直接結束。
                    如果 DbgJITDebugLaunchSetting 被設定 1,那麼程式會直接結束,並返回一個棧轉儲。
                    如果 DbgJITDebugLaunchSetting 被設定 2,那麼將立刻啟動在 DbgManagedDebugger 中指定的偵錯程式,而不會顯示訊息框。
                    如果 DbgJITDebugLaunchSetting 被設定 16,對於互動式程序會顯示前面的訊息框,而對於非互動式程序則會直接啟動在 DbgManagedDebugger 中指定的偵錯程式。

            B、眼見為實
                除錯原始碼:ExampleCore_8_01
                除錯任務:透過 Windows 偵錯程式生成轉儲檔案。
                1)、NTSD 除錯
                    編譯專案,開啟【Visual Studio 2022 Developer Command Prompt v17.9.6】命令列工具,輸入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\bin\Debug\net8.0\ExampleCore_8_01.exe】開啟偵錯程式。
                    進入偵錯程式,我們直接使用【g】命令,執行偵錯程式,直到看到偵錯程式輸入“Press any key to start”字樣,我們按Enter鍵,讓偵錯程式繼續執行,發現現在的偵錯程式已經中斷執行了。
                    我們可以執行【!clrstack】命令,檢視一下當前的執行緒呼叫棧。

1 0:000> !clrstack
2 OS Thread Id: 0x1764 (0)
3         Child SP               IP Call Site
4 000000132477E8A8 00007ff87ecbcf19 [HelperMethodFrame: 000000132477e8a8]
5 000000132477E9A0 00007FFF41F21AFB ExampleCore_8_01.Program.ProcessData(System.String)
6 000000132477EA00 00007FFF41F21A46 ExampleCore_8_01.Program.Run()
7 000000132477EA50 00007FFF41F21988 ExampleCore_8_01.Program.Main(System.String[])
8 0:000>

                    接下來,我們使用【.dump /mf F:\Test\TestDump\08dumpfile2.dmp】命令,生成轉儲檔案,儲存目錄在 F:\Test\TestDump 下,檔名稱是 08dumpfile2.dmp。說明一下,檔名為什麼加一個 2,因為我已經生成一個檔案了,檔名必須不同,否則會有錯誤。

1 0:000> .dump /mf F:\Test\TestDump\08dumpfile.dmp
2 Unable to create file 'F:\Test\TestDump\08dumpfile.dmp' - Win32 error 0n80
3     "檔案存在。"

                    檔名修改後,繼續執行,看到“Dump successfully written”字樣就說明成功了。

1 0:000> .dump /mf F:\Test\TestDump\08dumpfile2.dmp
2 Creating F:\Test\TestDump\08dumpfile2.dmp - mini user dump
3 Dump successfully written

                    我們需要再開啟一個【NTSD】偵錯程式,執行【NTSD -z F:\Test\TestDump\08dumpfile2.dmp】命令,載入我們剛剛生成的 DUMP 檔案。

1 ** Visual Studio 2022 Developer Command Prompt v17.9.6
2 ** Copyright (c) 2022 Microsoft Corporation
3 **********************************************************************
4 
5 D:\Program Files\Microsoft Visual Studio\2022\Community>NTSD -z F:\Test\TestDump\08dumpfile2.dmp

                    偵錯程式顯示如下:

 1 Microsoft (R) Windows Debugger Version 10.0.22621.2428 AMD64
 2 Copyright (c) Microsoft Corporation. All rights reserved.
 3 
 4 
 5 Loading Dump File [F:\Test\TestDump\08dumpfile2.dmp]
 6 User Mini Dump File with Full Memory: Only application data is available
 7 
 8 Symbol search path is: srv*
 9 Executable search path is:
10 Windows 10 Version 19045 MP (4 procs) Free x64
11 Product: WinNt, suite: SingleUserTS
12 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
13 Machine Name:
14 Debug session time: Wed Jun 26 17:02:22.000 2024 (UTC + 8:00)
15 System Uptime: 0 days 6:57:46.621
16 Process Uptime: 0 days 0:05:15.000
17 ..................................
18 This dump file has an exception of interest stored in it.
19 The stored exception information can be accessed via .ecxr.
20 (4fa8.1764): CLR exception - code e0434352 (first/second chance not available)
21 For analysis of this file, run !analyze -v
22 KERNELBASE!RaiseException+0x69:
23 00007ff8`7ecbcf19 0f1f440000      nop     dword ptr [rax+rax]

                    第一部分紅色標註的說明載入 DUMP 檔案的資訊,第二部分紅色標註的就是故障原因(CLR 異常)。
                    接下來,我們就可以使用各種命令除錯我們的程式了。

                2)、Windbg Preview 除錯
                    編譯專案,開啟【Windbg Preview】偵錯程式,依次點選【檔案】----【Launch executable】,載入我們的專案檔案 ExampleCore_8_01.exe,進入到偵錯程式中。
                    直接【g】命令執行偵錯程式,直到我們的控制檯程式輸入“Press any key to start”字樣,我們在控制檯程式中,按任何鍵繼續執行。此時,偵錯程式會中斷執行,因為丟擲了異常。
                    我們可以使用【!clrstack】命令檢視一下呼叫棧的情況。

1 0:000> !clrstack
2 OS Thread Id: 0x3758 (0)
3         Child SP               IP Call Site
4 000000DB2357E988 00007ff87ecbcf19 [HelperMethodFrame: 000000db2357e988] 
5 000000DB2357EA80 00007fff3fb21afb ExampleCore_8_01.Program.ProcessData(System.String) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 22]
6 000000DB2357EAE0 00007fff3fb21a46 ExampleCore_8_01.Program.Run() [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 15]
7 000000DB2357EB30 00007fff3fb21988 ExampleCore_8_01.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 8]

                    說明我們的程式執行到 ProcessData 方法的第 22 行出現了問題,因為我們丟擲了異常。紅色標註的 22 就是原始碼中發生問題的行號。

                    接下來,我們使用【.dump /mf F:\Test\TestDump\08dumpfile.dmp】命令,生成轉儲檔案,儲存目錄在 F:\Test\TestDump 下,檔名稱是 08dumpfile.dmp

1 0:000> .dump /mf F:\Test\TestDump\08dumpfile.dmp
2 Creating F:\Test\TestDump\08dumpfile.dmp - mini user dump
3 Dump successfully written

                    當我們看到了“Dump successfully written”字樣時,說明 Dump 檔案寫成功了,在指定目錄就能看到該檔案。
                    我們有了 DUMP 檔案,我們再開啟一個【Windbg Preview】偵錯程式載入 DUMP 檔案就可以了。依次點選【檔案】----【Open dump file】,在右側透過瀏覽按鈕選擇我們的 Dump 檔案,點選【open】按鈕,就可以了。

 1 ************* Preparing the environment for Debugger Extensions Gallery repositories **************
 2    ExtensionRepository : Implicit
 3    UseExperimentalFeatureForNugetShare : false
 4    AllowNugetExeUpdate : false
 5    NonInteractiveNuget : true
 6    AllowNugetMSCredentialProviderInstall : false
 7    AllowParallelInitializationOfLocalRepositories : true
 8 
 9    EnableRedirectToV8JsProvider : false
10 
11    -- Configuring repositories
12       ----> Repository : LocalInstalled, Enabled: true
13       ----> Repository : UserExtensions, Enabled: true
14 
15 >>>>>>>>>>>>> Preparing the environment for Debugger Extensions Gallery repositories completed, duration 0.000 seconds
16 
17 ************* Waiting for Debugger Extensions Gallery to Initialize **************
18 
19 >>>>>>>>>>>>> Waiting for Debugger Extensions Gallery to Initialize completed, duration 0.032 seconds
20    ----> Repository : UserExtensions, Enabled: true, Packages count: 0
21    ----> Repository : LocalInstalled, Enabled: true, Packages count: 41
22 
23 Microsoft (R) Windows Debugger Version 10.0.27553.1004 AMD64
24 Copyright (c) Microsoft Corporation. All rights reserved.
25 
26 
27 Loading Dump File [F:\Test\TestDump\08dumpfile.dmp]
28 User Mini Dump File with Full Memory: Only application data is available
29 
30 
31 ************* Path validation summary **************
32 Response                         Time (ms)     Location
33 Deferred                                       srv*
34 Symbol search path is: srv*
35 Executable search path is: 
36 Windows 10 Version 19045 MP (4 procs) Free x64
37 Product: WinNt, suite: SingleUserTS
38 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
39 Debug session time: Wed Jun 26 13:41:21.000 2024 (UTC + 8:00)
40 System Uptime: 0 days 3:36:45.501
41 Process Uptime: 0 days 0:16:15.000
42 .......................................................
43 This dump file has an exception of interest stored in it.
44 The stored exception information can be accessed via .ecxr.
45 (43e0.3758): CLR exception - code e0434352 (first/second chance not available)
46 For analysis of this file, run !analyze -v
47 KERNELBASE!RaiseException+0x69:
48 00007ff8`7ecbcf19 0f1f440000      nop     dword ptr [rax+rax]

                    第一部分紅色標註的說明載入 DUMP 檔案的資訊,第二部分紅色標註的就是故障原因(CLR 異常)。
                    接下來,我們就可以使用各種命令除錯我們的程式了。


        4.1.2、透過 ADPlus 生成轉儲檔案
            A、基礎知識
                ADPlus 工具能夠監測和自動化一個或者多個故障程序的轉儲檔案生成過程,並且能夠在發生崩潰時通知使用者或者計算機。ADPlus 工具位於 windbg 安裝目錄,最早叫 adplus.vbs,以VBScript指令碼提供,最新版改成了adplus.exe。我們可以使用【cmd】命令開啟命令列工具,直接輸入 adplus 就可以執行,效果如下:

  1 C:\Users\Administrator>adplus
  2 Starting ADPlus
  3 ********************************************************
  4 *                                                      *
  5 * ADPLus Flash V 7.01.007 08/11/2011                   *
  6 *                                                      *
  7 *   For ADPlus documentation see ADPlus.doc            *
  8 *   New command line options:                          *
  9 *     -pmn <procname> - process monitor                *
 10 *        waits for a process to start                  *
 11 *     -po <procname> - optional process                *
 12 *        won't fail if this process isn't running      *
 13 *     -mss <LocalCachePath>                            *
 14 *        Sets Microsoft's symbol server                *
 15 *     -r <quantity> <interval in seconds>              *
 16 *        Runs -hang multiple times                     *
 17 *                                                      *
 18 *   ADPlusManager - an additional tool to facilitate   *
 19 *   the use of ADPlus in distributed environments like *
 20 *   computer clusters.                                 *
 21 *   Learn about ADPlusManager in ADPlus.doc            *
 22 *                                                      *
 23 ********************************************************
 24 
 25 
 26 ADPlus Version 7.01.007 08/11/2011
 27 
 28 ====================
 29 |  ADPlus Usage    |
 30 ====================
 31  Command line syntax options
 32         ADPlus -?   or 'ADPlus -help
 33             Displays this information.
 34 
 35         ADPlus -HelpConfig
 36             Displays the built-in key-words and the default behavior settings
 37  38 
 39         ADPlus <runmode> -o <OutputDirectory> [options]
 40         Run Modes:
 41             -Crash    Runs ADPlus in Crash mode
 42             -Hang     Runs ADPlus in Hang mode
 43 
 44 Selecting processes to attach
 45         -p <PID>  Defines a Process ID to be attached
 46         -pn <ProcessName> Defines a process name to be attached
 47         -po <ProcessName> Defines an optional process name to be attached
 48         -pmn <ProcessName> Defines a process name to be monitored
 49                 ADPlus will keep monitoring if a process with this name starts
 50                 and attach
 51         -sc <spawning command> Defines the application and parameters to be
 52                    started in the debugger
 53                    The -sc switch, if used, must be the last one
 54         -iis All iis related processes will be attached
 55                   like inetinfo, dllhost,mtx, etc.
 56 
 57 Symbol Path Options
 58 -y <symbol path>   Defines the symbol path to be used
 59 -yp <symbol path to add>   Defines an additional symbol path
 60 -mss <local cache>   Adds Microsoft Symbol Server to the symbol path
 61 
 62 Memory Dump Options
 63 -FullOnFirst   Sets ADPlus to create full dumps on first chance exceptions
 64 -MiniOnSecond  Sets ADPlus to create mini dumps on second chance exceptions
 65 -NoDumpOnFirst Sets ADPlus to not create any dumps on first chance exceptions
 66 -NoDumpOnSecond  Sets ADPlus to not create any dumps on second chance exceptions
 67 -do Dump Only - changes default behavior to not include additional info, just a dump
 68 
 69 Miscellaneous Options
 70 -c <config file name>   Defines a configuration file to be used
 71 -o <output directory>   Defines the directory where logs and dumps are
 72                          to be placed.
 73 -r <quantity> <interval in seconds> for multiple attachments in hang mode
 74 -dbg <debugger>  Allows you to select the debugger to be used
 75                     cdb, windbg or ntsd  (default is cdb)
 76 -dp    Debuggers path
 77 -gs    only generates the script file
 78 
 79 -ce <custom exception code>   Defines a custom exception to be monitored
 80                  -ce 0x80501001
 81 
 82 -bp <breakpoint parameters>   Sets a breakpoint
 83       Syntax: -bp address;optional_additional_parameters
 84                    -bp MyModule!MyClass::MyMethod
 85                    -bp MyModule!MyClass::MyMethod;MiniDump
 86 
 87 -CTCF  Creates a full dump on CTL+C, and quits
 88 -CTCFB  Creates a full dump on CTL+C, and breaks into the debugger
 89 -CTCV  No special action on CTL+C, just breaks in for user interaction
 90 -lcq  sets the last script command to Q (quit)
 91 -lcg  sets the last script command to G (go)
 92 -lcgn  sets the last script command to GN (go not handled)
 93 -lcqd  sets the last script command to QD (quit and detach)
 94 -lcv  sets the last script command to void (no command; waits for user input)
 95 -q2  sets the return action for second chance exceptions to Q (quit)
 96 -g2  sets the return action for second chance exceptions to GN (go not handled)
 97 
 98 
 99 -quiet    No dialog boxes will be displayed (no more required)
100 -notify <destination>  Will send a message to the destination
101 
102 
103 Examples:
104   ADPlus -hang -iis -o c:\dumps
105       Produces memory dumps of IIS and all
106       MTS/COM+ packages currently running.
107 
108   ADPlus -crash -p 1896 -o c:\dumps -mss c:\symbols
109       Attaches the debugger to process with PID 1896
110       and monitors it for 1st and 2nd chance access violations and uses
111       Microsoft's public symbol server with c:\symbols as a local cache
112 
113 -------------------------------------------------------------------------------
114 
115   HELP and Documentation
116 
117   For more detailed information on how to use and config ADPlus please see
118   the debugger's help file (debugger.chm) under Extra Tools
119     However, be aware that this is a new version of ADPlus and debugger.chm
120     may take some time to be updated
121   Check for ADPlus.doc in the debuggers' folder
122 -------------------------------------------------------------------------------
123 Current log content
124 
125 ADPlus Engine Version: 7.01.007 08/11/2011
126 Command line arguments used were:

                ADPlus.exe 不僅可以在程式崩潰時手動執行來生成dmp檔案,也可以在崩潰之前就執行它,當程式崩潰時它會自動生成dmp檔案;甚至可以在程式沒有執行之前就先執行adplus,當程式崩潰時它會自動生成dmp檔案。

                ADPlus 可以在以下兩種模式下執行:
                  1)、掛起模式(Hang Mode):用於分析出現掛起現象的程序(例如:程式不執行或者 100% 的 CPU 使用率)。ADPlus 必須在程序掛起之後啟動。
                  2)、崩潰模式(Crash Mode):用於分析出現崩潰行為的程序。ADPlus 必須在程序崩潰之後啟動。

                ADPlus 可以控制生成轉儲檔案的型別,共有四個命令列開關控制這種行為:
                  1)、-FullOnFirst:將 ADPlus 設定為在首次出現異常時建立完整轉儲檔案。
                  2)、-MiniOnSecond:將 ADPlus 設定為在第二次出現異常時建立微型轉儲檔案。
                  3)、-NoDumpOnFirst:將 ADPlus 設定為在首次出現異常時不建立任何微型轉儲檔案。
                  4)、-NoDumpOnSecond:將 ADPlus 設定為在第二次出現異常時不建立任何微型轉儲檔案。

                ADPlus 還為使用者提供了一種功能強大的方式來配置資訊收集的頻率以及在何種條件下收集資訊,尤其是為偵錯程式提供了一個指令碼前端。這樣做不過是採用了一種對使用者更友好的方式來執行偵錯程式命令,並將它們的執行過程自動化。

                如果我們想了解 ADPlus 命令的使用,可以使用【adplus -?】命令檢視該命令的使用方法和各種引數。

            B、眼見為實
                除錯原始碼:ExampleCore_8_01
                除錯任務:透過 ADPlus 生成轉儲檔案。
                1)、崩潰模式
                    首先,我們先編譯我們的專案,開啟專案的可以執行程式檔案,也就是 EXE 檔案,直接雙擊執行。我們的程式輸出“Press any key to start”字樣。
                    在繼續之前,我們開啟【Visual Studio 2022 Developer Command Prompt v17.9.6】或者【cmd】命令列工具都可以,執行【adplus -pn ExampleCore_8_01.exe -crash -o F:\Test\TestDump】命令,-crash 將 ADPlus 設定為崩潰模式,-pn 告訴 ADPlus 要監控的程序名稱,該引數的好處是它能夠監視由 name 指定的程序的任意數量例項。-o 表示檔案儲存的目錄地址。
                    執行效果如圖:
                    

                    當執行完成後,ADPlus 會把結果日誌檔案儲存到我們設定的目錄。效果如圖:
                    

                    它會新建一個目錄 20240627_152459_Crash_Mode,在該目錄下存放日誌檔案。當我們的程序發生關閉事件時,ADPlus 將生成一個完全轉儲檔案,我的檔名是:FULLDUMP_SecondChance_clr_NET_CLR_ExampleCore_8_01.exe__3c04_2024-06-27_16-15-43-200_2c28.dmp,效果如圖:
                    

                    檔名太長,截圖沒有顯示全部。我們看到在 F:\Test\TestDump\20240627_152459_Crash_Mode 目錄下,有一個檔案 DebuggerScript.txt,其中包含了在 ADPlus 會話中使用的所有偵錯程式命令。


        4.1.3、轉儲檔案的除錯
            因為轉儲檔案只是程序狀態的一個靜態快照,因此,我們不能在程式碼上設定斷點以及單步除錯。最好把轉儲檔案看成是一種手動除錯,在使用轉儲檔案時,仍然可以使用大多數的偵錯程式命令。
            在準備除錯轉儲檔案之前,需要先獲取兩個關鍵資訊:符號檔案和資料訪問層(Data Access Layer,DAC)。由於在轉儲檔案中不包含任何符號資訊,因此當分析轉儲檔案時,符號檔案是非常重要的。這裡的資料訪問層(DAC)指的是 CLR 資料訪問層,SOS 將透過這個資訊來提供在除錯會話中需要的資料。

        4.1.4、資料訪問層
            先說明一下:對於所有版本的.NET Framework,DAC 的檔名 mscordacwks.dll,SOS 除錯擴充套件的檔名 sos.dll,在 Net 跨平臺版本名稱改了,名稱是 mscordaccore.d
            我先說說在 Net Framework 環境下的情況。在非託管除錯環境中,許多資訊都可以透過觀察記憶體來收集,而在託管程式碼中,SOS 依靠 CLR 來提供我們的除錯輸出以及結果。為了使 SOS 能夠正確解析傳遞給它的原始資料,SOS 將呼叫 CLR (即執行CLR程式碼)來輔助執行這個過程。CLR 中負責實現這個功能的元件就是資料訪問層,它包含在 mscordacwks.dll 中。現在,隨著 CLR 被不斷地增強,底層的 DAC 同樣隨各個版本(包含補丁)的不同而變化。透過檢視機器上每個.NET版本的安裝資料夾可以很容易地驗證這一點。例如,在我的機器上,mscordacwks.dll 位於以下資料夾中:C:\Windows\Microsoft.NET\Framework\v4.0.30319。效果如圖:
            

            64 位的目錄:C:\Windows\Microsoft.NET\Framework64\v4.0.30319,效果如圖:

            
            由於偵錯程式在其操作期間需要用到這個元件,因此要知道偵錯程式這個檔案的位置。在實時除錯過程通常不需要關心這個問題,因為 SOS 能夠從當前被除錯的 CLR 所在位置上找到這個檔案。在事後除錯(或者轉儲檔案)中,在程式中使用的 CLR 版本可能與轉儲檔案所在機器上的 CLR 版本不同。再次重申,SOS 偵錯程式擴充套件將呼叫 mscordacwks.dll 中的函式,這個動態庫將執行 CLR 程式碼,因此為偵錯程式指定正確的版本是非常重要的。
            由於 CLR 版本的正確與否對於除錯是否成功至關重要,因此,微軟公佈了 mscordacwks.dll 大部分符號,放在 Microsoft 共有符號伺服器上。只要將偵錯程式的符號路徑指向共有符號伺服器(使用 symfix 或者其他相關的命令),那麼偵錯程式就能找到這個檔案。但是,有時候需要我們顯示告訴 SOS 擴充套件命令在什麼位置上查詢該檔案。這些情況:當檔案不在公共符號伺服器上或者檔案沒有安裝在與生成轉儲檔案的機器上的同一個路徑下。此時,我們就可以使用【.cordll】命令來控制 mscordacwks.dll 載入的方式,並在處理版本不匹配問題時節約大量的時間。
            接下來,我們看看【.cordll】命令的開關:
              1)、-l  在預設載入路徑中搜尋 DLL並載入除錯模組。
              2)、-u  從記憶體中解除安裝除錯模組。
              3)、-e  啟用 CLR 除錯。
              4)、-d  禁用 CLR 除錯
              5)、-D  禁用 CLR 除錯並解除安裝除錯模組。
              6)、-N  重新載入除錯模組。
              7)、-lp  指定除錯模組的目錄。
              8)、-se  啟用使用短名字版本的除錯模組,mscordacwks.dll。
              9)、-sd  禁用使用短名字的除錯模組,mscordacwks.dll。如果指定了這個開關,那麼除錯模組要以以下格式
載入:mscordacwks_<spec>.dll,其中<spec>的形式為<architecture>_<architecture ><file version>,而<Architecture >可以是x86 或者amd64。
              10)、-ve  啟用 verbose 模式。當處理不匹配問題時,verbose 模式是非常有用的,因為它能給出偵錯程式如何載入除錯模組的資訊
              11)、-vd  禁用 verbose 模式。

            接下來,我們開始講講在 Net 5.0 和以上版本的情況,包括 NET 6.0、NET 7.0、NET 8.0,以後還會有更新的版本,都包含在內。
            在 NET 跨平臺版本中,名稱和檔案存放位置都發生了很大的變化,現在的檔名稱是:mscordaccore.dll。當然這個檔案也有兩個版本,一個是 x86 架構的,一個是 64 架構的。我們安裝幾個版本的 .NET runtime,就會有幾個版本的 DAC 檔案與之匹配。我們可以使用【cmd】命令,開啟命令列工具,然後輸入【dotnet --info】命令,檢視 dotnet 具體詳情。

 1 C:\Users\Administrator>dotnet --info
 2 .NET SDK:
 3  Version:           8.0.204
 4  Commit:            c338c7548c
 5  Workload version:  8.0.200-manifests.7d36c14f
 6 
 7 執行時環境:
 8  OS Name:     Windows
 9  OS Version:  10.0.19045
10  OS Platform: Windows
11  RID:         win-x64
12  Base Path:   C:\Program Files\dotnet\sdk\8.0.204\
13 
14 已安裝 .NET 工作負載:
15 沒有要顯示的已安裝工作負載。
16 
17 Host:
18   Version:      8.0.4
19   Architecture: x64
20   Commit:       2d7eea2529
21 
22 .NET SDKs installed:
23   6.0.402 [C:\Program Files\dotnet\sdk]
24   7.0.302 [C:\Program Files\dotnet\sdk]
25   8.0.201 [C:\Program Files\dotnet\sdk]
26   8.0.204 [C:\Program Files\dotnet\sdk]
27 
28 .NET runtimes installed:
29   Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
30   Microsoft.AspNetCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
31   Microsoft.AspNetCore.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
32   Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
33   Microsoft.AspNetCore.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
34   Microsoft.AspNetCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
35   Microsoft.AspNetCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
36   Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
37   Microsoft.NETCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
38   Microsoft.NETCore.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
39   Microsoft.NETCore.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
40   Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
41   Microsoft.NETCore.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
42   Microsoft.NETCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
43   Microsoft.NETCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
44   Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
45   Microsoft.WindowsDesktop.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
46   Microsoft.WindowsDesktop.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
47   Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
48   Microsoft.WindowsDesktop.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
49   Microsoft.WindowsDesktop.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
50   Microsoft.WindowsDesktop.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
51 
52 Other architectures found:
53   x86   [C:\Program Files (x86)\dotnet]
54     registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
55 
56 Environment variables:
57   Not set
58 
59 global.json file:
60   Not found
61 
62 Learn more:
63   https://aka.ms/dotnet/info
64 
65 Download .NET:
66   https://aka.ms/dotnet/download

            這個命令的輸出內容確實很多,加粗標註的就是【Runtime(執行時)】的版本,我們也可以使用【dotnet --list-runtimes】命令,只檢視執行時。

 1 C:\Users\Administrator>dotnet --list-runtimes
 2 Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 3 Microsoft.AspNetCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 4 Microsoft.AspNetCore.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 5 Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 6 Microsoft.AspNetCore.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 7 Microsoft.AspNetCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 8 Microsoft.AspNetCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
 9 Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
10 Microsoft.NETCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
11 Microsoft.NETCore.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
12 Microsoft.NETCore.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
13 Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
14 Microsoft.NETCore.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
15 Microsoft.NETCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
16 Microsoft.NETCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
17 Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
18 Microsoft.WindowsDesktop.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
19 Microsoft.WindowsDesktop.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
20 Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
21 Microsoft.WindowsDesktop.App 7.0.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
22 Microsoft.WindowsDesktop.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
23 Microsoft.WindowsDesktop.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

            這些【執行時(Runtimes)】的目錄是【C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App】,說明是 64位架構的。說明一下,在這個目錄下是找不到 mscordaccore.dll 檔案的,該檔案的目錄是:C:\Program Files\dotnet\shared\Microsoft.NETCore.App,桌面程式(WindowsDesktop)和Asp.Net 程式(AspNetCore)共用這個。效果如圖:

            

            隨便開啟一個資料夾下,都能找到 mscordaccore.dll 和 coreclr.dll 檔案,我開啟 8.0.4 資料夾,效果如圖:

            

            以上是 64 位架構的,接下來,我們看看 x86 架構的,該檔案的地址目錄是:C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App。
            效果如圖:

            

            在這裡,我同樣開啟最新版本的資料夾,該資料夾名稱是 8.0.4,效果如圖:

            
            如何知道是否需要使用【.cordll】命令?如果存在版本不匹配的 mscordaccore.dll(在 Net Framework 版本里提示的是 mscordacwks.dll) ,那麼 SOS 除錯擴充套件將輸出以下錯誤資訊,在跨平臺版本里 mscordaccore.dll 和 coreclr.dll 是一一對應的,在 .Net Framework 版本里 mscordacwks.dll 和 mscorwks.dll 是一一對應的。
            說明一下,我已經載入了一個 DUMP 檔案,只是執行了一個【!t】命令,就會輸出一下內容:

 1 0:000> !t
 2 Failed to load data access module, 0x80004002
 3 Verify that 1) you have a recent build of the debugger (10.0.18317.1001 or newer)
 4             2) the file mscordaccore.dll that matches your version of coreclr.dll is
 5                 in the version directory or on the symbol path
 6             3) or, if you are debugging a dump file, verify that the file
 7                 mscordaccore_<arch>_<arch>_<version>.dll is on your symbol path.
 8             4) you are debugging on a platform and architecture that supports this
 9                 the dump file. For example, an ARM dump file must be debugged
10                 on an X86 or an ARM machine; an AMD64 dump file must be
11                 debugged on an AMD64 machine.
12 
13 You can run the command '!setclrpath <directory>' to control the load path of mscordaccore.dll.
14 
15 Or you can also run the debugger command .cordll to control the debugger's
16 load of mscordaccore.dll. .cordll -ve -u -l will do a verbose reload.
17 If that succeeds, the SOS command should work on retry.
18 
19 If you are debugging a minidump, you need to make sure that your executable
20 path is pointing to coreclr.dll as well.
21 
22 For more information see https://go.microsoft.com/fwlink/?linkid=2135652              

              我們仔細看看這篇建議,第一建議很簡單,就是要確保我們的偵錯程式是最新版本。第二條建議是要確保 mscordaccore.dll(原著中是 mscordacwks.dll,也就是 NET FRAMEWORK 版本)的版本和所載入的 coreclr.dll(原著中的是 mscorwks.dll,也就是 NET FRAMEWORK 版本)是相匹配的。第三條建議是如果你除錯的 DUMP 檔案,要確保 mscordaccore_<arch>_<arch>_<version>.dll 檔案位於符號路徑中。這個名字其實就是使用了 -sd 命令開關啟用長名字,長名字只是將這個 DLL 檔案對應的架構以及構建編號新增到 DLL 的名字中。然後,就可以更新符號路徑,指向這個 DLL,並執行【.cordll】命令來重新載入 mscordaccore.dll。
              例如:如果在生成轉儲檔案時使用的是 mscordaccore.dll 的版本為 1.1.1.0,架構為 x86,那麼就可以將 mscordaccore.dll 重新命名為 mscordaccore_x86_x86_1.1.1.0.dll,並將偵錯程式的符號路徑指向這個重新命名的位置,接著使用【.cordll】命令來重新載入除錯模組。效果如圖:
              
              第四條建議確保執行偵錯程式的所在的架構與生成轉儲檔案的架構相同。由於偵錯程式要執行 DAC 中的程式碼,因此,用於除錯轉儲檔案的偵錯程式的架構資訊與建立轉儲檔案時使用的偵錯程式架構資訊要完全一樣。
              輸出的最後一行,要求可執行路徑指向 mscordaccore.dll。可執行路徑可以在偵錯程式中透過【.exepath】命令來控制。如果要新增可執行路徑,可以使用【!exepath +】命令。
              如果無法找到在生成轉儲檔案時使用的 DLL 正確版本,最簡單的方法就是,要求生成轉儲檔案的人員把相應的 mscordaccore.dll 檔案發給你,在收到檔案後,在按照之前給出的策略來載入它。在成功載入之後,SOS 偵錯程式擴充套件會充分發揮其功能。

              我們可以使用【Windbg Preview】偵錯程式,輸入【.cordll】命令,可以載入 mscordaccore.dll

1 0:000> .cordll
2 CLR DLL status: Loaded DLL C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\mscordaccore.dll               

            
        4.1.5、轉儲檔案分析:未處理的 .NET 異常。
            除錯原始碼:根據 ExampleCore_8_01 生成的 DUMP 檔案。
            除錯任務:使用偵錯程式分析 DUMP 檔案。
            1)、NTSD 除錯
                開啟【Visual Studio 2022 Developer Command Prompt v17.9.6】,輸入命令【NTSD -z F:\Test\TestDump\08dumpfile.dmp】開啟偵錯程式。
                我們成功開啟偵錯程式,並且偵錯程式已經中斷執行,因為出現了異常。

 1 Microsoft (R) Windows Debugger Version 10.0.22621.2428 AMD64
 2 Copyright (c) Microsoft Corporation. All rights reserved.
 3 
 4 
 5 Loading Dump File [F:\Test\TestDump\08dumpfile.dmp]--成功載入我們的 DUMP 檔案
 6 User Mini Dump File with Full Memory: Only application data is available
 7 
 8 Symbol search path is: srv*
 9 Executable search path is:
10 Windows 10 Version 19045 MP (4 procs) Free x64
11 Product: WinNt, suite: SingleUserTS
12 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
13 Machine Name:
14 Debug session time: Wed Jun 26 13:41:21.000 2024 (UTC + 8:00)
15 System Uptime: 0 days 3:36:45.501
16 Process Uptime: 0 days 0:16:15.000
17 ...................................
18 This dump file has an exception of interest stored in it.
19 The stored exception information can be accessed via .ecxr.
20 (43e0.3758): CLR exception - code e0434352 (first/second chance not available)發生了異常
21 For analysis of this file, run !analyze -v
22 KERNELBASE!RaiseException+0x69:
23 00007ff8`7ecbcf19 0f1f440000      nop     dword ptr [rax+rax]

                以上資訊說明,這個 DUMP 是由於引發了一個 CLR 異常而生成的。接著,我們檢視異常的詳細資訊,包括傳遞的引數,使用【kb】命令。

 1 0:000> kb
 2 RetAddr               : Args to Child                                                           : Call Site
 3 00007fff`9f5eb3a3     : 000001e5`f8809ce8 000000db`2357e7d0 00007fff`3fbe1080 00007fff`3fbd5e00 : KERNELBASE!RaiseException+0x69(丟擲異常)
 4 00007fff`9f5ead49     : 00000000`00000004 00000000`00000001 000000db`2357ec18 000000db`2357edb8 : coreclr!RaiseTheExceptionInternalOnly+0x26b
 5 00007fff`3fb21afb     : 000001e5`f8809ce8 00000226`8aad0a38 000001e5`f44cb1e8 00000226`8aae0000 : coreclr!IL_Throw
 6 00007fff`3fb21a46     : 000001e5`f8809630 00000000`00000000 000001e5`f44af4d0 000001e5`f8809c40 : 0x00007fff`3fb21afb
 7 00007fff`3fb21988     : 000001e5`f8809630 000001e5`f8809648 000000db`2357f1b8 000000db`2357eda9 : 0x00007fff`3fb21a46
 8 00007fff`9f68b8d3     : 000001e5`f8808e98 000000db`2357f1b8 000000db`2357f1b8 000000db`2357eda9 : 0x00007fff`3fb21988
 9 00007fff`9f5c0b19     : 00000000`00000000 00000000`00000130 000000db`2357edb8 00007fff`9f540232 : coreclr!CallDescrWorkerInternal+0x83
10 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56
11 00007fff`9f5bd730     : 000000db`2357ee38 00000000`00000000 00000000`00000048 00007fff`9f62d046 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1
12 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb
13 00007fff`9f5e2fc6     : 000001e5`f8808e98 000001e5`f8808e98 00000000`00000000 000000db`2357f1b8 : coreclr!RunMainInternal+0x11c
14 00007fff`9f5e32fb     : 000001e5`f44af4d0 000001e5`00000000 000001e5`f44af4d0 00000000`00000000 : coreclr!RunMain+0xd2
15 00007fff`9f539141     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf
16 00007fff`9f64e8b8     : 00000000`00000001 000000db`2357f301 000000db`2357f3e0 00007fff`9fa223ea : coreclr!CorHost2::ExecuteAssembly+0x281
17 00007fff`9fa42b76     : 000001e5`f4481250 000001e5`f4481030 00000000`00000000 000001e5`f4481030 : coreclr!coreclr_execute_assembly+0xd8
18 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a
19 00007fff`9fa42e5c     : 000001e5`f446de98 000000db`2357f609 00007fff`9fa7ca10 000001e5`f446de98 : hostpolicy!run_app_for_context+0x596
20 00007fff`9fa4379a     : 00000000`00000000 000001e5`f446de90 000001e5`f446de90 00000000`00000000 : hostpolicy!run_app+0x3c
21 00007fff`9fa9b5c9     : 000001e5`f447e468 000001e5`f447e350 00000000`00000000 000000db`2357f709 : hostpolicy!corehost_main+0x15a
22 00007fff`9fa9e066     : 000001e5`f447ddc0 000000db`2357fa90 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9
23 00007fff`9faa02ec     : 00007fff`9fad25f8 000001e5`f447c530 000000db`2357f9d0 000000db`2357f980 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6
24 00007fff`9fa9e644     : 000000db`2357fa90 000000db`2357fab0 000000db`2357fa01 000001e5`f447c9a0 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c
25 00007fff`9fa985a0     : 000000db`2357fab0 000001e5`f447fdf0 00000000`00000001 000001e5`f4460000 : hostfxr!fx_muxer_t::execute+0x494
26 *** WARNING: Unable to verify checksum for ExampleCore_8_01.exe
27 00007ff7`ae24f998     : 00007ff8`7f40f4e8 00007fff`9fa99b10 000000db`2357fc50 000001e5`f447bf30 : hostfxr!hostfxr_main_startupinfo+0xa0
28 00007ff7`ae24fda6     : 00007ff7`ae25b6c0 00000000`00000007 000001e5`f446de90 00000000`0000005e : ExampleCore_8_01_exe!exe_start+0x878
29 00007ff7`ae2512e8     : 00000000`00000000 00000000`00000000 000001e5`f446de90 00000000`00000000 : ExampleCore_8_01_exe!wmain+0x146
30 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : ExampleCore_8_01_exe!invoke_main+0x22
31 00007ff8`801d7344     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ExampleCore_8_01_exe!__scrt_common_main_seh+0x10c
32 00007ff8`816026b1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
33 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

                我們看堆疊知道最後丟擲異常,紅色標註的地址就是異常物件的具體地址。我們可以使用【!do 000001e5`f8809ce8】命令或者【!DumpObj 000001e5`f8809ce8】,這個命令輸入的內容太多。

 1 0:000> !do 000001e5`f8809ce8
 2 Name:        System.ArgumentNullException(空引用異常,就是我們丟擲的)
 3 MethodTable: 00007fff3fc22cb0
 4 EEClass:     00007fff3fbef210
 5 Tracked Type: false
 6 Size:        136(0x88) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Private.CoreLib.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007fff3fc77688  4000264        8 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
11 00007fff3fb0ec08  4000265       10        System.String  0 instance 000001e5f880d328 _message
12 00007fff3fba3060  4000266       18 ...tions.IDictionary  0 instance 0000000000000000 _data
13 00007fff3fba0820  4000267       20     System.Exception  0 instance 0000000000000000 _innerException
14 00007fff3fb0ec08  4000268       28        System.String  0 instance 0000000000000000 _helpURL
15 00007fff3fc09d98  4000269       30        System.Byte[]  0 instance 000001e5f880d4c8 _stackTrace
16 00007fff3fc09d98  400026a       38        System.Byte[]  0 instance 0000000000000000 _watsonBuckets
17 00007fff3fb0ec08  400026b       40        System.String  0 instance 0000000000000000 _stackTraceString
18 00007fff3fb0ec08  400026c       48        System.String  0 instance 0000000000000000 _remoteStackTraceString
19 00007fff3fa5c4d8  400026d       50      System.Object[]  0 instance 0000000000000000 _dynamicMethods
20 00007fff3fb0ec08  400026e       58        System.String  0 instance 0000000000000000 _source
21 00007fff3fb08b78  400026f       60       System.UIntPtr  1 instance 00007FFF3FB21AFA _ipForWatsonBuckets
22 00007fff3fb070a0  4000270       68        System.IntPtr  1 instance 0000000000000000 _xptrs
23 00007fff3fa91188  4000271       70         System.Int32  1 instance       -532462766 _xcode
24 00007fff3fa91188  4000272       74         System.Int32  1 instance      -2147467261 _HResult
25 00007fff3fb0ec08  4000383       78        System.String  0 instance 000002268aad0a38 _paramName

                我們也可以使用【!pe】命令,這個命令輸出就很簡潔了。

 1 0:000> !pe 000001e5`f8809ce8
 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: 000001e5f8809ce8
 6 Exception type:   System.ArgumentNullException
 7 Message:          Value cannot be null.
 8 InnerException:   <none>
 9 StackTrace (generated):
10     SP               IP               Function
11     000000DB2357EA80 00007FFF3FB21AFB ExampleCore_8_01!ExampleCore_8_01.Program.ProcessData(System.String)+0x8b
12     000000DB2357EAE0 00007FFF3FB21A46 ExampleCore_8_01!ExampleCore_8_01.Program.Run()+0x46
13     000000DB2357EB30 00007FFF3FB21988 ExampleCore_8_01!ExampleCore_8_01.Program.Main(System.String[])+0x58
14 
15 StackTraceString: <none>
16 HResult: 80004003

                在這裡看的就很清楚了,異常型別,錯誤碼和呼叫堆疊都是一目瞭然。


            2)、Windbg Preview 除錯
                我們開啟【Windbg Preview】偵錯程式,依次點選【檔案】----【Open dump file】,在右側選擇我們的 Dump 檔案,點選【open】按鈕,就開啟了偵錯程式。
                由於我們除錯的是 Dump 檔案,所以除錯剛開始的輸出也是不一樣的。

 1 ************* Preparing the environment for Debugger Extensions Gallery repositories **************
 2    ExtensionRepository : Implicit
 3    UseExperimentalFeatureForNugetShare : false
 4    AllowNugetExeUpdate : false
 5    NonInteractiveNuget : true
 6    AllowNugetMSCredentialProviderInstall : false
 7    AllowParallelInitializationOfLocalRepositories : true
 8 
 9    EnableRedirectToV8JsProvider : false
10 
11    -- Configuring repositories
12       ----> Repository : LocalInstalled, Enabled: true
13       ----> Repository : UserExtensions, Enabled: true
14 
15 >>>>>>>>>>>>> Preparing the environment for Debugger Extensions Gallery repositories completed, duration 0.000 seconds
16 
17 ************* Waiting for Debugger Extensions Gallery to Initialize **************
18 
19 >>>>>>>>>>>>> Waiting for Debugger Extensions Gallery to Initialize completed, duration 0.031 seconds
20    ----> Repository : UserExtensions, Enabled: true, Packages count: 0
21    ----> Repository : LocalInstalled, Enabled: true, Packages count: 41
22 
23 Microsoft (R) Windows Debugger Version 10.0.27553.1004 AMD64
24 Copyright (c) Microsoft Corporation. All rights reserved.
25 
26 
27 Loading Dump File [F:\Test\TestDump\08dumpfile.dmp]
28 User Mini Dump File with Full Memory: Only application data is available
29 
30 
31 ************* Path validation summary **************
32 Response                         Time (ms)     Location
33 Deferred                                       srv*
34 Symbol search path is: srv*
35 Executable search path is: 
36 Windows 10 Version 19045 MP (4 procs) Free x64
37 Product: WinNt, suite: SingleUserTS
38 Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
39 Debug session time: Wed Jun 26 13:41:21.000 2024 (UTC + 8:00)
40 System Uptime: 0 days 3:36:45.501
41 Process Uptime: 0 days 0:16:15.000
42 .......................................................
43 This dump file has an exception of interest stored in it.
44 The stored exception information can be accessed via .ecxr.
45 (43e0.3758): CLR exception - code e0434352 (first/second chance not available)
46 For analysis of this file, run !analyze -v
47 KERNELBASE!RaiseException+0x69:
48 00007ff8`7ecbcf19 0f1f440000      nop     dword ptr [rax+rax]

                紅色標註的都說明了載入了我們的 Dump 檔案,並且由於發生了一個 CLR 異常生成的轉儲檔案。
                我們可以使用【!clrstack】命令看看託管呼叫堆疊,這裡有用的資訊不多。

1 0:000> !clrstack
2 OS Thread Id: 0x3758 (0)
3         Child SP               IP Call Site
4 000000DB2357E988 00007ff87ecbcf19 [HelperMethodFrame: 000000db2357e988] 
5 000000DB2357EA80 00007fff3fb21afb ExampleCore_8_01.Program.ProcessData(System.String) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 22]
6 000000DB2357EAE0 00007fff3fb21a46 ExampleCore_8_01.Program.Run() [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 15]
7 000000DB2357EB30 00007fff3fb21988 ExampleCore_8_01.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 8]

                我們可以使用【kb】命令檢視非託管呼叫棧,包括引數。

 1 0:000> kb
 2 *** WARNING: Unable to verify checksum for ExampleCore_8_01.exe
 3  # RetAddr               : Args to Child                                                           : Call Site
 4 00 00007fff`9f5eb3a3     : 000001e5`f8809ce8 000000db`2357e7d0 00007fff`3fbe1080 00007fff`3fbd5e00 : KERNELBASE!RaiseException+0x69
 5 01 00007fff`9f5ead49     : 00000000`00000004 00000000`00000001 000000db`2357ec18 000000db`2357edb8 : coreclr!RaiseTheExceptionInternalOnly+0x26b [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 2795] 
 6 02 00007fff`3fb21afb     : 000001e5`f8809ce8 00000226`8aad0a38 000001e5`f44cb1e8 00000226`8aae0000 : coreclr!IL_Throw+0xb9 [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 4247] 
 7 03 00007fff`3fb21a46     : 000001e5`f8809630 00000000`00000000 000001e5`f44af4d0 000001e5`f8809c40 : ExampleCore_8_01!ExampleCore_8_01.Program.ProcessData+0x8b [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 24] 
 8 04 00007fff`3fb21988     : 000001e5`f8809630 000001e5`f8809648 000000db`2357f1b8 000000db`2357eda9 : ExampleCore_8_01!ExampleCore_8_01.Program.Run+0x46 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 15] 
 9 05 00007fff`9f68b8d3     : 000001e5`f8808e98 000000db`2357f1b8 000000db`2357f1b8 000000db`2357eda9 : ExampleCore_8_01!ExampleCore_8_01.Program.Main+0x58 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_8_01\Program.cs @ 8] 
10 06 00007fff`9f5c0b19     : 00000000`00000000 00000000`00000130 000000db`2357edb8 00007fff`9f540232 : coreclr!CallDescrWorkerInternal+0x83 [D:\a\_work\1\s\src\coreclr\vm\amd64\CallDescrWorkerAMD64.asm @ 100] 
11 07 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!CallDescrWorkerWithHandler+0x56 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 67] 
12 08 00007fff`9f5bd730     : 000000db`2357ee38 00000000`00000000 00000000`00000048 00007fff`9f62d046 : coreclr!MethodDescCallSite::CallTargetWorker+0x2a1 [D:\a\_work\1\s\src\coreclr\vm\callhelpers.cpp @ 570] 
13 09 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDescCallSite::Call+0xb [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 458] 
14 0a 00007fff`9f5e2fc6     : 000001e5`f8808e98 000001e5`f8808e98 00000000`00000000 000000db`2357f1b8 : coreclr!RunMainInternal+0x11c [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1304] 
15 0b 00007fff`9f5e32fb     : 000001e5`f44af4d0 000001e5`00000000 000001e5`f44af4d0 00000000`00000000 : coreclr!RunMain+0xd2 [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1375] 
16 0c 00007fff`9f539141     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000130 : coreclr!Assembly::ExecuteMainMethod+0x1bf [D:\a\_work\1\s\src\coreclr\vm\assembly.cpp @ 1504] 
17 0d 00007fff`9f64e8b8     : 00000000`00000001 000000db`2357f301 000000db`2357f3e0 00007fff`9fa223ea : coreclr!CorHost2::ExecuteAssembly+0x281 [D:\a\_work\1\s\src\coreclr\vm\corhost.cpp @ 349] 
18 0e 00007fff`9fa42b76     : 000001e5`f4481250 000001e5`f4481030 00000000`00000000 000001e5`f4481030 : coreclr!coreclr_execute_assembly+0xd8 [D:\a\_work\1\s\src\coreclr\dlls\mscoree\exports.cpp @ 504] 
19 0f (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : hostpolicy!coreclr_t::execute_assembly+0x2a [D:\a\_work\1\s\src\native\corehost\hostpolicy\coreclr.cpp @ 109] 
20 10 00007fff`9fa42e5c     : 000001e5`f446de98 000000db`2357f609 00007fff`9fa7ca10 000001e5`f446de98 : hostpolicy!run_app_for_context+0x596 [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 256] 
21 11 00007fff`9fa4379a     : 00000000`00000000 000001e5`f446de90 000001e5`f446de90 00000000`00000000 : hostpolicy!run_app+0x3c [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 285] 
22 12 00007fff`9fa9b5c9     : 000001e5`f447e468 000001e5`f447e350 00000000`00000000 000000db`2357f709 : hostpolicy!corehost_main+0x15a [D:\a\_work\1\s\src\native\corehost\hostpolicy\hostpolicy.cpp @ 426] 
23 13 00007fff`9fa9e066     : 000001e5`f447ddc0 000000db`2357fa90 00000000`00000000 00000000`00000000 : hostfxr!execute_app+0x2e9 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 145] 
24 14 00007fff`9faa02ec     : 00007fff`9fad25f8 000001e5`f447c530 000000db`2357f9d0 000000db`2357f980 : hostfxr!`anonymous namespace'::read_config_and_execute+0xa6 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 532] 
25 15 00007fff`9fa9e644     : 000000db`2357fa90 000000db`2357fab0 000000db`2357fa01 000001e5`f447c9a0 : hostfxr!fx_muxer_t::handle_exec_host_command+0x16c [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 1007] 
26 16 00007fff`9fa985a0     : 000000db`2357fab0 000001e5`f447fdf0 00000000`00000001 000001e5`f4460000 : hostfxr!fx_muxer_t::execute+0x494 [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 578] 
27 17 00007ff7`ae24f998     : 00007ff8`7f40f4e8 00007fff`9fa99b10 000000db`2357fc50 000001e5`f447bf30 : hostfxr!hostfxr_main_startupinfo+0xa0 [D:\a\_work\1\s\src\native\corehost\fxr\hostfxr.cpp @ 62] 
28 18 00007ff7`ae24fda6     : 00007ff7`ae25b6c0 00000000`00000007 000001e5`f446de90 00000000`0000005e : ExampleCore_8_01_exe!exe_start+0x878 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 240] 
29 19 00007ff7`ae2512e8     : 00000000`00000000 00000000`00000000 000001e5`f446de90 00000000`00000000 : ExampleCore_8_01_exe!wmain+0x146 [D:\a\_work\1\s\src\native\corehost\corehost.cpp @ 311] 
30 1a (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : ExampleCore_8_01_exe!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90] 
31 1b 00007ff8`801d7344     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ExampleCore_8_01_exe!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
32 1c 00007ff8`816026b1     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
33 1d 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

                000001e5`f8809ce8 這個引數就是異常例項的地址,我們可以使用【!do 000001e5`f8809ce8】命令檢視,但是使用【!pe 000001e5`f8809ce8】命令會更好。

 1 0:000> !pe 000001e5`f8809ce8
 2 Exception object: 000001e5f8809ce8
 3 Exception type:   System.ArgumentNullException
 4 Message:          Value cannot be null.
 5 InnerException:   <none>
 6 StackTrace (generated):
 7     SP               IP               Function
 8     000000DB2357EA80 00007FFF3FB21AFB ExampleCore_8_01!ExampleCore_8_01.Program.ProcessData(System.String)+0x8b
 9     000000DB2357EAE0 00007FFF3FB21A46 ExampleCore_8_01!ExampleCore_8_01.Program.Run()+0x46
10     000000DB2357EB30 00007FFF3FB21988 ExampleCore_8_01!ExampleCore_8_01.Program.Main(System.String[])+0x58
11 
12 StackTraceString: <none>
13 HResult: 80004003

                這裡的資訊就很清楚了,異常型別,呼叫堆疊一目瞭然。


    4.2、Windows 錯誤報告
        這一節的內容挺不好寫的,因為這裡面的內容發生了很大的變化,如果照著原文寫,很多內容會過時的,所以,這節就簡寫了,我會給出微軟最先有關文章的連線,大家可以去學習。
        Windows 錯誤報告(Windows Error Reporting,WER)是一種聚合故障資料的服務,使得 Microsoft 和獨立軟體供應商(Independent Software Vendor,ISV)可以很容易的訪問與他們程式相關的故障資料。
        我們先上一個圖,來說明一下 WER 服務的操作流程。
        

        假設在世界的某個地方,有一臺計算機正在執行由 ADND 企業開發的一個程式(就是圖中的 X 程序)。假設這個程式崩潰了,並且使用者看到了 Dr.Watson 介面並詢問是否將錯誤報告傳送給 Microsoft。使用者選擇了傳送,並且錯誤報告將透過安全通道(HTTPS)傳送給 WER 服務。然後,WER 將收到錯誤報告進行分門別類並儲存。要使用這些錯誤報告,來之 ADND 企業的使用者需要查詢 WER 服務,找出與其程式相關的崩潰並且獲得報告的錯誤資訊。如果 ADND 得到了這些錯誤報告,那麼就可以修正這個問題,並且提供一個回應,這樣下一次當使用者遇到了想通的崩潰情況時,Dr. Watson 將給出相應的回應。這個回應可以是一個補丁或者是其他一些幫助資訊。
        WER 服務是一種功能非常強大的機制,提供了對錯誤報告的聚合功能,ISV 可以查詢這些資訊來改程序序。此外,ISV 可以提供問題的回應,並且這個回應會整合到 WER 反饋迴圈中,從而可以使使用者很容易得到回應。
        要想使用 WER 服務,必須向 Windows 錯誤報告服務註冊,剩下的內容就是如何註冊賬號和使用了,內容太老就不多說了。

        在文章的最後,如果大家想了解 WER 最新的使用方法,我把 Microsoft 官網文章地址貼出來,大家可以自行學習。
        地址如下:https://learn.microsoft.com/zh-cn/windows/win32/wer/windows-error-reporting

五、總結
    這篇文章的終於寫完了,這篇文章的內容相對來說,不是很多。寫完一篇,就說明進步了一點點。Net 高階除錯這條路,也剛剛起步,還有很多要學的地方。皇天不負有心人,努力,不辜負自己,我相信付出就有回報,再者說,學習的過程,有時候,雖然很痛苦,但是,學有所成,學有所懂,這個開心的感覺還是不可言喻的。不忘初心,繼續努力。做自己喜歡做的,開心就好。

相關文章