/*++ 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[80]; LONG ActiveCount; ULONG ContentionCount; ULONG Displacement; BOOLEAN DisplayZero; ULONG End; USHORT Flag; RESOURCE_HASH_ENTRY HashEntry; ULONG Index; USHORT NumberOfExclusiveWaiters; USHORT NumberOfSharedWaiters; OWNER_ENTRY OwnerEntry; BOOLEAN Performance; ULONG PerformanceData; USHORT TableSize; RESOURCE_PERFORMANCE_DATA ResourcePerformanceData; ULONG ResourceHead; LIST_ENTRY List; PLIST_ENTRY Next; ULONG Result; ULONG ResourceToDump; PERESOURCE Resource; ERESOURCE ResourceContents; PNTDDK_ERESOURCE DdkResource; NTDDK_ERESOURCE DdkResourceContents; ULONG i; ULONG j; PKTHREAD Thread; LONG ThreadCount; UCHAR DdkThreadCount; UCHAR chSymbol[120]; BOOLEAN Verbose; PUCHAR s; ULONG TotalLocks; ULONG TotalUsedLocks; ULONG SkippedLocks; 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 != ' ') { sscanf(s,"%lx",&ResourceToDump); s = strpbrk( s, " " ); } else { gotBlank: s++; } } // // Dump performance data if requested. // if (Performance != FALSE) { dprintf("**** Dump Resource Performance Data ****\n\n"); PerformanceData = GetExpression("ExpResourcePerformanceData"); if ((PerformanceData == 0) || (ReadMemory((DWORD)PerformanceData, &ResourcePerformanceData, sizeof(RESOURCE_PERFORMANCE_DATA), &Result) == FALSE)) { // // The target build does not support resource performance data. // dprintf("%08lx: No resource performance data available\n", Result); } else { // // Output the summary statistics. // dprintf("Total resources initialized : %u\n", ResourcePerformanceData.TotalResourceCount); dprintf("Currently active resources : %u\n", ResourcePerformanceData.ActiveResourceCount); dprintf("Exclusive resource acquires : %u\n", ResourcePerformanceData.ExclusiveAcquire); dprintf("Shared resource acquires (fl) : %u\n", ResourcePerformanceData.SharedFirstLevel); dprintf("Shared resource acquires (sl) : %u\n", ResourcePerformanceData.SharedSecondLevel); dprintf("Starve resource acquires (fl) : %u\n", ResourcePerformanceData.StarveFirstLevel); dprintf("Starve resource acquires (sl) : %u\n", ResourcePerformanceData.StarveSecondLevel); dprintf("Shared wait resource acquires : %u\n", ResourcePerformanceData.WaitForExclusive); dprintf("Owner table expansions : %u\n", ResourcePerformanceData.OwnerTableExpands); dprintf("Maximum table expansion : %u\n\n", ResourcePerformanceData.MaximumTableExpand); // // Dump the inactive resource statistics. // dprintf(" Inactive Resource Statistics\n"); dprintf("Contention Number Initialization Address\n\n"); for (Index = 0; Index < RESOURCE_HASH_TABLE_SIZE; Index += 1) { End = FIELD_OFFSET(RESOURCE_PERFORMANCE_DATA, HashTable) + PerformanceData + sizeof(LIST_ENTRY) * Index; Next = ResourcePerformanceData.HashTable[Index].Flink; while ((ULONG)Next != End) { if (ReadMemory((DWORD)Next, &HashEntry, sizeof(RESOURCE_HASH_ENTRY), &Result) != FALSE) { GetSymbol(HashEntry.Address, &Buffer[0], &Displacement); dprintf("%10d %6d %s", HashEntry.ContentionCount, HashEntry.Number, &Buffer[0]); if (Displacement != 0) { dprintf("+0x%x", Displacement); } dprintf("\n"); } Next = HashEntry.ListEntry.Flink; } } // // 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 = GetExpression("ExpSystemResourcesList"); if ((ResourceHead == 0) || (ReadMemory((DWORD)ResourceHead, &List, sizeof(LIST_ENTRY), &Result) == FALSE)) { dprintf("%08lx: Unable to get value of ExpSystemResourcesList\n", ResourceHead ); return; } Next = List.Flink; if (Next == NULL) { dprintf("ExpSystemResourcesList is NULL!\n"); return; } // // Scan the resource list and dump the resource information. // while((ULONG)Next != ResourceHead) { Resource = CONTAINING_RECORD(Next, ERESOURCE, SystemResourcesList); if (ReadMemory((DWORD)Resource, &ResourceContents, sizeof(ERESOURCE), &Result) == FALSE) { dprintf("%08lx: Unable to read _ERESOURCE\n", Resource); continue; } else { if ((ResourceContents.ContentionCount != 0) || (DisplayZero != FALSE)) { GetSymbol(ResourceContents.Address, &Buffer[0], &Displacement); dprintf("%08lx %10d %s", Resource, ResourceContents.ContentionCount, &Buffer[0]); if (Displacement != 0) { dprintf("+0x%x", Displacement); } dprintf("\n"); } } Next = ResourceContents.SystemResourcesList.Flink; } 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"); } return; } // // Dump remaining lock data. // if (ResourceToDump == 0) { dprintf("**** DUMP OF ALL RESOURCE OBJECTS ****\n"); ResourceHead = GetExpression( "ExpSystemResourcesList" ); if ( !ResourceHead || !ReadMemory( (DWORD)ResourceHead, &List, sizeof(LIST_ENTRY), &Result) ) { dprintf("%08lx: Unable to get value of ExpSystemResourcesList\n", ResourceHead ); return; } Next = List.Flink; if (Next == NULL) { dprintf("ExpSystemResourcesList is NULL!\n"); return; } } else { Next = NULL; ResourceHead = 1; } TotalLocks = 0; TotalUsedLocks = 0; SkippedLocks = 0; while((ULONG)Next != ResourceHead) { if (Next != NULL) { Resource = CONTAINING_RECORD(Next,ERESOURCE,SystemResourcesList); } else { Resource = (PERESOURCE)ResourceToDump; } if ( !ReadMemory( (DWORD)Resource, &ResourceContents, sizeof(ERESOURCE), &Result) ) { dprintf("%08lx: Unable to read _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; if (DdkResource->OwnerThreads == &((PNTDDK_ERESOURCE)Resource)->InitialOwnerThreads[0]) { if ( !ReadMemory( (DWORD)Resource, &DdkResourceContents, sizeof(NTDDK_ERESOURCE), &Result) ) { dprintf("%08lx: Unable to read _NTDDK_ERESOURCE\n", Resource ); break; } DdkResource = &DdkResourceContents; ActiveCount = DdkResource->ActiveCount; ContentionCount = DdkResource->ContentionCount; Flag = DdkResource->Flag; NumberOfExclusiveWaiters = DdkResource->NumberOfExclusiveWaiters; NumberOfSharedWaiters = DdkResource->NumberOfSharedWaiters; TableSize = DdkResource->TableSize; } else { DdkResource = NULL; ActiveCount = ResourceContents.ActiveCount; ContentionCount = ResourceContents.ContentionCount; Flag = ResourceContents.Flag; NumberOfExclusiveWaiters = ResourceContents.NumberOfExclusiveWaiters; NumberOfSharedWaiters = ResourceContents.NumberOfSharedWaiters; TableSize = ResourceContents.OwnerThreads[0].TableSize; } TotalLocks++; if ((ResourceToDump != 0) || Verbose || (ActiveCount != 0)) { EXPRLastDump = (ULONG)Resource; if (SkippedLocks) { dprintf("\n"); SkippedLocks = 0; } dprintf("\n"); dumpSymbolicAddress((ULONG)Resource, chSymbol, TRUE); dprintf("Resource @ %s", chSymbol ); 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); } #ifdef i386 if (DdkResource != NULL) { if (DdkResource->CreatorBackTraceIndex != 0) { dprintf(" Created by:\n"); dumpBackTraceIndex( DdkResource->CreatorBackTraceIndex, " " ); } if (ActiveCount != 0) { dprintf(" Owned\n"); } } #endif // i386 if (ActiveCount != 0) { j = 0; dprintf(" Threads: "); if (DdkResource == NULL) { Thread = (PKTHREAD)ResourceContents.OwnerThreads[0].OwnerThread; ThreadCount = ResourceContents.OwnerThreads[0].OwnerCount; if (Thread != NULL) { j++; dprintf("%08lx-%02x ", Thread, ThreadCount); } Thread = (PKTHREAD)ResourceContents.OwnerThreads[1].OwnerThread; ThreadCount = ResourceContents.OwnerThreads[1].OwnerCount; if (Thread != NULL) { j++; dprintf("%08lx-%02x ", Thread, ThreadCount); } } for (i = 0; i < TableSize; i++) { if (DdkResource != NULL) { if ( !ReadMemory( (DWORD)&DdkResource->OwnerThreads[i], &Thread, sizeof (Thread), &Result) ) { dprintf("\n%08lx: DDK: Unable to read ThreadTable for resource\n",&ResourceContents.OwnerThreads[i] ); break; } // // Ddk resources can only ever be using a single // table entry. // if ( !ReadMemory( (DWORD)&DdkResource->OwnerCounts[i], &DdkThreadCount, sizeof (ThreadCount), &Result) ) { dprintf("\n%08lx: DDK: Unable to read ThreadCount for resource\n",&DdkResource->OwnerCounts[i]); break; ThreadCount = DdkThreadCount; } } else { if ( !ReadMemory( (DWORD)&ResourceContents.OwnerTable[i], &OwnerEntry, sizeof (OWNER_ENTRY), &Result) ) { dprintf("\n%08lx: Unable to read ThreadCount for resource\n", &Resource->OwnerTable[i]); break; } Thread = (PKTHREAD)OwnerEntry.OwnerThread; ThreadCount = OwnerEntry.OwnerCount; } if ((Thread == NULL) && (ThreadCount == 0)) { continue; } if (j == 4) { j = 0; dprintf("\n "); } dprintf("%08lx-%02x ", Thread, ThreadCount); j++; if ( CheckControlC() ) { return; } } if (j) { dprintf("\n"); } } } else { if ((SkippedLocks++ % 32) == 0) { if (SkippedLocks == 1) { dprintf("KD: Scanning for held locks." ); } else { dprintf("." ); } } } if (ResourceToDump != 0) { break; } Next = ResourceContents.SystemResourcesList.Flink; if ( CheckControlC() ) { return; } } if (SkippedLocks) { dprintf("\n"); } dprintf( "%u total locks", TotalLocks ); if (TotalUsedLocks) { dprintf( ", %u locks currently held", TotalUsedLocks ); } dprintf("\n"); return; } 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. --*/ { ULONG FastMutex; FAST_MUTEX FastMutexContents; 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) && (ReadMemory((DWORD)FastMutex, &FastMutexContents, sizeof(FAST_MUTEX), &Result) != FALSE)) { dprintf("%08lx %10u %s\n", FastMutex, FastMutexContents.Contention, &Name[0]); } return; }