注意 啟用此功能的指示僅適用於適用於 Windows 8 的 WDK。 針對 Windows 8.1,此功能已整合至驅動程式驗證器。 在執行 Windows 8.1 的電腦上,使用 系統低資源模擬 選項。
堆疊型失敗插入選項會在核心模式驅動程式中插入資源失敗。 此選項會使用特殊的驅動程式 KmAutoFail.sys,搭配 驅動程式驗證器 來滲透驅動程式錯誤處理路徑。 測試這些路徑歷來非常困難。 [堆疊式失敗注入] 選項會以可預測的方式插入資源失敗,使得所發現的問題可以重現。 由於錯誤路徑很容易重現,因此也可讓您輕鬆地驗證這些問題的修正程式。
為了協助您判斷錯誤的根本原因,會提供調試程序擴充功能,讓您確切地知道插入了哪些失敗,以及順序為何。
當在特定驅動程式上啟用堆疊式失敗注入選項時,該選項會攔截該驅動程式對核心的一些呼叫,並 Ndis.sys。 堆疊型失敗注入技術會查看呼叫堆疊,特別是來自啟用的驅動程式部分的呼叫堆疊。 如果這是程序第一次遇到該堆疊,呼叫將根據呼叫的語意而失敗。 否則,如果之前已經看到該呼叫,它會通過未變更。 堆疊型失敗插入包含邏輯來處理驅動程式可以多次載入和卸除的事實。 它會辨識呼叫堆疊是否相同,即使驅動程式重載至不同的記憶體位置也一樣。
當您將 驅動程式部署至測試電腦時,您可以啟用一或多個驅動程式的堆疊型失敗插入功能。 當您設定 驅動程式封裝專案的驅動程式驗證器屬性時,您可以選取 [堆疊型失敗插入] 選項。 您必須重啟電腦,才能啟動或停用 [堆棧型失敗插入] 選項。 您也可以執行測試公用程式,在測試計算機上啟用驅動程式驗證程式和這項功能。
重要 當您在測試計算機上啟用堆疊式失敗插入時,請務必不要同時選取 [低資源模擬]。
使用驅動程式驗證器的屬性頁
- 開啟驅動程式套件的屬性頁。 以滑鼠右鍵按兩下 方案總管 中的驅動程式套件專案,然後選取 [ 屬性]。
- 在驅動程式套件的屬性頁面中,按兩下 [ 組態屬性],按兩下 [ 驅動程式安裝],然後按兩下 [ 驅動程式驗證器]。
- 選取 [啟用驅動程序驗證器]。 當您在測試電腦上啟用驅動程式驗證器時,您可以選擇為計算機上的所有驅動程式啟用驅動程式驗證器、僅針對驅動程式專案,或指定驅動程式的清單。
- 在 堆棧型失敗插入器 底下,選取 (勾選) 堆棧型失敗插入。
- 按兩下 [ 套用 ] 或 [ 確定]。
- 如需詳細資訊 ,請參閱將驅動程式部署至測試計算機 。 測試計算機必須重新啟動才能啟用此選項。
使用啟用和停用驅動程式驗證器測試
您也可以執行公用程式測試來啟用驅動程式驗證器。 遵循 如何使用 Visual Studio 在運行時間測試驅動程式中所述的指示。 在 [ 所有測試\驅動程序驗證器 ] 測試類別下,選取 [啟用驅動程序驗證器] 和[停用驅動程序驗證程式] 和 [停用驅動程序驗證程式] 測試。 。
按兩下 [驅動程式測試群組] 視窗中的 [啟用驅動程式驗證器] (可能需要重新啟動)測試的名稱,以選取 [驅動程式驗證器] 選項。
選取 (勾選) 堆疊式失敗注入。
將這些測試新增至測試群組之後,您可以儲存測試群組。 若要啟用堆疊式故障注入,請在您已設定的測試計算機上執行 啟用驅動程式驗證器(可能需要重新啟動) 測試。
若要停用驅動程式驗證器,請執行 停用驅動程序驗證器(可能需要重新啟動) 測試。
使用堆疊型失敗插入進行測試時,其中一個重要考慮是發現的大部分錯誤都會導致錯誤檢查。 如果您的驅動程式是開機載入的驅動程式,這可能有點痛苦。 因此,如果停用驅動程式驗證工具,我們會自動停用堆疊型失敗注入。 這表示您可以使用 命令 !verifier –disable 停用驅動程序驗證器,以在調試程序開機時停用堆棧型失敗插入。
如果可能的話,針對使用堆疊型失敗插入的初始測試,請設定驅動程式,使其在開機時不會載入。 然後,您可以執行一些簡單的載入和卸除測試。 透過 Stack Based Failure Injection 發現的許多錯誤會在初始化或清除期間發生。 重複載入和卸除驅動程式是尋找這些驅動程式的好方法。
完成取得負載卸除測試成功所需的任何修正之後,您可以繼續進行以IOCTL為基礎的測試、完整的功能測試,最後進行壓力測試。 一般而言,如果您遵循此測試進度,則不會在壓力測試期間發現許多新問題,因為大部分的程式代碼路徑都已在此之前執行。
在堆疊型失敗插入中找到的大部分問題都會導致錯誤檢查。 為了協助判斷這些程式代碼錯誤的原因,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,則第二次呼叫清除函數時,會再次嘗試釋放該結構,從而導致程序錯誤檢查。
導致計算機變得沒有回應的錯誤較難診斷,但偵錯這些錯誤的程式很類似。 這些錯誤通常是由參考計數或自旋鎖問題所造成。 幸運的是,驅動程式驗證器 會在導致問題之前捕捉許多旋轉鎖的問題。 在這些情況下,請中斷除錯程序,然後使用除錯器擴充功能來轉儲堆疊式失敗注入已插入的錯誤清單。 快速查看有關最新失敗的程式碼時,可能會發現參照計數的操作:該參照計數會在失敗發生前被取得,但在失敗後未被釋放。 如果沒有,請在驅動程序中尋找正在等候自旋鎖的線程,或尋找明顯錯誤的引用計數。