堆疊型失敗插入

注意啟用此功能的指示僅適用于Windows 8的 WDK。 針對Windows 8.1,此功能已整合到驅動程式驗證器中。 在執行Windows 8.1的電腦上,使用系統化的低資源模擬選項。

堆疊型失敗插入選項會在核心模式驅動程式中插入資源失敗。 此選項會搭配 驅動程式驗證器 使用特殊的驅動程式KmAutoFail.sys,以滲透驅動程式錯誤處理路徑。 測試這些路徑在過去非常困難。 [堆疊型失敗插入] 選項會以可預測的方式插入資源失敗,這會使它發現可重現的問題。 由於錯誤路徑很容易重現,因此也可讓您輕鬆地驗證這些問題的修正程式。

為了協助您判斷錯誤的根本原因,會提供偵錯工具擴充功能,告訴您插入的失敗和順序為何。

在特定驅動程式上啟用堆疊式失敗插入選項時,它會攔截該驅動程式到核心的一些呼叫,並Ndis.sys。 堆疊型失敗插入會查看呼叫堆疊,特別是來自其啟用之驅動程式的呼叫堆疊部分。 如果這是第一次看到該堆疊,它會根據該呼叫的語意失敗呼叫。 否則,如果之前已經看到該呼叫,它會透過未變更傳遞。 堆疊式失敗插入包含邏輯,可處理驅動程式可以多次載入和卸載的事實。 即使驅動程式重載至不同的記憶體位置,它也會辨識呼叫堆疊相同。

啟用此選項

當您將 驅動程式部署至測試電腦時,您可以啟用一或多個驅動程式的堆疊式失敗插入功能。 當您 設定驅動程式封裝專案的驅動程式驗證器屬性時,您可以選取 [堆疊式失敗插入] 選項。 您必須重新開機電腦,才能啟用或停用堆疊式失敗插入選項。 您也可以執行測試公用程式,在測試電腦上啟用驅動程式驗證器和此功能。

