You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1365 lines
37 KiB
1365 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
critsect.c
|
|
|
|
Abstract:
|
|
|
|
Critical section debugger extension for both ntsd and kd.
|
|
|
|
Author:
|
|
|
|
Daniel Mihai (DMihai) 8-Feb-2001
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
ReadStructFieldVerbose( ULONG64 AddrStructBase,
|
|
PCHAR StructTypeName,
|
|
PCHAR StructFieldName,
|
|
PVOID Buffer,
|
|
ULONG BufferSize )
|
|
{
|
|
ULONG FieldOffset;
|
|
ULONG ErrorCode;
|
|
BOOL Success;
|
|
|
|
Success = FALSE;
|
|
|
|
//
|
|
// Get the field offset
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset (StructTypeName,
|
|
StructFieldName,
|
|
&FieldOffset );
|
|
|
|
if (ErrorCode == S_OK) {
|
|
|
|
//
|
|
// Read the data
|
|
//
|
|
|
|
Success = ReadMemory (AddrStructBase + FieldOffset,
|
|
Buffer,
|
|
BufferSize,
|
|
NULL );
|
|
|
|
if (Success != TRUE) {
|
|
|
|
dprintf ("Cannot read structure field value at 0x%p, error %u\n",
|
|
AddrStructBase + FieldOffset,
|
|
ErrorCode );
|
|
}
|
|
}
|
|
else {
|
|
|
|
dprintf ("Cannot get field offset of %s in %s, error %u\n",
|
|
StructFieldName,
|
|
StructTypeName,
|
|
ErrorCode );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
ReadPtrStructFieldVerbose( ULONG64 AddrStructBase,
|
|
PCHAR StructTypeName,
|
|
PCHAR StructFieldName,
|
|
PULONG64 Buffer )
|
|
{
|
|
ULONG FieldOffset;
|
|
ULONG ErrorCode;
|
|
BOOL Success;
|
|
|
|
Success = FALSE;
|
|
|
|
//
|
|
// Get the field offset inside the structure
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ( StructTypeName,
|
|
StructFieldName,
|
|
&FieldOffset );
|
|
|
|
if (ErrorCode == S_OK) {
|
|
|
|
//
|
|
// Read the data
|
|
//
|
|
|
|
if (ReadPointer(AddrStructBase + FieldOffset, Buffer) == FALSE) {
|
|
|
|
dprintf ("Cannot read structure field value at 0x%p\n",
|
|
AddrStructBase + FieldOffset);
|
|
}
|
|
else {
|
|
|
|
Success = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
dprintf ("Cannot get field offset of %s in structure %s, error %u\n",
|
|
StructFieldName,
|
|
StructTypeName,
|
|
ErrorCode );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
ULONG64
|
|
GetStackTraceAddress( ULONG StackTraceIndex,
|
|
ULONG PointerSize )
|
|
{
|
|
ULONG64 TraceDatabaseAddress;
|
|
ULONG64 TraceDatabase;
|
|
ULONG64 EntryIndexArray;
|
|
ULONG64 StackTraceAddress;
|
|
ULONG64 StackTraceToDump;
|
|
ULONG NumberOfEntriesAdded;
|
|
ULONG ErrorCode;
|
|
BOOL Success;
|
|
|
|
StackTraceToDump = 0;
|
|
|
|
//
|
|
// Stack trace database address
|
|
//
|
|
|
|
TraceDatabaseAddress = GetExpression("&NTDLL!RtlpStackTraceDataBase");
|
|
|
|
if ( TraceDatabaseAddress == 0 ) {
|
|
dprintf( "!cs: Unable to resolve NTDLL!RtlpStackTraceDataBase\n"
|
|
"Please check your symbols\n" );
|
|
|
|
goto Done;
|
|
}
|
|
|
|
if (ReadPointer (TraceDatabaseAddress, &TraceDatabase ) == FALSE) {
|
|
|
|
dprintf( "!cs: Cannot read pointer at NTDLL!RtlpStackTraceDataBase\n" );
|
|
|
|
goto Done;
|
|
}
|
|
else if (TraceDatabase == 0) {
|
|
|
|
dprintf( "NTDLL!RtlpStackTraceDataBase is NULL. Probably the stack traces are not enabled.\n" );
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the number of entries in the database
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose (TraceDatabase,
|
|
"NTDLL!_STACK_TRACE_DATABASE",
|
|
"NumberOfEntriesAdded",
|
|
&NumberOfEntriesAdded,
|
|
sizeof( NumberOfEntriesAdded ) );
|
|
|
|
if( Success == FALSE ) {
|
|
|
|
dprintf( "Cannot read the number of stack traces database entries\n" );
|
|
goto Done;
|
|
}
|
|
else if( StackTraceIndex == 0 ) {
|
|
|
|
dprintf( "No stack trace found.\n" );
|
|
goto Done;
|
|
}
|
|
else if( NumberOfEntriesAdded < StackTraceIndex ) {
|
|
|
|
dprintf( "Stack trace index 0x%X is invalid, current number of stack traces is 0x%X\n",
|
|
StackTraceIndex,
|
|
NumberOfEntriesAdded );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Find the array of stack traces
|
|
//
|
|
|
|
Success = ReadPtrStructFieldVerbose (TraceDatabase,
|
|
"NTDLL!_STACK_TRACE_DATABASE",
|
|
"EntryIndexArray",
|
|
&EntryIndexArray );
|
|
|
|
if( Success == FALSE ) {
|
|
|
|
dprintf( "Cannot read the stack database array address\n" );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Compute the address of our stack trace pointer
|
|
//
|
|
|
|
StackTraceAddress = EntryIndexArray - StackTraceIndex * PointerSize;
|
|
|
|
//
|
|
// Read the pointer to our trace entry in the array
|
|
//
|
|
|
|
if( ReadPointer (StackTraceAddress, &StackTraceToDump) == FALSE) {
|
|
|
|
dprintf( "Cannot read stack trace address at 0x%p\n",
|
|
StackTraceAddress );
|
|
|
|
StackTraceToDump = 0;
|
|
}
|
|
|
|
Done:
|
|
|
|
return StackTraceToDump;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
DumpStackTraceAtAddress (ULONG64 StackTraceAddress,
|
|
ULONG PointerSize)
|
|
{
|
|
ULONG64 CrtTraceAddress;
|
|
ULONG64 CodeAddress;
|
|
ULONG64 Displacement;
|
|
ULONG ErrorCode;
|
|
ULONG BackTraceFieldOffset;
|
|
USHORT Depth;
|
|
USHORT CrtEntryIndex;
|
|
BOOL Success;
|
|
CHAR Symbol[ 1024 ];
|
|
|
|
//
|
|
// Read the stack trace depth
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose (StackTraceAddress,
|
|
"NTDLL!_RTL_STACK_TRACE_ENTRY",
|
|
"Depth",
|
|
&Depth,
|
|
sizeof( Depth ));
|
|
|
|
if( Success == FALSE ) {
|
|
|
|
dprintf ("!cs: Cannot read depth for stack trace at 0x%p\n",
|
|
StackTraceAddress);
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Limit the depth to 20 to protect ourselves from corrupted data
|
|
//
|
|
|
|
Depth = __min( Depth, 20 );
|
|
|
|
//
|
|
// Get a pointer to the BackTrace array
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("NTDLL!_RTL_STACK_TRACE_ENTRY",
|
|
"BackTrace",
|
|
&BackTraceFieldOffset);
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("!cs: Cannot get the BackTrace field offset\n");
|
|
goto Done;
|
|
}
|
|
|
|
CrtTraceAddress = StackTraceAddress + BackTraceFieldOffset;
|
|
|
|
//
|
|
// Dump this stack trace
|
|
//
|
|
|
|
for( CrtEntryIndex = 0; CrtEntryIndex < Depth; CrtEntryIndex += 1 ) {
|
|
|
|
if (ReadPointer (CrtTraceAddress, &CodeAddress) == FALSE) {
|
|
|
|
dprintf ("!cs: Cannot read address at 0x%p\n",
|
|
CrtTraceAddress );
|
|
}
|
|
|
|
GetSymbol( CodeAddress,
|
|
Symbol,
|
|
&Displacement);
|
|
|
|
dprintf ("0x%p: %s+0x%I64X\n",
|
|
CodeAddress,
|
|
Symbol,
|
|
Displacement );
|
|
|
|
CrtTraceAddress += PointerSize;
|
|
}
|
|
|
|
Done:
|
|
|
|
NOTHING;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
DumpCriticalSection ( ULONG64 AddrCritSec,
|
|
ULONG64 AddrEndCritSect,
|
|
ULONG64 AddrDebugInfo,
|
|
ULONG PointerSize,
|
|
BOOL DumpStackTrace )
|
|
{
|
|
ULONG64 DebugInfo;
|
|
ULONG64 OtherDebugInfo;
|
|
ULONG64 CritSec;
|
|
ULONG64 SpinCount;
|
|
ULONG64 OwningThread;
|
|
ULONG64 LockSemaphore;
|
|
ULONG64 StackTraceAddress;
|
|
ULONG64 Displacement;
|
|
LONG LockCount;
|
|
LONG RecursionCount;
|
|
USHORT CreatorBackTraceIndex;
|
|
ULONG DebugInfoFieldOffset;
|
|
ULONG CriticalSectionFieldOffset;
|
|
ULONG ErrorCode;
|
|
BOOL HaveGoodSymbols;
|
|
BOOL Success;
|
|
CHAR Symbol[1024];
|
|
|
|
HaveGoodSymbols = FALSE;
|
|
|
|
//
|
|
// The caller must supply at least one of the
|
|
// critical section or debug information address.
|
|
//
|
|
|
|
if (AddrCritSec == 0 && AddrDebugInfo == 0) {
|
|
|
|
dprintf ("Internal debugger extension error: Both critical section and debug info are NULL\n");
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the field offsets for various structures and check if we have
|
|
// good symbols, with type information.
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("NTDLL!_RTL_CRITICAL_SECTION",
|
|
"DebugInfo",
|
|
&DebugInfoFieldOffset );
|
|
|
|
if (ErrorCode != S_OK)
|
|
{
|
|
dprintf( "Bad symbols for NTDLL (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
ErrorCode = GetFieldOffset ("NTDLL!_RTL_CRITICAL_SECTION_DEBUG",
|
|
"CriticalSection",
|
|
&CriticalSectionFieldOffset );
|
|
|
|
if (ErrorCode != S_OK)
|
|
{
|
|
dprintf( "Bad symbols for NTDLL (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
HaveGoodSymbols = TRUE;
|
|
|
|
//
|
|
// Read all the rest of the information we need
|
|
//
|
|
|
|
CritSec = AddrCritSec;
|
|
DebugInfo = AddrDebugInfo;
|
|
|
|
if (AddrCritSec == 0 || (AddrEndCritSect != 0 && AddrDebugInfo != 0)) {
|
|
|
|
//
|
|
// Read the critical section address
|
|
//
|
|
|
|
if (ReadPointer (AddrDebugInfo + CriticalSectionFieldOffset, &CritSec) == FALSE ) {
|
|
|
|
dprintf ("Cannot read the critical section address at 0x%p.\n"
|
|
"The memory is probably paged out or the active critical section list is corrupted.\n",
|
|
AddrDebugInfo + CriticalSectionFieldOffset );
|
|
|
|
//
|
|
// We don't have any useful information to dump
|
|
// since we can't read the address of the critical section structure.
|
|
//
|
|
// Just display the stack trace since the active critical section list
|
|
// might be corrupted.
|
|
//
|
|
|
|
DumpStackTrace = TRUE;
|
|
|
|
goto DisplayStackTrace;
|
|
}
|
|
|
|
if (AddrCritSec != 0 ) {
|
|
|
|
//
|
|
// We are dumpig all the critical sections in a range.
|
|
//
|
|
|
|
if (CritSec < AddrCritSec || CritSec > AddrEndCritSect) {
|
|
|
|
//
|
|
// We don't want to display this critical section
|
|
// because it is out of the range.
|
|
//
|
|
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the the critical section address from the DebugInfo
|
|
//
|
|
|
|
dprintf( "-----------------------------------------\n" );
|
|
|
|
dprintf ("DebugInfo = 0x%p\n",
|
|
AddrDebugInfo );
|
|
|
|
GetSymbol( CritSec,
|
|
Symbol,
|
|
&Displacement);
|
|
|
|
dprintf ("Critical section = 0x%p (%s+0x%I64X)\n",
|
|
CritSec,
|
|
Symbol,
|
|
Displacement );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We have the critical section address from our caller
|
|
//
|
|
|
|
GetSymbol( CritSec,
|
|
Symbol,
|
|
&Displacement);
|
|
|
|
dprintf( "-----------------------------------------\n" );
|
|
|
|
dprintf ("Critical section = 0x%p (%s+0x%I64X)\n",
|
|
AddrCritSec,
|
|
Symbol,
|
|
Displacement );
|
|
|
|
if (DebugInfo == 0) {
|
|
|
|
//
|
|
// Read the DebugInfo address from the critical section structure
|
|
//
|
|
|
|
if (ReadPointer (AddrCritSec + DebugInfoFieldOffset, &DebugInfo) == FALSE) {
|
|
|
|
dprintf ("Cannot read DebugInfo adddress at 0x%p. Possible causes:\n"
|
|
"\t- The critical section is not initialized, deleted or corrupted\n"
|
|
"\t- The critical section was a global variable in a DLL that was unloaded\n"
|
|
"\t- The memory is paged out\n",
|
|
AddrCritSec + DebugInfoFieldOffset );
|
|
}
|
|
}
|
|
|
|
if (DebugInfo != 0) {
|
|
|
|
dprintf ("DebugInfo = 0x%p\n",
|
|
DebugInfo );
|
|
}
|
|
else {
|
|
|
|
dprintf ("Uninitialized or deleted.\n");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read all the rest of the fields of this critical section
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose (CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"LockCount",
|
|
&LockCount,
|
|
sizeof( LockCount ) );
|
|
|
|
if( Success != TRUE )
|
|
{
|
|
//
|
|
// Couldn't read the LockCount so we cannot say if it's
|
|
// locked or not. This can happen especially in stress where everything is
|
|
// paged out because of memory pressure.
|
|
//
|
|
|
|
dprintf ("Cannot determine if the critical section is locked or not.\n" );
|
|
|
|
goto DisplayStackTrace;
|
|
}
|
|
|
|
//
|
|
// Determine if the critical section is locked or not
|
|
//
|
|
|
|
if (LockCount == -1) {
|
|
|
|
//
|
|
// The critical section is not locked
|
|
//
|
|
|
|
dprintf ("NOT LOCKED\n");
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The critical section is currently locked
|
|
//
|
|
|
|
dprintf ("LOCKED\n"
|
|
"LockCount = 0x%X\n",
|
|
LockCount );
|
|
|
|
//
|
|
// OwningThread
|
|
//
|
|
|
|
Success = ReadPtrStructFieldVerbose( CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"OwningThread",
|
|
&OwningThread);
|
|
|
|
if (Success != FALSE)
|
|
{
|
|
dprintf ("OwningThread = 0x%p\n",
|
|
OwningThread );
|
|
}
|
|
|
|
//
|
|
// RecursionCount
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose( CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"RecursionCount",
|
|
&RecursionCount,
|
|
sizeof( RecursionCount ) );
|
|
|
|
if (Success != FALSE)
|
|
{
|
|
dprintf ("RecursionCount = 0x%X\n",
|
|
RecursionCount);
|
|
}
|
|
}
|
|
|
|
//
|
|
// LockSemaphore
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose (CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"LockSemaphore",
|
|
&LockSemaphore,
|
|
sizeof( LockSemaphore ));
|
|
|
|
if (Success != FALSE)
|
|
{
|
|
dprintf ("LockSemaphore = 0x%X\n",
|
|
LockSemaphore );
|
|
}
|
|
|
|
//
|
|
// SpinCount
|
|
//
|
|
|
|
Success = ReadPtrStructFieldVerbose (CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"SpinCount",
|
|
&SpinCount);
|
|
|
|
if (Success != FALSE)
|
|
{
|
|
dprintf ("SpinCount = 0x%p\n",
|
|
SpinCount );
|
|
}
|
|
|
|
//
|
|
// Simple checks for orphaned critical sections
|
|
//
|
|
|
|
if (AddrDebugInfo != 0) {
|
|
|
|
//
|
|
// AddrDebugInfo is a DebugInfo address from the active list.
|
|
// Verify that the critical section's DebugInfo is pointing
|
|
// back to AddrDebugInfo.
|
|
//
|
|
|
|
Success = ReadPtrStructFieldVerbose (CritSec,
|
|
"NTDLL!_RTL_CRITICAL_SECTION",
|
|
"DebugInfo",
|
|
&OtherDebugInfo );
|
|
|
|
if (Success != FALSE && OtherDebugInfo != AddrDebugInfo)
|
|
{
|
|
dprintf ("\nWARNING: critical section DebugInfo = 0x%p doesn't point back\n"
|
|
"to the DebugInfo found in the active critical sections list = 0x%p.\n"
|
|
"The critical section was probably reused without calling DeleteCriticalSection.\n\n",
|
|
OtherDebugInfo,
|
|
AddrDebugInfo );
|
|
|
|
Success = ReadStructFieldVerbose (OtherDebugInfo,
|
|
"NTDLL!_RTL_CRITICAL_SECTION_DEBUG",
|
|
"CreatorBackTraceIndex",
|
|
&CreatorBackTraceIndex,
|
|
sizeof( CreatorBackTraceIndex ) );
|
|
|
|
StackTraceAddress = GetStackTraceAddress (CreatorBackTraceIndex,
|
|
PointerSize );
|
|
|
|
if (StackTraceAddress != 0)
|
|
{
|
|
dprintf ("\nStack trace for DebugInfo = 0x%p:\n\n",
|
|
OtherDebugInfo );
|
|
|
|
DumpStackTraceAtAddress (StackTraceAddress,
|
|
PointerSize);
|
|
}
|
|
|
|
//
|
|
// Dump the second stack trace too
|
|
//
|
|
|
|
DumpStackTrace = TRUE;
|
|
}
|
|
}
|
|
|
|
DisplayStackTrace:
|
|
|
|
if (!DumpStackTrace || DebugInfo == 0) {
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Dump the initialization stack trace for this critical section
|
|
//
|
|
|
|
Success = ReadStructFieldVerbose (DebugInfo,
|
|
"NTDLL!_RTL_CRITICAL_SECTION_DEBUG",
|
|
"CreatorBackTraceIndex",
|
|
&CreatorBackTraceIndex,
|
|
sizeof (CreatorBackTraceIndex));
|
|
|
|
if (Success != FALSE) {
|
|
|
|
StackTraceAddress = GetStackTraceAddress (CreatorBackTraceIndex,
|
|
PointerSize );
|
|
|
|
if (StackTraceAddress != 0)
|
|
{
|
|
dprintf ("\n\nStack trace for DebugInfo = 0x%p:\n\n",
|
|
DebugInfo );
|
|
|
|
DumpStackTraceAtAddress (StackTraceAddress,
|
|
PointerSize);
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
return HaveGoodSymbols;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
ULONG CriticalSectionFieldOffset;
|
|
ULONG DebugInfoFieldOffset;
|
|
ULONG LeftChildFieldOffset;
|
|
ULONG RightChildFieldOffset;
|
|
|
|
ULONG EnterThreadFieldOffset;
|
|
ULONG WaitThreadFieldOffset;
|
|
ULONG TryEnterThreadFieldOffset;
|
|
ULONG LeaveThreadFieldOffset;
|
|
|
|
|
|
BOOL
|
|
DumpCSTreeRecursively (ULONG Level,
|
|
ULONG64 TreeRoot)
|
|
{
|
|
ULONG64 CriticalSection;
|
|
ULONG64 LeftChild;
|
|
ULONG64 RightChild;
|
|
ULONG64 DebugInfo;
|
|
ULONG64 EnterThread;
|
|
ULONG64 WaitThread;
|
|
ULONG64 TryEnterThread;
|
|
ULONG64 LeaveThread;
|
|
ULONG64 ErrorCode;
|
|
BOOL Continue = TRUE;
|
|
|
|
if (CheckControlC()) {
|
|
|
|
Continue = FALSE;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the current CS address and dump information about it.
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + CriticalSectionFieldOffset, &CriticalSection) == FALSE) {
|
|
|
|
dprintf ("Cannot read CriticalSection address at %p\n",
|
|
TreeRoot + CriticalSectionFieldOffset);
|
|
goto Done;
|
|
}
|
|
|
|
if (ReadPointer (TreeRoot + DebugInfoFieldOffset, &DebugInfo) == FALSE) {
|
|
|
|
dprintf ("Cannot read DebugInfo address at %p\n",
|
|
TreeRoot + DebugInfoFieldOffset);
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the left child address.
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + LeftChildFieldOffset, &LeftChild) == FALSE) {
|
|
|
|
dprintf ("Cannot read left child address at %p\n",
|
|
TreeRoot + LeftChildFieldOffset);
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the right child address.
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + RightChildFieldOffset, &RightChild) == FALSE) {
|
|
|
|
dprintf ("Cannot read right child address at %p\n",
|
|
TreeRoot + RightChildFieldOffset);
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Dump the information about the current node.
|
|
//
|
|
|
|
dprintf ("%5u %p %p %p ",
|
|
Level,
|
|
TreeRoot,
|
|
CriticalSection,
|
|
DebugInfo);
|
|
|
|
if (EnterThreadFieldOffset != 0 ) {
|
|
|
|
//
|
|
// Read the EnterThread
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + EnterThreadFieldOffset, &EnterThread) == FALSE) {
|
|
|
|
dprintf ("Cannot read EnterThread at %p\n",
|
|
TreeRoot + EnterThreadFieldOffset);
|
|
|
|
goto OlderThan3591;
|
|
}
|
|
|
|
//
|
|
// Read the WaitThread
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + WaitThreadFieldOffset, &WaitThread) == FALSE) {
|
|
|
|
dprintf ("Cannot read WaitThread at %p\n",
|
|
TreeRoot + WaitThreadFieldOffset);
|
|
|
|
goto OlderThan3591;
|
|
}
|
|
|
|
//
|
|
// Read the TryEnterThread
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + TryEnterThreadFieldOffset, &TryEnterThread) == FALSE) {
|
|
|
|
dprintf ("Cannot read TryEnterThread at %p\n",
|
|
TreeRoot + TryEnterThreadFieldOffset);
|
|
|
|
goto OlderThan3591;
|
|
}
|
|
|
|
//
|
|
// Read the LeaveThread
|
|
//
|
|
|
|
if (ReadPointer (TreeRoot + LeaveThreadFieldOffset, &LeaveThread) == FALSE) {
|
|
|
|
dprintf ("Cannot read right LeaveThread at %p\n",
|
|
TreeRoot + LeaveThreadFieldOffset);
|
|
|
|
goto OlderThan3591;
|
|
}
|
|
|
|
dprintf ("%8p %8p %8p %8p\n",
|
|
EnterThread,
|
|
WaitThread,
|
|
TryEnterThread,
|
|
LeaveThread);
|
|
}
|
|
else {
|
|
|
|
dprintf ("\n");
|
|
}
|
|
|
|
OlderThan3591:
|
|
|
|
//
|
|
// Dump the left subtree.
|
|
//
|
|
|
|
if (LeftChild != 0) {
|
|
|
|
Continue = DumpCSTreeRecursively (Level + 1,
|
|
LeftChild);
|
|
|
|
if (Continue == FALSE) {
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dump the right subtree.
|
|
//
|
|
|
|
if (RightChild != 0) {
|
|
|
|
Continue = DumpCSTreeRecursively (Level + 1,
|
|
RightChild);
|
|
|
|
if (Continue == FALSE) {
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
return Continue;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
DisplayHelp( VOID )
|
|
{
|
|
dprintf( "!cs [-s] - dump all the active critical sections in the current process.\n" );
|
|
dprintf( "!cs [-s] address - dump critical section at this address.\n" );
|
|
dprintf( "!cs [-s] address1 address2 - dump all the active critical sections in this range.\n" );
|
|
dprintf( "!cs [-s] -d address - dump critical section corresponding to DebugInfo at this address.\n" );
|
|
dprintf( "\n\"-s\" will dump the critical section initialization stack trace if it's available.\n" );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DECLARE_API( cs )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump critical sections (both Kernel and User Debugger)
|
|
|
|
Arguments:
|
|
|
|
args - [address] [options]
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG64 AddrCritSec;
|
|
ULONG64 AddrEndCritSect;
|
|
ULONG64 AddrDebugInfo;
|
|
ULONG64 AddrListHead;
|
|
ULONG64 ListHead;
|
|
ULONG64 Next;
|
|
ULONG64 AddrTreeRoot;
|
|
ULONG64 TreeRoot = 0;
|
|
LPCSTR Current;
|
|
LPCSTR NextParam;
|
|
BOOL StackTraces = FALSE;
|
|
BOOL UseTree = FALSE;
|
|
BOOL HaveGoodSymbols;
|
|
ULONG ErrorCode;
|
|
ULONG ProcessLocksListFieldOffset;
|
|
ULONG Level;
|
|
ULONG PointerSize;
|
|
|
|
INIT_API();
|
|
|
|
AddrDebugInfo = 0;
|
|
AddrCritSec = 0;
|
|
AddrEndCritSect = 0;
|
|
|
|
//
|
|
// Parse the command line arguments for:
|
|
//
|
|
// -s : dump initialization stack traces
|
|
// -d : find the critical section using a DebugInfo pointer
|
|
//
|
|
|
|
for (Current = args; *Current != '\0'; Current += 1) {
|
|
|
|
if (*Current == '-') {
|
|
|
|
Current += 1;
|
|
switch (*Current) {
|
|
case '?':
|
|
case 'h':
|
|
case 'H':
|
|
|
|
//
|
|
// Need some help.
|
|
//
|
|
|
|
DisplayHelp();
|
|
|
|
goto Done;
|
|
|
|
|
|
case 's':
|
|
case 'S':
|
|
|
|
//
|
|
// Dump stack traces
|
|
//
|
|
|
|
StackTraces = TRUE;
|
|
|
|
if(*( Current + 1 ) != '\0') {
|
|
|
|
Current += 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
case 'T':
|
|
|
|
//
|
|
// Use the critical section tree
|
|
//
|
|
|
|
UseTree = TRUE;
|
|
|
|
if(*( Current + 1 ) != '\0') {
|
|
|
|
Current += 1;
|
|
}
|
|
|
|
do {
|
|
|
|
Current += 1;
|
|
}
|
|
while (*Current == ' ');
|
|
|
|
if (*Current != '\0') {
|
|
|
|
TreeRoot = GetExpression(Current);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
|
|
//
|
|
// The next parameter should be the DebugInfo
|
|
//
|
|
|
|
do {
|
|
|
|
Current += 1;
|
|
}
|
|
while (*Current == ' ');
|
|
|
|
AddrDebugInfo = GetExpression(Current);
|
|
|
|
if (AddrDebugInfo == 0) {
|
|
|
|
dprintf("!cs: expected DebugInfo address after -d\n");
|
|
|
|
//
|
|
// Decrement Current since the for loop will increment it again.
|
|
// Otherwise, if this is the end of the string we will overrun
|
|
// the args buffer.
|
|
//
|
|
|
|
Current -= 1;
|
|
|
|
goto Done;
|
|
}
|
|
else {
|
|
|
|
goto DoneParsingArguments;
|
|
}
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
Current += 1;
|
|
break;
|
|
|
|
default:
|
|
dprintf ("!cs: invalid option flag '-%c'\n",
|
|
*Current);
|
|
break;
|
|
}
|
|
}
|
|
else if(*Current == ' ') {
|
|
|
|
Current ++;
|
|
}
|
|
else {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
DoneParsingArguments:
|
|
|
|
//
|
|
// Get the size of a pointer
|
|
//
|
|
|
|
if (TargetMachine == IMAGE_FILE_MACHINE_I386) {
|
|
|
|
PointerSize = 4;
|
|
}
|
|
else {
|
|
|
|
PointerSize = 8;
|
|
}
|
|
|
|
if( AddrDebugInfo == 0 && UseTree == FALSE )
|
|
{
|
|
//
|
|
// If the user doesn't want us to use a DebugInfo
|
|
// then he might have asked us to dump a critical section
|
|
//
|
|
|
|
if (*Current != '\0')
|
|
{
|
|
AddrCritSec = GetExpression(Current);
|
|
|
|
if (AddrCritSec != 0) {
|
|
|
|
//
|
|
// We might have an additional argument if the user
|
|
// wants to dump all the active critical sections in
|
|
// an address range.
|
|
//
|
|
|
|
NextParam = strchr (Current,
|
|
' ' );
|
|
|
|
if (NextParam != NULL) {
|
|
|
|
AddrEndCritSect = GetExpression(NextParam);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start the real work
|
|
//
|
|
|
|
if ((AddrCritSec != 0 && AddrEndCritSect == 0) || AddrDebugInfo != 0)
|
|
{
|
|
//
|
|
// The user wants details only about this critical section
|
|
//
|
|
|
|
DumpCriticalSection (AddrCritSec, // critical section address
|
|
0, // end of address range if we are searching for critical sections
|
|
AddrDebugInfo, // debug info address
|
|
PointerSize,
|
|
StackTraces ); // dump the stack trace
|
|
}
|
|
else
|
|
{
|
|
if (UseTree == FALSE) {
|
|
|
|
//
|
|
// Parse all the critical sections list
|
|
//
|
|
|
|
//
|
|
// Get the offset of the list entry in the DebugInfo structure
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("NTDLL!_RTL_CRITICAL_SECTION_DEBUG",
|
|
"ProcessLocksList",
|
|
&ProcessLocksListFieldOffset );
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("Bad symbols for NTDLL (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Locate the address of the list head.
|
|
//
|
|
|
|
AddrListHead = GetExpression ("&NTDLL!RtlCriticalSectionList");
|
|
|
|
if (AddrListHead == 0 ) {
|
|
|
|
dprintf( "!cs: Unable to resolve NTDLL!RtlCriticalSectionList\n"
|
|
"Please check your symbols\n" );
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the list head
|
|
//
|
|
|
|
if (ReadPointer(AddrListHead, &ListHead) == FALSE) {
|
|
|
|
dprintf( "!cs: Unable to read memory at NTDLL!RtlCriticalSectionList\n" );
|
|
goto Done;
|
|
}
|
|
|
|
Next = ListHead;
|
|
|
|
while (Next != AddrListHead) {
|
|
|
|
if (CheckControlC()) {
|
|
|
|
break;
|
|
}
|
|
|
|
HaveGoodSymbols = DumpCriticalSection (
|
|
AddrCritSec, // critical section address
|
|
AddrEndCritSect, // end of address range if we are searching for critical sections
|
|
Next - ProcessLocksListFieldOffset, // debug info address
|
|
PointerSize,
|
|
StackTraces ); // dump the stack trace
|
|
|
|
//
|
|
// Read the pointer to Next element from the list
|
|
//
|
|
|
|
if( HaveGoodSymbols == FALSE )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ReadPointer (Next, &Next) == FALSE) {
|
|
|
|
dprintf ("!cs: Unable to read list entry at 0x%p - aborting.\n",
|
|
Next);
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Parse all the critical section tree in verifier.dll.
|
|
//
|
|
|
|
if (TreeRoot == 0) {
|
|
|
|
AddrTreeRoot = GetExpression ("&verifier!CritSectSplayRoot");
|
|
|
|
if (AddrTreeRoot == 0 ) {
|
|
|
|
dprintf( "!cs: Unable to resolve verifier!CritSectSplayRoot\n"
|
|
"Please check your symbols\n" );
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read the tree root.
|
|
//
|
|
|
|
if (ReadPointer(AddrTreeRoot, &TreeRoot) == FALSE) {
|
|
|
|
dprintf( "!cs: Unable to read memory at verifier!CritSectSplayRoot\n" );
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
dprintf ("Tree root %p\n",
|
|
TreeRoot);
|
|
|
|
//
|
|
// Get the offset of the CriticalSection in the CRITICAL_SECTION_SPLAY_NODE structure.
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"CriticalSection",
|
|
&CriticalSectionFieldOffset );
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("Bad symbols for verifier.dll (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the offset of the DebugInfo in the CRITICAL_SECTION_SPLAY_NODE structure.
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"DebugInfo",
|
|
&DebugInfoFieldOffset );
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("Bad symbols for verifier.dll (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the offset of the LeftChild in the CRITICAL_SECTION_SPLAY_NODE structure.
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("verifier!_RTL_SPLAY_LINKS",
|
|
"LeftChild",
|
|
&LeftChildFieldOffset );
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("Bad symbols for verifier.dll (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the offset of the RightChild in the CRITICAL_SECTION_SPLAY_NODE structure.
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("verifier!_RTL_SPLAY_LINKS",
|
|
"RightChild",
|
|
&RightChildFieldOffset );
|
|
|
|
if (ErrorCode != S_OK) {
|
|
|
|
dprintf ("Bad symbols for verifier.dll (error %u). Aborting.\n",
|
|
ErrorCode );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the offset of the EnterThread, WaitThread, TryEnterThread, LeaveThread fields
|
|
// in the CRITICAL_SECTION_SPLAY_NODE structure.
|
|
// These fields were added after build 3590 (.NET Server Beta 3).
|
|
//
|
|
|
|
ErrorCode = GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"EnterThread",
|
|
&EnterThreadFieldOffset );
|
|
|
|
if (ErrorCode == S_OK) {
|
|
|
|
GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"WaitThread",
|
|
&WaitThreadFieldOffset );
|
|
|
|
GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"TryEnterThread",
|
|
&TryEnterThreadFieldOffset );
|
|
|
|
GetFieldOffset ("verifier!_CRITICAL_SECTION_SPLAY_NODE",
|
|
"LeaveThread",
|
|
&LeaveThreadFieldOffset );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// These fields are either not defined or we have a problem with ths symbols.
|
|
//
|
|
|
|
EnterThreadFieldOffset = 0;
|
|
}
|
|
|
|
//
|
|
// Dump the tree recursively.
|
|
//
|
|
|
|
if (PointerSize == 4 ) {
|
|
|
|
dprintf ("Level %8s %8s %8s %8s %8s %8s %8s\n",
|
|
"Node",
|
|
"CS",
|
|
"Debug",
|
|
"Enter",
|
|
"Wait",
|
|
"TryEnter",
|
|
"Leave");
|
|
|
|
dprintf ("--------------------------------------------------------------------\n");
|
|
}
|
|
else {
|
|
|
|
dprintf ("Level %16s %16s %16s %8s %8s %8s %8s\n",
|
|
"Node",
|
|
"CS",
|
|
"Debug",
|
|
"Enter",
|
|
"Wait",
|
|
"TryEnter",
|
|
"Leave");
|
|
|
|
dprintf ("--------------------------------------------------------------------------------------------\n");
|
|
}
|
|
|
|
Level = 0;
|
|
|
|
DumpCSTreeRecursively (Level,
|
|
TreeRoot);
|
|
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|