/*++ Copyright (c) 1992 Microsoft Corporation Module Name: locks.c Abstract: WinDbg Extension Api Author: Ramon J San Andres (ramonsa) 5-Nov-1993 Environment: User Mode. Revision History: --*/ #include "precomp.h" #pragma hdrstop DECLARE_API( locks ) /*++ Routine Description: Dump kernel mode resource locks Arguments: arg - [-V] [-P] [Address] Return Value: None --*/ { UCHAR Buffer[256]; LONG ActiveCount; ULONG ContentionCount; ULONG64 Displacement; BOOLEAN DisplayZero; ULONG64 End; USHORT Flag; ULONG Index; USHORT NumberOfExclusiveWaiters; USHORT NumberOfSharedWaiters; BOOLEAN Performance; ULONG64 PerformanceData; ULONG TableSize; ULONG64 ResourceHead; ULONG64 Next; ULONG Result; ULONG64 ResourceToDump; ULONG64 Resource; ULONG64 DdkResource; ULONG i; ULONG j; ULONG64 Thread; LONG ThreadCount; UCHAR DdkThreadCount; BOOLEAN Verbose; PUCHAR s; ULONG TotalLocks; ULONG TotalUsedLocks; ULONG SkippedLocks; ULONG SizeOfListEntry, SizeofOwnerEntry; ULONG InitialOwnerThreadsOffset, OwnerThreadsOffset; ULONG dwProcessor=0; HRESULT hr = S_OK; INIT_API(); GetCurrentProcessor(Client, &dwProcessor, NULL); ResourceToDump = 0; DisplayZero = FALSE; Performance = FALSE; Verbose = FALSE; s = (PSTR)args; while ( s != NULL && *s ) { if (*s == '-' || *s == '/') { while (*++s) { switch (*s) { case 'D': case 'd': DisplayZero = TRUE; break; case 'P': case 'p': Performance = TRUE; break; case 'V': case 'v': Verbose = TRUE; break; case ' ': goto gotBlank; default: dprintf( "KD: !locks invalid option flag '-%c'\n", *s ); break; } } } else if (*s != ' ') { ResourceToDump = GetExpression(s); s = strpbrk( s, " " ); } else { gotBlank: s++; } } // // Dump performance data if requested. // if (Performance != FALSE) { UCHAR ResPerf[]="nt!_RESOURCE_PERFORMANCE_DATA"; ULONG TotalResourceCount, ActiveResourceCount, ExclusiveAcquire; ULONG SharedFirstLevel, SharedSecondLevel, StarveFirstLevel, StarveSecondLevel; ULONG WaitForExclusive, OwnerTableExpands, MaximumTableExpand; ULONG HashTableOffset; dprintf("**** Dump Resource Performance Data ****\n\n"); PerformanceData = GetExpression("nt!ExpResourcePerformanceData"); if ((PerformanceData == 0) || GetFieldValue(PerformanceData, ResPerf,"TotalResourceCount",TotalResourceCount)) { // // The target build does not support resource performance data. // dprintf("%08p: No resource performance data available\n", PerformanceData); } else { GetFieldOffset(ResPerf, "HashTable", &HashTableOffset); GetFieldValue(PerformanceData, ResPerf, "ActiveResourceCount", ActiveResourceCount); GetFieldValue(PerformanceData, ResPerf,"ExclusiveAcquire", ExclusiveAcquire); GetFieldValue(PerformanceData, ResPerf, "SharedFirstLevel", SharedFirstLevel); GetFieldValue(PerformanceData, ResPerf,"SharedSecondLevel", SharedSecondLevel); GetFieldValue(PerformanceData, ResPerf, "StarveFirstLevel", StarveFirstLevel); GetFieldValue(PerformanceData, ResPerf, "StarveSecondLevel", StarveSecondLevel); GetFieldValue(PerformanceData, ResPerf, "WaitForExclusive", WaitForExclusive); GetFieldValue(PerformanceData, ResPerf, "OwnerTableExpands", OwnerTableExpands); GetFieldValue(PerformanceData, ResPerf, "MaximumTableExpand", MaximumTableExpand); // // Output the summary statistics. // dprintf("Total resources initialized : %u\n", TotalResourceCount); dprintf("Currently active resources : %u\n", ActiveResourceCount); dprintf("Exclusive resource acquires : %u\n", ExclusiveAcquire); dprintf("Shared resource acquires (fl) : %u\n", SharedFirstLevel); dprintf("Shared resource acquires (sl) : %u\n", SharedSecondLevel); dprintf("Starve resource acquires (fl) : %u\n", StarveFirstLevel); dprintf("Starve resource acquires (sl) : %u\n", StarveSecondLevel); dprintf("Shared wait resource acquires : %u\n", WaitForExclusive); dprintf("Owner table expansions : %u\n", OwnerTableExpands); dprintf("Maximum table expansion : %u\n\n", MaximumTableExpand); // // Dump the inactive resource statistics. // dprintf(" Inactive Resource Statistics\n"); dprintf("Contention Number Initialization Address\n\n"); SizeOfListEntry = GetTypeSize("nt!_LIST_ENTRY"); for (Index = 0; Index < RESOURCE_HASH_TABLE_SIZE; Index += 1) { End = HashTableOffset + PerformanceData + SizeOfListEntry * Index; GetFieldValue(End,"nt!_LIST_ENTRY","Flink",Next); while (Next != End) { ULONG64 Address; ULONG ContentionCount, Number; if (CheckControlC()) { break; } if (!GetFieldValue(Next, "nt!_RESOURCE_HASH_ENTRY", "Address", Address)) { GetSymbol(Address, Buffer, &Displacement); GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","Number",Number); GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","ContentionCount",ContentionCount); dprintf("%10d %6d %s", ContentionCount, Number, Buffer); if (Displacement != 0) { dprintf("+0x%x", Displacement); } dprintf("\n"); } GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","ListEntry.Flink", Next); } } // // Dump the active resource statistics. // dprintf("\n Active Resource Statistics\n"); dprintf("Resource Contention Initialization Address\n\n"); // // Read the resource listhead and check if it is empty. // ResourceHead = GetNtDebuggerData( ExpSystemResourcesList ); if ((ResourceHead == 0) || (!GetFieldValue(ResourceHead, "nt!_LIST_ENTRY", "Flink", Next) == FALSE)) { dprintf("%08p: Unable to get value of ExpSystemResourcesList\n", ResourceHead ); hr = E_INVALIDARG; goto exitBangLocks; } if (Next == 0) { dprintf("ExpSystemResourcesList is NULL!\n"); hr = E_INVALIDARG; goto exitBangLocks; } // // Scan the resource list and dump the resource information. // while(Next != ResourceHead) { ULONG64 Address; if (CheckControlC()) { break; } Resource = Next; // SystemResourcesList is the first element in struct // CONTAINING_RECORD(Next, ERESOURCE, SystemResourcesList); if (!GetFieldValue(Resource, "nt!_ERESOURCE", "ContentionCount", ContentionCount) == FALSE) { dprintf("%08p: Unable to read _ERESOURCE\n", Resource); continue; } else { GetFieldValue(Resource,"nt!_ERESOURCE","Address",Address); GetFieldValue(Resource,"nt!_ERESOURCE","ContentionCount",ContentionCount); if ((ContentionCount != 0) || (DisplayZero != FALSE)) { GetSymbol(Address, Buffer, &Displacement); dprintf("%08p %10d %s", Resource, ContentionCount, Buffer); if (Displacement != 0) { dprintf("+0x%x", Displacement); } dprintf("\n"); } } GetFieldValue(Resource,"nt!_ERESOURCE","SystemResourcesList.Flink",Next); } dprintf("\n"); // // Dump the active fast mutex statistics. // dprintf("\n Active Fast Mutex Statistics\n"); dprintf("Address Contention Fast Mutex Name\n\n"); // // Dump statistics for static fast mutexes. // DumpStaticFastMutex("CmpKcbLock"); DumpStaticFastMutex("FsRtlCreateLockInfo"); DumpStaticFastMutex("MmPageFileCreationLock"); DumpStaticFastMutex("MmSectionCommitMutex"); DumpStaticFastMutex("MmSectionBasedMutex"); DumpStaticFastMutex("ObpRootDirectoryMutex"); DumpStaticFastMutex("PspActiveProcessMutex"); DumpStaticFastMutex("PspProcessLockMutex"); DumpStaticFastMutex("PspProcessSecurityLock"); DumpStaticFastMutex("SepLsaQueueLock"); dprintf("\n"); } hr = E_INVALIDARG; goto exitBangLocks; } // // Dump remaining lock data. // if (ResourceToDump == 0) { dprintf("**** DUMP OF ALL RESOURCE OBJECTS ****\n"); ResourceHead = GetNtDebuggerData( ExpSystemResourcesList ); if ( !ResourceHead || (GetFieldValue(ResourceHead, "nt!_LIST_ENTRY", "Flink", Next) != FALSE)) { dprintf("%08p: Unable to get value of ExpSystemResourcesList\n", ResourceHead ); hr = E_INVALIDARG; goto exitBangLocks; } if (Next == 0) { dprintf("ExpSystemResourcesList is NULL!\n"); hr = E_INVALIDARG; goto exitBangLocks; } } else { Next = 0; ResourceHead = 1; } TotalLocks = 0; TotalUsedLocks = 0; SkippedLocks = 0; // Get the offset of OwnerThreads in ERESOURCE if (GetFieldOffset("nt!_ERESOURCE", "OwnerThreads", &OwnerThreadsOffset)) { dprintf("Cannot get _ERESOURCE type\n"); hr = E_INVALIDARG; goto exitBangLocks; } if (!(SizeofOwnerEntry = GetTypeSize("nt!_OWNER_ENTRY"))) { dprintf("Cannot get nt!_OWNER_ENTRY type\n"); hr = E_INVALIDARG; goto exitBangLocks; } while(Next != ResourceHead) { ULONG64 OwnerThreads, OwnerCounts, OwnerTable; if (Next != 0) { Resource = Next;// SystemResourcesList is the first element of struct ERESOURCE // CONTAINING_RECORD(Next,ERESOURCE,SystemResourcesList); } else { Resource = ResourceToDump; } /* if ( GetFieldValue( Resource, "NTDDK_ERESOURCE", "OwnerThreads", OwnerThreads) ) { dprintf("%08lx: Unable to read NTDDK_ERESOURCE\n", Resource ); break; }*/ // // Detect here if this is an NtDdk resource, and behave // appropriatelty. If the OwnerThreads is a pointer to the initial // owner threads array (this must take into account that the LOCAL // data structure is a copy of what's in the remote machine in a // different address) // // DdkResource = (PNTDDK_ERESOURCE)&ResourceContents; { DdkResource = 0; GetFieldValue( Resource,"nt!_ERESOURCE","ActiveCount", ActiveCount); GetFieldValue( Resource,"nt!_ERESOURCE","ContentionCount",ContentionCount); GetFieldValue( Resource,"nt!_ERESOURCE","NumberOfExclusiveWaiters",NumberOfExclusiveWaiters); GetFieldValue( Resource,"nt!_ERESOURCE","NumberOfSharedWaiters",NumberOfSharedWaiters); GetFieldValue( Resource,"nt!_ERESOURCE","Flag",Flag); GetFieldValue( Resource,"nt!_ERESOURCE","OwnerTable",OwnerTable); TableSize = 0; if (OwnerTable != 0) { if (GetFieldValue(OwnerTable, "nt!_OWNER_ENTRY", "TableSize", TableSize)) { dprintf("\n%08p: Unable to read TableSize for resource\n", OwnerTable); break; } } } TotalLocks++; if ((ResourceToDump != 0) || Verbose || (ActiveCount != 0)) { EXPRLastDump = Resource; if (SkippedLocks) { dprintf("\n"); SkippedLocks = 0; } dprintf("\n"); dumpSymbolicAddress(Resource, Buffer, TRUE); dprintf("Resource @ %s", Buffer ); if (ActiveCount == 0) { dprintf(" Available\n"); } else if (Flag & ResourceOwnedExclusive) { TotalUsedLocks++; dprintf(" Exclusively owned\n"); } else { TotalUsedLocks++; dprintf(" Shared %u owning threads\n", ActiveCount); } if (ContentionCount != 0) { dprintf(" Contention Count = %u\n", ContentionCount); } if (NumberOfSharedWaiters != 0) { dprintf(" NumberOfSharedWaiters = %u\n", NumberOfSharedWaiters); } if (NumberOfExclusiveWaiters != 0) { dprintf(" NumberOfExclusiveWaiters = %u\n", NumberOfExclusiveWaiters); } if (ActiveCount != 0) { ULONG ThreadType; j = 0; dprintf(" Threads: "); if (DdkResource == 0) { GetFieldValue( Resource + OwnerThreadsOffset, "nt!_OWNER_ENTRY","OwnerThread",Thread); GetFieldValue( Resource + OwnerThreadsOffset, "nt!_OWNER_ENTRY","OwnerCount",ThreadCount); if (Thread != 0) { j++; dprintf("%08p-%02x ", Thread, ThreadCount); if (Thread & 3) { dprintf("*** Unknown owner, possibly FileSystem"); j=4; } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) || (ThreadType != ThreadObject)) { dprintf("*** Invalid thread"); j=4; } if (Verbose) { dprintf("\n\n"); DumpThread(dwProcessor, " ", Thread, 0xf ); } } GetFieldValue( Resource + OwnerThreadsOffset +SizeofOwnerEntry, "nt!_OWNER_ENTRY","OwnerThread",Thread); GetFieldValue( Resource + OwnerThreadsOffset +SizeofOwnerEntry, "nt!_OWNER_ENTRY","OwnerCount",ThreadCount); if (Thread != 0) { j++; dprintf("%08p-%02x ", Thread, ThreadCount); if (Thread & 3) { dprintf("*** Unknown owner, possibly FileSystem"); j=4; } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) || (ThreadType != ThreadObject)) { dprintf("*** Invalid thread"); j=4; } if (Verbose) { dprintf("\n\n"); DumpThread( dwProcessor, " ", Thread, 0xf ); } } } for (i = DdkResource ? 0 : 1; i < TableSize; i++) { { GetFieldValue( OwnerTable + SizeofOwnerEntry*i, "nt!_OWNER_ENTRY","OwnerThread",Thread); GetFieldValue( OwnerTable + SizeofOwnerEntry*i, "nt!_OWNER_ENTRY","OwnerCount",ThreadCount); } if ((Thread == 0) && (ThreadCount == 0)) { continue; } if (j == 4) { j = 0; dprintf("\n "); } dprintf("%08p-%02x ", Thread, ThreadCount); j++; if (Thread & 3) { dprintf("*** Unknown owner, possibly FileSystem"); j=4; } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) || (ThreadType != ThreadObject)) { dprintf("*** Invalid thread"); j=4; } // // In verbose mode also dump the thread stacks // if (Verbose) { dprintf("\n\n"); DumpThread( dwProcessor, " ", Thread, 0xf ); } if ( CheckControlC() ) { hr = E_INVALIDARG; goto exitBangLocks; } } if (j) { dprintf("\n"); } } } else { if ((SkippedLocks++ % 32) == 0) { if (SkippedLocks == 1) { dprintf("KD: Scanning for held locks." ); } else { dprintf("." ); } } } if (ResourceToDump != 0) { break; } GetFieldValue( Resource,"nt!_ERESOURCE","SystemResourcesList.Flink", Next); if ( CheckControlC() ) { hr = E_INVALIDARG; goto exitBangLocks; } } if (SkippedLocks) { dprintf("\n"); } dprintf( "%u total locks", TotalLocks ); if (TotalUsedLocks) { dprintf( ", %u locks currently held", TotalUsedLocks ); } dprintf("\n"); exitBangLocks: EXIT_API(); return hr; } VOID DumpStaticFastMutex ( IN PCHAR Name ) /*++ Routine Description: This function dumps the contention statistics for a fast mutex. Arguments: Name - Supplies a pointer to the symbol name for the fast mutex. Return Value: None. --*/ { ULONG64 FastMutex; ULONG Contention; ULONG Result; // // Get the address of the fast mutex, read the fast mutex contents, // and dump the contention data. // FastMutex = GetExpression(Name); if ((FastMutex != 0) && (!GetFieldValue(FastMutex, "nt!_FAST_MUTEX", "Contention", Contention))) { dprintf("%08p %10u %s\n", FastMutex, Contention, &Name[0]); } return; }