日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

DPC_WATCHDOG_VIOLATION藍屏分析

作者:xsinlink 更新時間: 2023-10-15 編程語言

文章目錄

  • DPC_WATCHDOG_VIOLATION藍屏分析
    • 1. 原理
      • 1.1 DPC WATCHDOG
      • 1.2 DPC超時時間獲取
    • 2. 實例
      • 2.1 分析
    • 3. 總結

DPC_WATCHDOG_VIOLATION藍屏分析

在Windows 10之下,引入了DPC_WATCHDOG_VIOLATION藍屏錯誤,這個錯誤引入的原因是為了防止內核代碼處于錯誤狀態在DPC以及以上級別執行太久,影響系統的穩定性。

對于DPC_WATCHDOG_VIOLATION這個藍屏錯誤碼,存在兩種類型(使用第一個參數來區分):

  1. 第一個參數為0表示單個DPC例程執行超時了(A single DPC or ISR exceeded its time allotment)。
  2. 第一個參數為1表示系統在DPC上面執行超時了(The system cumulatively spent an extended period of time at IRQL DISPATCH_LEVEL or above)。

這兩種藍屏代碼分別如下:

DPC_WATCHDOG_VIOLATION (133)
The DPC watchdog detected a prolonged run time at an IRQL of DISPATCH_LEVEL
or above.
Arguments:
Arg1: 0000000000000000, A single DPC or ISR exceeded its time allotment. The offending
	component can usually be identified with a stack trace.
Arg2: 0000000000000501, The DPC time count (in ticks).
Arg3: 0000000000000500, The DPC time allotment (in ticks).
Arg4: fffff8077ff73358, cast to nt!DPC_WATCHDOG_GLOBAL_TRIAGE_BLOCK, which contains
	additional information regarding this single DPC timeout


DPC_WATCHDOG_VIOLATION (133)
The DPC watchdog detected a prolonged run time at an IRQL of DISPATCH_LEVEL
or above.
Arguments:
Arg1: 0000000000000001, The system cumulatively spent an extended period of time at
	DISPATCH_LEVEL or above. The offending component can usually be
	identified with a stack trace.
Arg2: 0000000000001e00, The watchdog period.
Arg3: fffff80080705330, cast to nt!DPC_WATCHDOG_GLOBAL_TRIAGE_BLOCK, which contains
	additional information regarding the cumulative timeout
Arg4: 0000000000000000

下面我們通過要給實例來探討一下DPC_WATCHDOG_VIOLATION的基本原理以及分析過程。

1. 原理

1.1 DPC WATCHDOG

Windows對于系統運行在DPC和中斷的時間做了相關限定,一般來說DPC不能運行超過100MS,IRS不能運行超過25MS(但是實際值卻比這個要高)。

在Windows中,通過KeAccumulateTicks對運行時間做相關限定,如下:

__int64 __fastcall KeAccumulateTicks(...)
{
	//...
	if (pcrb->NestingLevel == 2 && pcrb->DpcRoutineActive)
	{
		DpcTimeCount = pcrb->DpcTimeCount;
		pcrb->DpcTime += KernelTime;
		NewDpcTimeCount = DpcTimeCount + 1;
		DpcTimeLimit = pcrb->DpcTimeLimit;
		pcrb->DpcTimeCount = NewDpcTimeCount;
		if (DpcTimeLimit)
		{
			//...
			if (NewDpcTimeCount > DpcTimeLimit)
			{
				if (KeEnableWatchdogTimeout)
				{
					HvlInvokeHypervisorDebugger(3i64, 0i64);
					if (!(_BYTE)KdDebuggerEnabled || (_BYTE)KdDebuggerNotPresent)
						KeBugCheckEx(
							0x133u,
							0i64,
							pcrb->DpcTimeCount,
							pcrb->DpcTimeLimit,
							(ULONG_PTR)&KeDpcWatchdogProfileGlobalTriageBlock);
				}
				pcrb->DpcTimeCount = 0;
				KiResetGlobalDpcWatchdogProfiler(pcrb);
			}
		}
	}

	//...
	if (Irql < 2u)
	{
		DpcWatchdogProfile = pcrb->DpcWatchdogProfile;
		pcrb->DpcWatchdogCount = 0;
		//...
		goto Over;
	}
	DpcWatchdogPeriod = pcrb->DpcWatchdogPeriod;
	NewDpcWatchdogCount = pcrb->DpcWatchdogCount + 1;
	pcrb->DpcWatchdogCount = NewDpcWatchdogCount;
	if (DpcWatchdogPeriod)
	{
		//...
		if (DpcWatchdogCount >= DpcWatchdogPeriod)
		{
			if (KeEnableWatchdogTimeout)
			{
				HvlInvokeHypervisorDebugger(2i64, 0i64);
				if (!(_BYTE)KdDebuggerEnabled || (_BYTE)KdDebuggerNotPresent)
					KeBugCheckEx(0x133u, 
						1ui64, 
						pcrb->DpcWatchdogPeriod, 
						(ULONG_PTR)&KeDpcWatchdogProfileGlobalTriageBlock,
						0i64);
			}
		}
	}
	//...
}

