Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Breaking on an Exception. Part 2.

 

LOCKS

 

·         We can see threads waiting for managed locks:

All managed objects have a syncblock (4 bytes) before their Method Table and properties. It can hold locking information for thread-safe operations. All syncblocks being used are stored in a table. A class like System.Threading.Monitor will use them to synchronize access to resources.

With this command we get the Index of the syncblock in the table, the address of the syncblock, the thread holding it & the object which syncblock we are waiting for. Let’s take a look to this sample:

0:006> !SyncBlk

Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner

   19 00358ddc            5         2 0032c848   ca8   0   01a2d45c System.Object

-----------------------------

Total           23

CCW             0

RCW             0

ComClassFactory 0

Free            0

 

Thread 0 is holding another 2 threads (MonitorHeld = 1 for each owner and 2 for each waiter) with a synchronization object.

We can analyze the thread call stacks and see which threads may be held by thread 0:

0:006> ~*kpL

...

 

   4  Id: 1868.ac Suspend: 1 Teb: 7ffdb000 Unfrozen

ChildEBP RetAddr 

0461e89c 77550690 ntdll!KiFastSystemCallRet(void)

0461e8a0 76207e09 ntdll!ZwWaitForMultipleObjects(void)+0xc

0461e93c 79ed98fd KERNEL32!WaitForMultipleObjectsEx(unsigned long nCount = 0x461e8f0, void ** lpHandles = 0x00358df0, int bWaitAll = 0, unsigned long dwMilliseconds = 0xffffffff, int bAlertable = 1)+0x11d

0461e9a4 79ed9889 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT(unsigned long nCount = 1, void ** lpHandles = 0x00358df0, int bWaitAll = 0, unsigned long dwMilliseconds = 0xffffffff, int bAlertable = 1)+0x6f

0461e9c4 79ed9808 mscorwks!Thread::DoAppropriateAptStateWait(int numWaiters = 1, void ** pHandles = 0x00358df0, int bWaitAll = 0, unsigned long timeout = 0xffffffff, WaitMode mode = WaitMode_Alertable (1))+0x3c

0461ea48 79ed96c4 mscorwks!Thread::DoAppropriateWaitWorker(int countHandles = 1, void ** handles = 0x00358df0, int waitAll = 0, unsigned long millis = 0xffffffff, WaitMode mode = WaitMode_Alertable (1))+0x13c

0461ea98 79ed9a62 mscorwks!Thread::DoAppropriateWait(int countHandles = 1, void ** handles = 0x00358df0, int waitAll = 0, unsigned long millis = 0xffffffff, WaitMode mode = WaitMode_Alertable (1), struct PendingSync * syncState = 0x00000000)+0x40

0461eaf4 79e78944 mscorwks!CLREvent::WaitEx(unsigned long dwMilliseconds = 0xffffffff, WaitMode mode = WaitMode_Alertable (1), struct PendingSync * syncState = 0x00000000)+0xf7

0461eb08 79ed7b37 mscorwks!CLREvent::Wait(unsigned long dwMilliseconds = 0xffffffff, int alertable = 1, struct PendingSync * syncState = 0x00000000)+0x17

0461eb94 79ed7a9e mscorwks!AwareLock::EnterEpilog(class Thread * pCurThread = 0x00386aa0, int timeOut = -1)+0x8c

0461ebb0 79ebd7e4 mscorwks!AwareLock::Enter(void)+0x61

0461ec50 0080201c mscorwks!JIT_MonEnterWorker_Portable(class Object * obj = 0x00386aa0)+0xb3

0461ec70 793b0d1f WindowsApplication1!WindowsApplication1.Form1.Run8(<HRESULT 0x80004001>)+0x24

...

 

   5  Id: 1868.1fa8 Suspend: 1 Teb: 7ffd9000 Unfrozen

ChildEBP RetAddr 

0446eac4 77550690 ntdll!KiFastSystemCallRet(void)

0446eac8 76207e09 ntdll!ZwWaitForMultipleObjects(void)+0xc

