当前位置: 欣欣网 > 码农

.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编程大全