Net 高階偵錯之一:開始認識一些偵錯工具

2023-10-24 15:00:23

一、簡介
    從今天開始一個長系列,Net 高階偵錯的相關文章,我自從學習了之後,以前很多模糊的地方現在很清楚了,原來自己的功力還是不夠,所以有很多不明白,通過學習 Net 高階偵錯,眼前豁然開朗,茅塞頓開。其實,剛開始要學習《Net 高階偵錯》,還是很是很困難的,很多工具不會用,又不知道如何偵錯,痛苦的又很多次想放棄,但是,最終還是堅持下來,收穫也不小。
    既然堅持下來了,我就把學習的過程記錄下來,也許以後自己的能用的到,可以方便查詢。或許,有其他人也有同樣的困擾,或許可以在我這裡得到一些幫助,有幫助我當然很開心。當然,Net 高階偵錯的路還很遠,我也是剛起步,不足之處太多,也希望大家原諒,有不對之處,歡迎指正。我入門《Net 高階偵錯》,還要感謝【 一線碼農】的視訊,幫了我很多的忙。
    偵錯環境
        作業系統:Windows Professional 10
        偵錯工具:Windbg Preview(可以去Microsoft Store 去下載)
        開發工具:Visual Studio 2022
        Net 版本:Net Framework 4.8
        CoreCLR原始碼:原始碼下載

二、偵錯工具介紹

    俗話說得好,工欲善其事,必先利其器,我們要想偵錯程式,必須有很好的工具,如果連偵錯工具都沒有,那真就成了巧婦難為無米之炊。所以,接下來,我先介紹一些偵錯工具,每種偵錯工具都有各自的用途。

    2.0、測試程式碼
        我們想要演示 Windbg 的使用過程,使用方法,偵錯程式的各種問題,必須有程式作為載體,由於這是【Net 高階偵錯】的第一節課,所以只是簡單的演示一下,例子程式碼沒有實際的作用,作為演示還是夠了的。
         本節有兩分程式碼,分別是:Example_1_1_1和 Example_1_1_2
         Example_1_1_1的程式碼如下:

 1 namespace Example_1_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("Hello World");
 8             Console.ReadLine();
 9         }
10     }
11 }


        
Example_1_1_2的程式碼如下:

 1 namespace Example_1_1_2
 2 {
 3     internal class Program
 4     {
 5         private static IList<byte[]> list=new List<byte[]>();
 6 
 7         static void Main(string[] args)
 8         {
 9             Task.Run(() =>
10             {
11                 for (int i = 0; i < int.MaxValue; i++)
12                 {
13                     list.Add(new byte[10000]);
14                     if (i % 10 == 0)
15                     {
16                         list[i] = null;
17                     }
18                     Console.WriteLine($"當前索引 Index={i}");
19                 }
20             });
21             Console.ReadLine();
22         }
23     }
24 }


     2.1、SOS
       【
SOS 偵錯擴充套件】允許我們檢視有關在 CLR 內執行的程式碼的資訊。 例如,可以使用 【SOS 偵錯擴充套件】顯示有關【託管堆】的資訊、查詢堆損壞情況、顯示【執行時】所使用的內部資料型別以及檢視有關在【執行時】內執行的所有受控程式碼的資訊。它就是一個 dll,包含一組存取 CLR 內部資料的介面函數,可以使我們使用 Windbg 偵錯程式偵錯 Net 程式,解決程式問題的時候更簡單。

            Windbg-----------------SOS------------CLR,這是一個有關SOS的示意圖,SOS的作用就像一箇中介者一樣,Windbg可以通過 SOS 來偵錯 CLR。

            
2.1.1、檔案位置
              
這個程式集是隨.NET Framework一起安裝的,一般不需要單獨安裝。SOS 偵錯擴充套件是有2個版本的,分別是32位元和64位元,安裝的位置如下
                32位元安裝位置:C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll
                 

         
                64位元安裝位置:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll
                 


            
2.1.2、如何載入
           
Windbg Preview 是不用單獨執行載入的工作的,它會自動載入它所需要的版本,如果是老版本的 Windbg,比如:windbg10 ,可以通過 .load 命令載入 SOS.dll。一般情況,使用windbg自帶的命令【.load sos】即可自動載入,使用【.chain】檢視載入是否成功。
               如果沒有載入 SOS.dll,我們可以手動載入,執行如下命令:

