我目前正试图debugging系统死锁,我很难理解这一点。
Child-SP RetAddr : Args to Child : Call Site fffff880`035cb760 fffff800`02ecef72 : 00000000`00000002 fffffa80`066e8b50 00000000`00000000 fffffa80`066a16e0 : nt!KiSwapContext+0x7a fffff880`035cb8a0 fffff800`02ee039f : fffffa80`0b9256b0 00000000`000007ff 00000000`00000000 00000000`00000000 : nt!KiCommitThreadWait+0x1d2 fffff880`035cb930 fffff880`0312a5e4 : 00000000`00000000 fffff800`00000000 fffffa80`079a3c00 00000000`00000000 : nt!KeWaitForSingleObject+0x19
为什么KeWaitForSingleObject的第一个参数是null?
除非我误解,不是等待的对象的第一个参数? 这个僵局是否仅仅是这个线程在等待什么或是这个普通的行为?
此外,我看到另一个进程(services.exe)显示类似的堆栈跟踪:
1: kd> .thread fffffa800d406b50 Implicit thread is now fffffa80`0d406b50 1: kd> kv *** Stack trace for last set context - .thread/.cxr resets it Child-SP RetAddr : Args to Child : Call Site fffff880`09ed4800 fffff800`02ecef72 : fffffa80`0d406b50 fffffa80`0d406b50 00000000`00000000 fffff8a0`00000000 : nt!KiSwapContext+0x7a fffff880`09ed4940 fffff800`02ee039f : 00000000`000000b4 fffffa80`0b1df7f0 00000000`0000005e fffff800`031ae5e7 : nt!KiCommitThreadWait+0x1d2 fffff880`09ed49d0 fffff800`031d1e3e : fffffa80`0d406b00 00000000`00000006 00000000`00000001 00000000`093bf000 : nt!KeWaitForSingleObject+0x19f fffff880`09ed4a70 fffff800`02ed87d3 : fffffa80`0d406b50 00000000`77502410 fffff880`09ed4ab8 fffffa80`0b171a50 : nt!NtWaitForSingleObject+0xde
这个线程本质上是在等待吗?
您正在调试一个64位进程。
记住这里解释的x64调用约定。 前四个参数在寄存器中传递。 之后,参数被压入堆栈。
不幸的是, kv
一味地显示栈参数。 事实上,在调用的时候确定前四个参数实际上是什么是非常困难的(有时是不可能的),因为它们可能没有存储在任何可以恢复的地方。
所以,你正在看第五个参数nt!NtWaitForSingleObject
,其中nullptr
是一个非常典型的Timeout
参数。
幸运的是,我们调试类型,都不会丢失! 有一个windbg扩展,在函数被调用时尽其所能地重构参数。 该扩展名为CMKD 。 您可以将扩展DLL放在winext
文件夹中,并像这样调用它:
0:000> !cmkd.stack -p Call Stack : 7 frames ## Stack-Pointer Return-Address Call-Site 00 000000a408c7fb28 00007ffda95b1148 ntdll!NtWaitForSingleObject+a Parameter[0] = 0000000000000034 Parameter[1] = 0000000000000000 Parameter[2] = 0000000000000000 Parameter[3] = (unknown) 01 000000a408c7fb30 00007ff7e44c13f1 KERNELBASE!WaitForSingleObjectEx+98 Parameter[0] = 0000000000000034 Parameter[1] = 00000000ffffffff Parameter[2] = 0000000000000000 Parameter[3] = 00007ff7e44cba28 02 000000a408c7fbd0 00007ff7e44c3fed ConsoleApplication2!main+41 Parameter[0] = (unknown) Parameter[1] = (unknown) Parameter[2] = (unknown) Parameter[3] = (unknown)
请注意,它并不总是成功地找到论据,因为其中一些是(unknown)
。 但是,它做得非常好,可以成为调试64位代码的宝贵工具。
这看起来像一个64位的操作系统,因此调用约定是不传递堆栈上的所有参数。 相反,前四个参数在RCX,RDX,R8和R9中传递,其余参数在堆栈上。 所以,如果你接到KeWaitForSingleObject的调用,很容易看到RCX中的内容,并从那里开始。 一旦你超出了一些栈帧,就很难分辨出来,因为有些东西会被加载到那个寄存器中。 原始值可能存储在某个地方,但是很难找到。