從這里看,如果超過時間并且KeEnableWatchdogTimeout為真的話,那么引發133藍屏。那么KeEnableWatchdogTimeout這個是什么時候初始化的呢?我們可以看到初始化的代碼如下:

char __fastcall KeInitSystem(int a1)
{
    //...
    if (!HviIsAnyHypervisorPresent())
      KeEnableWatchdogTimeout = 1;
    //...
}

HviIsAnyHypervisorPresent這個函數返回cpuid指令是否由hypervisor實現,如果不是的話,那么開啟WatchDog。

在上述中KeBugCheckEx存在兩種情況:

  1. 如果是pcrb->DpcRoutineActive在DPC例程中,那么判斷是否DPC例程超時,DPC例程判斷使用的是PRCB中的DpcTimeCountDpcTimeLimitTicks比較。
  2. 接著是判斷整體是否運行在DPC基本下面,使用的是PRCB中的DpcWatchdogCountDpcWatchdogPeriodTicks

由于DpcTimeCount表示的是單個DPC例程的執行時間,因此每一個DPC執行的時候都會設置改值為0。

__int64 __fastcall KiExecuteAllDpcs(...)
{
    //...
    a1->DpcTimeCount = 0;
    //...
}

1.2 DPC超時時間獲取

通過KeQueryDpcWatchdogInformation可以獲取到當前處理器的DPC超時時間信息,如下:

NTSTATUS KeQueryDpcWatchdogInformation(
  PKDPC_WATCHDOG_INFORMATION WatchdogInformation
);

typedef struct _KDPC_WATCHDOG_INFORMATION {
  ULONG DpcTimeLimit;
  ULONG DpcTimeCount;
  ULONG DpcWatchdogLimit;
  ULONG DpcWatchdogCount;
  ULONG Reserved;
} KDPC_WATCHDOG_INFORMATION, *PKDPC_WATCHDOG_INFORMATION;

其中:

  1. DpcTimeLimit : 當前處理器單個延遲過程調用的時間限制。如果已禁用DPC超時,則此值設置為0。
  2. DpcTimeCount : 如果已啟用DPC超時,則本次延遲過程調用的使用時間。
  3. DpcWatchdogLimit : 一系列延遲過程調用所允許的總時間限制。如果已禁用DPC看門狗,則此值設置為零。
  4. DpcWatchdogCount : 如果啟用DPC看門狗,則當前連續延遲過程調用序列的剩余時間值。

例如我們可以看到如下:

