水瓶座儿 发表于 2023-11-16 12:37:53

Net 高级调试之九:SOSEX 扩展命令介绍

一、介绍
    今天是《Net 高级调试》的第九篇文章。这篇文章设计的内容挺多的,比如:扩展的断点支持,如何查找元数据,栈回溯,对象检查,死锁检测等等,内容挺多的。功能特别强大,使用特别方便,但是需要说明一点,这些功能不是 SOS 的功能,是 SOSEX 的扩展功能,但是,这一系列功能只是支持 Net Framework,在 Net Core 跨平台版本是不支持的。虽然这些都是基础,如果这些掌握不好,以后的高级调试的道路,也不好走。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现。
     如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有些项目需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。

       调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
          操作系统:Windows Professional 10
          调试工具:Windbg Preview(可以去Microsoft Store 去下载)
          开发工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源码:源码下载

二、基础知识

    1、SOSEX调试扩展
        不得不说在调试 Net Framework 程序的时候,这个扩展调试组件的使用率是仅次于官方的 SOS 插件的,这个插件的一个特点就是能看到大量的命令是以 m 开头的,对应于 非托管命令的托管命令的表示。

        这一篇文章只是介绍常用的几个命令,如果大家想了解更多,可以使用【!sosex.help】命令,查看 SOSEX 插件的所有命令。

    2、几个相当实用的扩展命令。

        2.1、!mbp 下断点命令。
            相信大家都用过 !bp 命令,这个命令是可以对非托管函数下断点,如果我们想对托管函数下断点,就可以使用 【!mbp】 这个命令,它是【bp】命令托管形式,可以给托管方法下断点。

            【!mbp】命令使用的时候要注意一点,命令后跟的是文件名,包含后缀名,如果类是独立的类文件,就写这个类文件的名称就可以,如果是多个类包含在一个类文件里,就写包含多个类文件的名称就可以。

        2.2、观察对象布局。
            一般我们使用【!do】命令观察一个对象,但是这样只能观察到一个平面图,不能查看到立体的对象,如果想更全面的了解一个对象,我们可以使用【!mdt】命令,立体对象是指:引用类型包含引用类型,具有多层,【!do】命令只能查看当前层对象的结构。

        2.3、查找托管堆中指定的字符串。
            这个在 dump 调试的过程中做托管的内存搜索很有用,使用【!strings -m:xxx】命令。

        2.4、搜索元数据。
            我们在以前的调试 Notepad 的 SaveFile 方法的时候,使用【x】命令,这里也有对应的托管版本的命令,就是【!mx】,这个命令对于我们查找函数特别拥有。
        2.5、观察 Free 块。
            这是一个比较高级的命令,在分析托管堆碎片的时侯比较有用,能够提高分析效率。我们可以使用【!mfrag】命令。

        2.6、死锁检测。
            大家都知道【死锁】相互等待对方释放锁资源造成的,一旦出现了死锁的问题,我们可以用手工的方式分析出来的,但是这样费时费力,SOSEX 提供了一个检测命令,就是【!dlk(deadlock)】命令,用来检测死锁问题。

