共用方式為


分析停滯的通話問題

當進程直接或間接進行 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

以下是如何針對此問題進行疑難排解。

針對卡住的通話問題進行疑難排解 - 逐步進行

  1. 請確定偵錯工具正在偵錯擁有停滯儲存格的進程。 (這是包含可疑在 RPC.) 中掛斷之用戶端執行緒的程式

  2. 取得此執行緒的堆疊指標。 堆疊看起來會像上述範例所示的堆疊。 在此範例中,堆疊指標0x0068FBA0。

  3. 取得此執行緒的呼叫資訊。 若要這樣做,請使用 !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)

此處顯示的資訊可讓您追蹤呼叫。

  1. 網路位址是空的,表示本機電腦。 端點為 1120。 您必須判斷裝載此端點的進程。 這可以透過將此端點號碼傳遞至 !rpcexts.getendpointinfo 延伸模組來完成,如下所示:
    0:001> !rpcexts.getendpointinfo 1120
    Searching for endpoint info ...
    PID  CELL ID   ST PROTSEQ        ENDPOINT
    --------------------------------------------
    0278 0000.0001 01            TCP 1120
  1. 從上述資訊中,您可以看到進程0x278包含此端點。 您可以使用 !rpcexts.getcallinfo 擴充功能來判斷此進程是否知道有關此呼叫的任何資訊。 此延伸模組需要四個參數:在步驟 3) 中找到 的 CallIDIfStartProcNum (,以及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
  1. 步驟 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 分鐘。 因此,這必須是停滯的呼叫。

  1. 在將偵錯工具附加至伺服器進程之前的最後一個步驟,您可以使用服務執行緒識別碼來隔離目前應該服務呼叫的執行緒。 這是另一個儲存格編號;它會在步驟 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 呼叫。 如有必要,您可以重複此程式來追蹤此呼叫。

注意 如果您知道用戶端執行緒,此程式會示範如何尋找伺服器執行緒。 如需反向技術的範例,請參閱 從伺服器執行緒識別呼叫端