プロセッサ消費の追跡
1 つのアプリケーションがプロセッサのすべてのアテンションを消費している ("占有している") 場合、他のプロセスは "リソース不足" になり、実行できなくなります。
この種のバグを修正するには、以下の手順を使用します。
すべての CPU サイクルを使用しているアプリケーションのデバッグ
この問題の原因となっているアプリケーションを特定する:タスク マネージャーまたは Perfmon を使用して、プロセッサ サイクルの 99% または 100% を使用しているプロセスを見つけます。 これにより、問題のあるスレッドもわかることがあります。
このプロセスに WinDbg、KD、または CDB をアタッチします。
問題の原因となっているスレッドを特定する: 問題のあるアプリケーションに割り込みます。 !runaway 3 拡張機能を使用して、すべての CPU 時間が消費されている場所の "スナップショット" を作成します。 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
2 つの数値セットを比較し、ユーザー モード時間またはカーネル モード時間が最も増加しているスレッドを探します。 !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 と表示されています。 戻りアドレスは、2 行目に表示されている関数オフセット (BuggyProgram!OpenDestFileStream+0xB3) と等価です。 アプリケーションで使用できるシンボルがない場合、関数名が表示されないことがあります。 g (実行) コマンドを使用し、シンボリック アドレスまたは 16 進数アドレスを使用して、この戻りアドレスに到達するまで実行します。
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 ループのロジックの問題であることが判明します。