Crash Dump Analysis Patterns (Part 42n)

This is C++ condition variable Wait Chain pattern variant. When we have a waiting Blocked Thread stack trace that shows conditional variable implementation functions we are interested in the owner thread:

0:012> kL
# ChildEBP RetAddr
00 03a6f684 748d8ee9 ntdll!NtWaitForSingleObject+0xc
01 03a6f6f8 5f5fcba5 KERNELBASE!WaitForSingleObjectEx+0x99
02 03a6f70c 5f5fb506 msvcr120!Concurrency::details::ExternalContextBase::Block+0×37
03 03a6f778 639cea79 msvcr120!Concurrency::details::_Condition_variable::wait+0xab
04 03a6f7ac 639ceb58 msvcp120!do_wait+0×42
05 03a6f7c0 5c8c5a43 msvcp120!_Cnd_wait+0×10

WARNING: Stack unwind information not available. Following frames may be wrong.
06 03a6f7d0 5c8c4ee6 AppA!foo+0×48883
07 03a6f804 5c8c4bde AppA!foo+0×47d26
08 03a6f834 5c8c4b9c AppA!foo+0×47a1e
09 03a6f848 5c8c4a27 AppA!foo+0×479dc
0a 03a6f854 00dcc4e9 AppA!foo+0×47867
0b 03a6f86c 75823744 AppA+0×1c4e9
0c 03a6f880 76ef9e54 kernel32!BaseThreadInitThunk+0×24
0d 03a6f8c8 76ef9e1f ntdll!__RtlUserThreadStart+0×2f
0e 03a6f8d8 00000000 ntdll!_RtlUserThreadStart+0×1b

We see the thread is waiting for an event 4ac:

0:012> kv 2
# ChildEBP RetAddr Args to Child
00 03a6f684 748d8ee9 000004ac 00000000 00000000 ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
01 03a6f6f8 5f5fcba5 000004ac ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx+0×99 (FPO: [SEH])

0:012> !handle 4ac
Handle 000004ac
Type Event

Instead of digging into implementation internals we show a different approach. We can use Constant Subtrace analysis pattern to find out possible owner thread candidates, and we can also check raw stack region Execution Residue of different threads for Place Trace of synchronization primitives and associated symbolic references (beware of Coincidental Symbolic Information though), and, if possible, Past Stack Traces involving synchronization.

If we look at Stack Trace Collection we can match the following thread that has the same Constant Subtrace as our original waiting thread above:

14 Id: 17a0.39d0 Suspend: 1 Teb: fee4f000 Unfrozen
# ChildEBP RetAddr
00 0679f9f0 748d9edc ntdll!NtReadFile+0xc
01 0679fa54 5c8f38f2 KERNELBASE!ReadFile+0xec
WARNING: Stack unwind information not available. Following frames may be wrong.
02 0679fa84 5c8f3853 AppA!foo+0x76732
03 0679fac8 5c8f37cd AppA!foo+0x76693
04 0679fae0 5c8c4a27 AppA!foo+0x7660d
05 0679faec 00dcc4e9 AppA!foo+0×47867
06 0679fb04 75823744 AppA+0×1c4e9
07 0679fb18 76ef9e54 kernel32!BaseThreadInitThunk+0×24
08 0679fb60 76ef9e1f ntdll!__RtlUserThreadStart+0×2f
09 0679fb70 00000000 ntdll!_RtlUserThreadStart+0×1b

When we dump raw stack data from all threads using this WinDbg script and search for 000004ac we find its occurrences in the raw stack that corresponds to thread #14 we already found:

TEB at fee4f000
ExceptionList: 0679fa44
StackBase: 067a0000
StackLimit: 0679e000

0679f90c 0679f918
0679f910 5f5a4894 msvcr120!Concurrency::details::SchedulerBase::CurrentContext+0×1e
0679f914 00000033
0679f918 0679f950
0679f91c 5f5a48ca msvcr120!Concurrency::details::LockQueueNode::LockQueueNode+0×2a
0679f920 0679f998
0679f924 071cf9ac
0679f928 0679f94c
0679f92c 5f5ff57e msvcr120!Concurrency::critical_section::_Acquire_lock+0×2e
0679f930 071cf9ac
0679f934 0679f994
0679f938 0679f998
0679f93c 00000001
0679f940 070f4bfc
0679f944 0679f970
0679f948 5f5ff41f msvcr120!Concurrency::critical_section::lock+0×31
0679f94c 0679f980
0679f950 5f5ff6dc msvcr120!Concurrency::critical_section::scoped_lock::scoped_lock+0×3b
0679f954 0679f998
0679f958 76f08bcc ntdll!NtSetEvent+0xc
0679f95c 748e30b0 KERNELBASE!SetEvent+0×10
0679f960 000004ac
0679f964 00000000
0679f968 0679f984
0679f96c 5f5fcbe6 msvcr120!Concurrency::details::ExternalContextBase::Unblock+0×3f
0679f970 000004ac
0679f974 03a6f750
0679f978 03a6f750
0679f97c 0679f9b4
0679f980 5f5fb70d msvcr120!Concurrency::details::_Condition_variable::notify_all+0×3f
0679f984 0679f9b4
0679f988 5f5fb722 msvcr120!Concurrency::details::_Condition_variable::notify_all+0×54
0679f98c 07481380
0679f990 05ba6f60
0679f994 071cf9ac

Both methods point to the same possible owner thread which is also blocked in reading a file.

- Dmitry Vostokov @ + -

Leave a Reply