三、调试过程
    废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。

    1、测试源码
        1.1、Example_9_1_1 项目的源码
            Program 的代码:
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gifhttps://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif 1 namespace Example_9_1_1 2 { 3   internal class Program 4   { 5         static void Main(string[] args) 6         { 7             int a = 10; 8             int b = 11; 9             int c = 12;10 11             var person = new Person();12 13             person.Show();14 15             Console.ReadLine();16         }17   }18 }View Code
            Address的代码:
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gifhttps://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif1 namespace Example_9_1_12 {3   public class Address4   {5         public string Name { get; set; }6   }7 }View Code
            Person的代码:
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gifhttps://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif 1 namespace Example_9_1_1 2 { 3   public class Person 4   { 5         public string Name { get; set; } 67         public Address Address { get; set; } = new Address() { Name = "河北省" }; 89         public void Show()10         {11             Console.WriteLine("Hello Person");12         }13   }14 }View Code
        1.2、Example_9_1_2 项目的源码
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gifhttps://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif 1 namespace Example_9_1_2 2 { 3   internal class Program 4   { 5         public static Person person=new Person(); 6         public static Student student = new Student(); 78         static void Main(string[] args) 9         {10             Task.Run(() =>11             {12               lock (person)13               {14                     Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已经进入1111 Person");15                     Thread.Sleep(1000);16                     lock (student)17                     {18                         Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已经进入1111 Student");19                         Console.ReadLine();20                     }21               }22             });23 24             Task.Run(() =>25             {26               lock(student)27               {28                     Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已经进入2222 Student");29                     Thread.Sleep(1000);30                     lock (person)31                     {32                         Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已经进入2222 Person");33                         Console.ReadLine();34                     }35               }36             });37 38             Console.ReadLine();39         }40   }41 42   public class Student43   {44   }45 46   public class Person47   {48   }49 }View Code
    2、眼见为实
        项目的所有操作都是一样的,所以就在这里说明一下,但是每个测试例子,都需要重新启动,并加载相应的应用程序,加载方法都是一样的。流程如下:我们编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。

        2.1、我们使用【!mbp】命令给托管函数下断点(CLR没加载就可以下断点)。
            调试代码:Example_9_1_1
            我们的任务:给 Program 类的 Main 方法的第十行:int b = 11;下断点。
            我们进入 Windbg 界面,不需要运行,就可以直接下断点,虽然这个时候 CLR 还没有加载。【!mbp】命令后是程序文件名,必须包含文件扩展名,否则无效。
1 0:000> !mbp Program.cs 102 The CLR has not yet been initialized in the process.(CLR还没有被初始化)3 Breakpoint resolution will be attempted when the CLR is initialized.            我们已经成功下了断点,但是英文提示 CLR 还没有被加载,我们使用【lm】命令查看一下加载模块信息,显示如下。
1 0:000> lm2 start    end      module name3 00a20000 00a28000   Example_9_1_1 C (pdb symbols)          C:\ProgramData\Dbg\sym\Example_9_1_1.pdb\...\Example_9_1_1.pdb4 5bff0000 5c08f000   apphelp    (deferred)             5 71520000 71572000   MSCOREE    (deferred)             6 762f0000 76503000   KERNELBASE   (deferred)             7 76ca0000 76d90000   KERNEL32   (deferred)             8 76f10000 770b2000   ntdll      (pdb symbols)          C:\ProgramData\Dbg\sym\wntdll.pdb\DBC8C8F74C0E3696E951B77F0BB8569F1\wntdll.pdb            的确如此,加载的模块很少,根本没又看到 CLR 的影子。
            然后,我们【g】一下,就会到我们下的断点处暂停。效果如图:
            https://img2023.cnblogs.com/blog/1048776/202311/1048776-20231115112753248-892379690.png
            接下来,我们在另外一个类中给一个方法直接下断点,看看效果怎么样,如截图:
            https://img2023.cnblogs.com/blog/1048776/202311/1048776-20231115113225876-40104240.png
             我们执行【!mbp】命令,后面跟类名,包含后缀名和行号。
1 0:000> !mbp Person.cs 132 The CLR has not yet been initialized in the process.3 Breakpoint resolution will be attempted when the CLR is initialized.             然后,我们【g】一下,就会到我们下的断点处暂停。效果如图:
             https://img2023.cnblogs.com/blog/1048776/202311/1048776-20231115113709760-673144813.png


        2.2、我们如何立体的观察一个对象。
            调试代码:Example_9_1_1
            我们来看看 Person 对象的结构,它包含 string 类型的 Name 字段,包含 Address 引用类型,Address 又包含 String 类型的 Name字段,我们如何立体的查看它的结构呢?
            我们进入到 Windbg 调试界面,如果有断点存在,我们可以使用【bc *】命令将所有的断点全部清除,然后再使用【g】命令,运行程序,会在【Console.ReadLine();】这样代码处暂停,然后我们点击【Break】按钮,就是调试程序了。
            我们现在托管堆中查找一下 Person 对象,有了对象,才可以查看它的结构,使用【!dumpheap -type Person】命令查找。