重要 當您在測試電腦上啟用堆疊式失敗插入時,請務必不要同時選取 [ 低資源模擬]。

  • 使用驅動程式驗證器屬性頁

    1. 開啟驅動程式套件的屬性頁。 以滑鼠右鍵按一下方案總管中的驅動程式套件專案,然後選取 [屬性]。
    2. 在驅動程式套件的屬性頁面中,按一下 [ 組態屬性]、[ 驅動程式安裝],然後按一下 [ 驅動程式驗證器]。
    3. 選取 [啟用驅動程式驗證器]。 當您在測試電腦上啟用驅動程式驗證程式時,您可以選擇為電腦上的所有驅動程式啟用驅動程式驗證器、僅限驅動程式專案,或指定驅動程式的清單。
    4. [堆疊型失敗插入器] 底下,選取 [ (檢查堆疊型失敗插入) 。
    5. 按一下 [套用][確定]
    6. 如需詳細資訊 ,請參閱將驅動程式部署至測試電腦 。 測試電腦必須重新開機才能啟用此選項。
  • 使用啟用和停用驅動程式驗證器測試

    1. 您也可以執行公用程式測試來啟用驅動程式驗證器。 請遵循 如何使用 Visual Studio 在執行時間測試驅動程式中所述的指示。 在 [ 所有測試\驅動程式驗證器 ] 測試類別下,選取 [ 啟用驅動程式驗證程式] (可能需要重新開機) 停用驅動程式驗證程式 (可能需要重新開機) 測試。

    2. 按一下 [驅動程式測試群組] 視窗中的[啟用驅動程式驗證器] (可能需要重新開機) 測試,以選取 [驅動程式驗證器] 選項。

    3. 選取 [ (檢查堆疊型失敗插入) 。

    4. 將這些測試新增至測試群組之後,您可以儲存測試群組。 若要啟用堆疊式失敗插入,請在您已設定測試的電腦上執行 啟用驅動程式驗證程式 (可能重新開機) 測試。

      若要停用驅動程式驗證程式,請執行 停用驅動程式驗證程式, (可能需要重新開機) 測試。

使用堆疊式失敗插入選項

使用堆疊式失敗插入進行測試時,其中一個重要考慮是它發現的大部分錯誤都會導致錯誤檢查。 如果您的驅動程式是開機載入的驅動程式,這可能有點麻煩。 因此,如果停用驅動程式驗證器,我們會自動停用堆疊式失敗插入。 這表示您可以使用 !verifier –disable命令停用驅動程式驗證程式,在開機時從偵錯工具停用堆疊式失敗插入。

如果可能的話,針對使用堆疊式失敗插入的初始測試,請設定驅動程式,使其不會在開機時載入。 然後,您可以執行一些簡單的載入和卸載測試。 堆疊式失敗插入發現的許多 Bug 會在初始化或清除期間發生。 重複載入和卸載驅動程式是尋找這些驅動程式的好方法。

進行任何需要修正才能讓負載卸載測試成功之後,您可以繼續進行 IOCTL 型測試、完整功能測試,最後進行壓力測試。 一般而言,如果您遵循此測試進展,則不會在壓力測試期間發現許多新問題,因為大部分的程式碼路徑都已在之前執行。

使用堆疊式失敗插入 (SBFI) 偵錯工具擴充功能

大部分的堆疊式失敗插入發現問題會導致錯誤檢查。 為了協助判斷這些程式碼錯誤的原因,WDK 會提供堆疊型失敗插入偵錯工具擴充功能和必要的符號。 安裝程式會在偵錯工具系統上安裝這兩者。 預設位置為 C:\Program Files (x86) \Windows Kits\8.0\Debuggers\< arch >

執行偵錯工具擴充功能

  • 從偵錯工具命令提示字元中,輸入下列命令: < path > \kmautofaildbg.dll.autofail。 例如,假設偵錯工具延伸模組安裝在 c:\dbgext,而且 kmautofail.pdb 位於符號路徑中,您會輸入下列命令:

    !c:\dbgext\kmautofaildbg.dll.autofail
    

這會將資訊傾印到您的偵錯工具,其中顯示插入最近失敗的呼叫堆疊。 每個專案看起來會像下面這樣,取自實際的測試回合。 在下列範例中,在 Mydriver.sys 上啟用堆疊式失敗插入

Sequence: 2, Test Number: 0, Process ID: 0, Thread ID: 0
                 IRQ Level: 2, HASH: 0xea98a56083aae93c
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c77566 mydriver!AddDestination+0x66
 0xfffff88003c5eeb2 mydriver!ProcessPacketDestination+0x82
 0xfffff88003c7db82 mydriver!ProcessPacketSource+0x8b2
 0xfffff88003c5d0d8 mydriver!ForwardPackets+0xb8
 0xfffff88003c81102 mydriver!RoutePackets+0x142
 0xfffff88003c83826 mydriver!RouteNetBufferLists+0x306
 0xfffff88003c59a76 mydriver!DeviceSendPackets+0x156
 0xfffff88003c59754 mydriver!ProcessingComplete+0x4a4
 0xfffff88001b69b81 systemdriver2!ProcessEvent+0x1a1
 0xfffff88001b3edc4 systemdriver1!CallChildDriver+0x20
 0xfffff88001b3fc0a systemdriver1!ProcessEvent+0x3e
 0xfffff800c3ea6eb9 nt!KiRetireDpcList+0x209
 0xfffff800c3ea869a nt!KiIdleLoop+0x5a

在輸出頂端,序號會計算插入的錯誤數目。 此範例顯示在此測試回合期間插入的第二個錯誤。 進程識別碼為 0,因此這是系統進程。 IRQL 為 2,因此這會在分派層級呼叫。

從堆疊中,KmAutoFail 是堆疊型失敗插入驅動程式。 KmAutoFail 函式名稱會指出從Mydriver.sys攔截和插入錯誤的函式呼叫。 在這裡,失敗的函式是 ExAllocatePoolWithTag。 KmAutoFail 中攔截對 Ntoskrnl.sys 或Ndis.sys呼叫的所有函式都會使用此命名慣例。 接下來,我們會看到呼叫堆疊,其中包含正在測試的驅動程式 (Mydriver.sys) 。 這是呼叫堆疊的一部分,用來判斷堆疊的唯一性。 因此,偵錯工具延伸模組所傾印的每個專案在呼叫堆疊的這個部分都會是唯一的。 其餘的呼叫堆疊會指出誰呼叫驅動程式。 這的主要重要性是,驅動程式是透過 IOCTL) 或核心模式驅動程式,從使用者模式 (呼叫。

請注意,如果驅動程式從 DriverEntry 常式傳回失敗,通常會在不同的記憶體位置進行重載嘗試。 在此情況下,來自先前位置的呼叫堆疊可能會包含「垃圾」,而不是從驅動程式堆疊資訊。 但這不是問題;它會告訴您驅動程式已正確處理插入的錯誤。

下一個專案顯示透過使用者模式的 IOCTL 呼叫驅動程式。 請注意進程識別碼和 IRQ 層級。 由於 Mydriver.sys 是 NDIS 篩選驅動程式,因此 IOCTL 會通過Ndis.sys。 請注意,nt!NtDeviceIoControlFile 位於堆疊上。 您在使用 IOCTLs 的驅動程式上執行的任何測試都會經歷此函式。

Sequence: 5, Test Number: 0, Process ID: 2052, Thread ID: 4588
                 IRQ Level: 0, HASH: 0xecd4650e9c25ee4
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c6fb39 mydriver!SendMultipleOids+0x41
 0xfffff88003c7157b mydriver!PvtDisconnect+0x437
 0xfffff88003c71069 mydriver!NicDisconnect+0xd9
 0xfffff88003ca3538 mydriver!NicControl+0x10c
 0xfffff88003c99625 mydriver!DeviceControl+0x4c5
 0xfffff88001559d93 NDIS!ndisDummyIrpHandler+0x73
 0xfffff88001559339 NDIS!ndisDeviceControlIrpHandler+0xc9
 0xfffff800c445cc96 nt!IovCallDriver+0x3e6
 0xfffff800c42735ae nt!IopXxxControlFile+0x7cc
 0xfffff800c4274836 nt!NtDeviceIoControlFile+0x56
 0xfffff800c3e74753 nt!KiSystemServiceCopyEnd+0x13

分析堆疊型失敗插入的結果

您正在驅動程式上執行測試,並突然遇到問題。 最有可能是錯誤檢查,但也可能是因為電腦沒有回應。 如何找到原因? 假設它是錯誤檢查,請先使用上述延伸模組來尋找插入失敗的清單,然後使用偵錯工具命令: !analyze –v

最常見的錯誤檢查是因為未檢查配置是否成功所造成。 在此情況下,錯誤檢查分析中的堆疊可能與插入最後一次失敗的堆疊幾乎完全相同。 在失敗配置 (通常) 的下一行之後,驅動程式會存取 null 指標。 這種類型的 Bug 很容易修正。 有時候失敗的配置是清單的一或兩個,但此類型仍然很容易找到並修正。

清除期間會發生第二個最常見的錯誤檢查。 在此情況下,驅動程式可能會偵測到配置失敗,並跳到清除;但在清除期間,驅動程式並未檢查指標,並再次存取 Null 指標。 緊密相關的案例是可以呼叫清除兩次。 如果清除在釋放結構之後未將結構的指標設定為 null,則第二次呼叫清除函式時會嘗試第二次釋放結構,導致錯誤檢查。

導致電腦沒有回應的錯誤較難以診斷,但偵錯它們的程式很類似。 這些錯誤通常是由參考計數或微調鎖定問題所造成。 幸運的是, 驅動程式驗證器 會在導致問題之前攔截許多微調鎖定問題。 在這些情況下,中斷偵錯工具,並使用偵錯工具擴充功能傾印堆疊型失敗插入所插入的錯誤清單。 快速查看有關最新失敗的程式碼可能會顯示參考計數,該計數會在失敗之前取得,但之後不會釋放。 如果沒有,請在驅動程式中尋找等候微調鎖定的執行緒,或尋找明顯錯誤的參考計數。