Debugging Tools for Windows |
Critical sections can be displayed in user mode by a variety of different methods. The exact meaning of each field depends on the version of Microsoft Windows version you are using.
Critical sections can be displayed by the !ntsdexts.locks extension, the !critsec extension, the !cs extension, and the dt (Display Type) command.
The !ntsdexts.locks extension displays a list of critical sections associated with the current process. If the -v option is used, all critical sections are displayed. Here is an example:
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
....
Scanned 37 critical sections
If you know the address of the critical section you wish to display, you can use the !critsec extension. This displays the same collection of information as !ntsdexts.locks. For example:
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
The !cs extension is only available in Microsoft Windows XP and later versions of Windows. It can display a critical section based on its address, search an address range for critical sections, and even display the stack trace associated with each critical section. Some of these features require full Windows symbols to work properly. If Application Verifier is active, !cs -t can be used to display the critical section tree. See the !cs reference page for details and examples.
The information displayed by !cs is slightly different than that shown by !ntsdexts.locks and !critsec. For example:
-----------------------------------------
Critical section = 0x77fc49e0 (ntdll!FastPebLock+0x0)
DebugInfo = 0x77fc3e00
LOCKED
LockCount = 0x0
OwningThread = 0x00000c78
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x00000000
The dt (Display Type) command can be used to display the literal contents of the RTL_CRITICAL_SECTION structure. For example:
+0x000 DebugInfo : 0x77fc3e00
+0x004 LockCount : 0
+0x008 RecursionCount : 1
+0x00c OwningThread : 0x00000c78
+0x010 LockSemaphore : (null)
+0x014 SpinCount : 0
The most important fields of the critical section structure are as follows:
A newly initialized critical section looks like this:
CritSec mymodule!cs+0 at 00433E60
LockCount NOT LOCKED
RecursionCount 0
OwningThread 0
EntryCount 0
ContentionCount 0
The debugger displays "NOT LOCKED" as the value for LockCount. The actual value of this field for an unlocked critical section is -1. You can verify this with the dt (Display Type) command:
+0x000 DebugInfo : 0x77fcec80
+0x004 LockCount : -1
+0x008 RecursionCount : 0
+0x00c OwningThread : (null)
+0x010 LockSemaphore : (null)
+0x014 SpinCount : 0
When the first thread calls the EnterCriticalSection routine, the critical section's LockCount, RecursionCount, EntryCount and ContentionCount fields are all incremented by one, and OwningThread becomes the thread ID of the caller. EntryCount and ContentionCount are not incremented. For example:
CritSec mymodule!cs+0 at 00433E60
LockCount 0
RecursionCount 1
OwningThread 4d0
EntryCount 0
ContentionCount 0
At this point, four different things can happen.
CritSec mymodule!cs+0 at 00433E60
LockCount 1
RecursionCount 2
OwningThread 4d0
EntryCount 0
ContentionCount 0
CritSec mymodule!cs+0 at 00433E60
LockCount 1
RecursionCount 1
OwningThread 4d0
EntryCount 1
ContentionCount 1
CritSec mymodule!cs+0 at 00433E60
LockCount NOT LOCKED
RecursionCount 0
OwningThread 0
EntryCount 0
ContentionCount 0
When any thread calls LeaveCriticalSection, Windows decrements LockCount and RecursionCount. This feature has both good and bad aspects. It allows a device driver to enter a critical section on one thread and leave the critical section on another thread. However, it also makes it possible to accidentally call LeaveCriticalSection on the wrong thread, or to call LeaveCriticalSection too many times and cause LockCount to reach values lower than -1. This corrupts the critical section and causes all threads to wait indefinitely on the critical section.
In Microsoft Windows Server 2003 Service Pack 1 and later versions of Windows, the LockCount field is parsed as follows:
As an example, suppose the LockCount is -22. The lowest bit can be determined in this way:
Evaluate expression: 0 = 00000000
The next-lowest bit can be determined in this way:
Evaluate expression: 1 = 00000001
The ones-complement of the remaining bits can be determined in this way:
Evaluate expression: 5 = 00000005
In this example, the first bit is 0 and therefore the critical section is locked. The second bit is 1, and so no thread has been woken for this lock. The complement of the remaining bits is 5, and so there are five threads waiting for this lock.
For information about how to debug critical section time outs, see Critical Section Time Outs. For general information about critical sections, see the Microsoft Windows SDK, the Windows Driver Kit (WDK), or Microsoft Windows Internals by Mark Russinovich and David Solomon.