一、簡介
從今天開始一個長系列,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 高階偵錯這條路,也剛剛起步,還有很多要學的地方。皇天不負有心人,努力,不辜負自己,我相信付出就有回報,再者說,學習的過程,有時候,雖然很痛苦,但是,學有所成,學有所懂,這個開心的感覺還是不可言喻的。不忘初心,繼續努力。做自己喜歡做的,開心就好。