跟踪占用大量处理器资源的进程
如果一个应用程序消耗 (“hogging”) 所有处理器的注意力,其他进程最终将“饥饿”并无法运行。
使用以下过程更正此类 bug。
调试使用所有 CPU 周期的应用程序
确定导致此问题的应用程序: 使用 任务管理器 或 Perfmon 查找使用处理器周期 99% 或 100% 的进程。 这也可能会告诉你有问题的线程。
将 WinDbg、KD 或 CDB 附加到此过程。
确定导致问题的线程: 闯入有问题的应用程序。 使用 !runaway 3 扩展获取所有 CPU 时间的“快照”。 使用 g (Go) 并等待几秒钟。 然后闯入并再次使用 !失控 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! 第二行中显示的函数偏移量 (BuggyProgram!OpenDestFileStream+0xB3) 。 如果应用程序没有可用的符号,则可能不会显示函数名称。 使用符号或十六进制地址,使用 g (Go) 命令执行,直到到达此返回地址:
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 () 函数永远不会返回。
再次闯入线程并在 BuggyProgram 上设置断点!SaveMsgToDestFolder+0xB3 与 bp (Set Breakpoint) 命令。 然后重复使用 g 命令。 如果此断点立即命中,无论你已执行目标多少次,很可能已识别出有问题的函数:
0:002> bp BuggyProgram!SaveMsgToDestFolder+0xb3 0:002> g 0:002> g
使用 p (Step) 命令继续执行函数,直到确定指令循环序列所在的位置。 然后,可以分析应用程序的源代码,以确定旋转线程的原因。 原因通常是在 while、 do-while、 goto 或 for 循环的逻辑中出现问题。