0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll

              我們可以通過【.chain】命令檢查是否成功載入 SOS.dll。紅色字型顯示已經載入了 SOS 偵錯擴充套件。

 1 0:000> .chain
 2 Extension DLL search Path:
 3     C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\arcade;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\pri;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86;C:\Users\Administrator\AppData\Local\Dbg\EngineExtensions32;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\amd64;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\dotnet\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\DTS\Binn\;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\;D:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;D:\XIMEA\API;C:\XIMEA\API;D:\Program Files\Git\cmd;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Users\Administrator\.dotnet\tools
 4 Extension DLL chain:
 5     C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll: image 4.8.4300.0, API 1.0.0, built Thu Oct  8 08:41:14 2020
 6         [path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll]
 7     JsProvider: image 10.0.25877.1004, API 0.0.0, 
 8         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\JsProvider.dll]
 9     DbgModelApiXtn: image 10.0.25877.1004, API 0.0.0, 
10         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\DbgModelApiXtn.dll]
11     F:\Software\DebugTools\SOS\SOSEX\sosex_32\sosex.dll: image 4.5.0.0, API 1.0.0, built Fri Mar  7 23:17:26 2014
12         [path: F:\Software\DebugTools\SOS\SOSEX\sosex_32\sosex.dll]
13     CLRComposition: image 10.0.25877.1004, API 0.0.0, 
14         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\CLRComposition.dll]
15     wow64exts: image 10.0.25877.1004, API 1.0.0, 
16         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP\wow64exts.dll]
17     dbghelp: image 10.0.25877.1004, API 10.0.6, 
18         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\dbghelp.dll]
19     exts: image 10.0.25877.1004, API 1.0.0, 
20         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP\exts.dll]
21     uext: image 10.0.25877.1004, API 1.0.0, 
22         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\uext.dll]
23     ntsdexts: image 10.0.25877.1004, API 1.0.0, 
24         [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP\ntsdexts.dll]

            2.1.3、如何使用
                
說到是第一節講 Windbg 使用的文章,所以具體的使用步驟還是要說明的詳細一點。程式碼案例:Example_1_1_1
                1)、載入程式集
                  A、編譯程式原始碼,生成 Dll 或者是 Exe 程式集,可以在拷貝地址,當然這是我的習慣,你可以選擇 Windbg 查詢檔案也是可以的。
                       

                  B、開啟 Windbg 偵錯程式。通過選單選擇【檔案】-->【launch executable】,彈出視窗,找到指定的程式集檔案,選擇開啟,就進入了 Windbg 偵錯程式頁面,是暫停的狀態,此時,就可以根據自己的需要,選擇下一步的操作。
                      

                     

        
              2)我們執行一些命令,來一個直觀感覺。
                     A、.cls 剛進來,內容太多,可以清楚一下螢幕。
                        .cls

                        

                          【Debuggee is running】其實這裡是停在了 Console.ReadLine();這行程式碼這裡,點選【Break】按鈕,我們就行偵錯了。

                        


                     
D、~os 切換到主執行緒。

1 0:001> ~0s
2 eax=00000000 ebx=000000a4 ecx=00000000 edx=00000000 esi=004ff10c edi=00000000
3 eip=773410fc esp=004feff4 ebp=004ff054 iopl=0         nv up ei pl nz na po nc
4 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
5 ntdll!NtReadFile+0xc:
6 773410fc c22400          ret     24h

                     E、!sos.help 我們可以檢視 SOS 的所有命令。

 1 0:000> !sos.help
 2 -------------------------------------------------------------------------------
 3 SOS is a debugger extension DLL designed to aid in the debugging of managed
 4 programs. Functions are listed by category, then roughly in order of
 5 importance. Shortcut names for popular functions are listed in parenthesis.
 6 Type "!help <functionname>" for detailed info on that function. 
 7 
 8 Object Inspection                  Examining code and stacks
 9 -----------------------------      -----------------------------
