/*++ Copyright (c) 1992 Microsoft Corporation Module Name: heap.c Abstract: WinDbg Extension Api Author: Ramon J San Andres (ramonsa) 5-Nov-1993 Environment: User Mode. Revision History: --*/ #include "precomp.h" #include "heap.h" #pragma hdrstop ULONG PageSize; VOID DebugPageHeapExtension( IN PCSTR lpArgumentString ); BOOL GetPageSize() { KDDEBUGGER_DATA64 kdd; if (GetDebuggerData('GBDK', &kdd, sizeof(kdd))) { // // Kernel target // PageSize = (ULONG) kdd.MmPageSize; return TRUE; } else { // // User maode // SYSTEM_BASIC_INFORMATION sysInfo; if (!NtQuerySystemInformation( SystemBasicInformation, &sysInfo, sizeof(sysInfo), NULL)) { PageSize = sysInfo.PageSize; return TRUE; } } return FALSE; } /* #if defined(TARGET_i386) #define STACK_TRACE_DATABASE_SUPPORT 1 #elif defined(TARGET_ALPHA) #define STACK_TRACE_DATABASE_SUPPORT 0 #elif i386 #define STACK_TRACE_DATABASE_SUPPORT 1 #else #define STACK_TRACE_DATABASE_SUPPORT 0 #endif*/ #define STACK_TRACE_DATABASE_SUPPORT 0 #if 0 // BUGBUG This was X86 specific := HOST_i386 ULONG xRtlCompareMemoryUlong( PVOID Source, ULONG Length, ULONG Pattern ) { ULONG CountLongs; PULONG p = (PULONG)Source; PCHAR p1, p2; if (((ULONG)p & (sizeof( ULONG )-1)) || (Length & (sizeof( ULONG )-1)) ) { return( 0 ); } CountLongs = Length / sizeof( ULONG ); while (CountLongs--) { if (*p++ != Pattern) { p1 = (PCHAR)(p - 1); p2 = (PCHAR)&Pattern; Length = p1 - (PCHAR)Source; while (*p1++ == *p2++) { if (p1 > (PCHAR)p) { break; } Length++; } } } return( Length ); } #define RtlCompareMemoryUlong xRtlCompareMemoryUlong #define RtlCompareMemory memcmp #endif #define STOP_ON_ALLOC 1 #define STOP_ON_REALLOC 2 #define STOP_ON_FREE 3 typedef struct _HEAP_STOP_ON_TAG { union { ULONG HeapAndTagIndex; struct { USHORT TagIndex; USHORT HeapIndex; }; }; } HEAP_STOP_ON_TAG, *PHEAP_STOP_ON_TAG; typedef struct _HEAP_STATE { BOOLEAN ShowHelp; BOOLEAN ExitDumpLoop; BOOLEAN ComputeSummary; BOOLEAN ValidateHeap; BOOLEAN DumpHeapEntries; BOOLEAN DumpHeapTags; BOOLEAN DumpHeapPseudoTags; BOOLEAN DumpGlobalTags; BOOLEAN DumpHeapSegments; BOOLEAN DumpHeapFreeLists; BOOLEAN DumpStackBackTrace; BOOLEAN SetStopOnBreakPoint; BOOLEAN RemoveStopOnBreakPoint; BOOLEAN EnableHeapChecking; BOOLEAN EnableHeapValidateOnCall; BOOLEAN DisableHeapChecking; BOOLEAN DisableHeapValidateOnCall; BOOLEAN ToggleAPICallTracing; ULONG64 HeapToDump; ULONG64 HeapEntryToDump; ULONG64 ReservedSize; ULONG64 CommittedSize; ULONG64 AllocatedSize; ULONG64 FreeSize; ULONG64 OverheadSize; ULONG NumberOfHeaps; ULONG HeapIndex; PULONG64 HeapsList; ULONG StopOnOperation; ULONG64 StopOnAddress; HEAP_STOP_ON_TAG StopOnTag; WCHAR StopOnTagName[ 24 ]; ULONG FreeListCounts[ HEAP_MAXIMUM_FREELISTS ]; ULONG64 TotalFreeSize; ULONG64 HeapAddress; ULONG64 Heap; // HEAP ULONG SegmentNumber; ULONG64 SegmentAddress; ULONG64 Segments[ HEAP_MAXIMUM_SEGMENTS ]; // Ptr to HEAP_SEGMENT } HEAP_STATE, *PHEAP_STATE; BOOL ConvertTagNameToIndex( IN PHEAP_STATE State ); BOOL GetHeapTagEntry( IN ULONG64 Heap, IN USHORT TagIndex, OUT PULONG64 TagEntry ); VOID WalkHEAP( IN PHEAP_STATE State ); VOID WalkHEAP_SEGMENT( IN PHEAP_STATE State ); BOOL ValidateHeapHeader( IN ULONG64 HeapAddress // IN PHEAP Heap ); BOOL ValidateHeapEntry( IN PHEAP_STATE State, IN ULONG64 PrevEntryAddress, IN ULONG64 PrevEntry, IN ULONG64 EntryAddress, IN ULONG64 Entry ); VOID DumpHeapEntry( IN PHEAP_STATE State, IN ULONG64 EntryAddress, IN ULONG64 Entry ); #if STACK_TRACE_DATABASE_SUPPORT VOID DumpStackBackTraceIndex( IN PHEAP_STATE State, IN USHORT BackTraceIndex ); #endif // STACK_TRACE_DATABASE_SUPPORT BOOLEAN HeapExtInitialized; ULONG64 pNtGlobalFlag; ULONG64 pRtlpHeapInvalidBreakPoint; ULONG64 pRtlpHeapInvalidBadAddress; ULONG64 pRtlpGlobalTagHeap; //HEAP MyLocalRtlpGlobalTagHeap; #if STACK_TRACE_DATABASE_SUPPORT ULONG64 pRtlpStackTraceDataBase;// PSTACK_TRACE_DATABASE * ULONG64 RtlpStackTraceDataBase; // PSTACK_TRACE_DATABASE STACK_TRACE_DATABASE StackTraceDataBase; BOOLEAN HaveCopyOfStackTraceDataBase; #endif // STACK_TRACE_DATABASE_SUPPORT ULONG64 pRtlpHeapStopOn; // PHEAP_STOP_ON_VALUES BOOLEAN RtlpHeapInvalidBreakPoint; PVOID RtlpHeapInvalidBadAddress; ULONG HeapEntryTypeSize = 8; DECLARE_API( heap ) /*++ Routine Description: Dump user mode heap (Kernel debugging) If an address if not given or an address of 0 is given, then the process heap is dumped. If the address is -1, then all the heaps of the process are dumped. If detail is specified, it defines how much detail is shown. A detail of 0, just shows the summary information for each heap. A detail of 1, shows the summary information, plus the location and size of all the committed and uncommitted regions. A detail of 3 shows the allocated and free blocks contained in each committed region. A detail of 4 includes all of the above plus a dump of the free lists. Arguments: args - [address [detail]] Return Value: None --*/ { BOOL b, GotHeapsList, ArgumentsSpecified; ULONG64 pHeapsList; ULONG PtrSize; ULONG NtGlobalFlag; LPSTR p; ULONG i; ULONG DashBArgumentState; ULONG64 AddressToDump; HEAP_STATE State; UCHAR ArgumentBuffer[ 16 ]; ULONG TagIndex; ULONG64 pTagEntry; // PHEAP_TAG_ENTRY ULONG64 TagEntry; // HEAP_TAG_ENTRY ULONG64 pPseudoTagEntry; // PHEAP_PSEUDO_TAG_ENTRY // HEAP_PSEUDO_TAG_ENTRY PseudoTagEntry; BOOLEAN HeapHeaderModified; BOOLEAN RtlpHeapInvalidBreakPoint; ULONG64 RtlpHeapInvalidBadAddress; ULONG LocalHeapSignature; ULONG AlOffset, FlagOffset, TagEntrySize, pseudoTagEntrySize; ULONG64 AlignRound; ULONG64 SystemRangeStart = GetExpression("NT!MmSystemRangeStart"); ULONG64 ProcessPeb; PCSTR Current; // // Parse the command line arguments for heap options // that don't require to building the process heap list // (i.e pageheap, leak detection, search a block) // for (Current = args; *Current != '\0'; Current++) { if (*Current == '-') { Current++; switch (*Current) { case 'p': DebugPageHeapExtension( ++Current ); return S_OK; case 'l': case 'L': HeapDetectLeaks(); return S_OK; case 'x': case 'X': HeapFindBlock( args ); return S_OK; case 's': case 'S': HeapStat(++Current); return S_OK; } } } // BUGBUG - not initializing the signature, as we have no local copy // MyLocalRtlpGlobalTagHeap.Signature = 0; LocalHeapSignature = 0; #if STACK_TRACE_DATABASE_SUPPORT HaveCopyOfStackTraceDataBase = FALSE; #endif // STACK_TRACE_DATABASE_SUPPORT memset( &State, 0, FIELD_OFFSET( HEAP_STATE, FreeListCounts ) ); AddressToDump = (ULONG)-1; ArgumentsSpecified = FALSE; p = (LPSTR)args; if (p != NULL) while (*p) { if (*p == '-') { ArgumentsSpecified = TRUE; p += 1; while (*p && *p != ' ') { switch (*p) { case 'v': case 'V': State.ValidateHeap = TRUE; break; case 'a': case 'A': State.DumpHeapEntries = TRUE; State.DumpHeapFreeLists = TRUE; State.DumpHeapSegments = TRUE; break; case 'h': case 'H': State.DumpHeapEntries = TRUE; break; case 'f': case 'F': State.DumpHeapFreeLists = TRUE; break; case 'm': case 'M': State.DumpHeapSegments = TRUE; break; case 't': State.DumpHeapTags = TRUE; break; case 'T': State.DumpHeapPseudoTags = TRUE; break; case 'g': case 'G': State.DumpGlobalTags = TRUE; break; case 'k': case 'K': State.DumpStackBackTrace = TRUE; break; case 's': case 'S': State.ComputeSummary = TRUE; break; case 'd': State.DisableHeapChecking = TRUE; break; case 'D': State.DisableHeapValidateOnCall = TRUE; break; case 'e': State.EnableHeapChecking = TRUE; break; case 'E': State.EnableHeapValidateOnCall = TRUE; break; case 'B': State.RemoveStopOnBreakPoint = TRUE; DashBArgumentState = 0; State.StopOnOperation = 0; State.StopOnAddress = 0; State.StopOnTag.HeapIndex = 0; State.StopOnTag.TagIndex = 0; State.StopOnTagName[ 0 ] = UNICODE_NULL; break; case 'b': State.SetStopOnBreakPoint = TRUE; DashBArgumentState = 0; State.StopOnOperation = 0; State.StopOnAddress = 0; State.StopOnTag.HeapIndex = 0; State.StopOnTag.TagIndex = 0; State.StopOnTagName[ 0 ] = UNICODE_NULL; break; default: dprintf( "HEAPEXT: !heap invalid option flag '-%c'\n", *p ); case '?': State.ShowHelp = TRUE; break; } p += 1; } } else if (*p != ' ') { if (State.SetStopOnBreakPoint) { switch (DashBArgumentState) { case 0: DashBArgumentState += 1; if (sscanf( p, "%s", ArgumentBuffer ) == 1) { if (!_stricmp( ArgumentBuffer, "alloc" )) { State.StopOnOperation = STOP_ON_ALLOC; } else if (!_stricmp( ArgumentBuffer, "realloc" )) { State.StopOnOperation = STOP_ON_REALLOC; } else if (!_stricmp( ArgumentBuffer, "free" )) { State.StopOnOperation = STOP_ON_FREE; } } if (State.StopOnOperation == 0) { dprintf( "HEAPEXT: Invalid first argument to -b switch.\n" ); State.ShowHelp = TRUE; } break; case 1: if (sscanf( p, "%ws", &State.StopOnTagName ) != 1) { State.StopOnTagName[ 0 ] = UNICODE_NULL; dprintf( "HEAPEXT: Invalid second argument to -b switch.\n" ); State.ShowHelp = TRUE; } break; default: dprintf( "HEAPEXT: Too many parameters specified to -b switch\n" ); State.ShowHelp = TRUE; break; } } else if (State.RemoveStopOnBreakPoint) { switch (DashBArgumentState) { case 0: DashBArgumentState += 1; if (sscanf( p, "%s", ArgumentBuffer ) == 1) { if (!_stricmp( ArgumentBuffer, "alloc" )) { State.StopOnOperation = STOP_ON_ALLOC; } else if (!_stricmp( ArgumentBuffer, "realloc" )) { State.StopOnOperation = STOP_ON_REALLOC; } else if (!_stricmp( ArgumentBuffer, "free" )) { State.StopOnOperation = STOP_ON_FREE; } } break; default: dprintf( "HEAPEXT: Too many parameters specified to -B switch\n" ); State.ShowHelp = TRUE; break; } } else { ArgumentsSpecified = TRUE; sscanf( p, "%I64lx", &AddressToDump ); } if ((p = strpbrk( p, " " )) == NULL) { p = ""; } } else { p++; } } if (State.ShowHelp) { dprintf( "usage: !heap [address] [-? ] [-v] [[-a] | [-h] [-f] [-m]] [-t] [-s]\n" ); dprintf( " [-d | -D | -e | -E]\n" ); dprintf( " [-b [alloc | realloc | free] [tag]]\n" ); dprintf( " [-B [alloc | realloc | free]]\n" ); dprintf( " address - specifies either a heap number (1-n), or a heap address.\n" ); dprintf( " Zero specifies all heaps in the process.\n" ); dprintf( " -1 is the default and specifies the process heap.\n" ); dprintf( " -? displays this help message.\n" ); dprintf( " -v validates the specified heap(s).\n" ); dprintf( " -a displays all the information for the specified heap(s).\n" ); dprintf( " This can take a long time.\n" ); dprintf( " -h displays all the entries for the specified heap(s).\n" ); dprintf( " -f displays all the free list entries for the specified heap(s).\n" ); dprintf( " -l detects leaked heap blocks.\n" ); dprintf( " -x search the heap block containing the address.\n" ); dprintf( " -x -v search the whole process virtual space for given address .\n" ); dprintf( " -k displays any associated stack back trace for each entry (x86 only).\n" ); dprintf( " -m displays all the segment entries for the specified heap(s).\n" ); dprintf( " -t displays the tag information for the specified heap(s).\n" ); dprintf( " -T displays the pseudo tag information for the specified heap(s).\n" ); dprintf( " -g displays the global tag information generated by tag by DLL\n" ); dprintf( " -s displays summary information for the specified heap(s).\n" ); dprintf( " -e enables heap checking for the specified heap(s).\n" ); dprintf( " -d disables heap checking for the specified heap(s).\n" ); dprintf( " -E enables validate on call for the specified heap(s).\n" ); dprintf( " -D disables validate on call for the specified heap(s).\n" ); dprintf( " -b creates a conditional breakpoint in the heap manager.\n" ); dprintf( " alloc | realloc | free specifies which action to stop.\n" ); dprintf( " address either specifies the address of a block to stop on.\n" ); dprintf( " or a heap, in which case the tag argument is required,\n" ); dprintf( " and is the tag name within the heap specified by address.\n" ); dprintf( " -B removes a conditional breakpoint in the heap manager.\n" ); dprintf( " if the type is not specified then all breakpoints are removed.\n" ); dprintf (" -p -? extensive page heap related help. \n"); dprintf (" -p Dump all page heaps. \n"); dprintf (" -p -h ADDR Detailed dump of page heap at ADDR. \n"); dprintf (" -p -a ADDR Figure out what heap block is at ADDR. \n"); dprintf (" -p -t [N] Dump N collected traces with heavy heap users.\n"); dprintf (" -p -tc [N] Dump N traces sorted by count usage (eqv. with -t).\n"); dprintf (" -p -ts [N] Dump N traces sorted by size.\n"); dprintf (" -p -fi [N] Dump last N fault injection traces.\n"); return S_OK; } i = (ULONG)State.EnableHeapChecking + (ULONG)State.EnableHeapValidateOnCall + (ULONG)State.DisableHeapChecking + (ULONG)State.DisableHeapValidateOnCall + (ULONG)State.ToggleAPICallTracing; if (i > 1) { dprintf( "HEAPEXT: -d, -D, -e and -E flags are mutually exclusive\n" ); return E_INVALIDARG; } if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) { if (pRtlpHeapStopOn == 0) { dprintf( "HEAPEXT: Unable to %s heap breakpoint due to missing or invalid NTDLL symbols.\n", State.SetStopOnBreakPoint ? "set" : "remove" ); return E_INVALIDARG; } if (State.HeapToDump == 0) { dprintf( "HEAPEXT: Must specify either heap index or heap address to -b command.\n" ); return E_INVALIDARG; } } // // Ok, so this is a !heap command for NT heap manager. // if (!HeapExtInitialized) { pNtGlobalFlag = GetExpression( "NTDLL!NtGlobalFlag" ); if (pNtGlobalFlag == 0 || !ReadMemory( pNtGlobalFlag, &NtGlobalFlag, sizeof( NtGlobalFlag ), NULL ) ) { dprintf( "HEAPEXT: Unable to get address of NTDLL!NtGlobalFlag.\n" ); return E_INVALIDARG; } pRtlpHeapInvalidBreakPoint = GetExpression( "NTDLL!RtlpHeapInvalidBreakPoint" ); if (pRtlpHeapInvalidBreakPoint == 0) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapInvalidBreakPoint.\n" ); } pRtlpHeapInvalidBadAddress = GetExpression( "NTDLL!RtlpHeapInvalidBadAddress" ); if (pRtlpHeapInvalidBadAddress == 0) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapInvalidBadAddress.\n" ); } pRtlpGlobalTagHeap = GetExpression( "NTDLL!RtlpGlobalTagHeap" ); if (pRtlpGlobalTagHeap == 0) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpGlobalTagHeap.\n" ); } if (!ReadPointer( pRtlpGlobalTagHeap,&pRtlpGlobalTagHeap)) { dprintf( "HEAPEXT: Unable to get address of *NTDLL!RtlpGlobalTagHeap.\n" ); } pRtlpHeapStopOn = GetExpression( "NTDLL!RtlpHeapStopOn" ); if (pRtlpHeapStopOn == 0) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapStopOn\n" ); } #if STACK_TRACE_DATABASE_SUPPORT pRtlpStackTraceDataBase = GetExpression( "NTDLL!RtlpStackTraceDataBase" ); if (pRtlpStackTraceDataBase == 0) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpStackTraceDataBase\n" ); } #endif // STACK_TRACE_DATABASE_SUPPORT HeapExtInitialized = TRUE; } if (!GetPageSize()) { dprintf("Unable to get PageSize.\n"); return E_INVALIDARG; } if (!ArgumentsSpecified) { if ((NtGlobalFlag & (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS | FLG_HEAP_VALIDATE_ALL | FLG_HEAP_ENABLE_TAGGING | FLG_USER_STACK_TRACE_DB | FLG_HEAP_DISABLE_COALESCING ) ) != 0 ) { dprintf( "NtGlobalFlag enables following debugging aids for new heaps:" ); if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) { dprintf( " tail checking\n" ); } if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) { dprintf( " free checking\n" ); } if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) { dprintf( " validate parameters\n" ); } if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) { dprintf( " validate on call\n" ); } if (NtGlobalFlag & FLG_HEAP_ENABLE_TAGGING) { dprintf( " heap tagging\n" ); } if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) { dprintf( " stack back traces\n" ); } if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) { dprintf( " disable coalescing of free blocks\n" ); } } } { INIT_API(); } GetPebAddress( 0, &ProcessPeb); if (AddressToDump == (ULONG64)-1) { GetFieldValue(ProcessPeb, "PEB", "ProcessHeaps", AddressToDump); } PtrSize = IsPtr64() ? 8 : 4; HeapEntryTypeSize = GetTypeSize("_HEAP_ENTRY"); GetFieldOffset("_HEAP", "AlignRound", &AlOffset); GetFieldOffset("_HEAP", "Flags", &FlagOffset); TagEntrySize = GetTypeSize( "_HEAP_TAG_ENTRY"); pseudoTagEntrySize = GetTypeSize( "_HEAP_PSEUDO_TAG_ENTRY"); GotHeapsList = FALSE; GetFieldValue(ProcessPeb, "PEB", "NumberOfHeaps", State.NumberOfHeaps); GetFieldValue(ProcessPeb, "PEB", "ProcessHeaps", pHeapsList); if (State.NumberOfHeaps == 0) { dprintf( "No heaps to display.\n" ); } else if (!pHeapsList) { dprintf( "Unable to get address of ProcessHeaps array\n" ); } else { State.HeapsList = malloc( State.NumberOfHeaps * sizeof(ULONG64) ); // To Keep PHEAP if (State.HeapsList == NULL) { dprintf( "Unable to allocate memory to hold ProcessHeaps array\n" ); } else { ULONG i; // // Read the array of heap pointers // GotHeapsList = TRUE; for (i=0;i= SystemRangeStart) { State.HeapToDump = AddressToDump; } else { State.HeapToDump = (ULONG64)-1; } } } State.HeapIndex = 0; } else { if (!ArgumentsSpecified || AddressToDump < 0x10000) { dprintf( "You must specify the actual heap address since\n" ); dprintf( "array of process heaps is inaccessable\n" ); State.ExitDumpLoop = TRUE; } else { State.HeapToDump = AddressToDump; } } if (State.DumpGlobalTags) { dprintf( "Global Tags defined for each DLL that makes an untagged allocation.\n" ); if (LocalHeapSignature != HEAP_SIGNATURE) { b = GetFieldValue( pRtlpGlobalTagHeap, "_HEAP", "Signature", LocalHeapSignature); if (b) { dprintf( "HEAPEXT: Unable to read RtlpGlobalTagHeap\n" ); if (State.HeapsList != NULL) { free( State.HeapsList ); } EXIT_API(); return E_INVALIDARG; } } GetFieldValue(pRtlpGlobalTagHeap, "_HEAP", "TagEntries", pTagEntry); if (pTagEntry == 0) { dprintf( " no global tags currently defined.\n" ); } else { ULONG NextAvailableTagIndex; GetFieldValue(pRtlpGlobalTagHeap, "_HEAP", "NextAvailableTagIndex", NextAvailableTagIndex); dprintf( " Tag Name Allocs Frees Diff Allocated\n" ); for (TagIndex=1; TagIndex= State.Segments[ i ] && AddressToDump < LastValidEntry ) { State.HeapToDump = State.HeapAddress; if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) { State.StopOnAddress = AddressToDump; } else { State.HeapEntryToDump = AddressToDump; } } } } } } if (State.HeapToDump == (ULONG64)-1) { State.HeapIndex += 1; continue; } if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) { ULONG64 pul; ULONG Off; switch( State.StopOnOperation) { case STOP_ON_ALLOC: if (State.StopOnTagName[0] == UNICODE_NULL) { GetFieldOffset("_HEAP_STOP_ON_VALUES","AllocAddress", &Off); pul = pRtlpHeapStopOn + Off;; } else { GetFieldOffset("_HEAP_STOP_ON_VALUES","AllocTag.HeapAndTagIndex", &Off); pul = pRtlpHeapStopOn + Off;; } break; case STOP_ON_REALLOC: if (State.StopOnTagName[0] == UNICODE_NULL) { GetFieldOffset("_HEAP_STOP_ON_VALUES","ReAllocAddress", &Off); pul = pRtlpHeapStopOn + Off;; } else { GetFieldOffset("_HEAP_STOP_ON_VALUES","ReAllocTag.HeapAndTagIndex", &Off); pul = pRtlpHeapStopOn + Off;; } break; case STOP_ON_FREE: if (State.StopOnTagName[0] == UNICODE_NULL) { GetFieldOffset("_HEAP_STOP_ON_VALUES","FreeAddress", &Off); pul = pRtlpHeapStopOn + Off;; } else { GetFieldOffset("_HEAP_STOP_ON_VALUES","FreeTag.HeapAndTagIndex", &Off); pul = pRtlpHeapStopOn + Off;; } break; default: pul = 0; break; } if (pul != 0) { if (State.StopOnTagName[0] == UNICODE_NULL) { if (State.RemoveStopOnBreakPoint) { State.StopOnAddress = 0; } b = WriteMemory( pul, &State.StopOnAddress, PtrSize, NULL ); } else { if (!ConvertTagNameToIndex( &State )) { dprintf( "HEAPEXT: Unable to convert tag name %ws to an index\n", State.StopOnTagName ); b = TRUE; } else { b = WriteMemory( pul, &State.StopOnTag.HeapAndTagIndex, sizeof( State.StopOnTag.HeapAndTagIndex ), NULL ); } } if (!b) { dprintf( "HEAPEXT: Unable to set heap breakpoint - write memory to %x failed\n", pul ); } else { if (State.SetStopOnBreakPoint) { if (State.StopOnTagName[0] == UNICODE_NULL) { dprintf( "HEAPEXT: Enabled heap breakpoint for %s of block %x\n", State.StopOnOperation == STOP_ON_ALLOC ? "Alloc" : State.StopOnOperation == STOP_ON_REALLOC ? "ReAlloc" : "Free", State.StopOnAddress ); } else { dprintf( "HEAPEXT: Enabled heap breakpoint for %s of block with tag %ws\n", State.StopOnOperation == STOP_ON_ALLOC ? "Alloc" : State.StopOnOperation == STOP_ON_REALLOC ? "ReAlloc" : "Free", State.StopOnTagName ); } } else { dprintf( "HEAPEXT: Disabled heap breakpoint for %s\n", State.StopOnOperation == STOP_ON_ALLOC ? "Alloc" : State.StopOnOperation == STOP_ON_REALLOC ? "ReAlloc" : "Free" ); } } } } if (State.ValidateHeap) { ValidateHeapHeader( State.HeapAddress ); } HeapHeaderModified = FALSE; GetFieldValue(State.HeapAddress, "_HEAP", "AlignRound", AlignRound); if (State.EnableHeapChecking || State.EnableHeapValidateOnCall) { if (!(Flags & HEAP_TAIL_CHECKING_ENABLED)) { AlignRound += CHECK_HEAP_TAIL_SIZE; b = WriteMemory( (State.HeapAddress + AlOffset), &AlignRound, sizeof( AlignRound ), NULL ); } else { b = TRUE; } if (b) { HeapHeaderModified = TRUE; Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED; if (State.EnableHeapValidateOnCall) { Flags |= HEAP_VALIDATE_ALL_ENABLED; } b = WriteMemory( (State.HeapAddress + FlagOffset), (LPCVOID)&Flags, sizeof( Flags ), NULL ); } if (!b) { dprintf( "HEAPEXT: Unable to enable heap checking for heap %p\n", State.HeapAddress ); InitTypeRead( (State.HeapAddress), _HEAP); } else { if (State.EnableHeapValidateOnCall) { dprintf( "HEAPEXT: Enabled validate on call heap checking for heap %p\n", State.HeapAddress ); } else { dprintf( "HEAPEXT: Enabled heap checking for heap %p\n", State.HeapAddress ); } } } else if (State.DisableHeapChecking || State.DisableHeapValidateOnCall) { if (State.DisableHeapValidateOnCall) { if (Flags & HEAP_VALIDATE_ALL_ENABLED) { Flags &= ~HEAP_VALIDATE_ALL_ENABLED; b = WriteMemory( State.HeapAddress + FlagOffset, (LPCVOID)&Flags, sizeof( Flags ), NULL ); } else { b = TRUE; } } else { if (Flags & HEAP_TAIL_CHECKING_ENABLED) { HeapHeaderModified = TRUE; AlignRound -= CHECK_HEAP_TAIL_SIZE; b = WriteMemory( State.HeapAddress + AlOffset, (LPCVOID)&AlignRound, sizeof( AlignRound ), NULL ); } else { b = TRUE; } if (b) { Flags &= ~(HEAP_VALIDATE_PARAMETERS_ENABLED | HEAP_VALIDATE_ALL_ENABLED | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED ); b = WriteMemory( State.HeapAddress + FlagOffset, (LPCVOID)&Flags, sizeof( Flags ), NULL ); } } if (!b) { dprintf( "HEAPEXT: Unable to disable heap checking for heap %p\n", State.HeapAddress ); InitTypeRead( (State.HeapAddress), _HEAP); } else { if (State.DisableHeapValidateOnCall) { dprintf( "HEAPEXT: Disabled validate on call heap checking for heap %p\n", State.HeapAddress ); } else { dprintf( "HEAPEXT: Disabled heap checking for heap %p\n", State.HeapAddress ); } } } else if (State.ToggleAPICallTracing) { Flags ^= HEAP_CREATE_ENABLE_TRACING; b = WriteMemory( State.HeapAddress + FlagOffset, (LPCVOID)&Flags, sizeof( Flags ), NULL ); if (!b) { dprintf( "HEAPEXT: Unable to toggle API call tracing for heap %p\n", State.HeapAddress ); InitTypeRead( (State.HeapAddress), _HEAP); } else { HeapHeaderModified = TRUE; if (Flags & HEAP_CREATE_ENABLE_TRACING) { dprintf( "HEAPEXT: Enabled API call tracing for heap %p\n", State.HeapAddress ); } else { dprintf( "HEAPEXT: Disabled API call tracing for heap %p\n", State.HeapAddress ); } } } else if (State.DumpHeapTags) { GetFieldValue(State.HeapAddress, "_HEAP", "TagEntries", pTagEntry); if (pTagEntry == 0) { dprintf( " no tags currently defined for this heap.\n" ); } else { ULONG NextAvailableTagIndex; GetFieldValue(State.HeapAddress, "_HEAP", "NextAvailableTagIndex", NextAvailableTagIndex); dprintf( " Tag Name Allocs Frees Diff Allocated\n" ); for (TagIndex=1; TagIndex%4u", TagIndex | HEAP_PSEUDO_TAG_FLAG, HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT ); } else if (TagIndex < HEAP_MAXIMUM_FREELISTS) { dprintf( "%04x: Objects=%4u", TagIndex | HEAP_PSEUDO_TAG_FLAG, TagIndex << HEAP_GRANULARITY_SHIFT ); } else { dprintf( "%04x: VirtualAlloc", TagIndex | HEAP_PSEUDO_TAG_FLAG ); } dprintf( " %8d %8d %6d %8d\n", (ULONG)ReadField(Allocs), (ULONG)ReadField(Frees), (ULONG)ReadField(Allocs) - (ULONG)ReadField(Frees), (ULONG)ReadField(Size) << HEAP_GRANULARITY_SHIFT ); } } } } // BUGBUG - Cannot write whole struct - change to write specific fields only // /* if (HeapHeaderModified && (State.Heap.HeaderValidateCopy != NULL)) { b = WriteMemory( (ULONG_PTR)State.Heap.HeaderValidateCopy, &State.Heap, sizeof( State.Heap ), NULL ); if (!b) { dprintf( "HEAPEXT: Unable to update header validation copy at %p\n", State.Heap.HeaderValidateCopy ); } }*/ if (State.HeapEntryToDump != 0 || State.DumpHeapEntries || State.DumpHeapSegments || State.DumpHeapFreeLists ) { WalkHEAP( &State ); } } else { dprintf( "\n" ); } State.HeapIndex += 1; } if (State.HeapsList != NULL) { free( State.HeapsList ); } EXIT_API(); return S_OK; } BOOL ConvertTagNameToIndex( IN PHEAP_STATE State ) { ULONG TagIndex; ULONG64 pTagEntry; // PHEAP_TAG_ENTRY ULONG64 pPseudoTagEntry; BOOL b; PWSTR s; WCHAR TagName[ 24 ]; ULONG NextAvailableTagIndex, TagEntrySize; if (State->RemoveStopOnBreakPoint) { State->StopOnTag.HeapAndTagIndex = 0; return TRUE; } if (!_wcsnicmp( State->StopOnTagName, L"Objects", 7 )) { GetFieldValue(State->Heap, "_HEAP", "PseudoTagEntries", pPseudoTagEntry); if (pPseudoTagEntry == 0) { return FALSE; } s = &State->StopOnTagName[ 7 ]; if (*s == L'>') { GetFieldValue(State->Heap, "_HEAP", "ProcessHeapsListIndex", State->StopOnTag.HeapIndex); State->StopOnTag.TagIndex = HEAP_PSEUDO_TAG_FLAG; return TRUE; } else if (*s == L'=') { while (*++s == L' ') ; State->StopOnTag.TagIndex = (USHORT)_wtoi( s ); if (State->StopOnTag.TagIndex > 0 && State->StopOnTag.TagIndex < (HEAP_MAXIMUM_FREELISTS >> HEAP_GRANULARITY_SHIFT) ) { GetFieldValue(State->Heap, "_HEAP", "ProcessHeapsListIndex", State->StopOnTag.HeapIndex); State->StopOnTag.TagIndex = (State->StopOnTag.TagIndex >> HEAP_GRANULARITY_SHIFT) | HEAP_PSEUDO_TAG_FLAG; return TRUE; } } } GetFieldValue(State->Heap, "_HEAP", "TagEntries", pTagEntry); if (pTagEntry == 0) { return FALSE; } GetFieldValue(State->HeapAddress, "_HEAP", "NextAvailableTagIndex", NextAvailableTagIndex); TagEntrySize = GetTypeSize("_HEAP_TAG_ENTRY"); for (TagIndex=1; TagIndexStopOnTagName, TagName )) { GetFieldValue( pTagEntry,"_HEAP_TAG_ENTRY","TagIndex",State->StopOnTag.TagIndex); return TRUE; } } return FALSE; } BOOL GetHeapTagEntry( IN ULONG64 Heap, IN USHORT TagIndex, OUT PULONG64 TagEntry ) { BOOL b; ULONG64 pTagEntries;// PHEAP_TAG_ENTRY ULONG NextAvailableTagIndex; ULONG64 pPseudoTagEntries; // PHEAP_PSEUDO_TAG_ENTRY b = FALSE; if (TagIndex & HEAP_PSEUDO_TAG_FLAG) { TagIndex &= ~HEAP_PSEUDO_TAG_FLAG; GetFieldValue(Heap, "_HEAP", "PseudoTagEntries", pPseudoTagEntries); if (pPseudoTagEntries == 0) { return FALSE; } // BUGBUG - Cannot copy name /* if (TagIndex == 0) { swprintf( TagEntry->TagName, L"Objects>%4u", HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT ); } else if (TagIndex < HEAP_MAXIMUM_FREELISTS) { swprintf( TagEntry->TagName, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT ); } else { swprintf( TagEntry->TagName, L"VirtualAlloc" ); } TagEntry->TagIndex = TagIndex; TagEntry->CreatorBackTraceIndex = 0;*/ *TagEntry = pPseudoTagEntries + TagIndex * GetTypeSize("_HEAP_PSEUDO_TAG_ENTRY"); b = !InitTypeRead(*TagEntry, _HEAP_TAG_ENTRY); } else if (TagIndex & HEAP_GLOBAL_TAG) { if (GetFieldValue(pRtlpGlobalTagHeap, "_HEAP", "NextAvailableTagIndex",NextAvailableTagIndex)) { return FALSE; } TagIndex &= ~HEAP_GLOBAL_TAG; if (TagIndex < NextAvailableTagIndex) { GetFieldValue(pRtlpGlobalTagHeap, "_HEAP", "TagEntries", pTagEntries); if (pTagEntries == 0) { return FALSE; } *TagEntry = pTagEntries; b = ! (BOOL) InitTypeRead(pTagEntries, _HEAP_TAG_ENTRY); } } else { if (GetFieldValue(Heap, "_HEAP", "NextAvailableTagIndex",NextAvailableTagIndex)) { return FALSE; } if (TagIndex < NextAvailableTagIndex) { GetFieldValue(Heap, "_HEAP", "TagEntries", pTagEntries); if (pTagEntries == 0) { return FALSE; } *TagEntry = pTagEntries; b = ! (BOOL) InitTypeRead(pTagEntries, _HEAP_TAG_ENTRY); } } return b; } VOID WalkHEAP( IN PHEAP_STATE State ) { BOOL b; ULONG64 FreeListHead; ULONG i; ULONG64 Head, Next; // HEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocEntry; ULONG64 TagEntry; // HEAP_TAG_ENTRY ULONG64 FreeEntryAddress; ULONG64 FreeEntry; // HEAP_FREE_ENTRY ULONG64 UCRSegment, UnusedUnCommittedRanges; ULONG64 CapturedUCRSegment; // HEAP_UCR_SEGMENT ULONG AlignRound, Offset, ListSize, FreeListOffset; GetFieldOffset("_HEAP", "VirtualAllocdBlocks", &Offset); if (InitTypeRead(State->HeapAddress, _HEAP)) { return; } AlignRound = (ULONG)ReadField(AlignRound) - GetTypeSize( "_HEAP_ENTRY" ); if ((ULONG)ReadField(Flags) & HEAP_TAIL_CHECKING_ENABLED) { AlignRound -= CHECK_HEAP_TAIL_SIZE; } dprintf( " Flags: %08x\n", (ULONG)ReadField(Flags) ); dprintf( " ForceFlags: %08x\n", (ULONG)ReadField(ForceFlags) ); dprintf( " Granularity: %u bytes\n", AlignRound + 1 ); dprintf( " Segment Reserve: %08x\n", (ULONG)ReadField(SegmentReserve) ); dprintf( " Segment Commit: %08x\n", (ULONG)ReadField(SegmentCommit) ); dprintf( " DeCommit Block Thres:%08x\n", (ULONG)ReadField(DeCommitFreeBlockThreshold) ); dprintf( " DeCommit Total Thres:%08x\n", (ULONG)ReadField(DeCommitTotalFreeThreshold) ); dprintf( " Total Free Size: %08x\n", (ULONG)ReadField(TotalFreeSize) ); dprintf( " Max. Allocation Size:%08x\n", (ULONG)ReadField(MaximumAllocationSize) ); dprintf( " Lock Variable at: %08x\n", (ULONG)ReadField(LockVariable) ); dprintf( " Next TagIndex: %04x\n", (ULONG)ReadField(NextAvailableTagIndex) ); dprintf( " Maximum TagIndex: %04x\n", (ULONG)ReadField(MaximumTagIndex) ); dprintf( " Tag Entries: %08x\n", (ULONG)ReadField(TagEntries) ); dprintf( " PsuedoTag Entries: %08x\n", (ULONG)ReadField(PseudoTagEntries) ); dprintf( " Virtual Alloc List: %08p\n", State->HeapAddress + Offset); UCRSegment = ReadField(UCRSegments); UnusedUnCommittedRanges = ReadField(UnusedUnCommittedRanges); Head = State->HeapAddress + Offset; Next = ReadField(VirtualAllocdBlocks.Flink); while (Next != Head) { ULONG Flags, TagIndex; if (InitTypeRead( Next, _HEAP_VIRTUAL_ALLOC_ENTRY)) { dprintf( " Unable to read _HEAP_VIRTUAL_ALLOC_ENTRY structure at %p\n", Next ); break; } if (State->DumpHeapEntries) { dprintf( " %08p: %08x [%02x] - busy (%x)", Next, (ULONG)ReadField(CommitSize), (ULONG)ReadField(CommitSize) - (ULONG)ReadField(BusyBlock.Size), Flags = (ULONG)ReadField(BusyBlock.Flags) ); if ((ULONG)ReadField(BusyBlock.Flags) & HEAP_ENTRY_FILL_PATTERN) { dprintf( ", tail fill" ); } if ((ULONG)ReadField(ExtraStuff.Settable)) { dprintf( " (Handle %08x)", (ULONG)ReadField(ExtraStuff.Settable) ); } if (TagIndex = (ULONG)ReadField(ExtraStuff.TagIndex)) { WCHAR TagName[32]; if (GetHeapTagEntry( State->Heap, (USHORT) (TagIndex), &TagEntry )) { GetFieldValue(TagEntry, "_HEAP_TAG_ENTRY", "TagName", TagName); dprintf( " (%ws)", TagName ); } else { dprintf( " (Tag %x)", (TagIndex) ); } } if ((Flags) & HEAP_ENTRY_SETTABLE_FLAGS) { dprintf( ", user flags (%x)", ((Flags) & HEAP_ENTRY_SETTABLE_FLAGS) >> 5 ); } dprintf( "\n" ); #if STACK_TRACE_DATABASE_SUPPORT DumpStackBackTraceIndex( State, (ULONG)ReadField(ExtraStuff.AllocatorBackTraceIndex) ); #endif // STACK_TRACE_DATABASE_SUPPORT } if (ReadField(Entry.Flink) == Next) { dprintf( " **** List is hosed\n"); break; } Next = ReadField(Entry.Flink); } dprintf( " UCR FreeList: %p\n", UnusedUnCommittedRanges ); while (UCRSegment != 0) { b = (BOOL) InitTypeRead( UCRSegment, _HEAP_UCR_SEGMENT); if (b) { dprintf( " Unable to read _HEAP_UCR_SEGMENT structure at %08p\n", UCRSegment ); break; } else { dprintf( " UCRSegment - %08p: %08I64x . %08I64x\n", UCRSegment, ReadField(CommittedSize), ReadField(ReservedSize) ); } if (State->ComputeSummary) { State->OverheadSize += ReadField(CommittedSize); } UCRSegment = ReadField(Next); } InitTypeRead(State->HeapAddress, _HEAP); dprintf( " FreeList Usage: %08x %08x %08x %08x\n", (ULONG)ReadField(u.FreeListsInUseUlong[0]), (ULONG)ReadField(u.FreeListsInUseUlong[1]), (ULONG)ReadField(u.FreeListsInUseUlong[2]), (ULONG)ReadField(u.FreeListsInUseUlong[3]) ); if (State->ComputeSummary) { State->OverheadSize += GetTypeSize( "_HEAP" ); dprintf( "Committed Allocated Free OverHead\n" ); dprintf( "% 8x % 8x % 8x % 8x\r", State->CommittedSize, State->AllocatedSize, State->FreeSize, State->OverheadSize ); } GetFieldOffset ("_HEAP", "FreeLists", &Offset); ListSize = GetTypeSize("LIST_ENTRY"); GetFieldOffset ("_HEAP_FREE_ENTRY", "FreeList", &FreeListOffset); for (i=0; iHeapAddress + Offset + ListSize * i; GetFieldValue(FreeListHead, "LIST_ENTRY", "Flink", Flink); GetFieldValue(FreeListHead, "LIST_ENTRY", "Blink", Blink); if (Flink != Blink || Flink != FreeListHead ) { ULONG Count = 0; dprintf( " FreeList[ %02x ] at %08p: %08p . %08p ", i, FreeListHead, Blink, Flink ); if (State->DumpHeapFreeLists) { dprintf("\n"); } Next = Flink; while (Next != FreeListHead) { Count++; FreeEntryAddress = Next - FreeListOffset; b = (BOOL) InitTypeRead ( FreeEntryAddress, _HEAP_FREE_ENTRY); if (b) { dprintf( " Unable to read HEAP_ENTRY structure at %08p\n", FreeEntryAddress ); break; } if (State->DumpHeapFreeLists) { dprintf( " %08x: %05x . %05x [%02x] - free\n", FreeEntryAddress, (ULONG)ReadField(PreviousSize) << HEAP_GRANULARITY_SHIFT, (ULONG)ReadField(Size) << HEAP_GRANULARITY_SHIFT, (ULONG)ReadField(Flags) ); } Next = ReadField(FreeList.Flink); if (CheckControlC()) { return; } } if (!State->DumpHeapFreeLists) { dprintf( " (%ld block%c)\n", Count, (Count == 1 ? ' ' : 's') ); } } } for (i=0; iSegments[ i ] != 0) { State->SegmentNumber = i; State->SegmentAddress = State->Segments[ i ]; WalkHEAP_SEGMENT( State ); } if (State->ExitDumpLoop || CheckControlC()) { break; } } if (State->HeapAddress == State->HeapToDump) { State->ExitDumpLoop = TRUE; } return; } VOID WalkHEAP_SEGMENT( IN PHEAP_STATE State ) { ULONG64 Segment; // PHEAP_SEGMENT BOOL b; BOOLEAN DumpEntry; ULONG64 EntryAddress, PrevEntryAddress, NextEntryAddress; // PHEAP_ENTRY ULONG64 Entry, PrevEntry; ULONG64 UnCommittedRanges; // PHEAP_UNCOMMMTTED_RANGE ULONG64 UnCommittedRangeStart, UnCommittedRange, UnCommittedRangeEnd; ULONG64 BaseAddress, LastValidEntry; ULONG NumberOfUnCommittedPages, NumberOfPages; ULONG EntryOffset; Segment = State->Segments[ State->SegmentNumber ]; if (State->ComputeSummary) { State->OverheadSize += GetTypeSize( "_HEAP_SEGMENT" ); dprintf( "% 8x % 8x % 8x % 8x\r", State->CommittedSize, State->AllocatedSize, State->FreeSize, State->OverheadSize ); } InitTypeRead(Segment, _HEAP_SEGMENT); if (State->DumpHeapSegments) { dprintf( " Segment%02u at %08x:\n", State->SegmentNumber, State->SegmentAddress ); dprintf( " Flags: %08x\n", (ULONG)ReadField(Flags) ); dprintf( " Base: %08p\n", BaseAddress = ReadField(BaseAddress) ); dprintf( " First Entry: %08x\n", (ULONG)ReadField(FirstEntry) ); dprintf( " Last Entry: %08p\n", LastValidEntry = ReadField(LastValidEntry) ); dprintf( " Total Pages: %08x\n", NumberOfPages = (ULONG)ReadField(NumberOfPages) ); dprintf( " Total UnCommit: %08x\n", NumberOfUnCommittedPages = (ULONG)ReadField(NumberOfUnCommittedPages) ); dprintf( " Largest UnCommit:%08x\n", (ULONG)ReadField(LargestUnCommittedRange) ); dprintf( " UnCommitted Ranges: (%u)\n", (ULONG)ReadField(NumberOfUnCommittedRanges) ); } UnCommittedRangeStart = UnCommittedRanges = ReadField(UnCommittedRanges); while (UnCommittedRanges != 0) { b = (BOOL) InitTypeRead( UnCommittedRanges, _HEAP_UNCOMMMTTED_RANGE); if (b) { dprintf( " unable to read uncommited range structure at %p\n", UnCommittedRanges ); return; } if (State->DumpHeapSegments) { dprintf( " %08I64x: %08x\n", ReadField(Address), (ULONG) ReadField(Size) ); } UnCommittedRanges = ReadField(Next); if (CheckControlC()) { break; } } if (State->DumpHeapSegments) { dprintf( "\n" ); } if (!GetPageSize()) { dprintf("Unable to get PageSize.\n"); return; } State->CommittedSize += ( NumberOfPages - NumberOfUnCommittedPages ) * PageSize; if (State->ComputeSummary) { dprintf( "% 8x % 8x % 8x % 8x\r", State->CommittedSize, State->AllocatedSize, State->FreeSize, State->OverheadSize ); } if (State->DumpHeapEntries) { dprintf( " Heap entries for Segment%02u in Heap %p\n", State->SegmentNumber, State->HeapAddress ); } UnCommittedRangeEnd = UnCommittedRanges; UnCommittedRanges = UnCommittedRangeStart; if (BaseAddress == State->HeapAddress) { GetFieldOffset("_HEAP", "Entry", &EntryOffset); EntryAddress = State->HeapAddress + EntryOffset; } else { GetFieldOffset("_HEAP_SEGMENT", "Entry", &EntryOffset); EntryAddress = State->Segments[ State->SegmentNumber ] + EntryOffset; } PrevEntryAddress = 0; while (EntryAddress < LastValidEntry) { ULONG Flags, Size, UnusedBytes; b = (BOOL) InitTypeRead(EntryAddress, _HEAP_ENTRY); if (b) { dprintf( " unable to read heap entry at %08p\n", EntryAddress ); break; } NextEntryAddress = EntryAddress + (Size = (ULONG) ReadField(Size) * HeapEntryTypeSize); Flags = (ULONG) ReadField(Flags); UnusedBytes = (ULONG) ReadField(UnusedBytes); if (State->DumpHeapEntries) { DumpEntry = TRUE; } else if (PrevEntryAddress != 0 && (State->HeapEntryToDump == PrevEntryAddress || (State->HeapEntryToDump > PrevEntryAddress && State->HeapEntryToDump <= NextEntryAddress ) ) ) { DumpEntry = TRUE; } else { DumpEntry = FALSE; } if (DumpEntry) { DumpHeapEntry( State, EntryAddress, EntryAddress ); } if (!(Flags & HEAP_ENTRY_BUSY)) { State->TotalFreeSize += Size; } if (State->ComputeSummary) { if (Flags & HEAP_ENTRY_BUSY) { State->AllocatedSize += Size << HEAP_GRANULARITY_SHIFT; State->AllocatedSize -= UnusedBytes; State->OverheadSize += UnusedBytes; } else { State->FreeSize += Size << HEAP_GRANULARITY_SHIFT; } } if (State->ValidateHeap) { if (!ValidateHeapEntry( State, PrevEntryAddress, PrevEntryAddress, EntryAddress, EntryAddress ) ) { if (State->DumpHeapEntries) { break; } } } if (Size == 0 || CheckControlC()) { break; } PrevEntryAddress = EntryAddress; // PrevEntry = Entry; EntryAddress = NextEntryAddress; if (Flags & HEAP_ENTRY_LAST_ENTRY) { if (State->ComputeSummary) { dprintf( "% 8x % 8x % 8x % 8x\r", State->CommittedSize, State->AllocatedSize, State->FreeSize, State->OverheadSize ); } InitTypeRead(UnCommittedRanges, _HEAP_UNCOMMMTTED_RANGE); if (EntryAddress == ReadField(Address)) { Size = (ULONG) ReadField(Size); if (DumpEntry) { dprintf( " %p: %08x - uncommitted bytes.\n", EntryAddress, Size ); } PrevEntryAddress = 0; EntryAddress += Size; UnCommittedRanges = ReadField(Next);; } else { break; } } } if (State->ComputeSummary) { dprintf( "% 8x % 8x % 8x % 8x\r", State->CommittedSize, State->AllocatedSize, State->FreeSize, State->OverheadSize ); } return; } struct { BOOL HaveOffset; ULONG Offset; LPSTR Description; } FieldOffsets[] = { 0, 0, "Entry", 0, 0, "Signature", 0, 0, "Flags", 0, 0, "ForceFlags", 0, 0, "VirtualMemoryThreshold", 0, 0, "SegmentReserve", 0, 0, "SegmentCommit", 0, 0, "DeCommitFreeBlockThreshold", 0, 0, "DeCommitTotalFreeThreshold", 0, 0, "TotalFreeSize", 0, 0, "MaximumAllocationSize", 0, 0, "ProcessHeapsListIndex", 0, 0, "HeaderValidateLength", 0, 0, "HeaderValidateCopy", 0, 0, "NextAvailableTagIndex", 0, 0, "MaximumTagIndex", 0, 0, "TagEntries", 0, 0, "UCRSegments", 0, 0, "UnusedUnCommittedRanges", 0, 0, "AlignRound", 0, 0, "AlignMask", 0, 0, "VirtualAllocdBlocks", 0, 0, "Segments", 0, 0, "FreeListsInUse", 0, 0, "FreeListsInUseTerminate", 0, 0, "AllocatorBackTraceIndex", 0, 0, "Reserved1", 0, 0, "PseudoTagEntries", 0, 0, "FreeLists", 0, 0, "LockVariable", // 1, GetTypeSize("HEAP"), "Uncommitted Ranges", 0, 0xFFFF, NULL }; BOOL ValidateHeapHeader( IN ULONG64 HeapAddress ) { PVOID CurrentHeaderValidate; PVOID PreviousHeaderValidate; ULONG i, n, nEqual; ULONG64 HeaderValidateCopy; BOOL b; if (InitTypeRead(HeapAddress, _HEAP)) { return FALSE; } if (ReadField(Signature) != HEAP_SIGNATURE) { dprintf( "Heap at %p contains invalid signature.\n" ); return FALSE; } n = (ULONG) ReadField(HeaderValidateLength); if (n == 0 || (HeaderValidateCopy = ReadField(HeaderValidateCopy)) == 0) { return TRUE; } b = FALSE; CurrentHeaderValidate = malloc( n ); if (CurrentHeaderValidate != NULL) { PreviousHeaderValidate = malloc( n ); if (PreviousHeaderValidate != NULL) { b = ReadMemory( HeapAddress, CurrentHeaderValidate, n, NULL ); if (b) { b = ReadMemory( (HeaderValidateCopy), PreviousHeaderValidate, n, NULL ); if (b) { nEqual = (ULONG)RtlCompareMemory( CurrentHeaderValidate, PreviousHeaderValidate, n ); if (nEqual != n) { dprintf( "HEAPEXT: Heap %p - headers modified (%p is %x instead of %x)\n", HeapAddress, HeapAddress + nEqual, *(PULONG)((PCHAR)CurrentHeaderValidate + nEqual), *(PULONG)((PCHAR)PreviousHeaderValidate + nEqual) ); for (i=0; FieldOffsets[ i ].Description != NULL; i++) { if (!FieldOffsets[i].HaveOffset) { GetFieldOffset("_HEAP", FieldOffsets[i].Description, &FieldOffsets[i].Offset); FieldOffsets[i].HaveOffset = TRUE; } if (nEqual >= FieldOffsets[ i ].Offset && nEqual < FieldOffsets[ i+1 ].Offset ) { dprintf( " This is located in the %s field of the heap header.\n", FieldOffsets[ i ].Description ); } } b = FALSE; } } else { dprintf( "HEAPEXT: Unable to read copy of heap headers.\n" ); } } else { dprintf( "HEAPEXT: Unable to read heap headers.\n" ); } } else { dprintf( "HEAPEXT: Unable to allocate memory for heap header copy.\n" ); } } else { dprintf( "HEAPEXT: Unable to allocate memory for heap header.\n" ); } return b; } UCHAR CheckHeapFillPattern[ 20 ] = { CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL, CHECK_HEAP_TAIL_FILL }; BOOL ValidateHeapEntry( IN PHEAP_STATE State, IN ULONG64 PrevEntryAddress, IN ULONG64 PrevEntry, IN ULONG64 EntryAddress, IN ULONG64 Entry ) { UCHAR EntryTail[ 20 ]; // CHECK_HEAP_TAIL_SIZE ULONG FreeFill[ 256 ]; ULONG64 FreeAddress; ULONG tSize, cb, cbEqual; BOOL b; ULONG PreviousSize, Flags, Size, UnusedBytes, SmallTagIndex; ULONG SizeOfEntry; SizeOfEntry = GetTypeSize("_HEAP_ENTRY"); InitTypeRead(EntryAddress, _HEAP_ENTRY); (PreviousSize = (ULONG) ReadField(PreviousSize)); (Size = (ULONG) ReadField(Size)); (Flags = (ULONG) ReadField(Flags)); UnusedBytes = (ULONG) ReadField(UnusedBytes); SmallTagIndex = (ULONG) ReadField(SmallTagIndex); InitTypeRead(PrevEntryAddress, _HEAP_ENTRY); if (PrevEntryAddress == 0 && PreviousSize != 0) { dprintf( " PreviousSize field is non-zero when it should be zero to mark first entry\n" ); return FALSE; } if (PrevEntryAddress != 0 && PreviousSize != (ULONG) ReadField(Size)) { dprintf( " PreviousSize field does not match size in previous entry\n" ); return FALSE; } if (Flags & HEAP_ENTRY_BUSY) { if (Flags & HEAP_ENTRY_FILL_PATTERN) { tSize = (Size << HEAP_GRANULARITY_SHIFT) - UnusedBytes; b = ReadMemory( (EntryAddress+ HeapEntryTypeSize + tSize), EntryTail, sizeof( EntryTail ), NULL ); if (b) { cbEqual = (ULONG)RtlCompareMemory( EntryTail, CheckHeapFillPattern, CHECK_HEAP_TAIL_SIZE ); if (cbEqual != CHECK_HEAP_TAIL_SIZE) { dprintf( " Heap block at %p modified at %p past requested size of %x (%x * 8 - %x)\n", EntryAddress, EntryAddress + HeapEntryTypeSize + tSize + cbEqual, tSize, Size, UnusedBytes ); return FALSE; } } else { dprintf( " Unable to read tail of heap block at %p\n", EntryAddress ); return FALSE; } } } else { if (Flags & HEAP_ENTRY_FILL_PATTERN) { tSize = (Size - 2) << HEAP_GRANULARITY_SHIFT; if (Flags & HEAP_ENTRY_EXTRA_PRESENT && tSize > GetTypeSize( "_HEAP_FREE_ENTRY_EXTRA" ) ) { tSize -= GetTypeSize( "_HEAP_FREE_ENTRY_EXTRA" ); } FreeAddress = EntryAddress + GetTypeSize("_HEAP_FREE_ENTRY"); while (tSize != 0) { if (tSize > sizeof( FreeFill )) { cb = sizeof( FreeFill ); } else { cb = tSize; } b = ReadMemory( FreeAddress, FreeFill, cb, NULL ); if (b) { cbEqual = (ULONG)RtlCompareMemoryUlong( FreeFill, cb, FREE_HEAP_FILL ); if (cbEqual != cb) { \ dprintf( " Free Heap block %p modified at %p after it was freed\n", EntryAddress, FreeAddress + cbEqual ); return FALSE; } } else { dprintf( " Unable to portion of free heap block at %p\n", EntryAddress ); return FALSE; } tSize -= cb; } } } return TRUE; } VOID DumpHeapEntry( IN PHEAP_STATE State, IN ULONG64 EntryAddress, IN ULONG64 Entry ) { BOOL b; WCHAR TagName[32]; // HEAP_ENTRY_EXTRA EntryExtra; ULONG64 TagEntry; // HEAP_TAG_ENTRY // HEAP_FREE_ENTRY_EXTRA FreeExtra; ULONG64 p; USHORT BackTraceIndex; ULONG PreviousSize, Size, Flags, UnusedBytes, SmallTagIndex; ULONG SizeOfEntry; SizeOfEntry = GetTypeSize("_HEAP_ENTRY"); InitTypeRead(EntryAddress, _HEAP_ENTRY); dprintf( " %p: %05x . %05x [%02x]", EntryAddress, (PreviousSize = (ULONG) ReadField(PreviousSize)) << HEAP_GRANULARITY_SHIFT, (Size = (ULONG) ReadField(Size)) << HEAP_GRANULARITY_SHIFT, (Flags = (ULONG) ReadField(Flags)) ); BackTraceIndex = 0; UnusedBytes = (ULONG) ReadField(UnusedBytes); SmallTagIndex = (ULONG) ReadField(SmallTagIndex); if (Flags & HEAP_ENTRY_BUSY) { dprintf( " - busy (%x)", (Size << HEAP_GRANULARITY_SHIFT) - UnusedBytes ); if (Flags & HEAP_ENTRY_FILL_PATTERN) { dprintf( ", tail fill" ); } if (Flags & HEAP_ENTRY_EXTRA_PRESENT) { p = EntryAddress + SizeOfEntry * (Size - 1); b = (BOOL) InitTypeRead( p, _HEAP_ENTRY_EXTRA); if (b) { dprintf( " - unable to read heap entry extra at %p", p ); } else { BackTraceIndex = (USHORT)ReadField(AllocatorBackTraceIndex); if ((ULONG)ReadField(Settable)) { dprintf( " (Handle %08x)", (ULONG)ReadField(Settable) ); } if ((ULONG)ReadField(TagIndex)) { if (GetHeapTagEntry( State->Heap, (USHORT)ReadField(TagIndex), &TagEntry )) { GetFieldValue(TagEntry, "_HEAP_TAG_ENTRY", "TagName", TagName); dprintf( " (%ws)", TagName ); } else { dprintf( " (Tag %x)", (ULONG)ReadField(TagIndex) ); } } } } else if (SmallTagIndex) { if (GetHeapTagEntry( State->Heap, (USHORT) SmallTagIndex, &TagEntry )) { GetFieldValue(TagEntry, "_HEAP_TAG_ENTRY", "TagName", TagName); dprintf( " (%ws)", TagName ); } else { dprintf( " (Tag %x)", SmallTagIndex ); } } if (Flags & HEAP_ENTRY_SETTABLE_FLAGS) { dprintf( ", user flags (%x)", (Flags & HEAP_ENTRY_SETTABLE_FLAGS) >> 5 ); } dprintf( "\n" ); } else { if (Flags & HEAP_ENTRY_FILL_PATTERN) { dprintf( " free fill" ); } if (Flags & HEAP_ENTRY_EXTRA_PRESENT) { p = (EntryAddress + SizeOfEntry * (Size - 1)); b = (BOOL) InitTypeRead( p, _HEAP_ENTRY_EXTRA); if (b) { dprintf( " - unable to read heap free extra at %p", p ); } else { BackTraceIndex = (USHORT)ReadField(FreeBackTraceIndex); if (GetHeapTagEntry( State->Heap, (USHORT)ReadField(TagIndex), &TagEntry )) { GetFieldValue(TagEntry, "_HEAP_TAG_ENTRY", "TagName", TagName); dprintf( " (%ws)", TagName ); } else { dprintf( " (Tag %x at %p)", (ULONG)ReadField(TagIndex), p ); } } } dprintf( "\n" ); } #if STACK_TRACE_DATABASE_SUPPORT DumpStackBackTraceIndex( State, BackTraceIndex ); #endif // STACK_TRACE_DATABASE_SUPPORT return; } #if STACK_TRACE_DATABASE_SUPPORT && 0 VOID DumpStackBackTraceIndex( IN PHEAP_STATE State, IN USHORT BackTraceIndex ) { BOOL b; PRTL_STACK_TRACE_ENTRY pBackTraceEntry; RTL_STACK_TRACE_ENTRY BackTraceEntry; ULONG i; CHAR Symbol[ 1024 ]; ULONG_PTR Displacement; ULONG NumberOfEntriesAdded; PRTL_STACK_TRACE_ENTRY *EntryIndexArray; // Indexed by [-1 .. -NumberOfEntriesAdded] if (State->DumpStackBackTrace && BackTraceIndex != 0 && pRtlpStackTraceDataBase != NULL ) { if (!HaveCopyOfStackTraceDataBase) { b = ReadMemory( (ULONG_PTR)pRtlpStackTraceDataBase, &RtlpStackTraceDataBase, sizeof( RtlpStackTraceDataBase ), NULL ); if (!b || RtlpStackTraceDataBase == NULL) { State->DumpStackBackTrace = FALSE; return; } b = ReadMemory( (ULONG_PTR)RtlpStackTraceDataBase, &StackTraceDataBase, sizeof( StackTraceDataBase ), NULL ); if (!b) { State->DumpStackBackTrace = FALSE; return; } HaveCopyOfStackTraceDataBase = TRUE; } if (BackTraceIndex < StackTraceDataBase.NumberOfEntriesAdded) { b = ReadMemory( (ULONG_PTR)(StackTraceDataBase.EntryIndexArray - BackTraceIndex), &pBackTraceEntry, sizeof( pBackTraceEntry ), NULL ); if (!b) { dprintf( " unable to read stack back trace index (%x) entry at %p\n", BackTraceIndex, (StackTraceDataBase.EntryIndexArray - BackTraceIndex) ); return; } b = ReadMemory( (ULONG_PTR)pBackTraceEntry, &BackTraceEntry, sizeof( BackTraceEntry ), NULL ); if (!b) { dprintf( " unable to read stack back trace entry at %p\n", BackTraceIndex, pBackTraceEntry ); return; } dprintf( " Stack trace (%u) at %x:\n", BackTraceIndex, pBackTraceEntry ); for (i=0; i