Поделиться через


Отслеживание программы, загружающей процессор

Если одно приложение потребляет ("hogging") все внимание процессора, другие процессы в конечном итоге будут "голодать" и не могут выполняться.

Чтобы исправить ошибку такого рода, используйте следующую процедуру.

Отладка приложения, использующее все циклы ЦП

  1. Определить, какое приложение вызывает эту проблему: использовать диспетчер задач или Perfmon, чтобы найти, какой процесс использует 99% или 100% циклов процессора. Это также может указать вам на обидный поток.

  2. Подключите WinDbg, KD или CDB к этому процессу.

  3. определите, какой поток вызывает проблему: разорвать приложение. Используйте расширение !runaway 3, чтобы сделать "моментальный снимок" того, куда уходит всё время ЦПУ. Используйте g (Go) и подождите несколько секунд. Затем проникните и используйте !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 сортирует по убыванию времени работы ЦП, проблемный поток обычно находится в начале списка. В этом случае поток 0x4E0 вызывает проблему.

  4. Используйте команды ~ (состояние потока) и ~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
    
  5. Используйте kb (Display Stack Backtrace) для получения трассировки стека этого потока:

    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. Задайте точку останова для возвращаемого адреса текущей функции. В этом случае возвращаемый адрес отображается в первой строке как 0x77F6C600. Возвращаемый адрес эквивалентен смещению функции, показанного во второй строке (BuggyProgram!OpenDestFileStream+0xB3). Если для приложения нет символов, имя функции может не отображаться. Используйте команду g (Go) для выполнения до достижения этого возвращаемого адреса с помощью символьного или шестнадцатеричного адреса.

    0:002> g BuggyProgram!OpenDestFileStream+0xb3
    
  7. Если эта точка останова достигнута, повторите процесс. Например, предположим, что точка останова была достигнута. Необходимо выполнить следующие действия.

    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
    
  8. Наконец, вы найдете точку останова, которая не срабатывает. В этом случае следует предположить, что последняя команда g запустила целевой процесс, и он не прерывался. Это означает, что функция SaveMsgToDestFolder() никогда не вернет.

  9. Снова вломите в поток и установите точку останова на BuggyProgram! SaveMsgToDestFolder+0xB3 с помощью команды bp (Задать точку останова). Затем многократно используйте команду g. Если эта точка останова срабатывает сразу же, независимо от того, сколько раз вы запустили целевой код, скорее всего, вы определили проблемную функцию.

    0:002> bp BuggyProgram!SaveMsgToDestFolder+0xb3
    
    0:002> g 
    
    0:002> g 
    
  10. Используйте команду p (шаг) для продолжения работы с функцией, пока не определите участок, где находится циклическая последовательность инструкций. Затем можно проанализировать исходный код приложения, чтобы определить причину вращающегося потока. Как правило, причина будет проблемой в логике в то время как, do-while, gotoили для цикла.