Aufspüren eines Prozessorhogs

Wenn eine Anwendung die gesamte Aufmerksamkeit des Prozessors verbraucht ("hogging"), werden andere Prozesse am Ende "ausgehungern" und können nicht ausgeführt werden.

Verwenden Sie das folgende Verfahren, um einen solchen Fehler zu beheben.

Debuggen einer Anwendung, die alle CPU-Zyklen verwendet

  1. Identifizieren Sie, welche Anwendung dieses Problem verursacht: Verwenden Sie den Task-Manager oder Perfmon , um zu ermitteln, welcher Prozess 99% oder 100% der Prozessorzyklen verwendet. Dies kann Ihnen auch den beleidigenden Thread mitteilen.

  2. Fügen Sie WinDbg, KD oder CDB an diesen Prozess an.

  3. Identifizieren Sie, welcher Thread das Problem verursacht: Brechen Sie in die beleidigende Anwendung ein. Verwenden Sie die Erweiterung !runaway 3, um einen "Momentaufnahme" zu nehmen, wo die gesamte CPU-Zeit hingeht. Verwenden Sie g (Go), und warten Sie einige Sekunden. Dann brechen Sie ein, und verwenden Sie !runaway 3 erneut.

    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
    

    Vergleichen Sie die beiden Zahlensätze, und suchen Sie nach dem Thread, dessen Benutzermoduszeit oder Kernelmoduszeit am meisten zugenommen hat. Da !runaway nach absteigender CPU-Zeit sortiert wird, ist der beleidigende Thread in der Regel derjenige, der ganz oben in der Liste steht. In diesem Fall verursacht thread 0x4E0 das Problem.

  4. Verwenden Sie die Befehle ~ (Threadstatus) und ~s (Aktuellen Thread festlegen), um dies zum aktuellen Thread zu machen:

    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
    
  5. Verwenden Sie kb (Display Stack Backtrace), um eine Stapelablaufverfolgung dieses Threads zu erhalten:

    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
    
  6. Legen Sie einen Haltepunkt für die Rückgabeadresse der derzeit ausgeführten Funktion fest. In diesem Fall wird die Rückgabeadresse in der ersten Zeile als 0x77F6C600 angezeigt. Die Rückgabeadresse entspricht dem Funktionsoffset in der zweiten Zeile (BuggyProgram! OpenDestFileStream+0xB3). Wenn keine Symbole für die Anwendung verfügbar sind, wird der Funktionsname möglicherweise nicht angezeigt. Verwenden Sie den Befehl g (Go), um auszuführen, bis diese Rückgabeadresse erreicht ist, entweder mithilfe der symbolischen oder hexadezimalen Adresse:

    0:002> g BuggyProgram!OpenDestFileStream+0xb3
    
  7. Wenn dieser Haltepunkt erreicht wird, wiederholen Sie den Vorgang. Angenommen, dieser Haltepunkt ist erreicht. Die folgenden Schritte sollten ausgeführt werden:

    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
    

    Wenn dies erreicht ist, fahren Sie mit fort:

    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
    
  8. Schließlich finden Sie einen Haltepunkt, der nicht erreicht wird. In diesem Fall sollten Sie davon ausgehen, dass der letzte g-Befehl das ausgeführte Ziel festgelegt hat und nicht unterbrochen wurde. Dies bedeutet, dass die Funktion SaveMsgToDestFolder() nie zurückgegeben wird.

  9. Brechen Sie erneut in den Thread ein, und legen Sie einen Haltepunkt für BuggyProgram fest! SaveMsgToDestFolder+0xB3 mit dem Befehl bp (Breakpoint festlegen). Verwenden Sie dann wiederholt den g-Befehl . Wenn dieser Haltepunkt sofort erreicht, unabhängig davon, wie oft Sie das Ziel ausgeführt haben, ist es sehr wahrscheinlich, dass Sie die beleidigende Funktion identifiziert haben:

    0:002> bp BuggyProgram!SaveMsgToDestFolder+0xb3
    
    0:002> g 
    
    0:002> g 
    
  10. Verwenden Sie den Befehl p (Step), um die Funktion zu durchlaufen, bis Sie den Ort identifizieren, an dem sich die Schleifenreihenfolge der Anweisungen befindet. Anschließend können Sie den Quellcode der Anwendung analysieren, um die Ursache des sich drehenden Threads zu identifizieren. Die Ursache stellt sich in der Regel als ein Problem in der Logik einer Zeit, während, goto oder for-Schleife heraus.