1 0:006> !dumpheap -type Person2Address       MT   Size3 029324c8 00ee4e28       16   4 5 Statistics:6       MT    Count    TotalSize Class Name7 00ee4e28      1         16 Example_9_1_1.Person8 Total 1 objects            上面列表中有了Person 对象地址和方法表的地址。
1 0:006> !mdt 029324c82 029324c8 (Example_9_1_1.Person)3   k__BackingField:NULL (System.String)4   k__BackingField:029324ec (Example_9_1_1.Address)5 0:006> !mdt 029324c8 -r6 029324c8 (Example_9_1_1.Person)7   k__BackingField:NULL (System.String)8   k__BackingField:029324ec (Example_9_1_1.Address)9         k__BackingField:029324d8 (System.String) Length=3, String="河北省"            如果我们想看【!mdt】如何使用,可以使用【!sosex.help !mdt】,这个输出太多,折叠了,可以自行查看。
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gifhttps://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif1 0:006> !sosex.help !mdt2 SOSEX - Copyright 2007-2014 by Steve Johnson - http://www.stevestechspot.com/3 To report bugs or offer feedback about SOSEX, please email sjjohnson@pobox.com4   5 mdt6 Usage: !sosex.mdt [-r[:level]] [-e[:level]] [-start:index] [-count:n]7   8 Sample usages:9 "!sosex.mdt typeName"(displays the names of the member fields of the specified type) 10 "!sosex.mdt argName"   (displays the values of the fields of the specified parameter object. -r is valid) 11 "!sosex.mdt localName" (displays the values of the fields of the specified local variable. -r is valid) 12 "!sosex.mdt ADDR"      (displays the values of the fields of the object located at ADDR. -r is valid) 13 "!sosex.mdt MT ADDR"   (displays the values of the fields of the value type specified by MT located at ADDR. -r is valid) 1415 Displays the fields of the specified object or type, optionally recursively. 1617 If -r is specified, fields will be displayed recursively down the object graph.The -r switch is ONLY18 applicable where an address is used, either by passing an address explicitly or when a param/local name19 that resolves to an address is specified.To limit the levels of the graph that are displayed, append the desired 20 maximum level, preceded by a colon.e.g. !mdt myVar -r:3 2122 The -e switch causes certain collection types to be expanded.The currently expandable collection types are: 23 Array, ArrayList, List, Hashtable and Dictionary.You can also specify a maximum expansion level by appending24 the desired maximum level, preceded by a colon.e.g. !mdt myColl -e:3.The minimum (and default) level is 2,25 which means that the collection is expanded to show each element address and it's top level fields. 2627 If you pass -e for collection expansion, you can also pass -start:index to specify a start index and/or -count:n28 to specify the number of elements to expand. 2930 Sample of collection expansion: 31 0:000> !mdt -e ht1 32 061ab360 (System.Collections.Hashtable) 33 Count = 2 34 061ab3a0 35   key:061ab3c8 (BOXED System.Int32) BOXEDVAL=0x7b 36   val:061ab3d4 (BOXED System.Int32) BOXEDVAL=0x4ce 37 061ab3b8 38   key:061ab3e0 (BOXED System.Int32) BOXEDVAL=0x1c8 39   val:061ab3ec (BOXED System.Int32) BOXEDVAL=0x11d0 4041 0:000> !mdt -e ht2 42 061ab3f8 (System.Collections.Hashtable) 43 Count = 2 44 061ab438 45   key:061ab078 (System.String: "456key") 46   val:061ab094 (System.String: "456value") 47 061ab444 48   key:061ab03c (System.String: "123key") 49   val:061ab058 (System.String: "123value") 5051 The scope frame defaults to zero, but may be overridden via the !mframe command.IMPORTANT:The current 52 scope frame corresponds to the frames listed by the !mk command. 5354 IMPORTANT NOTE: Sosex distinguishes between numeric and non-numeric strings in order to determine whether an 55 address or a type/arg/local name is being passed in ambiguous circumstances.If you want to pass a string value 56 that could be interpreted as an expression by the debugger, enclose the name in single-quotes.For example, for 57 a local named d2, call: !mdt 'd2'.If the name were not quoted in this circumstance, !mdt would attempt to display 58 an object located at address 0xd2. 5960 Frame info for the sample output below: 61 0:000> !mdv 62 Frame 0x0: (ConsoleTestApp.ConsoleTestApp.Main(System.String[])): 63 :args:0x2371804 (System.String[]) 64 :theGuid:{29b9c9c8-3751-42be-8c7a-8b92ff499588} VALTYPE (MT=6cd46c60, ADDR=002ff1bc) (System.Guid) 65 :d2:0x63718e0 (System.AppDomain) 66 :hMod:0x67280000 (System.Int32) 67 :dummy:0x23721a4 (System.String) STRVAL="This is "THE" way to test!" 68 :numThreads:0x2 (System.Int32) 69 :theDate:2008/01/02 03:04:05.678 VALTYPE (MT=6cd49e98, ADDR=002ff1ac) (System.DateTime) 70 :ts1:VALTYPE (MT=001e3198, ADDR=002ff1a4) (ConsoleTestApp.TestStruct) 71 :ft:0x637544c (ConsoleTestApp.FTEST) 72 :g1: 73 :g2: 74 :rnd:null (System.Random) 75 :threads:null (System.Threading.Thread[]) 76 :i:0x0 (System.Int32) 77 :ex:null (System.Exception) 78 :CS$0$0000:VALTYPE (MT=001e3198, ADDR=002ff198) (ConsoleTestApp.TestStruct) 79 :CS$4$0001:0x0 (System.Boolean) 808182 Sample output: 83 0:000> !mdt theGuid 84 002ff1bc (System.Guid) {29b9c9c8-3751-42be-8c7a-8b92ff499588} VALTYPE (MT=6cd46c60, ADDR=002ff1bc) 8586 0:000> !mdt ft 87 0637544c (ConsoleTestApp.FTEST) 88    _s1:06375460 (System.String: "String 1") 89    _s2:06375484 (System.String: "String 2") 90    _arr:063755c4 (System.String[,,], Elements: 8) 9192 0:000> !mdt 63718e0 93 063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy) 94    _rp:063718b4 (System.Runtime.Remoting.Proxies.RemotingProxy) 95    _stubData:023725dc (BOXED System.IntPtr) VALTYPE (MT=6cd6b114, ADDR=023725e0) 96    _pMT:6cd6902c (System.IntPtr) 97    _pInterfaceMT:00000000 (System.IntPtr) 98    _stub:6d601e70 (System.IntPtr) 99 100 0:000> !mdt 63718e0 -r101 063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)102    _rp:063718b4 (System.Runtime.Remoting.Proxies.RemotingProxy)103       _tp:063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)104          105       _identity:06371698 (System.Runtime.Remoting.Identity)106          _flags:0x4 (System.Int32)107          _tpOrObject:063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)108             109          _ObjURI:02376858 (System.String: "/f578dbe2_cf0c_4e30_882b_14126f0b1654/kq_om1xc5idnrbhnqnr77cs0_1.rem")110          _URL:NULL (System.String)111          _objRef:023769e0 (System.Runtime.Remoting.ObjRef)112             uri:02376858 (System.String: "/f578dbe2_cf0c_4e30_882b_14126f0b1654/kq_om1xc5idnrbhnqnr77cs0_1.rem")113             typeInfo:02376cc4 (System.Runtime.Remoting.TypeInfo)114                serverType:02377ea4 (System.String: "System.AppDomain, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")115                serverHierarchy:NULL (System.Object[])116                interfacesImplemented:0237808c (System.String[], Elements: 2)117             envoyInfo:NULL (System.Runtime.Remoting.IEnvoyInfo)118             channelInfo:0237872c (System.Runtime.Remoting.ChannelInfo)119                channelData:02378738 (System.Object[], Elements: 1)120             objrefFlags:0x0 (System.Int32)121             srvIdentity:023769fc (System.Runtime.InteropServices.GCHandle) VALTYPE (MT=6cd6bcb8, ADDR=023769fc)122                m_handle:004e11ec (System.IntPtr)123             domainID:0x2 (System.Int32)124          _channelSink:06371890 (System.Runtime.Remoting.Channels.CrossAppDomainSink)125             _xadData:023753c0 (System.Runtime.Remoting.Channels.CrossAppDomainData)126                _ContextID:023753e0 (BOXED System.Int32) BOXEDVAL=0x69B9B0127                _DomainID:0x2 (System.Int32)128                _processGuid:023750bc (System.String: "81174e11_728a_4211_a674_f6f4d79419ba")129          _envoyChain:063718a8 (System.Runtime.Remoting.Messaging.EnvoyTerminatorSink)130          _dph:NULL (System.Runtime.Remoting.Contexts.DynamicPropertyHolder)131          _lease:NULL (System.Runtime.Remoting.Lifetime.Lease)132       _serverObject:NULL (System.MarshalByRefObject)133       _flags:0x3 (System.Runtime.Remoting.Proxies.RealProxyFlags)134       _srvIdentity:063718d0 (System.Runtime.InteropServices.GCHandle) VALTYPE (MT=6cd6bcb8, ADDR=063718d0)135          m_handle:004e11ec (System.IntPtr)136       _optFlags:0x7000000 (System.Int32)137       _domainID:0x2 (System.Int32)138       _ccm:NULL (System.Runtime.Remoting.Messaging.ConstructorCallMessage)139       _ctorThread:0x0 (System.Int32)140    _stubData:023725dc (BOXED System.IntPtr) VALTYPE (MT=6cd6b114, ADDR=023725e0)141       m_value:ffffffff (System.UIntPtr)142    _pMT:6cd6902c (System.IntPtr)143    _pInterfaceMT:00000000 (System.IntPtr)144    _stub:6d601e70 (System.IntPtr)145 146 0:000> !mdt args147 02371804 (System.String[], Elements: 0)148 149 0:000> !mdt 001e3198 002ff1a4 -r150 002ff1a4 (ConsoleTestApp.TestStruct) VALTYPE (MT=001e3198, ADDR=002ff1a4)151    Member1:0x4D2 (System.UInt32)152    Member2:0x162E (System.UInt32)View Code
        2.3、使用【!strings】命令在托管堆中查找字符串。
            调试代码:Example_9_1_1
            我们进入到 Windbg 调试界面,如果有断点存在,我们可以使用【bc *】命令将所有的断点全部清除,然后再使用【g】命令,运行程序,会在【Console.ReadLine();】这样代码处暂停,然后我们点击【Break】按钮,就是调试程序了。
            如果我们直接使用【!strings】命令,没有任何参数,会把托管堆中的所有字符串全部打印出来。