0446eb64 79ed98fd KERNEL32!WaitForMultipleObjectsEx(unsigned long nCount = 0x446eb18, void ** lpHandles = 0x00358df0, int bWaitAll = 0, unsigned long dwMilliseconds = 0xffffffff, int bAlertable = 1)+0x11d

0446ebcc 79ed9889 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT(unsigned long nCount = 1, void ** lpHandles = 0x00358df0, int bWaitAll = 0, unsigned long dwMilliseconds = 0xffffffff, int bAlertable = 1)+0x6f

0446ebec 79ed9808 mscorwks!Thread::DoAppropriateAptStateWait(int numWaiters = 1, void ** pHandles = 0x00358df0, int bWaitAll = 0, unsigned long timeout = 0xffffffff, WaitMode mode = WaitMode_Alertable (1))+0x3c

0446ec70 79ed96c4 mscorwks!Thread::DoAppropriateWaitWorker(int countHandles = 1, void ** handles = 0x00358df0, int waitAll = 0, unsigned long millis = 0xffffffff, WaitMode mode = WaitMode_Alertable (1))+0x13c

0446ecc0 79ed9a62 mscorwks!Thread::DoAppropriateWait(int countHandles = 1, void ** handles = 0x00358df0, int waitAll = 0, unsigned long millis = 0xffffffff, WaitMode mode = WaitMode_Alertable (1), struct PendingSync * syncState = 0x00000000)+0x40

0446ed1c 79e78944 mscorwks!CLREvent::WaitEx(unsigned long dwMilliseconds = 0xffffffff, WaitMode mode = WaitMode_Alertable (1), struct PendingSync * syncState = 0x00000000)+0xf7

0446ed30 79ed7b37 mscorwks!CLREvent::Wait(unsigned long dwMilliseconds = 0xffffffff, int alertable = 1, struct PendingSync * syncState = 0x00000000)+0x17

0446edbc 79ed7a9e mscorwks!AwareLock::EnterEpilog(class Thread * pCurThread = 0x0038ec10, int timeOut = -1)+0x8c

0446edd8 79f59024 mscorwks!AwareLock::Enter(void)+0x61

0446ee40 79fc6352 mscorwks!AwareLock::Contention(int timeOut = -1)+0x199

0446eee0 0080201c mscorwks!JITutil_MonContention(class AwareLock * lock = 0x00358ddc)+0xa3

0446ef00 793b0d1f WindowsApplication1!WindowsApplication1.Form1.Run8(<HRESULT 0x80004001>)+0x24

...

0:006> ~4e !CLRStack -p

OS Thread Id: 0xac (4)

ESP       EIP    

0461eb30 77550f34 [GCFrame: 0461eb30]

0461ec00 77550f34 [HelperMethodFrame_1OBJ: 0461ec00] System.Threading.Monitor.Enter(System.Object)

0461ec58 0080201c WindowsApplication1.Form1.Run8()

...

 

So thread 5 is indeed waiting for our problematic syncblock, and I guess thread 4, too. But I couldn’t see any reference to the syncblock or the related object in the parameters of the call stack of thread 4. Well, the object we used with Monitor.Enter should be near the top of the stack of the thread, as we passed it as a parameter to the managed function at the top of the managed call stack:

0:006> ~4e !dso

OS Thread Id: 0xac (4)

ESP/REG  Object   Name

0461eb68 01a2d45c System.Object

0461ebc0 01a2d45c System.Object

0461ebd4 01a2d45c System.Object

0461ec30 01a669a4 System.Threading.ThreadStart

0461ec3c 01a2d45c System.Object

0461ec58 01a669fc System.Threading.ThreadHelper

...

 

And indeed it is, so both threads 4 & 5 are the ones being held by thread 0.

·         We can see threads waiting for unmanaged locks:

A class like System.Threading.Mutex is not based on syncblocks, but on kernel mutexes instead. When not working with syncblocks, we can use the same unmanaged commands we’ll use with any unmanaged app:

0:006> !locks

0:006> !critsec

0:006> !SIEExtPub.critlist

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

 

Note: this is the last post on the MANAGED DEBUGGING with WINDBG series. I hope my posts help you the same way they help me in my everyday work.

Regards,

 

Alex (Alejandro Campos Magencio)