10 DumpObj (do)                       Threads
11 DumpArray (da)                     ThreadState
12 DumpStackObjects (dso)             IP2MD
13 DumpHeap                           U
14 DumpVC                             DumpStack
15 GCRoot                             EEStack
16 ObjSize                            CLRStack
17 FinalizeQueue                      GCInfo
18 PrintException (pe)                EHInfo
19 TraverseHeap                       BPMD 
20                                    COMState
21 
22 Examining CLR data structures      Diagnostic Utilities
23 -----------------------------      -----------------------------
24 DumpDomain                         VerifyHeap
25 EEHeap                             VerifyObj
26 Name2EE                            FindRoots
27 SyncBlk                            HeapStat
28 DumpMT                             GCWhere
29 DumpClass                          ListNearObj (lno)
30 DumpMD                             GCHandles
31 Token2EE                           GCHandleLeaks
32 EEVersion                          FinalizeQueue (fq)
33 DumpModule                         FindAppDomain
34 ThreadPool                         SaveModule
35 DumpAssembly                       ProcInfo 
36 DumpSigElem                        StopOnException (soe)
37 DumpRuntimeTypes                   DumpLog
38 DumpSig                            VMMap
39 RCWCleanupList                     VMStat
40 DumpIL                             MinidumpMode 
41 DumpRCW                            AnalyzeOOM (ao)
42 DumpCCW
43 
44 Examining the GC history           Other
45 -----------------------------      -----------------------------
46 HistInit                           FAQ
47 HistRoot
48 HistObj
49 HistObjFind
50 HistClear

                    F、!dumpheap -stat 我們可以檢視託管堆。

 1 0:000> !dumpheap -stat
 2 Statistics:
 3       MT    Count    TotalSize Class Name
 4 6f545468        1           12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
 5 6f544888        1           12 System.Security.HostSecurityManager
 6 6f543d78        1           12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorlib]]
 7 6f5a9b0c        1           16 System.IO.TextReader+SyncTextReader
 8 ......
 9 6f545c40        3          806 System.Byte[]
10 6f542c60       10         2986 System.Char[]
11 6f5424e4      166         6100 System.String
12 6f542788        6        17748 System.Object[]
13 Total 332 objects

                    G、!eeheap -gc 我們可以檢視託管堆的佈局。

 1 0:000> !eeheap -gc
 2 Number of GC Heaps: 1
 3 generation 0 starts at 0x024d1018
 4 generation 1 starts at 0x024d100c
 5 generation 2 starts at 0x024d1000
 6 ephemeral segment allocation context: none
 7  segment     begin  allocated      size
 8 024d0000  024d1000  024d5ff4  0x4ff4(20468)
 9 Large object heap starts at 0x034d1000
10  segment     begin  allocated      size
11 034d0000  034d1000  034d5558  0x4558(17752)
12 Total Size:              Size: 0x954c (38220) bytes.
13 ------------------------------
14 GC Heap Size:    Size: 0x954c (38220) bytes.

                    H、.hh 命令可以檢視命令的幫助檔案。

                      

    2.2、SOSEX

       SOSEX 這款 dll 也是分兩個版本的,分別是:32位元和64位元。但是說明一下,這個版本只能使用 Net Framework 環境下,Net Core,Net5、Net6、Net7等以上不能使用的。下載地址就不貼了,上網一找,也不難。SOSEX 是 SOS 非常有力的擴充套件,提供了非常多的實用函數。

            1)、測試程式碼
                Example_1_1_1
            2)、簡單命令的執行
                A、!sosex.help
檢視 SOSEX的幫助命令

 1 0:000> !sosex.help
 2 SOSEX - Copyright 2007-2014 by Steve Johnson - http://www.stevestechspot.com/
 3 To report bugs or offer feedback about SOSEX, please email [email protected]
 4 Quick Ref:
 5 --------------------------------------------------
 6 bhi       [filename]                                     BuildHeapIndex - Builds an index file for heap objects.
 7 bpsc      (Deprecated.  Use !mbp instead)
 8 chi                                                      ClearHeapIndex - Frees all resources used by the heap index and removes it from memory.
 9 dlk       [-d]                                           Displays deadlocks between SyncBlocks and/or ReaderWriterLocks
