Partilhar via


Rastreando um hog processador

Se um aplicativo estiver consumindo ("hogging") toda a atenção do processador, outros processos acabarão "famintos" e não poderão ser executados.

Use o procedimento a seguir para corrigir um bug desse tipo.

Depurando um aplicativo que está usando todos os ciclos de CPU

  1. Identifique qual aplicativo está causando esse problema: Use o Gerenciador de Tarefas ou o Perfmon para descobrir qual processo está usando 99% ou 100% dos ciclos do processador. Isso também pode indicar o thread ofensivo.

  2. Anexe WinDbg, KD ou CDB a esse processo.

  3. Identifique qual thread está causando o problema: Interrompa o aplicativo ofensivo. Use a extensão !runaway 3 para obter um "instantâneo" de onde todo o tempo da CPU está indo. Use g (Go) e aguarde alguns segundos. Em seguida, interrompa e use !runaway 3 novamente.

    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
    

    Compare os dois conjuntos de números e procure o thread cujo tempo no modo de usuário ou no modo kernel aumentou mais. Como !runaway classifica por tempo de CPU decrescente, o thread ofensivo geralmente é aquele na parte superior da lista. Nesse caso, o thread 0x4E0 está causando o problema.

  4. Use os comandos ~ (Status do Thread) e ~s (Definir Thread Atual) para tornar este o thread atual:

    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. Use kb (Exibir Backtrace de Pilha) para obter um rastreamento de pilha deste thread:

    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. Defina um ponto de interrupção no endereço de retorno da função em execução no momento. Nesse caso, o endereço de retorno é mostrado na primeira linha como 0x77F6C600. O endereço de retorno é equivalente ao deslocamento de função mostrado na segunda linha (BuggyProgram! OpenDestFileStream+0xB3). Se nenhum símbolo estiver disponível para o aplicativo, o nome da função poderá não aparecer. Use o comando g (Go) para executar até que esse endereço de retorno seja atingido, usando o endereço simbólico ou hexadecimal:

    0:002> g BuggyProgram!OpenDestFileStream+0xb3
    
  7. Se esse ponto de interrupção for atingido, repita o processo. Por exemplo, suponha que esse ponto de interrupção seja atingido. As seguintes etapas devem ser executadas:

    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
    

    Se isso for atingido, continue com:

    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. Por fim, você encontrará um ponto de interrupção que não foi atingido. Nesse caso, você deve supor que o último comando g defina o destino em execução e ele não foi interrompido. Isso significa que a função SaveMsgToDestFolder() nunca retornará.

  9. Interrompa o thread novamente e defina um ponto de interrupção no BuggyProgram! SaveMsgToDestFolder+0xB3 com o comando bp (Set Breakpoint). Em seguida, use o comando g repetidamente. Se esse ponto de interrupção atingir imediatamente, independentemente de quantas vezes você executou o destino, é muito provável que você tenha identificado a função ofensiva:

    0:002> bp BuggyProgram!SaveMsgToDestFolder+0xb3
    
    0:002> g 
    
    0:002> g 
    
  10. Use o comando p (Etapa) para prosseguir com a função até identificar o local onde está a sequência de instruções em loop. Em seguida, você pode analisar o código-fonte do aplicativo para identificar a causa do thread giratório. A causa geralmente será um problema na lógica de um loop while, do-while, goto ou for .