1 0:006> !strings 2 Address    Gen    Length   Value 3 --------------------------------------- 4 02931228    0          0    5 02931254    0         94   E:\Visual Studio 2022\...\Example_9_1_1\bin\Debug\ 6 02931320    0      118   E:\Visual Studio 2022\...\Example_9_1_1\bin\Debug\Example_9_1_1.exe.Config 7 0293148c    0          4   true 8 029314a4    0         32   PARTIAL_TRUST_VISIBLE_ASSEMBLIES 9 02931538    0      111   E:\Visual Studio 2022\..\Example_9_1_1\bin\Debug\Example_9_1_1.exe10 ...(省略很多)11 02933d8c    0          8   encoding12 02933dac    0          6   stream13 0293421c    0          5   bytes14 02934234    0          5   chars15 0293424c    0          9   charCount16 0293426c    0          9   charIndex17 0293428c    0          9   byteCount18 029349fc    0          5   count19 02934a14    0          6   offset20 ---------------------------------------21 167 strings            上面的内容太多,我省略了,没必要全部显示出来。
1 0:006> !strings -m:河北* 2 Address    Gen    Length   Value 3 --------------------------------------- 4 029324d8    0          3   河北省 5 --------------------------------------- 6 1 matching string 7 0:006> !strings /m:河北* 8 Address    Gen    Length   Value 9 ---------------------------------------10 029324d8    0          3   河北省11 ---------------------------------------12 1 matching string            m 前的可以是英文横线-,也可以是英文/斜线,可以使用 * 进行模糊匹配。有一点要注意:值是:河北省,可以通过 -m:河北*,这个参数是有效的,如果是 -m:河北省* 就是找不到的,体会使用的细节吧,完整字符串不需要增加 * 星号,增加时找不到的。
1 0:006> !strings /m:河北省2 Address    Gen    Length   Value3 ---------------------------------------4 029324d8    0          3   河北省5 ---------------------------------------6 1 matching string
        2.4、我们在托管堆中使用【!mx】命令查找 Person 类型的 Show 方法。
            调试代码:Example_9_1_1
            我们进入到 Windbg 调试界面,如果有断点存在,我们可以使用【bc *】命令将所有的断点全部清除,然后再使用【g】命令,运行程序,会在【Console.ReadLine();】这样代码处暂停,然后我们点击【Break】按钮,就是调试程序了。
