一、简介
从今天开始一个长系列,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 <strong>C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll:</strong> 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 sjjohnson@pobox.com
- 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 高级调试这条路,也刚刚起步,还有很多要学的地方。皇天不负有心人,努力,不辜负自己,我相信付出就有回报,再者说,学习的过程,有时候,虽然很痛苦,但是,学有所成,学有所懂,这个开心的感觉还是不可言喻的。不忘初心,继续努力。做自己喜欢做的,开心就好。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |