This article aims to build upon what
@Patrick originally wrote here -
PDC_WATCHDOG_TIMEOUT (14f)
This bugcheck is fairly uncommon and is typically found with devices such as tablets like the Surface Pro. PDC is an acronym for Power Dependency Coordinator and is a component of Modern Standby. This power policy was introduced with Windows 8 and developed further with the introduction of Windows 10. Essentially, its purpose was to allow devices to 'instantly' wake from sleep. It was primarily introduced as Microsoft began to shift its focus to portable devices such as tablets.
Modern Standby is directly analogous to how if you press the power button on your smartphone, it won't be powered off, but rather go into a very low powered idle state and respond to certain events such as app notifications, etc.
It is very important to note that
not all systems will support Modern Standby and to support it, the firmware must meet these requirements:
- x86-x64 processor architecture must have the ability to support hibernation.
- The processor must provide a power engine plugin (PEP) driver. Windows 8.1+ provides this for Intel processors natively.
- The ACPI table must support the following flag - ACPI_S0_LOW_POWER_IDLE_FADT.
As a side note, I believe that the ACPI specification refers to the FADT flag as LOW_POWER_S0_IDLE_CAPABLE instead (please see
Section 5.2.9 of the ACPI Specification).
To check if a system supports Modern Standby, then open a Command Prompt window and enter the following command:
Code:
powercfg /availablesleepstates
You should see output which similar to the below:
Rich (BB code):
The following sleep states are available on this system:
Standby (S3)
Hibernate
Hybrid Sleep
Fast Startup
The following sleep states are not available on this system:
Standby (S1)
The system firmware does not support this standby state.
Standby (S2)
The system firmware does not support this standby state.
Standby (S0 Low Power Idle) << Modern Standby
The system firmware does not support this standby state.
Notice how my system doesn't support Modern Standby? S0 is the power state which is used to represent modern standby. We'll discuss this in greater depth later on in this article, but for now, let's look at the difference between Connected and Disconnected Modern Standby.
There is very little difference between connected and disconnected standby. The main difference being that with connected standby, network traffic still continues and the device is able to be woken by certain network notifications with connected standby. Disconnected standby was introduced with the advent of Windows 10 and therefore you can safely assume that Windows 8+ devices will be using connected standby if the system firmware supports it.
For S3 systems, a device is either in the active powered on state or is in the S3 sleep state. With modern standby devices, devices slowly reduce their power consumption gradually, which enables them to wake faster from sleep states.
Modern Standby Phases:
There is a number of phases associated with modern standby, and this bugcheck can occur within any stage, therefore it is important to understand what each stage does and where the system is as it is transitioning to S0. The following table illustrates all these phases and what they do.
Phase Name | Description | Duration |
Connection Phase | The system is checking for active remote desktop connections, and monitoring for outstanding power IRPs. | Until all remote desktop connections have elapsed |
Presence Phase | Windows 10 only | |
Process Lifetime Manager (PLM) Phase | Suspend all Microsoft Store apps and await for any audio playback to ease | Usually 5 seconds, however, can be longer if audio playback is still continuing |
Maintenance Phase | Completes necessary maintenance tasks | Around 1 second; can be much longer on AC power |
Desktop Activity Moderator (DAM) Phase | Checks for outstanding power IRPs related to currently running desktop applications using PowerRequestExecutionRequired function | Usually immediate, but can be indefinite on systems using AC power. This is typically a maximum of 5 minutes on battery-powered devices. |
Low Power Phase | Notify all hardware devices which have subscribed to this power event. This informs them to place the device into a low powered state. | Usually 5 seconds |
Resiliency Notification Phase | Notify network adapters of the low power state, these are turned off if they do not support S3. | Usually around 1 second |
Resiliency Phase | System prepares to enter the low power idle state. Power manager will listen to activator requests to wake from ‘sleep’. | N/A |
Now, we have a greater understanding of Modern Standby, it's much easier to understand the meaning of the bugcheck parameters. We'll begin exploring each one in the preceding section.
Bugcheck Parameters:
The first parameter refers to the client ID which corresponds to an enumeration type called PDCCLIENTID, this uniquely identifies the PDC client which the client type corresponds to. Please refer to an omitted version of the enumeration below:
Rich (BB code):
4: kd> dt PDCCLIENTID
nt!PDCCLIENTID
PDC_INVALID_CLIENT = 0n0
PDC_PLM_CLIENT = 0n1
PDC_NQM_CLIENT = 0n2
PDC_WNS_CLIENT = 0n3
PDC_DAM_CLIENT = 0n4
PDC_WCM_CLIENT = 0n5
PDC_NCSI_CLIENT = 0n7
PDC_DHCP_CLIENT = 0n8
PDC_TCPIP_CLIENT = 0n9
PDC_WU_CLIENT = 0n11
PDC_GP_CLIENT = 0n12
PDC_NCA_CLIENT = 0n14
PDC_BI_CLIENT = 0n15
PDC_MSCHED_CLIENT = 0n16
[...]
Now, Parameter 2 decides the meaning of other the parameters and therefore we'll look into each one individually. It's interesting to note that all the values for the second parameter are stored within an enumeration type called PDC_CLIENT_TYPE.
Rich (BB code):
3: kd> dt PDC_CLIENT_TYPE
PdcNotificationClient = 0n0
PdcResiliencyClient = 0n1
PdcActivator = 0n2
PdcSuspendResumeClient = 0n3
PdcTaskClient = 0n4
PdcSignalClient = 0n5
PdcPpmProfileClient = 0n6
PdcInvalidType = 0n7
Parameter 2 = 0x1
This indicates that the notification client has failed to respond. The notification client can be a number of different types; these can be found in an enumeration called PDC_NOTIFICATION_TYPE.
Rich (BB code):
3: kd> dt PDC_NOTIFICATION_TYPE
PdcLowPowerNotification = 0n0
PdcPlmNotification = 0n1
PdcDamNotification = 0n2
PdcResiliencyNotification = 0n3
PdcShellNotification = 0n4
PdcInvalidNotification = 0n5
PdcMaxNotification = 0n5
The third parameter will contain the address of the _PDC_NOTIFICATION_CLIENT which has failed to respond.
Rich (BB code):
3: kd> dt _PDC_NOTIFICATION_CLIENT ffffc001079315c0
pdc!_PDC_NOTIFICATION_CLIENT
+0x000 Context : _PDC_COMMON_CONTEXT
+0x020 ClientId : 4
+0x024 ReplyReceived : 0 ''
+0x028 Control : 0xfffff800`f0be7f80 _PDC_NOTIFICATION_CONTROL
Notice how the ReplyReceived field is 0? This means that no response has been received and therefore the notification client will not respond. The Client ID indicates the PDC client which has failed to respond. In this case, it is the Desktop Activity Moderator (DAM).
Now, to find out which type of notification client has failed to respond, we can dump the PDC_NOTIFICATION_CONTROL structure.
Rich (BB code):
3: kd> dt _PDC_NOTIFICATION_CONTROL 0xfffff800`f0be7f80
pdc!_PDC_NOTIFICATION_CONTROL
+0x000 NotificationType : 2 ( PdcDamNotification )
+0x008 ClientContext : (null)
+0x010 PdcSequence : 0x66
+0x018 Callback : 0xfffff800`f0bee9e0 long pdc!PdcpCommonNotificationCompleteCallback+0
+0x020 Armed : 0x1 ''
+0x021 AllClientsResponded : 0 ''
+0x022 WaitTimerExpired : 0x1 '' << The wait timer has expired since no response was provided
+0x023 CatchupReplyPending : 0 ''
+0x024 CatchupRepliesBlockingTransition : 0 ''
+0x028 PendingActivation : _PDC_ACTIVATE_NOTIFICATION_PARAMETERS
+0x058 CurrentValue : 0x79315c0
+0x05c ClientStatus : 0n-16383
+0x060 ClientList : _LIST_ENTRY [ 0xffffc001`079315c0 - 0x00000068`f145fcb8 ]
+0x070 WatchdogTimeout : 3
The NotificationType field provides the type of notification which we were waiting for. Now, looking back at the PDC phases, we can see that we were waiting for a response from the Desktop Activity Monitor component.
Parameter 2 = 0x2
This indicates that a resiliency client has failed to respond. The type of resiliency client can be found within the PDC_RESILIENCY_TYPE structure which is part of the PDC_RESILIENCY_CLIENT structure. The address of the client object can be found within the third parameter.
Rich (BB code):
3: kd> dt _PDC_RESILIENCY_CLIENT
pdc!_PDC_RESILIENCY_CLIENT
+0x000 Context : _PDC_COMMON_CONTEXT
+0x020 ResiliencyType : PDC_RESILIENCY_TYPE
+0x028 ClientReferences : Uint8B
+0x030 PoResiliencyClient : UChar
+0x034 CurrentTransactionId : Uint4B
+0x038 OneTimeTransaction : UChar
+0x03c CurrentState : PDC_CLIENT_STATE
+0x040 NextState : PDC_CLIENT_STATE
+0x044 ClientId : Uint4B
The enumeration contains the following values:
Rich (BB code):
3: kd> dt PDC_RESILIENCY_TYPE
PdcIdleResiliency = 0n0
PdcHostResiliency = 0n1
PdcIoResiliency = 0n2
PdcDamResiliency = 0n3
PdcNqmResiliency = 0n4
PdcWcmResiliency = 0n5
PdcInvalidResiliency = 0n6
PdcMaxResiliency = 0n6
PdcLightestResiliency = 0n5
Parameter 2 = 0x3
This indicates that an activator client held a reference for too long and therefore has led to a timeout condition. Again, the third parameter will contain the address of the appropriate structure, which in this case, is the PDC_ACTIVICATOR_CLIENT.
Rich (BB code):
3: kd> dt _PDC_ACTIVATOR_CLIENT
pdc!_PDC_ACTIVATOR_CLIENT
+0x000 Context : _PDC_COMMON_CONTEXT
+0x020 ClientId : Uint4B
+0x028 ResiliencyContexts : [6] _RESILIENCY_CONTEXT
+0x088 CompletionStatus : Int4B
+0x08c CurrentActivity : PDC_ACTIVITY_TYPE
+0x090 PendingResiliency : PDC_RESILIENCY_TYPE
+0x098 ReplyMessage : _PDC_MESSAGE
+0x188 ActivationStats : Ptr64 _PDC_ACTIVATION_STATS
+0x190 WatchdogContext : Ptr64 _PDC_WATCHDOG_CONTEXT
+0x198 ContinuouslyBlockingDrips : Uint8B
Activators are special system services that are able to block a device from leaving the active power state and transitioning into the low powered idle state. These services can be a number of different system components including Windows Update and the Group Policy manager. Each activator will hold an activation reference will is activated under certain conditions, these conditions vary for each activator.
For example, with the Windows Update service activator, the reference flag will be enabled when the service is scanning for any critical updates; while this reference is held, the device will held in the active power state. Once, the Windows Update service has finished scanning for any critical updates, then the reference flag will be cleared and the device will able to transition into a low powered idle state.
Going back to the PDC_ACTIVATOR_CLIENT structure, there is a number of useful fields that can provide greater context to the type of activator and why it has timed out. The
CompletionStatus indicates the status of the activator and if it is still active, whereas, the
CurrentActivity provides an index into the PDC_ACTIVITY_TYPE enumeration.
Rich (BB code):
3: kd> dt PDC_ACTIVITY_TYPE
PdcNetwork = 0n0
PdcSystem = 0n1
PdcTimer = 0n2
PdcAllNetworks = 0n3
PdcInvalidActivity = 0n4
PdcMaxActivity = 0n4
The
ActivationStats field contains a pointer to the PDC_ACTIVATION_STATS structure which provides some useful statistics about the activator including its maximum duration and activation count.
Rich (BB code):
3: kd> dt _PDC_ACTIVATION_STATS
pdc!_PDC_ACTIVATION_STATS
+0x000 Link : _LIST_ENTRY
+0x010 ClientId : Uint4B
+0x014 ActiveActivations : Uint4B
+0x018 ActivationCount : Uint4B
+0x020 ActivationTime : Uint8B
+0x028 ActivatedTime : Uint8B
+0x030 MaxActivationDuration : Uint8B
Parameter 2 - 0x100
This parameter value shows that the Win32k.sys (Windows subsystem) wasn't able to respond to a monitor-on power request within the specified timeout limit. Effectively, a power IRP was pending for too long and therefore lead to the bugcheck. It is important to note that the monitor-on power requests come from a variety of wake sources. These wake sources are any inputs which are able to 'wake' the system from the idle power state. It should be noted that the wake source operation is the same for both x86-x64 and ARM architectures.
One such wake source is the power button, if this is pressed, then a request will be sent to ensure that the monitor is powered on. Similarly, if you were to open the lid of a laptop, then a monitor on power request will also be sent. Other wake sources include the keyboard and mouse. The complete list of wake sources can be found here -
Modern Standby wake sources
The third parameter of this bugcheck indicates the reason why a monitor-on power request was sent. The number of available reasons are stored within an enumerator called POWER_MONITOR_REQUEST_REASON. Please refer to the documentation here -
POWER_MONITOR_REQUEST_REASON (ntpoapi.h) - Windows drivers
Parameter 4
Rich (BB code):
3: kd> dt _PDC_14F_TRIAGE
pdc!_PDC_14F_TRIAGE
+0x000 ClientProcess : Ptr64 _EPROCESS
+0x008 CallbackThread : Ptr64 _ETHREAD
The fourth parameter is a pointer to the _PDC_14F_TRIAGE structure for most scenarios. This structure simply wraps up the client process and call back thread of the crashed process in one neat location.
Debugging Methodology:
There are two primary methods of debugging this issue: obtaining a sleep study report and WPA trace or examining the crash dump. A sleep study report can be obtained using the following command from an elevated command prompt:
If the command has run successfully, then you should see the following output which indicates where the report has been saved.
Code:
C:\WINDOWS\system32>powercfg.exe /sleepstudy
Sleep Study report saved to file path C:\WINDOWS\system32\sleepstudy-report.html.
Since this article will primarily be focusing on crash dump analysis, I highly recommend reading the following articles for WPA traces and sleep study reports:
In terms of crash dump debugging, I would recommend examining the parameters and then checking for any power IRPs which may be pending. I have written about a very similar bugcheck here which provides further clarification on this -
Debugging Stop 0x17C – PDC_LOCK_WATCHDOG