1 0:006> !mx Example_9_1_1!*Show*2 AppDomain 6e9fc7a8 (Shared Domain)3 ---------------------------------------------------------4 5 AppDomain 00920db8 (Example_9_1_1.exe)6 ---------------------------------------------------------7 module: Example_9_1_18   class: Example_9_1_1.Person9   Show()            【!mx】命令的参数是:模块名!*方法名*,* 星号表示模糊匹配。也可以通过【命名空间.类名.方法名】 来查找。
1 0:006> !mx Example_9_1_1!Example_9_1_1.Person.Show2 AppDomain 6e9fc7a8 (Shared Domain)3 ---------------------------------------------------------4 5 AppDomain 00920db8 (Example_9_1_1.exe)6 ---------------------------------------------------------7 module: Example_9_1_18   class: Example_9_1_1.Person9   Show()            红色标记的 Show 方法,在 Windbg 里是可以点击的,就相当于执行【!dumpmd】命令,查看方法的描述符。
1 0:006> !dumpmd 00ee4e082 Method Name:Example_9_1_1.Person.Show()3 Class:      00ee13404 MethodTable:00ee4e285 mdToken:      060000086 Module:       00ee40447 IsJitted:   yes8 CodeAddr:   00f309d09 Transparency: Critical            【!name2ee】可以查找方法,但是不支持模糊匹配,要把完整路径,类名、方法名全部写出来才可以,否则找不到。
0:006> !name2ee Example_9_1_1!Example_9_1_1.Person.ShowModule:      00ee4044Assembly:    Example_9_1_1.exeToken:       06000008MethodDesc:00ee4e08Name:      Example_9_1_1.Person.Show()JITTED Code Address: 00f309d0            省略模式是找不到的。
1 0:006> !name2ee Example_9_1_1!*Show* 2 Module:      00ee4044 3 Assembly:    Example_9_1_1.exe 4 0:006> !name2ee Example_9_1_1!Example_9_1_1.Show* 5 Module:      00ee4044 6 Assembly:    Example_9_1_1.exe 7 0:006> !name2ee Example_9_1_1!Example_9_1_1.Person.S* 8 Module:      00ee4044 9 Assembly:    Example_9_1_1.exe10 0:006> !name2ee Example_9_1_1!Example_9_1_1.Person.Sho*11 Module:      00ee404412 Assembly:    Example_9_1_1.exe            【!mx】命令可以支持模糊查询,方法,类型都能查找,比如:我们能查找 Person 类型,Person 的成员都显示出来了。
1 0:006> !mx Example_9_1_1!*Person* 2 AppDomain 6e9fc7a8 (Shared Domain) 3 --------------------------------------------------------- 45 AppDomain 00920db8 (Example_9_1_1.exe) 6 --------------------------------------------------------- 7 module: Example_9_1_1 8   class: Example_9_1_1.Person 9   get_Name()10   set_Name(string)11   get_Address()12   set_Address(Example_9_1_1.Address)13   Show()14   .ctor()15   k__BackingField {fieldtype: string} 16   k__BackingField {fieldtype: Example_9_1_1.Address}
        2.5、观察 Free 块。
            调试代码:Example_9_1_1
            我们进入到 Windbg 调试界面,如果有断点存在,我们可以使用【bc *】命令将所有的断点全部清除,然后再使用【g】命令,运行程序,会在【Console.ReadLine();】这样代码处暂停,然后我们点击【Break】按钮,就是调试程序了。
            1 0:006> !mfrag2 Searching for pinned handles...3 FreeBlock          Size   NextObject   MT         Name4 -----------------------------------------------------------------5 03931010             14   03931020   6ceb2788   System.Object[] (Pinned Handle @ 00ec13fc)6 03932328             14   03932338   6ceb2788   System.Object[] (Pinned Handle @ 00ec13f4)7 03932548             14   03932558   6ceb2788   System.Object[] (Pinned Handle @ 00ec13f0)8 03933558             14   03933568   6ceb2788   System.Object[] (Pinned Handle @ 00ec13ec)9 4 free blocks, 56 bytes            如果大家不知道什么是 free 块,我们可以使用【!dumpheap -stat】命令,查看一下托管堆的统计情况。