10 dumpfd    <FieldAddr>                                    Dumps the properties of a FieldDef structure
11 dumpgen   <GenNum> [-free] [-stat] [-type <TYPE_NAME>]   Dumps the contents of the specified generation
12                    [-nostrings]
13 finq      [GenNum] [-stat]                               Displays objects in the finalization queue
14 frq       [-stat]                                        Displays objects in the Freachable queue
15 gcgen     <ObjectAddr>                                   Displays the GC generation of the specified object
16 gch       [HandleType]... [-stat]                        Lists all GCHandles, optionally filtered by specified handle types
17 help      [CommandName]                                  Display this screen or details about the specified command
18 lhi       [filename]                                     LoadHeapIndex - load the heap index into memory.
19 mbc       <SOSEX breakpoint ID | *>                      Clears the specified or all managed breakpoints
20 mbd       <SOSEX breakpoint ID | *>                      Disables the specified or all managed breakpoints
21 mbe       <SOSEX breakpoint ID | *>                      Enables the specified or all managed breakpoints
22 mbl       [SOSEX breakpoint ID]                          Prints the specified or all managed breakpoints
23 mbm       <Type/MethodFilter> [ILOffset] [Options]       Sets a managed breakpoint on methods matching the specified filter
24 mbp       <SourceFile> <nLineNum> [ColNum] [Options]     Sets a managed breakpoint at the specified source code location
25 mdso      [Options]                                      Dumps object references on the stack and in CPU registers in the current context
26 mdt       [TypeName | VarName | MT] [ADDR] [Options]     Displays the fields of an object or type, optionally recursively
27 mdv       [nFrameNum]                                    Displays arguments and locals for a managed frame
28 mfrag     [-stat] [-mt:<MT>]                             Reports free blocks, the type of object following the free block, and fragmentation statistics
29 mframe    [nFrameNum]                                    Displays or sets the current managed frame for the !mdt and !mdv commands
30 mgu       // TODO: Document
31 mk        [FrameCount] [-l] [-p] [-a]                    Prints a stack trace of managed and unmanaged frames
32 mln       [expression]                                   Displays the type of managed data located at the specified address or the current instruction pointer
33 mlocks    [-d]                                           Lists all managed lock objects and CriticalSections and their owning threads
34 mroot     <ObjectAddr> [-all]                            Displays GC roots for the specified object
35 mt        (no parameters)                                Steps into the managed method at the current position
36 mu        [address] [-s] [-il] [-n]                      Displays a disassembly around the current instruction with interleaved source, IL and asm code
37 muf       [MD Address | Code Address] [-s] [-il] [-n]    Displays a disassembly with interleaved source, IL and asm code
38 mwaits    [-d | LockAddr]                                Lists all waiting threads and, if known, the locks they are waiting on
39 mx        <Filter String>                                Displays managed type/field/method names matching the specified filter string
40 rcw       [Object or SyncBlock Addr]                     Displays Runtime Callable Wrapper (RCW) COM interop data.
41 refs      <ObjectAddr> [-target|-source]                 Displays all references from and to the specified object
42 rwlock    [ObjectAddr | -d]                              Displays all RWLocks or, if provided a RWLock address, details of the specified lock
43 sosexhelp [CommandName]                                  Display this screen or details about the specified command
44 strings   [ModuleAddress] [Options]                      Search the managed heap or a module for strings matching the specified criteria
45 
46 ListGcHandles - See gch
47 
48 Use !help <command> or !sosexhelp <command> for more details about each command.
49 You can also use the /? (or -?) option on any command to get help for that command.


                B、!strings 我們可以把程序中所有的字串找出來。

 1 0:000> !strings
 2 Address    Gen    Length   Value
 3 ---------------------------------------
 4 024d1228    0          0   
 5 024d1254    0        121   E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\AdvancedDebug.NetFramework.Example_1_1_1\bin\Debug\
 6 024d1354    0        145   E:\Visual Studio 2022\Source\Projects
 7 ......
 8 024d34c0    0          3   Nov
 9 024d34d4    0          3   Dec
