如果一个应用程序用尽(“独占”)处理器所有的注意信号,其他进程将会最后饿死和无法运行。
使用以下步骤改正这类隐错。
调试一个占用所有处理器周期的应用程序- 识别是哪个应用程序引起这个问题:使用任务管理器Task Manager或Perfmon找出是哪个进程使用了 99% 或 100% 的处理器周期。可能也会告诉你违反规则的线程是哪个。
- 把WinDbg、KD或CDB附上这个进程。
- 识别是哪个线程引起该问题:打断违反规则的应用程序。使用 !runaway 3 扩展取得消耗所有处理器时间的进程“快照”。使用 g (运行)并且等待几秒。然后打断程序、再一次使用 !runaway 3。
0:002> !runaway 3 User Mode Time Thread Time 4e0 0:12:16.0312 268 0:00:00.0000 22c 0:00:00.0000 Kernel Mode Time Thread Time 4e0 0:00:05.0312 268 0:00:00.0000 22c 0:00:00.0000 0:002> g 0:001> !runaway 3 User Mode Time Thread Time 4e0 0:12:37.0609 3d4 0:00:00.0000 22c 0:00:00.0000 Kernel Mode Time Thread Time 4e0 0:00:07.0421 3d4 0:00:00.0000 22c 0:00:00.0000
比较两组数字,并且寻找其用户模式时间或内核模式时间增加最多那个线程。因为 !runaway 按CPU时间降序来分类,违反规则的线程通常是在列表顶端的那个。这里,是线程0x4E0引起该问题。
- 使用 ~ (线程状态)和 ~s (设定当前线程)命令制造这个当前线程:
0:001> ~ 0 Id: 3f4.3d4 Suspend: 1 Teb: 7ffde000 Unfrozen . 1 Id: 3f4.22c Suspend: 1 Teb: 7ffdd000 Unfrozen 2 Id: 3f4.4e0 Suspend: 1 Teb: 7ffdc000 Unfrozen 0:001> ~2s
- 使用 kb (显示栈回溯)获得该线程的一个栈跟踪:
0:002> kb FramePtr RetAddr Param1 Param2 Param3 Function Name 0b4ffc74 77f6c600 000000c8.00000000 77fa5ad0 BuggyProgram!CreateMsgFile+0x1b 0b4ffce4 01836060 0184f440 00000001 0b4ffe20 BuggyProgram!OpenDestFileStream+0xb3 0b4ffd20 01843eba 02b5b920 00000102 02b1e0e0 BuggyProgram!SaveMsgToDestFolder+0xb3 0b4ffe20 01855924 0b4ffef0 00145970 0b4ffef0 BuggyProgram!DispatchToConn+0xa4 0b4ffe5c 77e112e6 01843e16 0b4ffef0 0b4fff34 RPCRT4!DispatchToStubInC+0x34 0b4ffeb0 77e11215 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStubWorker@RPC_INTERFACE@@AAEJPAU_RPC_MESSAGE@@IPAJ@Z+0xb0 0b4ffed0 77e1a3b1 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStub@RPC_INTERFACE@@QAEJPAU_RPC_MESSAGE@Z+0x41 0b4fff40 77e181e4 02b1e0b0 00000074 0b4fff90 RPCRT4!?ReceiveOriginalCall@OSF_SCONNECTION@Z+0x14b 0b4fff60 77e1a5df 02b1e0b0 00000074 00149210 RPCRT4!?DispatchPacket@OSF_SCONNECTION@+0x91 0b4fff90 77e1ac1c 77e15eaf 00149210 0b4fffec RPCRT4!?ReceiveLotsaCalls@OSF_ADDRESS@@QAEXXZ+0x76
- 在正在运行中的函数的返回地址上设定一个断点。这里,返回地址在第一行显示,是0x77F6C600。返回地址等于第二行所显示的函数偏移量 (BuggyProgram!OpenDestFileStream+0xB3)。如果该应用程序没有对应的符号,则不会显示函数名字。使用 g (运行)命令运行直至到达这个返回地址,使用其符号或十六进制地址均可:
0:002> g BuggyProgram!OpenDestFileStream+0xb3
- 如果这个断点命中,重复该进程。例如,假设这个断点命中。应该采取以下步骤:
0:002> kb FramePtr RetAddr Param1 Param2 Param3 Function Name 0b4ffce4 01836060 0184f440 00000001 0b4ffe20 BuggyProgram!OpenDestFileStream+0xb3 0b4ffd20 01843eba 02b5b920 00000102 02b1e0e0 BuggyProgram!SaveMsgToDestFolder+0xb3 0b4ffe20 01855924 0b4ffef0 00145970 0b4ffef0 BuggyProgram!DispatchToConn+0xa4 0b4ffe5c 77e112e6 01843e16 0b4ffef0 0b4fff34 RPCRT4!DispatchToStubInC+0x34 0b4ffeb0 77e11215 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStubWorker@RPC_INTERFACE@@AAEJPAU_RPC_MESSAGE@@IPAJ@Z+0xb0 0b4ffed0 77e1a3b1 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStub@RPC_INTERFACE@@QAEJPAU_RPC_MESSAGE@Z+0x41 0b4fff40 77e181e4 02b1e0b0 00000074 0b4fff90 RPCRT4!?ReceiveOriginalCall@OSF_SCONNECTION@Z+0x14b 0b4fff60 77e1a5df 02b1e0b0 00000074 00149210 RPCRT4!?DispatchPacket@OSF_SCONNECTION@+0x91 0b4fff90 77e1ac1c 77e15eaf 00149210 0b4fffec RPCRT4!?ReceiveLotsaCalls@OSF_ADDRESS@@QAEXXZ+0x76 0:002> g BuggyProgram!SaveMsgToDestFolder+0xb3
如果它命中,继续:
0:002> kb FramePtr RetAddr Param1 Param2 Param3 Function Name 0b4ffd20 01843eba 02b5b920 00000102 02b1e0e0 BuggyProgram!SaveMsgToDestFolder+0xb3 0b4ffe20 01855924 0b4ffef0 00145970 0b4ffef0 BuggyProgram!DispatchToConn+0xa4 0b4ffe5c 77e112e6 01843e16 0b4ffef0 0b4fff34 RPCRT4!DispatchToStubInC+0x34 0b4ffeb0 77e11215 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStubWorker@RPC_INTERFACE@@AAEJPAU_RPC_MESSAGE@@IPAJ@Z+0xb0 0b4ffed0 77e1a3b1 0b4ffef0 00000000 0b4fff34 RPCRT4!?DispatchToStub@RPC_INTERFACE@@QAEJPAU_RPC_MESSAGE@Z+0x41 0b4fff40 77e181e4 02b1e0b0 00000074 0b4fff90 RPCRT4!?ReceiveOriginalCall@OSF_SCONNECTION@Z+0x14b 0b4fff60 77e1a5df 02b1e0b0 00000074 00149210 RPCRT4!?DispatchPacket@OSF_SCONNECTION@+0x91 0b4fff90 77e1ac1c 77e15eaf 00149210 0b4fffec RPCRT4!?ReceiveLotsaCalls@OSF_ADDRESS@@QAEXXZ+0x76 0:002> g BuggyProgram!DispatchToConn+0xa4
- 最后你将会发现某个断点没有命中。如果发生这种情况,那么你不妨假定,最后一个 g 命令让目标运行了而且没有在该断点上中断下来。这就是说,SaveMsgToDestFolder()函数永远不会返回。
- 再次打断线程,并且用 bp (设定断点)命令在BuggyProgram!SaveMsgToDestFolder+0xB3上设定一个断点。然后重复使用 g 命令。不管你执行了该目标多少次,只要直接命中这个断点,就很有可能是找到违反规则的函数了:
0:002> bp BuggyProgram!SaveMsgToDestFolder+0xb3 0:002> g 0:002> g
- 使用 p (步进)命令进入该函数,直至你识别出循环指令序列所在。然后你可以分析该应用程序的源代码,以确定自旋的线程是哪个。通常最终发现,问题是出在while、do-while、goto或者for循环逻辑上的。