1 0:006> !dumpheap -stat 2 Statistics: 3       MT    Count    TotalSize Class Name 4 6ceb5468      1         12 System.Collections.Generic.GenericEqualityComparer`1[] 5 6ceb4888      1         12 System.Security.HostSecurityManager 6 6ceb3d78      1         12 System.Collections.Generic.ObjectEqualityComparer`1[] 7 ... 8 00919e18       10          116      Free(这就是 free 块,我们可以点击前面的地址) 9 ...10 6ceb5c40      3          806 System.Byte[]11 6ceb2c60       10         2988 System.Char[]12 6ceb24e4      167         5906 System.String13 6ceb2788      6      17748 System.Object[]14 Total 335 objects            这十个 free 块的详情如下:
1 0:006> !DumpHeap /d -mt 00919e18 2Address       MT   Size 3 02931000 00919e18       10 Free 4 0293100c 00919e18       10 Free 5 02931018 00919e18       10 Free 6 02931fd0 00919e18       10 Free 7 02933df4 00919e18       10 Free 8 03931000 00919e18       10 Free 9 03931010 00919e18       14 Free10 03932328 00919e18       14 Free11 03932548 00919e18       14 Free12 03933558 00919e18       14 Free13 14 Statistics:15       MT    Count    TotalSize Class Name16 00919e18       10          116      Free17 Total 10 objects
        2.6、我们使用【!dlk】命令检测死锁的问题。
            调试代码:Example_9_1_2
            这个项目的调试过程有些不同,我们可以直接运行我们的应用程序,然后打开 Windbg,通过【File】菜单选择【Attach to process】附加进程来调试程序。
            程序运行的结果如图:
            https://img2023.cnblogs.com/blog/1048776/202311/1048776-20231115141515095-1378679252.png            只会输出这两行文字,其他的无法输出,因为已经死锁了。剩下的就交给 Windbg 吧,Windbg 附加进程完毕,直接输入【!dlk】命令来检查。