10 024d3698    0          6   zh-CHS
11 024d36b4    0          6   zh-CHT
12 024d36d0    0          5   zh-CN
13 024d3764    0          5   zh-cn
14 024d3924    0          5   zh-CN
15 ......
16 024d3cf8    0          3   936
17 024d3d0c    0          1   3
18 024d3d1c    0          1   2
19 024d3d2c    0          1   0
20 024d3d3c    0          1   0
21 024d3d4c    0         24   NLS_CodePage_936_3_2_0_0
22 024d3e2c    0          8   encoding
23 024d3e4c    0          6   stream
24 024d42bc    0          5   bytes
25 024d42d4    0          5   chars
26 024d42ec    0          9   charCount
27 024d430c    0          9   charIndex
28 024d432c    0          9   byteCount
29 024d4a9c    0          5   count
30 024d4ab4    0          6   offset
31 ---------------------------------------
32 166 strings


                C、!finq 可以檢視終端子佇列。

 1 0:000> !finq
 2 Generation 0:
 3 Address       Size   Type
 4 ---------------------------------------------
 5 024d1e34        20   Microsoft.Win32.SafeHandles.SafePEFileHandle
 6 024d24d8        44   System.Threading.ReaderWriterLock
 7 024d2638        20   Microsoft.Win32.SafeHandles.SafeFileHandle
 8 024d3d8c        20   Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
 9 024d3da0        20   Microsoft.Win32.SafeHandles.SafeFileMappingHandle
10 024d4a30        52   System.Threading.Thread
11 024d4ad0        20   Microsoft.Win32.SafeHandles.SafeFileHandle
12 7 objects, 196 bytes
13 
14 Generation 1:
15 Address       Size   Type
16 ---------------------------------------------
17 0 objects, 0 bytes
18 
19 Generation 2:
20 Address       Size   Type
21 ---------------------------------------------
22 0 objects, 0 bytes
23 
24 TOTAL: 7 objects, 196 bytes


                D、!mlocks 判斷當前是否有死鎖。

 1 0:000> !mlocks
 2 Examining SyncBlocks...
 3 Scanning for ReaderWriterLock instances...
 4 Scanning for holders of ReaderWriterLock locks...
 5 Scanning for ReaderWriterLockSlim instances...
 6 Scanning for holders of ReaderWriterLockSlim locks...
 7 Examining CriticalSections...
 8 
 9 ClrThread  DbgThread  OsThread    LockType    Lock      LockLevel
10 ----------------------------------------------------------------------
11 0x1        0          0x1028      thinlock    024d4e90  (recursion:0)



    2.3、Net 反編譯工具
             
Net 反編譯器可以編譯 IL 程式碼,讓 IL 程式碼轉成 C# 程式碼,這裡推薦兩款工具。
           
  2.3.1、ILSpy
               官網地址:https://github.com/icsharpcode/ILSpy
               映象地址:https://sourceforge.net/projects/ilspy.mirror/files/latest/download
                


           2.3.2、DnSpy
               這個工具不僅可以可以反編譯 C# 程式碼,還可以對 Net Framework 程式進行偵錯。
               官網地址:https://github.com/dnSpy/dnSpy/releases
               
其他下載:https://filehippo.com/zh/download_dnspy/
               


    2.4、PerfView
        
這是 CLR 團隊調優 CLR 使用的工具,可以實時監控程式的行為,比如:程式的 GC 觸發的情況。
            官網地址:https://github.com/microsoft/perfview
          微軟官網:https://www.microsoft.com/en-us/download/details.aspx?id=28567
         1)、測試程式碼
            Example_1_1_2

         2)、使用 Perfview 監控程式。

            Perfview使用很簡單,先開啟 Perfview 軟體,然後電機選單【collect】--->【collect】,開啟【Collecting data over a user specified interval】視窗,什麼也不用選擇,直接點選視窗中的【Start Collection】按鈕,開始採集資料。

            

            

           當 Perfview 開始採集資料的時候,我們開啟我們的測試程式【Example_1_1_2.exe】,執行到10000,關閉程式,點選【StopCollection】按鈕。Perfview 開始生成資料,可以觀察狀態列,檢視 Perfview 的動作。
            

            

            還有很多資料,不能一一展示,大家可以自己動手測試下。我截了一張圖,表示一下。
              

 


三、結束
    站在高人的肩膀之上,自己輕鬆了很多,但是,自己還是一個小學生,Net 高階偵錯這條路,也剛剛起步,還有很多要學的地方。皇天不負有心人,努力,不辜負自己,我相信付出就有回報,再者說,學習的過程,有時候,雖然很痛苦,但是,學有所成,學有所懂,這個開心的感覺還是不可言喻的。不忘初心,繼續努力。做自己喜歡做的,開心就好。