分析停滯的通話問題
當進程直接或間接進行 RPC 呼叫,同時保存重要區段或資源時,就會發生常見問題。 在此情況下,RPC 呼叫會移至另一個進程或電腦,並分派至管理員常式 (伺服器常式) ,然後停止回應或花費太長的時間。 這會導致原始呼叫端遇到重大區段逾時。
透過偵錯工具檢查時,RPC 位於擁有重要區段的執行緒堆疊之上,但不清楚它正在等候的內容。
以下是這類堆疊的其中一個範例。 有許多變化。
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
以下是如何針對此問題進行疑難排解。
針對卡住的通話問題進行疑難排解 - 逐步進行
請確定偵錯工具正在偵錯擁有停滯儲存格的進程。 (這是包含可疑在 RPC.) 中掛斷之用戶端執行緒的程式
取得此執行緒的堆疊指標。 堆疊看起來會像上述範例所示的堆疊。 在此範例中,堆疊指標0x0068FBA0。
取得此執行緒的呼叫資訊。 若要這樣做,請使用 !rpcexts.rpcreadstack 擴充功能搭配執行緒堆疊指標做為其參數,如下所示:
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)
此處顯示的資訊可讓您追蹤呼叫。
- 網路位址是空的,表示本機電腦。 端點為 1120。 您必須判斷裝載此端點的進程。 這可以透過將此端點號碼傳遞至 !rpcexts.getendpointinfo 延伸模組來完成,如下所示:
0:001> !rpcexts.getendpointinfo 1120
Searching for endpoint info ...
PID CELL ID ST PROTSEQ ENDPOINT
--------------------------------------------
0278 0000.0001 01 TCP 1120
- 從上述資訊中,您可以看到進程0x278包含此端點。 您可以使用 !rpcexts.getcallinfo 擴充功能來判斷此進程是否知道有關此呼叫的任何資訊。 此延伸模組需要四個參數:在步驟 3) 中找到 的 CallID、 IfStart和 ProcNum (,以及0x278 的 ProcessID :
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
- 步驟 5 中的資訊很有用,但有些縮寫。 資料格識別碼會在第二個數據行中指定為 0000.0004。 如果您將進程識別碼和此儲存格編號傳遞至 !rpcexts.getdbgcell 延伸模組,您會看到此儲存格的更容易閱讀顯示:
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
這會顯示呼叫處於「分派」狀態,這表示 已離開 RPC 執行時間。 上次更新時間為 470.25。 您可以使用 !rpcexts.rpctime 延伸模組來瞭解目前的時間:
0:001> !rpcexts.rpctime
Current time is: 6003, 422
這會顯示此通話的最後一個連絡人大約在 5533 秒前,大約是 92 分鐘。 因此,這必須是停滯的呼叫。
- 在將偵錯工具附加至伺服器進程之前的最後一個步驟,您可以使用服務執行緒識別碼來隔離目前應該服務呼叫的執行緒。 這是另一個儲存格編號;它會在步驟 6 中顯示為 「0x0.2」。 您可以使用它,如下所示:
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)
現在您已知道您要尋找進程0x278中的執行緒0x1A4。
執行緒可能會進行另一個 RPC 呼叫。 如有必要,您可以重複此程式來追蹤此呼叫。
注意 如果您知道用戶端執行緒,此程式會示範如何尋找伺服器執行緒。 如需反向技術的範例,請參閱 從伺服器執行緒識別呼叫端。