1 0:005> !dlk 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 Scanning for threads waiting on SyncBlocks... 9 Scanning for threads waiting on ReaderWriterLock locks...10 Scanning for threads waiting on ReaderWriterLocksSlim locks...11 Scanning for threads waiting on CriticalSections...12 *DEADLOCK DETECTED*13 CLR thread 0x4 holds the lock on SyncBlock 016d038c OBJ:033024d4(Clr 4号线程拥有 Student 的锁)14 ...and is waiting for the lock on SyncBlock 016d0358 OBJ:033024c8(等待 Person 释放锁)15 CLR thread 0x3 holds the lock on SyncBlock 016d0358 OBJ:033024c8(CLR 3 号线程拥有 Person 的锁)16 ...and is waiting for the lock on SyncBlock 016d038c OBJ:033024d4(等待 Student 对象释放锁)17 CLR Thread 0x4 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x18 Native) 18 CLR Thread 0x3 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x18 Native) 19 20 21 1 deadlock detected.(检测到死锁)            如果我们通过手工检查,需要检测同步块索引。
1 0:005> !syncblk 2 Index         SyncBlock MonitorHeld Recursion Owning Thread Info          SyncBlock Owner 3   8 016d0358            3         1 016e6710 43a8   3   033024c8 Example_9_1_2.Person 4   9 016d038c            3         1 016e7738 3698   4   033024d4 Example_9_1_2.Student 5 ----------------------------- 6 Total         9 7 CCW             1 8 RCW             2 9 ComClassFactory 010 Free            0            我们继续使用【!t】命令,查看一下线程的信息。
1 0:005> !t 2 ThreadCount:      4 3 UnstartedThread:0 4 BackgroundThread: 3 5 PendingThread:    0 6 DeadThread:       0 7 Hosted Runtime:   no 8                                                                        Lock   9      ID OSID ThreadOBJ    State GC Mode   GC Alloc ContextDomain   Count Apt Exception10    0    1 2314 016a70d0   2a020 Preemptive03304F08:00000000 016a03b8 1   MTA 11    2    2 43b8 016b8088   2b220 Preemptive00000000:00000000 016a03b8 0   MTA (Finalizer) 12    3    3 43a8 016e6710   3029220 Preemptive03306444:00000000 016a03b8 1   MTA (Threadpool Worker) 13    4    4 3698 016e7738   3029220 Preemptive0330E6E0:00000000 016a03b8 1   MTA (Threadpool Worker)             然后我们切换到3和4号线程分别看一下线程栈的情况。
            以下是3号线程的情况。
