Diagnosing high CPU by studying profiling results, example

Raymond Chen

Raymond

A customer asked for assistance determining why their program demonstrated sporadic high CPU. It occurred only at a client location, so they couldn’t set up a full debugging environment. They were able to convince the client to trace one of the spikes in a profiler. During this capture, high CPU was recorded for around 20% of the running time. Here is the drill-down of where most of that time was going.

Stack% Weight
[Root]19.36
 |- ntdll.dll!RtlUserThreadStart19.36
 |    kernel32.dll!BaseThreadInitThunk19.36
 |    contoso.exe!__wmainCRTStartup19.36
 |    contoso.exe!wWinMain19.36
 | ⋮
 |    fabrikam.dll!Widget::~Widget19.36
 |    fabrikam.dll!WidgetProxy::Disconnect19.36
 |    fabrikam.dll!WidgetProxy::DisconnectAll19.36
 |    fabrikam.dll!WidgetProxy::TransactCommand19.36
 |    fabrikam.dll!WidgetProxy::WaitForResponse19.36
 |    |- user32.dll!MsgWaitForMultipleObjectsEx18.63
 |    |    |- kernel32.dll!WaitForMultipleObjectsExImplementation17.37
 |    |    |    |- KernelBase.dll!WaitForMultipleObjectsEx16.55
 |    |    |    |    |- ntdll.dll!ZwWaitForMultipleObjects8.69
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceCopyEnd4.76
 |    |    |    |    |    |    |- ntoskrnl.exe!NtWaitForMultipleObjects4.71
 |    |    |    |    |    |    |    |- ntoskrnl.exe!ObpWaitForMultipleObjects4.02
 |    |    |    |    |    |    |    |    |- ntoskrnl.exe!ObpWaitForMultipleObjects4.02
 |    |    |    |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |    |    |    |    |    |- ntoskrnl.exe!KiCheckForKernelApcDelivery<0.01
 |    |    |    |    |    |    |    |    |- ntoskrnl.exe!KiDpcInterrupt<0.01
 |    |    |    |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatch<0.01
 |    |    |    |    |    |    |    |- ntoskrnl.exe!NtWaitForMultipleObjects0.49
 |    |    |    |    |    |    |    |- ntoskrnl.exe!memcpy0.20
 |    |    |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |    |    |    |    |- ntoskrnl.exe!KiDpcInterrupt<0.01
 |    |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceCopyEnd0.05
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemCall641.84
 |    |    |    |    |    |- ntdll.dll!ZwWaitForMultipleObjects1.20
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceExit0.56
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceRepeat0.16
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceGdiTebAccess0.13
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceStart0.03
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceCopyStart0.01
 |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |    |- KernelBase.dll!BaseSetLastNTError6.57
 |    |    |    |    |    |- ntdll.dll!RtlNtStatusToDosError6.33
 |    |    |    |    |    |    |- ntdll.dll!RtlNtStatusToDosErrorNoTeb6.05
 |    |    |    |    |    |    |    |- ntdll.dll!RtlNtStatusToDosErrorNoTeb6.04
 |    |    |    |    |    |    |    |- ntoskrnl.exe!KiDpcInterrupt<0.01
 |    |    |    |    |    |    |    |- ntoskrnl.exe!KiApcInterrupt<0.01
 |    |    |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |    |    |    |- ntdll.dll!RtlNtStatusToDosError0.29
 |    |    |    |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |    |    |- KernelBase.dll!BaseSetLastNTError0.19
 |    |    |    |    |    |- ntdll.dll!RtlSetLastWin32Error0.05
 |    |    |    |    |- ntdll.dll!memcpy0.15
 |    |    |    |    |- KernelBase.dll!memcpy0.01
 |    |    |    |    |- ntoskrnl.exe!KiApcInterrupt<0.01
 |    |    |    |    |- ntoskrnl.exe!KiDpcInterrupt<0.01
 |    |    |    |- kernel32.dll!WaitForMultipleObjectsExImplementation0.60
 |    |    |    |- ntdll.dll!memcpy0.16
 |    |    |    |- kernel32.dll!WaitForMultipleObjectsEx0.04
 |    |    |    |- kernel32.dll!memcpy0.02
 |    |    |    |- ntoskrnl.exe!KiInterruptDispatchNoLock<0.01
 |    |    |    |- ntoskrnl.exe!KiApcInterrupt<0.01
 |    |    |- ntoskrnl.exe!KiDpcInterrupt<0.01
 |    |    |- ntoskrnl.exe!KiApcInterrupt<0.01
 |    |- KernelBase.dll!GetTickCount0.24
 |    |- KernelBase.dll!GetLastError0.20
 |    |- fabrikam.dll!GetLastError0.09
 |    |- kernel32.dll!GetLastError0.02

From this chart, you can see that all of the time is consumed in Widget­Proxy::Wait­For­Response, and most of that time is in Msg­Wait­For­Multiple­Objects­Ex. There is a lot of detail inside that function, so let’s hide everything that contributes less than one percent.

Stack% Weight
 |    fabrikam.dll!WidgetProxy::WaitForResponse19.36
 |    |- user32.dll!MsgWaitForMultipleObjectsEx18.63
 |    |    |- kernel32.dll!WaitForMultipleObjectsExImplementation17.37
 |    |    |    |- KernelBase.dll!WaitForMultipleObjectsEx16.55
 |    |    |    |    |- ntdll.dll!ZwWaitForMultipleObjects8.69
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemServiceCopyEnd4.76
 |    |    |    |    |    |    |- ntoskrnl.exe!NtWaitForMultipleObjects4.71
 |    |    |    |    |    |- ntoskrnl.exe!KiSystemCall641.84
 |    |    |    |    |- KernelBase.dll!BaseSetLastNTError6.57

If we look only at the time spent in Wait­For­Multiple­Objects­Ex, 8.69%/16.55% = around half of the time is spent in Zw­Wait­For­Multiple­Objects (the kernel part of the function which does the waiting), and 6.57%/16.55% = around 40% of the time is the time setting the last error code.

That’s odd. Why is this function spending nearly half of its time setting the last error code?

My theory: Because the call is failing with an error!

That explains the high CPU. The call to Wait­For­Multiple­Objects­Ex is failing, which means that instead of waiting, it returns immediately with an error code. The Widget­Proxy::Wait­For­Response function doesn’t quite know what to do in that case, so it shrugs its shoulders and tries waiting again. Eventually, whatever it’s waiting for actually happens, and the call returns, but instead of waiting at low CPU, it accidentally created a CPU spin loop.

Raymond Chen
Raymond Chen

Follow Raymond   

0 comments

Comments are closed.