二、基础知识
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 的代码:
Address的代码:
Person的代码:
1.2、Example_9_1_2 项目的源码
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 10 2 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> lm 2 start end module name 3 00a20000 00a28000 Example_9_1_1 C (pdb symbols) C:\ProgramData\Dbg\sym\Example_9_1_1.pdb\...\Example_9_1_1.pdb 4 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】一下,就会到我们下的断点处暂停。效果如图:
接下来,我们在另外一个类中给一个方法直接下断点,看看效果怎么样,如截图:
我们执行【!mbp】命令,后面跟类名,包含后缀名和行号。
1 0:000> !mbp Person.cs 13 2 The CLR has not yet been initialized in the process. 3 Breakpoint resolution will be attempted when the CLR is initialized.
然后,我们【g】一下,就会到我们下的断点处暂停。效果如图:
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 Person 2 Address MT Size 3 029324c8 00ee4e28 16 4 5 Statistics: 6 MT Count TotalSize Class Name 7 00ee4e28 1 16 Example_9_1_1.Person 8 Total 1 objects
上面列表中有了Person 对象地址和方法表的地址。
1 0:006> !mdt 029324c8 2 029324c8 (Example_9_1_1.Person) 3 <Name>k__BackingField:NULL (System.String) 4 <Address>k__BackingField:029324ec (Example_9_1_1.Address) 5 0:006> !mdt 029324c8 -r 6 029324c8 (Example_9_1_1.Person) 7 <Name>k__BackingField:NULL (System.String) 8 <Address>k__BackingField:029324ec (Example_9_1_1.Address) 9 <Name>k__BackingField:029324d8 (System.String) Length=3, String="河北省"
如果我们想看【!mdt】如何使用,可以使用【!sosex.help !mdt】,这个输出太多,折叠了,可以自行查看。
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.exe 10 ...(省略很多) 11 02933d8c 0 8 encoding 12 02933dac 0 6 stream 13 0293421c 0 5 bytes 14 02934234 0 5 chars 15 0293424c 0 9 charCount 16 0293426c 0 9 charIndex 17 0293428c 0 9 byteCount 18 029349fc 0 5 count 19 02934a14 0 6 offset 20 --------------------------------------- 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 Value 3 --------------------------------------- 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_1 8 class: Example_9_1_1.Person 9 Show()
【!mx】命令的参数是:模块名!*方法名*,* 星号表示模糊匹配。也可以通过【命名空间.类名.方法名】 来查找。
1 0:006> !mx Example_9_1_1!Example_9_1_1.Person.Show 2 AppDomain 6e9fc7a8 (Shared Domain) 3 --------------------------------------------------------- 4 5 AppDomain 00920db8 (Example_9_1_1.exe) 6 --------------------------------------------------------- 7 module: Example_9_1_1 8 class: Example_9_1_1.Person 9 Show()
红色标记的 Show 方法,在 Windbg 里是可以点击的,就相当于执行【!dumpmd】命令,查看方法的描述符。
1 0:006> !dumpmd 00ee4e08 2 Method Name: Example_9_1_1.Person.Show() 3 Class: 00ee1340 4 MethodTable: 00ee4e28 5 mdToken: 06000008 6 Module: 00ee4044 7 IsJitted: yes 8 CodeAddr: 00f309d0 9 Transparency: Critical
【!name2ee】可以查找方法,但是不支持模糊匹配,要把完整路径,类名、方法名全部写出来才可以,否则找不到。
0:006> !name2ee Example_9_1_1!Example_9_1_1.Person.Show Module: 00ee4044 Assembly: Example_9_1_1.exe Token: 06000008 MethodDesc: 00ee4e08 Name: 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.exe 10 0:006> !name2ee Example_9_1_1!Example_9_1_1.Person.Sho* 11 Module: 00ee4044 12 Assembly: Example_9_1_1.exe
【!mx】命令可以支持模糊查询,方法,类型都能查找,比如:我们能查找 Person 类型,Person 的成员都显示出来了。
1 0:006> !mx Example_9_1_1!*Person* 2 AppDomain 6e9fc7a8 (Shared Domain) 3 --------------------------------------------------------- 4 5 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> !mfrag 2 Searching for pinned handles... 3 FreeBlock Size NextObject MT Name 4 ----------------------------------------------------------------- 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[[System.String, mscorlib]] 5 6ceb4888 1 12 System.Security.HostSecurityManager 6 6ceb3d78 1 12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorlib]] 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.String 13 6ceb2788 6 17748 System.Object[] 14 Total 335 objects
这十个 free 块的详情如下:
1 0:006> !DumpHeap /d -mt 00919e18 2 Address 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 Free 10 03932328 00919e18 14 Free 11 03932548 00919e18 14 Free 12 03933558 00919e18 14 Free 13 14 Statistics: 15 MT Count TotalSize Class Name 16 00919e18 10 116 Free 17 Total 10 objects
2.6、我们使用【!dlk】命令检测死锁的问题。
调试代码:Example_9_1_2
这个项目的调试过程有些不同,我们可以直接运行我们的应用程序,然后打开 Windbg,通过【File】菜单选择【Attach to process】附加进程来调试程序。
程序运行的结果如图:
只会输出这两行文字,其他的无法输出,因为已经死锁了。剩下的就交给 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[Example_9_1_2.Student](Clr 4号线程拥有 Student 的锁) 14 ...and is waiting for the lock on SyncBlock 016d0358 OBJ:033024c8[Example_9_1_2.Person](等待 Person 释放锁) 15 CLR thread 0x3 holds the lock on SyncBlock 016d0358 OBJ:033024c8[Example_9_1_2.Person](CLR 3 号线程拥有 Person 的锁) 16 ...and is waiting for the lock on SyncBlock 016d038c OBJ:033024d4[Example_9_1_2.Student](等待 Student 对象释放锁) 17 CLR Thread 0x4 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x18 Native) [f:\dd\ndp\clr\src\BCL\system\threading\monitor.cs @ 62,9] 18 CLR Thread 0x3 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x18 Native) [f:\dd\ndp\clr\src\BCL\system\threading\monitor.cs @ 62,9] 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 0 10 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 Context Domain Count Apt Exception 10 0 1 2314 016a70d0 2a020 Preemptive 03304F08:00000000 016a03b8 1 MTA 11 2 2 43b8 016b8088 2b220 Preemptive 00000000:00000000 016a03b8 0 MTA (Finalizer) 12 3 3 43a8 016e6710 3029220 Preemptive 03306444:00000000 016a03b8 1 MTA (Threadpool Worker) 13 4 4 3698 016e7738 3029220 Preemptive 0330E6E0:00000000 016a03b8 1 MTA (Threadpool Worker)
然后我们切换到3和4号线程分别看一下线程栈的情况。
以下是3号线程的情况。
1 0:005> ~~[43a8]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=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 5 ntdll!NtWaitForMultipleObjects+0xc: 6 76f8166c c21400 ret 14h 7 8 0:003> !clrstack 9 OS Thread Id: 0x43a8 (3) 10 Child SP IP Call Site 11 05c4f450 76f8166c [GCFrame: 05c4f450] 12 05c4f530 76f8166c [GCFrame: 05c4f530] 13 05c4f54c 76f8166c [HelperMethodFrame_1OBJ: 05c4f54c] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef) 14 05c4f5c8 6d298468 System.Threading.Monitor.Enter(System.Object, Boolean ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\monitor.cs @ 62] 15 05c4f5d8 01560d03 Example_9_1_2.Program+c.b__2_0() [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_9_1_2\Program.cs @ 20] 16 05c4f640 6d2fd4bb System.Threading.Tasks.Task.InnerInvoke() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2884] 17 05c4f64c 6d2fb731 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498] 18 05c4f670 6d2fb6fc System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2861] 19 05c4f674 6d298604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 980] 20 05c4f6e0 6d298537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 928] 21 05c4f6f4 6d2fb4b2 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827] 22 05c4f758 6d2fb357 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2767] 23 05c4f768 6d2fb29d System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2704] 24 05c4f76c 6d26eb7d System.Threading.ThreadPoolWorkQueue.Dispatch() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 820] 25 05c4f7bc 6d26e9db System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 1161] 26 05c4f9dc 6e2bf036 [DebuggerU2MCatchHandlerFrame: 05c4f9dc]
我们看看 4 号线程栈的情况。
1 0:003> ~~[3698]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=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 5 ntdll!NtWaitForMultipleObjects+0xc: 6 76f8166c c21400 ret 14h 7 8 9 0:004> !clrstack 10 OS Thread Id: 0x3698 (4) 11 Child SP IP Call Site 12 05e0ef00 76f8166c [GCFrame: 05e0ef00] 13 05e0efe0 76f8166c [GCFrame: 05e0efe0] 14 05e0effc 76f8166c [HelperMethodFrame_1OBJ: 05e0effc] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef) 15 05e0f078 6d298468 System.Threading.Monitor.Enter(System.Object, Boolean ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\monitor.cs @ 62] 16 05e0f088 01560b73 Example_9_1_2.Program+c.b__2_1() [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_9_1_2\Program.cs @ 34] 17 05e0f0f0 6d2fd4bb System.Threading.Tasks.Task.InnerInvoke() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2884] 18 05e0f0fc 6d2fb731 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498] 19 05e0f120 6d2fb6fc System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2861] 20 05e0f124 6d298604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 980] 21 05e0f190 6d298537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 928] 22 05e0f1a4 6d2fb4b2 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827] 23 05e0f208 6d2fb357 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2767] 24 05e0f218 6d2fb29d System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2704] 25 05e0f21c 6d26eb7d System.Threading.ThreadPoolWorkQueue.Dispatch() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 820] 26 05e0f26c 6d26e9db System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() [f:\dd\ndp\clr\src\BCL\system\threading\threadpool.cs @ 1161] 27 05e0f48c 6e2bf036 [DebuggerU2MCatchHandlerFrame: 05e0f48c]
红色部分是需要特别关注的。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!