Partilhar via


Analisando um problema de chamada paralisada

Um problema comum ocorre quando um processo faz uma chamada RPC, direta ou indiretamente, enquanto mantém uma seção crítica ou um recurso. Nesse caso, a chamada RPC vai para outro processo ou computador e despacha para a rotina do gerente (rotina do servidor), que, em seguida, trava ou leva muito tempo. Isso faz com que o chamador original encontre um tempo limite de seção crítico.

Quando examinado por meio do depurador, o RPC está sobre a pilha do thread que possui a seção crítica, mas não está claro o que ele está esperando.

Aqui está um exemplo dessa pilha. Muitas variações são possíveis.

0:002> ~1k
ChildEBP RetAddr
0068fba0 77e9e8eb ntdll!ZwWaitForSingleObject+0xb
0068fbc8 4efeff73 KERNEL32!WaitForSingleObjectEx+0x5a
0068fbe8 4eff0012 RPCRT4!UTIL_WaitForSyncIO+0x21
0068fc0c 4efe6e2b RPCRT4!UTIL_GetOverlappedResultEx+0x44
0068fc44 4ef973bf RPCRT4!WS_SyncRecv+0x12a
0068fc68 4ef98d5a RPCRT4!OSF_CCONNECTION__TransSendReceive+0xcb
0068fce4 4ef9b682 RPCRT4!OSF_CCONNECTION__SendFragment+0x297
0068fd38 4ef9a5a8 RPCRT4!OSF_CCALL__SendNextFragment+0x272
0068fd88 4ef9a9cb RPCRT4!OSF_CCALL__FastSendReceive+0x165
0068fda8 4ef9a7f8 RPCRT4!OSF_CCALL__SendReceiveHelper+0xed
0068fdd4 4ef946a7 RPCRT4!OSF_CCALL__SendReceive+0x37
0068fdf0 4efd56b3 RPCRT4!I_RpcSendReceive+0xc4
0068fe08 01002850 RPCRT4!NdrSendReceive+0x4f
0068ff40 01001f32 rtclnt+0x2850
0068ffb4 77e92ca8 rtclnt+0x1f32
0068ffec 00000000 KERNEL32!CreateFileA+0x11b

Veja como solucionar esse problema.

Solução de problemas de chamada paralisada – passo a passo

  1. Verifique se o depurador está depurando o processo que possui a célula presa. (Esse é o processo que contém o thread do cliente suspeito de travamento no RPC.)

  2. Obtenha o ponteiro de pilha desse thread. A pilha será semelhante à mostrada no exemplo anterior. Neste exemplo, o ponteiro de pilha é 0x0068FBA0.

  3. Obtenha as informações de chamada para esse thread. Para fazer isso, use a extensão !rpcexts.rpcreadstack com o ponteiro de pilha de threads como parâmetro, da seguinte maneira:

0:001> !rpcexts.rpcreadstack 68fba0
    CallID: 1
    IfStart: 19bb5061
    ProcNum: 0
            Protocol Sequence:      "ncacn_ip_tcp"  (Address: 00692ED8)
            NetworkAddress: ""      (Address: 00692F38)
            Endpoint:       "1120"  (Address: 00693988)

As informações exibidas aqui permitirão que você rastreie a chamada.

  1. O endereço de rede está vazio, o que indica o computador local. O ponto de extremidade é 1120. Você precisa determinar qual processo hospeda esse ponto de extremidade. Isso pode ser feito passando esse número de ponto de extremidade para a extensão !rpcexts.getendpointinfo , da seguinte maneira:
    0:001> !rpcexts.getendpointinfo 1120
    Searching for endpoint info ...
    PID  CELL ID   ST PROTSEQ        ENDPOINT
    --------------------------------------------
    0278 0000.0001 01            TCP 1120
  1. Nas informações anteriores, você pode ver que o processo 0x278 contém esse ponto de extremidade. Você pode determinar se esse processo sabe alguma coisa sobre essa chamada usando a extensão !rpcexts.getcallinfo . Essa extensão precisa de quatro parâmetros: CallID, IfStart e ProcNum (que foram encontrados na etapa 3) e o ProcessID de 0x278:
    0:001> !rpcexts.getcallinfo 1 19bb5061 0 278
    Searching for call info ...
    PID  CELL ID   ST PNO IFSTART  TIDNUMBER CALLFLAG CALLID   LASTTIME CONN/CLN
    ----------------------------------------------------------------------------
    0278 0000.0004 02 000 19bb5061 0000.0002 00000001 00000001 00072c09 0000.0003
  1. As informações na etapa 5 são úteis, mas um pouco abreviadas. A ID da célula é fornecida na segunda coluna como 0000.0004. Se você passar a ID do processo e esse número de célula para a extensão !rpcexts.getdbgcell , verá uma exibição mais legível desta célula:
    0:001> !rpcexts.getdbgcell 278 0.4
    Getting cell info ...
    Call
    Status: Dispatched
    Procedure Number: 0
    Interface UUID start (first DWORD only): 19BB5061
    Call ID: 0x1 (1)
    Servicing thread identifier: 0x0.2
    Call Flags: cached
    Last update time (in seconds since boot):470.25 (0x1D6.19)
    Owning connection identifier: 0x0.3

Isso mostra que a chamada está no estado "despachado", o que significa que é deixou o tempo de execução do RPC. A última hora de atualização é 470.25. Você pode aprender a hora atual usando a extensão !rpcexts.rpctime :

    0:001> !rpcexts.rpctime
    Current time is: 6003, 422

Isso mostra que o último contato com essa chamada foi há aproximadamente 5.533 segundos, o que é de cerca de 92 minutos. Portanto, essa deve ser uma chamada paralisada.

  1. Como última etapa antes de anexar um depurador ao processo do servidor, você pode isolar o thread que deve atender à chamada usando o identificador de thread de manutenção. Esse é outro número de célula; ele apareceu na etapa 6 como "0x0.2". Você pode usá-lo da seguinte maneira:
    0:001> !rpcexts.getdbgcell 278 0.2
    Getting cell info ...
    Thread
    Status: Dispatched
    Thread ID: 0x1A4 (420)
    Last update time (in seconds since boot):470.25 (0x1D6.19)

Agora você sabe que está procurando 0x1A4 de thread no processo 0x278.

É possível que o thread estivesse fazendo outra chamada RPC. Se necessário, você pode rastrear essa chamada repetindo este procedimento.

Nota Este procedimento mostra como localizar o thread do servidor se você souber o thread do cliente. Para obter um exemplo da técnica inversa, consulte Identificando o chamador do thread do servidor.