1 0:005> ~~s 2 eax=00000000 ebx=00000001 ecx=00000000 edx=00000000 esi=00000001 edi=00000001 3 eip=76f8166c esp=05c4f0f8 ebp=05c4f288 iopl=0         nv up ei pl nz na po nc 4 cs=0023ss=002bds=002bes=002bfs=0053gs=002b             efl=00000202 5 ntdll!NtWaitForMultipleObjects+0xc: 6 76f8166c c21400          ret   14h 78 0:003> !clrstack 9 OS Thread Id: 0x43a8 (3)10 Child SP       IP Call Site11 05c4f450 76f8166c 12 05c4f530 76f8166c 13 05c4f54c 76f8166c System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)14 05c4f5c8 6d298468 System.Threading.Monitor.Enter(System.Object, Boolean ByRef) 15 05c4f5d8 01560d03 Example_9_1_2.Program+c.b__2_0() 16 05c4f640 6d2fd4bb System.Threading.Tasks.Task.InnerInvoke() 17 05c4f64c 6d2fb731 System.Threading.Tasks.Task.Execute() 18 05c4f670 6d2fb6fc System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) 19 05c4f674 6d298604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 20 05c4f6e0 6d298537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 21 05c4f6f4 6d2fb4b2 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) 22 05c4f758 6d2fb357 System.Threading.Tasks.Task.ExecuteEntry(Boolean) 23 05c4f768 6d2fb29d System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 24 05c4f76c 6d26eb7d System.Threading.ThreadPoolWorkQueue.Dispatch() 25 05c4f7bc 6d26e9db System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 26 05c4f9dc 6e2bf036             我们看看 4 号线程栈的情况。
1 0:003> ~~s 2 eax=00000000 ebx=00000001 ecx=00000000 edx=00000000 esi=00000001 edi=00000001 3 eip=76f8166c esp=05e0eba8 ebp=05e0ed38 iopl=0         nv up ei pl nz na po nc 4 cs=0023ss=002bds=002bes=002bfs=0053gs=002b             efl=00000202 5 ntdll!NtWaitForMultipleObjects+0xc: 6 76f8166c c21400          ret   14h 789 0:004> !clrstack10 OS Thread Id: 0x3698 (4)11 Child SP       IP Call Site12 05e0ef00 76f8166c 13 05e0efe0 76f8166c 14 05e0effc 76f8166c System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)15 05e0f078 6d298468 System.Threading.Monitor.Enter(System.Object, Boolean ByRef) 16 05e0f088 01560b73 Example_9_1_2.Program+c.b__2_1() 17 05e0f0f0 6d2fd4bb System.Threading.Tasks.Task.InnerInvoke() 18 05e0f0fc 6d2fb731 System.Threading.Tasks.Task.Execute() 19 05e0f120 6d2fb6fc System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) 20 05e0f124 6d298604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 21 05e0f190 6d298537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 22 05e0f1a4 6d2fb4b2 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) 23 05e0f208 6d2fb357 System.Threading.Tasks.Task.ExecuteEntry(Boolean) 24 05e0f218 6d2fb29d System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 25 05e0f21c 6d26eb7d System.Threading.ThreadPoolWorkQueue.Dispatch() 26 05e0f26c 6d26e9db System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 27 05e0f48c 6e2bf036             红色部分是需要特别关注的。

四、总结
    终于写完了,写作的过程是累并快乐着。学习过程真的没那么轻松,还好是自己比较喜欢这一行,否则真不知道自己能不能坚持下来。老话重谈,《高级调试》的这本书第一遍看,真的很晕,第二遍稍微好点,不学不知道,一学吓一跳,自己欠缺的很多。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
来源:https://www.cnblogs.com/PatrickLiu/archive/2023/11/16/17831265.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Net 高级调试之九:SOSEX 扩展命令介绍