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
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.)
Obtenha o ponteiro de pilha desse thread. A pilha será semelhante à mostrada no exemplo anterior. Neste exemplo, o ponteiro de pilha é 0x0068FBA0.
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.
- 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
- 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
- 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.
- 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.