當前位置: 妍妍網 > 碼農

.NET 高級偵錯:認識偵錯工具

2024-02-07碼農


一、簡介

Net 高級偵錯的相關文章,我自從學習了之後,以前很多模糊的地方現在很清楚了,原來自己的功力還是不夠,所以有很多不明白,透過學習 Net 高級偵錯,眼前豁然開朗,茅塞頓開。

其實,剛開始要學習【Net 高級偵錯】,還是很是很困難的,很多工具不會用,又不知道如何偵錯,痛苦的又很多次想放棄,但是,最終還是堅持下來,收獲也不小。

既然堅持下來了,我就把學習的過程記錄下來,也許以後自己的能用的到,可以方便查詢。或許,有其他人也有同樣的困擾,或授權以在我這裏得到一些幫助,有幫助我當然很開心。當然,Net 高級偵錯的路還很遠,我也是剛起步,不足之處太多,也希望大家原諒,有不對之處,歡迎指正。

偵錯環境

  • 作業系統:Windows Professional 10

  • 偵錯工具:Windbg Preview(可以去Microsoft Store 去下載)

  • 開發工具:Visual Studio 2022

  • Net 版本:Net Framework 4.8

  • CoreCLR源碼:https://sourceforge.net/projects/coreclr.mirror/files/latest/download

    二、偵錯工具介紹

    俗話說得好,工欲善其事,必先利其器,我們要想偵錯程式,必須有很好的工具,如果連偵錯工具都沒有,那真就成了巧婦難為無米之炊。

    所以,接下來,我先介紹一些偵錯工具,每種偵錯工具都有各自的用途。

    測試程式碼

    我們想要演示 Windbg 的使用過程,使用方法,偵錯程式的各種問題,必須有程式作為載體,由於這是【Net 高級偵錯】的第一節課,所以只是簡單的演示一下,例子程式碼沒有實際的作用,作為演示還是夠了的。

    本節有兩分程式碼,分別是:Example_1_1_1和 Example_1_1_2

    Example_1_1_1的程式碼如下:

    namespaceExample_1_1_1
    {
    internal classProgram
    {
    staticvoidMain(string[] args)
    {
    Console.WriteLine("Hello World");
    Console.ReadLine();
    }
    }
    }

    Example_1_1_2的程式碼如下:

    namespaceExample_1_1_2
    {
    internal classProgram
    {
    privatestatic IList<byte[]> list=new List<byte[]>();
    staticvoidMain(string[] args)
    {
    Task.Run(() =>
    {
    for (int i = 0; i < int.MaxValue; i++)
    {
    list.Add(newbyte[10000]);
    if (i % 10 == 0)
    {
    list[i] = null;
    }
    Console.WriteLine($"當前索引 Index={i}");
    }
    });
    Console.ReadLine();
    }
    }
    }

    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 偵錯擴充套件。

    0:000> .chain
    Extension DLL search Path:
    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
    Extension DLL chain:
    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
    [path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll]
    JsProvider: image 10.0.25877.1004, API 0.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\JsProvider.dll]
    DbgModelApiXtn: image 10.0.25877.1004, API 0.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\DbgModelApiXtn.dll]
    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
    [path: F:\Software\DebugTools\SOS\SOSEX\sosex_32\sosex.dll]
    CLRComposition: image 10.0.25877.1004, API 0.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\CLRComposition.dll]
    wow64exts: image 10.0.25877.1004, API 1.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP\wow64exts.dll]
    dbghelp: image 10.0.25877.1004, API 10.0.6, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\dbghelp.dll]
    exts: image 10.0.25877.1004, API 1.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\WINXP\exts.dll]
    uext: image 10.0.25877.1004, API 1.0.0, 
    [path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2306.14001.0_x64__8wekyb3d8bbwe\x86\winext\uext.dll]
    ntsdexts: image 10.0.25877.1004, API 1.0.0, 
    [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 剛進來,內容太多,可以清楚一下螢幕。

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

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

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

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

    0:000> !sos.help
    -------------------------------------------------------------------------------
    SOS is a debugger extension DLL designed to aid in the debugging of managed
    programs. Functions are listed by category, then roughly in order of
    importance. Shortcut names for popular functions are listed in parenthesis.
    Type "!help <functionname>"for detailed info on that function
    Object Inspection Examining code and stacks
    ----------------------------- -----------------------------
    DumpObj (do) Threads
    DumpArray (da) ThreadState
    DumpStackObjects (dso) IP2MD
    DumpHeap U
    DumpVC DumpStack
    GCRoot EEStack
    ObjSize CLRStack
    FinalizeQueue GCInfo
    PrintException (pe) EHInfo
    TraverseHeap BPMD 
    COMState
    Examining CLR data structures Diagnostic Utilities
    ----------------------------- -----------------------------
    DumpDomain VerifyHeap
    EEHeap VerifyObj
    Name2EE FindRoots
    SyncBlk HeapStat
    DumpMT GCWhere
    Dump class ListNearObj (lno)
    DumpMD GCHandles
    Token2EE GCHandleLeaks
    EEVersion FinalizeQueue (fq)
    DumpModule FindAppDomain
    ThreadPool SaveModule
    DumpAssembly ProcInfo 
    DumpSigElem StopOnException (soe)
    DumpRuntimeTypes DumpLog
    DumpSig VMMap
    RCWCleanupList VMStat
    DumpIL MinidumpMode 
    DumpRCW AnalyzeOOM (ao)
    DumpCCW
    Examining the GC history Other
    ----------------------------- -----------------------------
    HistInit FAQ
    HistRoot
    HistObj
    HistObjFind



    HistClear

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

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

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

    0:000> !eeheap -gc
    Number of GC Heaps: 1
    generation 0 starts at 0x024d1018
    generation 1 starts at 0x024d100c
    generation 2 starts at 0x024d1000
    ephemeral segment allocation context: none
     segment begin allocated size
    024d0000 024d1000 024d5ff4 0x4ff4(20468)
    Large object heap starts at 0x034d1000
     segment begin allocated size
    034d0000 034d1000 034d5558 0x4558(17752)
    Total Size: Size: 0x954c (38220) bytes.
    ------------------------------
    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的幫助命令

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

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

    0:000> !strings
    Address Gen Length Value
    ---------------------------------------
    024d1228 0 0
    024d1254 0 121 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\AdvancedDebug.NetFramework.Example_1_1_1\bin\Debug\
    024d1354 0 145 E:\Visual Studio 2022\Source\Projects
    ......
    024d34c0 0 3 Nov
    024d34d4 0 3 Dec
    024d3698 0 6 zh-CHS
    024d36b4 0 6 zh-CHT
    024d36d0 0 5 zh-CN
    024d3764 0 5 zh-cn
    024d3924 0 5 zh-CN
    ......
    024d3cf8 0 3 936
    024d3d0c 0 1 3
    024d3d1c 0 1 2
    024d3d2c 0 1 0
    024d3d3c 0 1 0
    024d3d4c 0 24 NLS_CodePage_936_3_2_0_0
    024d3e2c 0 8 encoding
    024d3e4c 0 6 stream
    024d42bc 0 5 bytes
    024d42d4 0 5 chars
    024d42ec 0 9 charCount
    024d430c 0 9 charIndex
    024d432c 0 9 byteCount
    024d4a9c 0 5 count
    024d4ab4 0 6 offset
    ---------------------------------------
    166 strings

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

    0:000> !finq
    Generation 0:
    Address Size Type
    ---------------------------------------------
    024d1e34 20 Microsoft.Win32.SafeHandles.SafePEFileHandle
    024d24d8 44 System.Threading.ReaderWriterLock
    024d2638 20 Microsoft.Win32.SafeHandles.SafeFileHandle
    024d3d8c 20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
    024d3da0 20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
    024d4a30 52 System.Threading.Thread
    024d4ad0 20 Microsoft.Win32.SafeHandles.SafeFileHandle
    7 objects, 196 bytes
    Generation 1:
    Address Size Type
    ---------------------------------------------
    0 objects, 0 bytes
    Generation 2:
    Address Size Type
    ---------------------------------------------
    0 objects, 0 bytes
    TOTAL: 7 objects, 196 bytes


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

    0:000> !mlocks
    Examining SyncBlocks...
    Scanning for ReaderWriterLock instances...
    Scanning for holders of ReaderWriterLock locks...
    Scanning for ReaderWriterLockSlim instances...
    Scanning for holders of ReaderWriterLockSlim locks...
    Examining Criticalps...
    ClrThread DbgThread OsThread LockType Lock LockLevel
    ----------------------------------------------------------------------
    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 高級偵錯這條路,也剛剛起步,還有很多要學的地方。

    轉自:可均可可

    連結:cnblogs.com/PatrickLiu/p/17781974.html

    - EOF -

    技術群: 添加小編微信並備註進群

    小編微信:mm1552923

    公眾號:dotNet編程大全