How to find the owner of a critical section


Many times in my life I have needed to debug a deadlock. You have one thread trying to acquire a critical section, and it can be a pain to determine which thread has it.


Setup: Go get OS Symbols. Having OS symbols is a must, and will make your life easier in many ways. To get OS symbols the easy way, use symbol server. Here is an article about how to do this — http://support.microsoft.com/default.aspx?scid=kb;en-us;319037. For Visual Studio.NET 2003 (and future versions), symsrv.dll is already included in the product. Here is another article on the subject if you prefer — http://www.codeproject.com/debug/postmortemdebug_standalone1.asp.


How to:
1. Go to the thread that is trying to acquire the critical section. Your stack should look like:


7ffe0304() 
ntdll.dll!_ZwWaitForSingleObject@12() ntdll.dll!RtlpWaitForCriticalSection(_RTL_CRITICAL_SECTION * CriticalSection=0x00452ba0)
ntdll.dll!_RtlEnterCriticalSection@4()
deadlock.exe!ThreadFunc(void * __formal=0x00000000)  Line 24 + 0xd bytes C++
kernel32.dll!BaseThreadStart(unsigned long (void *)* lpStartAddress=0x00417681, void * lpParameter=0x00000000)  Line 532 + 0x6 bytes C


2. Go to the ntdll.dll!_RtlEnterCriticalSection frame on the callstack
3. Open the memory window, set display to ‘4-byte integer’, and evaluate ‘@vframe’
4. The first DWORD should be the return address. In my example, this would be the address of ‘deadlock.exe!ThreadFunc Line 24 + 0xd bytes’. If this isn’t the case, then the debugger is having difficulty walking the stack. See if you can find the return address.
5. The DWORD after the return address is the address of the critical section. Drag this into the memory window
6. The fourth DWORD is the ID of the thread owning the critical section. You can drag this to the watch window if you want to see this value in decimal.


Alternatively, if @vframe is correct, you can evaluate this in the watch window ‘*((DWORD*)(*(DWORD*)(@vframe+4))+3)’. You still need to be at the ntdll.dll!_RtlEnterCriticalSection@4() frame:


  *((DWORD*)(*(DWORD*)(@vframe+4))+3) 3304 unsigned long


Happy debugging.


Comments (4)

  1. Pavel Lebedinsky says:

    Or you can do it easy way and just use !locks windbg extension.

    In VS 2003 you can even load it in VC debugger. Open Immediate window then type this:

    .load c:debuggerswinxpntsdexts.dll

    !locks

    (assuming you are on XP and have windbg installed in c:debuggers).

    You can get windbg at http://www.microsoft.com/whdc/ddk/debugging/default.mspx

  2. foo says:

    Of course !locks needs symbols, but that’s really easy too:

    .symfix

    .reload

    in windbg is all you need.

  3. Pavel Lebedinsky says:

    That’s true. On my machine I have the _NT_SYMBOL_PATH env variable set to the symbol server, that’s probably why it worked in VC. On a clean system you’d need to configure symbol path first.

  4. Gregg Miskelly says:

    That is correct. VS doesn’t have a ‘.symfix’ command.