0: kd> dt nt!_KPRCB  fffff8007bfe9180 DPC*
   +0x3310 DpcRuntimeHistoryHashTable : 0xffffaf0c`1f0d5690 _RTL_HASH_TABLE
   +0x3318 DpcRuntimeHistoryHashTableCleanupDpc : 0xffffaf0c`1f122c40 _KDPC
   +0x3338 DpcDelegateThread : 0xffffaf0c`1f150140 _KTHREAD
   +0x3340 DpcData : [2] _KDPC_DATA
   +0x33a0 DpcStack : 0xfffff800`7c395fb0 Void
   +0x33ac DpcRequestRate : 0
   +0x33b4 DpcLastCount : 0x67518e
   +0x33ba DpcRoutineActive : 0 ''
   +0x33bc DpcRequestSummary : 0n131174
   +0x33bc DpcRequestSlot : [2] 0n102
   +0x33bc DpcNormalProcessingActive : 0y0
   +0x33bc DpcNormalProcessingRequested : 0y1
   +0x33bc DpcNormalThreadSignal : 0y1
   +0x33bc DpcNormalTimerExpiration : 0y0
   +0x33bc DpcNormalDpcPresent : 0y0
   +0x33bc DpcNormalLocalInterrupt : 0y1
   +0x33bc DpcNormalPriorityAntiStarvation : 0y1
   +0x33bc DpcNormalSwapToDpcDelegate : 0y0
   +0x33bc DpcNormalSpare : 0y00000000 (0)
   +0x33bc DpcThreadActive : 0y0
   +0x33bc DpcThreadRequested : 0y1
   +0x33bc DpcThreadSpare : 0y00000000000000 (0)
   +0x7e40 DpcGate : _KGATE
   +0x7ea8 DpcWatchdogPeriodTicks : 0n7680
   +0x7eac DpcWatchdogCount : 0n7680
   +0x7eb4 DpcWatchdogProfileCumulativeDpcThresholdTicks : 0x1b80
   +0x7eec DpcWatchdogSequenceNumber : 0x18794c
   +0x814c DpcTime : 0x4c56
   +0x815c DpcTimeCount : 0
   +0x8160 DpcTimeLimitTicks : 0x500
   +0x8240 DpcWatchdogProfileSingleDpcThresholdTicks : 0x496
   +0x85d8 DpcWatchdogDpc : _KDPC
   +0x8618 DpcWatchdogTimer : _KTIMER
   +0x88a8 DpcWatchdogProfile : 0xffffaf0c`1f16a000  -> 0x00000000`00000011 Void
   +0x88b0 DpcWatchdogProfileCurrentEmptyCapture : 0xffffaf0c`1f181cf8  -> (null) 

綜上,針對DPC的監控有兩個:

  1. 使用DpcTimeCountDpcTimeLimitTicks來監控單個DPC例程執行的時間,防止DPC例程執行過長。
  2. 使用DpcWatchdogCountDpcWatchdogPeriodTicks來監控CPU執行的DPC時間總和,防止CPU一直執行一系列DPC函數。

2. 實例

下面我們通過一個DPC_WATCHDOG_VIOLATION藍屏轉儲文件來進行實例分析。

2.1 分析

我們先來看一下藍屏代碼DPC_WATCHDOG_VIOLATION,如下:

DPC_WATCHDOG_VIOLATION (133)
The DPC watchdog detected a prolonged run time at an IRQL of DISPATCH_LEVEL
or above.
Arguments:
Arg1: 0000000000000001, The system cumulatively spent an extended period of time at
	DISPATCH_LEVEL or above. The offending component can usually be
	identified with a stack trace.
Arg2: 0000000000001e00, The watchdog period.
Arg3: fffff80080705330, cast to nt!DPC_WATCHDOG_GLOBAL_TRIAGE_BLOCK, which contains
	additional information regarding the cumulative timeout
Arg4: 0000000000000000

此時調用堆棧如下:

0: kd> k
 # Child-SP          RetAddr           Call Site
00 fffff800`7c39cdd8 fffff800`7fc31c8f nt!KeBugCheckEx
01 fffff800`7c39cde0 fffff800`7fc318a1 nt!KeAccumulateTicks+0x20f
02 fffff800`7c39ce50 fffff800`7fc2fa8a nt!KiUpdateRunTime+0x61
03 fffff800`7c39ceb0 fffff800`7fc2f876 nt!KeClockInterruptNotify+0x11a
04 fffff800`7c39cf40 fffff800`7fc14650 nt!HalpTimerClockIpiRoutine+0x16
05 fffff800`7c39cf70 fffff800`7fe1e05a nt!KiCallInterruptServiceRoutine+0xa0
06 fffff800`7c39cfb0 fffff800`7fe1e8c7 nt!KiInterruptSubDispatchNoLockNoEtw+0xfa
07 fffff882`a7a57430 fffff800`7fd67972 nt!KiInterruptDispatchNoLockNoEtw+0x37
08 fffff882`a7a575c0 fffff800`7fc6cb64 nt!KiAcquireKobjectLockSafe+0x32
09 fffff882`a7a575f0 fffff800`7fc44136 nt!KeSetEvent+0x64
0a fffff882`a7a57680 fffff800`7fc18496 nt!IopCompleteRequest+0x396
0b fffff882`a7a57760 fffff800`7fc70846 nt!KiDeliverApc+0x1b6
0c fffff882`a7a57820 fffff800`7fc72169 nt!KiSwapThread+0x936
0d fffff882`a7a57900 fffff800`7fc75e26 nt!KiCommitThreadWait+0x159
0e fffff882`a7a579a0 fffff800`8cce1cc1 nt!KeDelayExecutionThread+0x416
0f fffff882`a7a57a30 fffff800`8cce3b7a xxxDrv_x64+0x1cc1
10 fffff882`a7a57a60 fffff800`7fd54295 xxxDrv_x64+0x3b7a
11 fffff882`a7a57af0 fffff800`7fe20e34 nt!PspSystemThreadStartup+0x55
12 fffff882`a7a57b40 00000000`00000000 nt!KiStartSystemThread+0x34

這里我們可以得出的是藍屏并不是執行某個DPC例程導致的時間夠長,而是該CPU在DPC模式下面執行過長。

此時我們看到DCP的時間如下:

0: kd> dt nt!_KPRCB  fffff8007bfe9180 DPC*
   +0x7ea8 DpcWatchdogPeriodTicks : 0n7680
   +0x7eac DpcWatchdogCount : 0n7680
   +0x815c DpcTimeCount : 0
   +0x8160 DpcTimeLimitTicks : 0x500

從這里我們也可以發現:

  1. DpcTimeCount為0,說明我們沒有執行DPC例程。
  2. DpcWatchdogCountDpcWatchdogPeriodTicks相等,說明CPU在DPC下面執行時間過長。

我們可以看一下當前DPC的信息,如下:

0: kd> !dpcs 0
CPU Type      KDPC       Function
 0: Normal  : 0xffffaf0c32787278 0xfffff8007fe56420 nt!IopPassiveInterruptDpc
 0: Normal  : 0xffffaf0c21e7b520 0xfffff80082092550 Wdf01000!FxInterrupt::_InterruptDpcThunk
 0: Normal  : 0xffffaf0c23ce9808 0xfffff80082c314d0 ndis!ndisInterruptDpc
 0: Normal  : 0xffffaf0c21eb26e8 0xfffff800858666c0 dxgkrnl!DpiFdoDpcForIsr
 0: Normal  : 0xfffff882acf91f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff8007bff15d8 0xfffff8007fc26b60 nt!PpmPerfAction
 0: Normal  : 0xffffaf0c2173a0c8 0xfffff80082ec51d0 tcpip!TcpPeriodicTimeoutHandler
 0: Normal  : 0xffffaf0c211f71e8 0xfffff80082732000 stornvme!NVMeCompletionDpcRoutine
 0: Normal  : 0xfffff8007bff1c68 0xfffff8007fd4c800 nt!KiEntropyDpcRoutine
 0: Normal  : 0xfffff882a8ab6f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff882ad376f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xffffaf0c1f122c40 0xfffff8007fd3fd30 nt!KiDpcRuntimeHistoryHashTableCleanupDpcRoutine
 0: Normal  : 0xffffaf0c231d0de0 0xfffff80082092550 Wdf01000!FxInterrupt::_InterruptDpcThunk
 0: Normal  : 0xffffaf0c21d93db0 0xfffff800b332b470 i8042prt!I8042KeyboardIsrDpc
 0: Normal  : 0xffffaf0c23ce9a48 0xfffff80082c314d0 ndis!ndisInterruptDpc
 0: Normal  : 0xfffff882a8476f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xffffaf0c21233000 0xfffff8008278c0f0 storport!RaidpAdapterRedirectDpcRoutine
 0: Normal  : 0xffffaf0c23081270 0xfffff800ae096d90 Ndu!NduQueryStatisticsDpc
 0: Normal  : 0xfffff882a7f26f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff882ac1a6f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Threaded: 0xfffff8007bff1758 0xfffff8007fd59dc0 nt!KiDpcWatchdog

發現大量DPC,為什么呢?導致DPC執行不了的一個原因是CPU一直處于高IRQL下面,例如中斷,是不是CPU一直處于中斷下面呢?

我們可以使用!dpcwatchdog查看dpcwatchdog的情況,如下:

0: kd> !dpcwatchdog 

All durations are in seconds (1 System tick = 15.625000 milliseconds)

Circular Kernel Context Logger history: !logdump 0x2
DPC and ISR stats: !intstats /d

--------------------------------------------------
CPU#0
--------------------------------------------------
Current DPC: No Active DPC

Pending DPCs:
----------------------------------------
CPU Type      KDPC       Function
 0: Normal  : 0xffffaf0c32787278 0xfffff8007fe56420 nt!IopPassiveInterruptDpc
 0: Normal  : 0xffffaf0c21e7b520 0xfffff80082092550 Wdf01000!FxInterrupt::_InterruptDpcThunk
 0: Normal  : 0xffffaf0c23ce9808 0xfffff80082c314d0 ndis!ndisInterruptDpc
 0: Normal  : 0xffffaf0c21eb26e8 0xfffff800858666c0 dxgkrnl!DpiFdoDpcForIsr
 0: Normal  : 0xfffff882acf91f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff8007bff15d8 0xfffff8007fc26b60 nt!PpmPerfAction
 0: Normal  : 0xffffaf0c2173a0c8 0xfffff80082ec51d0 tcpip!TcpPeriodicTimeoutHandler
 0: Normal  : 0xffffaf0c211f71e8 0xfffff80082732000 stornvme!NVMeCompletionDpcRoutine
 0: Normal  : 0xfffff8007bff1c68 0xfffff8007fd4c800 nt!KiEntropyDpcRoutine
 0: Normal  : 0xfffff882a8ab6f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff882ad376f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xffffaf0c1f122c40 0xfffff8007fd3fd30 nt!KiDpcRuntimeHistoryHashTableCleanupDpcRoutine
 0: Normal  : 0xffffaf0c231d0de0 0xfffff80082092550 Wdf01000!FxInterrupt::_InterruptDpcThunk
 0: Normal  : 0xffffaf0c21d93db0 0xfffff800b332b470 i8042prt!I8042KeyboardIsrDpc
 0: Normal  : 0xffffaf0c23ce9a48 0xfffff80082c314d0 ndis!ndisInterruptDpc
 0: Normal  : 0xfffff882a8476f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xffffaf0c21233000 0xfffff8008278c0f0 storport!RaidpAdapterRedirectDpcRoutine
 0: Normal  : 0xffffaf0c23081270 0xfffff800ae096d90 Ndu!NduQueryStatisticsDpc
 0: Normal  : 0xfffff882a7f26f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Normal  : 0xfffff882ac1a6f60 0xfffff8007fc13ff0 nt!PopExecuteProcessorCallback
 0: Threaded: 0xfffff8007bff1758 0xfffff8007fd59dc0 nt!KiDpcWatchdog


c0000094 Exception in kdexts.dpcwatchdog debugger extension.
      PC: 00007ffa`507625c0  VA: 00000000`00000000  R/W: 0  Parameter: 00000000`00000000

同樣也可以看到大量Pending DPCs,Current DPC: No Active DPC。

3. 總結

DPC_WATCHDOG_VIOLATION有兩種藍屏情況:

  1. Parameter 1 = 0說明當前正在執行DPC例程,并且超時了,可以直接看到DPC超時的原因(或者由于其他線程導致該DPC一直無法退出)。
  2. Parameter 1 = 1說明此時該CPU一直處于DPC狀態執行,這個原因需要具體分析。

有兩個WinDBG命令可以協助分析該藍屏:

  1. !dpcwatchdog查看所有cpu的dpcwatchdog信息。
  2. !dpcs查看指定cpu的dpc例程信息。

原文鏈接:https://blog.csdn.net/xsinlink/article/details/133760020

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新