#define DEBUG_PAGE_HEAP 1 #include "..\..\ntos\rtl\heappagi.h" __inline BOOLEAN CheckInterrupted( VOID ) { if ( CheckControlC() ) { dprintf( "\nInterrupted\n\n" ); return TRUE; } return FALSE; } __inline PVOID FetchRemotePVOID( PVOID Address ) { PVOID RemoteValue = NULL; ReadMemory( Address, &RemoteValue, sizeof( PVOID ), NULL ); return RemoteValue; } __inline BOOL WriteRemotePVOID( PVOID Address, PVOID Value ) { return WriteMemory( Address, &Value, sizeof( PVOID ), NULL ); } #define FETCH_REMOTE_FIELD( StructBase, StructType, FieldName ) \ FetchRemotePVOID((PCHAR)(StructBase) + FIELD_OFFSET( StructType, FieldName )) #define WRITE_REMOTE_FIELD( StructBase, StructType, FieldName, Value ) \ WriteRemotePVOID((PCHAR)(StructBase) + FIELD_OFFSET( StructType, FieldName ), (Value)); #define DUMP_REMOTE_FIELD( DumpName, StructBase, StructType, FieldName ) \ dprintf( "%s%08X\n", (DumpName), FETCH_REMOTE_FIELD( StructBase, StructType, FieldName )) VOID DebugPageHeapLocateFaultAllocation( PVOID RemoteHeap, PVOID AddressOfFault ); VOID DebugPageHeapReportAllocation( PVOID RemoteHeap, PVOID RemoteHeapNode, PCHAR NodeType, PVOID AddressOfFault ); BOOLEAN DebugPageHeapExtensionShowHeapList( VOID ); #ifdef DPH_CAPTURE_STACK_TRACE VOID DebugPageHeapReportStackTrace( ULONG StackTraceLength, PVOID StackTraceBuffer[] ); ULONG DebugPageHeapFetchRemoteStackTrace( PVOID RemoteStackNode, PVOID StackBuffer[] ); #endif // DPH_CAPTURE_STACK_TRACE VOID DebugPageHeapExtensionFind( PCSTR ArgumentString ) { PVOID RemoteHeapList; PVOID RemoteHeap; PVOID RemoteVirtualNode; PVOID RemoteVirtualBase; ULONG RemoteVirtualSize; PVOID AddressOfFault; AddressOfFault = (PVOID) strtoul( ArgumentString, NULL, 16 ); RemoteHeapList = (PVOID) GetExpression( "NTDLL!RtlpDebugPageHeapListHead" ); RemoteHeap = FetchRemotePVOID( RemoteHeapList ); if ( RemoteHeap == NULL ) { dprintf( "\nNo debug page heaps active in process (or bad symbols)\n\n" ); AddressOfFault = NULL; } if (( AddressOfFault == NULL ) || ( strchr( ArgumentString, '?' ))) { dprintf( "\nUsage: !dphfind
\n\n" " where is typically the address of\n" " a fault attemping to reference heap memory\n" " from a debug page heap allocation\n\n" ); return; } // // Find the heap that contains the range of virtual addresses that // contain the AddressOfFault. // for (;;) { // // The heap header contains a linked list of virtual memory // allocations. // RemoteVirtualNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pVirtualStorageListHead ); while ( RemoteVirtualNode != NULL ) { RemoteVirtualBase = FETCH_REMOTE_FIELD( RemoteVirtualNode, DPH_HEAP_ALLOCATION, pVirtualBlock ); RemoteVirtualSize = (ULONG) FETCH_REMOTE_FIELD( RemoteVirtualNode, DPH_HEAP_ALLOCATION, nVirtualBlockSize ); if (( RemoteVirtualBase == NULL ) || ( RemoteVirtualSize == 0 )) { dprintf( "\nPAGEHEAP: Heap 0x%08X appears to have an invalid\n" " virtual allocation list\n\n", RemoteHeap ); } if (((PCHAR) AddressOfFault >= ((PCHAR) RemoteVirtualBase )) && ((PCHAR) AddressOfFault <= ((PCHAR) RemoteVirtualBase + RemoteVirtualSize ))) { // // The fault appears to have occurred in the range of this // heap, so we'll search the busy and free lists for the // closest match and report it. Then exit. // DebugPageHeapLocateFaultAllocation( RemoteHeap, AddressOfFault ); return; } if ( CheckInterrupted() ) { return; } RemoteVirtualNode = FETCH_REMOTE_FIELD( RemoteVirtualNode, DPH_HEAP_ALLOCATION, pNextAlloc ); } // // Not found in this heap. Continue with next heap or end // of heap list. // if ( CheckInterrupted() ) { return; } RemoteHeap = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pNextHeapRoot ); if ( RemoteHeap == NULL ) { dprintf( "\nPAGEHEAP: Could not find a debug heap containing\n" " the virtual address 0x%08X\n\n", AddressOfFault ); return; } } } VOID DebugPageHeapLocateFaultAllocation( PVOID RemoteHeap, PVOID AddressOfFault ) { PVOID ClosestHeapNode; PVOID ClosestDifference; PVOID RemoteHeapNode; PVOID RemoteAllocBase; ULONG RemoteAllocSize; ULONG RemoteFreeListSize; ClosestHeapNode = NULL; // // First search the busy list for the containing allocation, if any. // RemoteHeapNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pBusyAllocationListHead ); while ( RemoteHeapNode != NULL ) { RemoteAllocBase = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pVirtualBlock ); RemoteAllocSize = (ULONG) FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, nVirtualBlockSize ); if (((PCHAR) AddressOfFault >= ((PCHAR) RemoteAllocBase )) && ((PCHAR) AddressOfFault < ((PCHAR) RemoteAllocBase + RemoteAllocSize ))) { // // The fault appears to have occurred in this allocation's // memory (which includes the NO_ACCESS page beyond the user // portion of the allocation). // DebugPageHeapReportAllocation( RemoteHeap, RemoteHeapNode, "allocated", AddressOfFault ); return; } if ( CheckInterrupted() ) { return; } RemoteHeapNode = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pNextAlloc ); } // // Failed to find containing allocation on busy list, so search free. // RemoteHeapNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pFreeAllocationListHead ); while ( RemoteHeapNode != NULL ) { RemoteAllocBase = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pVirtualBlock ); RemoteAllocSize = (ULONG) FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, nVirtualBlockSize ); if (((PCHAR) AddressOfFault >= ((PCHAR) RemoteAllocBase )) && ((PCHAR) AddressOfFault < ((PCHAR) RemoteAllocBase + RemoteAllocSize ))) { // // The fault appears to have occurred in this freed alloc's // memory. // DebugPageHeapReportAllocation( RemoteHeap, RemoteHeapNode, "freed", AddressOfFault ); return; } if ( CheckInterrupted() ) { return; } RemoteHeapNode = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pNextAlloc ); } // // Failed to find containing allocation in free list, but we wouldn't // have gotten this far if the debug heap did not contain the virtual // address range of the fault. So, report it as a wild pointer that // could have been freed memory. // RemoteFreeListSize = (ULONG) FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, nFreeAllocations ); dprintf( "\nPAGEHEAP: %x references memory contained in the heap %x,\n" " but does not reference an existing allocated or\n" " recently freed heap block. It is possible that\n" " the memory at %x could previously have been\n" " allocated and freed, but it must have been freed\n" " prior to the most recent %d frees.\n\n", AddressOfFault, RemoteHeap, AddressOfFault, RemoteFreeListSize ); } VOID DebugPageHeapReportAllocation( PVOID RemoteHeap, PVOID RemoteHeapNode, PCHAR NodeType, PVOID AddressOfFault ) { PVOID RemoteUserBase; ULONG RemoteUserSize; PVOID EndOfBlock; ULONG PastTheBlock; ULONG BeforeTheBlock; RemoteUserBase = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pUserAllocation ); RemoteUserSize = (ULONG) FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, nUserRequestedSize ); EndOfBlock = (PCHAR) RemoteUserBase + RemoteUserSize - 1; if ( AddressOfFault > EndOfBlock ) { PastTheBlock = (PCHAR) AddressOfFault - (PCHAR) EndOfBlock; dprintf( "\nPAGEHEAP: %x is 0x%x bytes beyond the end of %s heap block at\n" " %x of 0x%x bytes", AddressOfFault, PastTheBlock, NodeType, RemoteUserBase, RemoteUserSize ); } else if ( AddressOfFault >= RemoteUserBase ) { dprintf( "\nPAGEHEAP: %x references %s heap block at\n" " %x of 0x%x bytes", AddressOfFault, NodeType, RemoteUserBase, RemoteUserSize ); } else { BeforeTheBlock = (PCHAR) RemoteUserBase - (PCHAR) AddressOfFault; dprintf( "\nPAGEHEAP: %x is 0x%x bytes before the %s heap block at\n" " %x of 0x%x bytes", AddressOfFault, BeforeTheBlock, NodeType, RemoteUserBase, RemoteUserSize ); } #ifdef DPH_CAPTURE_STACK_TRACE { PVOID RemoteStackTraceBuffer[ DPH_MAX_STACK_LENGTH ]; ULONG RemoteStackTraceLength; PVOID RemoteStackNode; RemoteStackNode = FETCH_REMOTE_FIELD( RemoteHeapNode, DPH_HEAP_ALLOCATION, pStackTrace ); RemoteStackTraceLength = DebugPageHeapFetchRemoteStackTrace( RemoteStackNode, RemoteStackTraceBuffer ); if ( RemoteStackTraceLength ) { dprintf( ", %s by:\n\n", NodeType ); DebugPageHeapReportStackTrace( RemoteStackTraceLength, RemoteStackTraceBuffer ); } else { dprintf( "(stack trace not available)\n\n" ); } } #else // ! DPH_CAPTURE_STACK_TRACE dprintf( "\n\n" ); #endif // DPH_CAPTURE_STACK_TRACE } #ifdef DPH_CAPTURE_STACK_TRACE VOID DebugPageHeapReportStackTrace( ULONG StackTraceLength, PVOID StackTraceBuffer[] ) { CHAR SymbolName[ 256 ]; ULONG Displacement; ULONG i; for ( i = 0; i < StackTraceLength; i++ ) { GetSymbol( StackTraceBuffer[ i ], SymbolName, &Displacement ); GetSymbol( StackTraceBuffer[ i ], SymbolName, &Displacement ); dprintf( " %08x %s+0x%x\n", StackTraceBuffer[ i ], SymbolName, Displacement ); } dprintf( "\n" ); } ULONG DebugPageHeapFetchRemoteStackTrace( PVOID RemoteStackNode, PVOID StackBuffer[] ) { ULONG RemoteStackLength; if ( RemoteStackNode == NULL ) return 0; RemoteStackLength = (ULONG) FETCH_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, Length ); if ( RemoteStackLength > 0 ) { ZeroMemory( StackBuffer, RemoteStackLength * sizeof( PVOID )); ReadMemory( (PCHAR) RemoteStackNode + FIELD_OFFSET( DPH_STACK_TRACE_NODE, Address ), StackBuffer, RemoteStackLength * sizeof( PVOID ), NULL ); while (( RemoteStackLength > 0 ) && ( StackBuffer[ RemoteStackLength ] == NULL )) --RemoteStackLength; } return RemoteStackLength; } #endif // DPH_CAPTURE_STACK_TRACE #define FORMAT_TYPE_BUSY_LIST 0 #define FORMAT_TYPE_FREE_LIST 1 #define FORMAT_TYPE_VIRT_LIST 2 BOOLEAN DebugPageHeapDumpThisList( PVOID RemoteList, PCH ListName, ULONG FormatType ) { PVOID RemoteNode = RemoteList; PVOID RemoteBase; ULONG RemoteSize; PVOID RemoteUser; ULONG RemoteUsiz; ULONG RemoteFlag; PVOID RemoteValu; dprintf( "\n%s:\n", ListName ); switch ( FormatType ) { case FORMAT_TYPE_BUSY_LIST: dprintf( "UserAddr UserSize VirtAddr VirtSize UserFlag UserValu\n" ); break; case FORMAT_TYPE_FREE_LIST: dprintf( "UserAddr UserSize VirtAddr VirtSize\n" ); break; } while ( RemoteNode ) { RemoteBase = FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, pVirtualBlock ); RemoteSize = (ULONG) FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, nVirtualBlockSize ); RemoteUser = FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, pUserAllocation ); RemoteUsiz = (ULONG) FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, nUserRequestedSize ); RemoteFlag = (ULONG) FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, UserFlags ); RemoteValu = FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, UserValue ); RemoteNode = FETCH_REMOTE_FIELD( RemoteNode, DPH_HEAP_ALLOCATION, pNextAlloc ); switch ( FormatType ) { case FORMAT_TYPE_BUSY_LIST: dprintf(( RemoteFlag || RemoteValu ) ? "%08X %08X %08X %08X %08X %08X\n" : "%08X %08X %08X %08X\n", RemoteUser, RemoteUsiz, RemoteBase, RemoteSize, RemoteFlag, RemoteValu ); break; case FORMAT_TYPE_FREE_LIST: dprintf( "%08X %08X %08X %08X\n", RemoteUser, RemoteUsiz, RemoteBase, RemoteSize ); break; case FORMAT_TYPE_VIRT_LIST: dprintf( "%08X - %08X (%08X)\n", RemoteBase, (PCH)RemoteBase + RemoteSize, RemoteSize ); break; } if ( CheckInterrupted() ) { return FALSE; } } return TRUE; } BOOLEAN DebugPageHeapDumpThisHeap( PVOID RemoteHeap ) { PVOID RemoteNode; dprintf( "\nDPH Heap at %08X:\n\n", RemoteHeap ); DUMP_REMOTE_FIELD( "Signature: ", RemoteHeap, DPH_HEAP_ROOT, Signature ); DUMP_REMOTE_FIELD( "HeapFlags: ", RemoteHeap, DPH_HEAP_ROOT, HeapFlags ); DUMP_REMOTE_FIELD( "VirtualRanges: ", RemoteHeap, DPH_HEAP_ROOT, nVirtualStorageRanges ); DUMP_REMOTE_FIELD( "VirtualCommit: ", RemoteHeap, DPH_HEAP_ROOT, nVirtualStorageBytes ); DUMP_REMOTE_FIELD( "BusyAllocs: ", RemoteHeap, DPH_HEAP_ROOT, nBusyAllocations ); DUMP_REMOTE_FIELD( "BusyVirtual: ", RemoteHeap, DPH_HEAP_ROOT, nBusyAllocationBytesCommitted ); DUMP_REMOTE_FIELD( "BusyReadWrite: ", RemoteHeap, DPH_HEAP_ROOT, nBusyAllocationBytesAccessible ); DUMP_REMOTE_FIELD( "FreeAllocs: ", RemoteHeap, DPH_HEAP_ROOT, nFreeAllocations ); DUMP_REMOTE_FIELD( "FreeVirtual: ", RemoteHeap, DPH_HEAP_ROOT, nFreeAllocationBytesCommitted ); DUMP_REMOTE_FIELD( "AvailAllocs: ", RemoteHeap, DPH_HEAP_ROOT, nAvailableAllocations ); DUMP_REMOTE_FIELD( "AvailVirtual: ", RemoteHeap, DPH_HEAP_ROOT, nAvailableAllocationBytesCommitted ); DUMP_REMOTE_FIELD( "NodePools: ", RemoteHeap, DPH_HEAP_ROOT, nNodePools ); DUMP_REMOTE_FIELD( "NodeVirtual: ", RemoteHeap, DPH_HEAP_ROOT, nNodePoolBytes ); DUMP_REMOTE_FIELD( "AvailNodes: ", RemoteHeap, DPH_HEAP_ROOT, nUnusedNodes ); #ifdef DPH_CAPTURE_STACK_TRACE { PVOID RemoteStackTraceBuffer[ DPH_MAX_STACK_LENGTH ]; ULONG RemoteStackTraceLength; PVOID RemoteStackNode; DUMP_REMOTE_FIELD( "StackTraces: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceBNodes ); DUMP_REMOTE_FIELD( "StackBytes: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceBytesCommitted ); DUMP_REMOTE_FIELD( "StackAvail: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceStorage ); DUMP_REMOTE_FIELD( "StackWasted: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceBytesWasted ); DUMP_REMOTE_FIELD( "StackBDepth: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceBDepth ); DUMP_REMOTE_FIELD( "StackBCollide: ", RemoteHeap, DPH_HEAP_ROOT, nStackTraceBHashCollisions ); RemoteStackNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pStackTraceCreator ); RemoteStackTraceLength = DebugPageHeapFetchRemoteStackTrace( RemoteStackNode, RemoteStackTraceBuffer ); if ( RemoteStackTraceLength ) { dprintf( "\nHeap created by:\n\n" ); DebugPageHeapReportStackTrace( RemoteStackTraceLength, RemoteStackTraceBuffer ); } } #endif // DPH_CAPTURE_STACK_TRACE if ( ! DebugPageHeapDumpThisList( FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pVirtualStorageListHead ), "VirtualList", FORMAT_TYPE_VIRT_LIST )) { return FALSE; } if ( ! DebugPageHeapDumpThisList( FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pNodePoolListHead ), "NodePoolList", FORMAT_TYPE_VIRT_LIST )) { return FALSE; } if ( ! DebugPageHeapDumpThisList( FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pAvailableAllocationListHead ), "AvailableList", FORMAT_TYPE_VIRT_LIST )) { return FALSE; } if ( ! DebugPageHeapDumpThisList( FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pFreeAllocationListHead ), "FreeList", FORMAT_TYPE_FREE_LIST )) { return FALSE; } if ( ! DebugPageHeapDumpThisList( FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pBusyAllocationListHead ), "BusyList", FORMAT_TYPE_BUSY_LIST )) { return FALSE; } dprintf( "\n" ); return TRUE; } VOID DebugPageHeapExtensionDump( PCSTR ArgumentString ) { PVOID RemoteHeapList; PVOID RemoteHeap; PVOID RemoteHeapToDump; BOOLEAN AnyDumps = FALSE; RemoteHeapToDump = (PVOID) strtoul( ArgumentString, NULL, 16 ); RemoteHeapList = (PVOID) GetExpression( "NTDLL!RtlpDebugPageHeapListHead" ); RemoteHeap = FetchRemotePVOID( RemoteHeapList ); if (( RemoteHeap == NULL ) || ( RemoteHeapToDump == NULL ) || ( strchr( ArgumentString, '?' ))) { dprintf( "\nUsage: !dphdump \n\n" " where is the heap base address or heap handle\n\n" ); DebugPageHeapExtensionShowHeapList(); return; } while ( RemoteHeap != NULL ) { if ((((ULONG)RemoteHeapToDump & 0xFFFF0000 ) == ((ULONG)RemoteHeap & 0xFFFF0000 )) || ((ULONG)RemoteHeapToDump == 0xFFFFFFFF )) { AnyDumps = TRUE; if ( ! DebugPageHeapDumpThisHeap( RemoteHeap )) return; } if ( CheckInterrupted() ) { return; } RemoteHeap = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pNextHeapRoot ); } if ( ! AnyDumps ) { dprintf( "\nDebug page heap \"0x%x\" not found in process\n\n", RemoteHeapToDump ); DebugPageHeapExtensionShowHeapList(); } } BOOLEAN DebugPageHeapExtensionShowHeapList( VOID ) { PVOID RemoteHeapList = (PVOID)GetExpression( "NTDLL!RtlpDebugPageHeapListHead" ); PVOID RemoteHeap = FetchRemotePVOID( RemoteHeapList ); if ( RemoteHeap == NULL ) { dprintf( "\nNo debug page heaps active in process (or bad symbols)\n" ); return FALSE; } else { dprintf( "\nDebug page heaps active in process:\n\n" ); do { dprintf( "0x%08x\n", RemoteHeap ); RemoteHeap = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pNextHeapRoot ); } while ( RemoteHeap ); dprintf( "\n" ); return TRUE; } } #ifdef DPH_CAPTURE_STACK_TRACE typedef struct _LOCAL_STACK_NODE LOCAL_STACK_NODE, *PLOCAL_STACK_NODE; struct _LOCAL_STACK_NODE { PLOCAL_STACK_NODE Left; PLOCAL_STACK_NODE Right; PVOID RemoteStackNode; ULONG RemoteBusyCount; ULONG RemoteBusyBytes; }; BOOLEAN DebugPageHeapSortByCount; BOOLEAN DebugPageHeapReportStackNode( PLOCAL_STACK_NODE LocalStackNode ) { PVOID RemoteStackTraceBuffer[ DPH_MAX_STACK_LENGTH ]; ULONG RemoteStackTraceLength; RemoteStackTraceLength = DebugPageHeapFetchRemoteStackTrace( LocalStackNode->RemoteStackNode, RemoteStackTraceBuffer ); if ( RemoteStackTraceLength ) { ULONG Count = LocalStackNode->RemoteBusyCount; ULONG Bytes = LocalStackNode->RemoteBusyBytes; ULONG Average; if ( Count > 0 ) { Average = ( Bytes + ( Count / 2 )) / Count; if ( Count > 1 ) { dprintf( "%7d bytes in %d allocations (avg %d bytes/alloc):\n\n", Bytes, Count, Average ); } else { dprintf( "%7d bytes in 1 allocation:\n\n", Bytes, Average ); } DebugPageHeapReportStackTrace( RemoteStackTraceLength, RemoteStackTraceBuffer ); dprintf( "\n" ); } } else { dprintf( "Error retrieving remote stack trace\n\n" ); } return TRUE; } BOOLEAN DebugPageHeapReportStackTree( PLOCAL_STACK_NODE StackTree ) { if ( CheckInterrupted() ) return FALSE; if ( StackTree->Left ) if ( ! DebugPageHeapReportStackTree( StackTree->Left )) return FALSE; if ( StackTree->RemoteStackNode ) if ( ! DebugPageHeapReportStackNode( StackTree )) return FALSE; if ( StackTree->Right ) if ( ! DebugPageHeapReportStackTree( StackTree->Right )) return FALSE; return TRUE; } INT DebugPageHeapCompareStackNodeToValues( PLOCAL_STACK_NODE Node, PVOID RemoteStackNode, ULONG RemoteBusyCount, ULONG RemoteBusyBytes ) { INT Difference; if ( DebugPageHeapSortByCount ) { Difference = ( Node->RemoteBusyCount - RemoteBusyCount ); if ( Difference == 0 ) Difference = ( Node->RemoteBusyBytes - RemoteBusyBytes ); } else { Difference = ( Node->RemoteBusyBytes - RemoteBusyBytes ); if ( Difference == 0 ) Difference = ( Node->RemoteBusyCount - RemoteBusyCount ); } if ( Difference == 0 ) Difference = ((ULONG)Node->RemoteStackNode - (ULONG)RemoteStackNode ); return Difference; } PLOCAL_STACK_NODE DebugPageHeapFindOrInsertStackNodeInBtree( PLOCAL_STACK_NODE Node, PVOID RemoteStackNode, ULONG RemoteBusyCount, ULONG RemoteBusyBytes ) { PLOCAL_STACK_NODE NewNode; INT Difference; for (;;) { Difference = DebugPageHeapCompareStackNodeToValues( Node, RemoteStackNode, RemoteBusyCount, RemoteBusyBytes ); if ( Difference < 0 ) { // go left if ( Node->Left ) Node = Node->Left; else break; } else if ( Difference > 0 ) { // go right if ( Node->Right ) Node = Node->Right; else break; } else { // equal return Node; } } NewNode = HeapAlloc( GetProcessHeap(), 0, sizeof( LOCAL_STACK_NODE )); if ( NewNode != NULL ) { NewNode->Left = NULL; NewNode->Right = NULL; NewNode->RemoteStackNode = RemoteStackNode; NewNode->RemoteBusyCount = RemoteBusyCount; NewNode->RemoteBusyBytes = RemoteBusyBytes; if ( Difference < 0 ) Node->Left = NewNode; else Node->Right = NewNode; } return NewNode; } VOID DebugPageHeapFreeBtree( PLOCAL_STACK_NODE Tree ) { if ( Tree->Left ) DebugPageHeapFreeBtree( Tree->Left ); if ( Tree->Right ) DebugPageHeapFreeBtree( Tree->Right ); HeapFree( GetProcessHeap(), 0, Tree ); } BOOLEAN DebugPageHeapDumpThisHeapHogs( PVOID RemoteHeap ) { ULONG RemoteBusyCount; ULONG RemoteBusyBytes; PVOID RemoteStackNode; PVOID RemoteBusyNode; PLOCAL_STACK_NODE TreeRoot; PVOID LocalNode; BOOLEAN Success; dprintf( "\nDPH Heap at %08X, hogs:\n\n", RemoteHeap ); // // Walk busy list and build sorted list of stack traces // by total busy size, descending. Note that we expect // multiple references to the same stack trace in the // busy list. Would be nice to verify that busy counts // in stack trace nodes agree with number found in busy // list. // TreeRoot = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( LOCAL_STACK_NODE )); if ( TreeRoot == NULL ) return FALSE; RemoteBusyNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pBusyAllocationListHead ); while ( RemoteBusyNode ) { RemoteStackNode = FETCH_REMOTE_FIELD( RemoteBusyNode, DPH_HEAP_ALLOCATION, pStackTrace ); RemoteBusyCount = (ULONG)FETCH_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, BusyCount ); RemoteBusyBytes = (ULONG)FETCH_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, BusyBytes ); LocalNode = DebugPageHeapFindOrInsertStackNodeInBtree( TreeRoot, RemoteStackNode, RemoteBusyCount, RemoteBusyBytes ); if (( ! LocalNode ) || ( CheckInterrupted() )) { DebugPageHeapFreeBtree( TreeRoot ); return FALSE; } RemoteBusyNode = FETCH_REMOTE_FIELD( RemoteBusyNode, DPH_HEAP_ALLOCATION, pNextAlloc ); } // // Now walk btree left to right and dump traces. Empty tree root // has NULL for RemoteStackNode, so skip that case. // Success = DebugPageHeapReportStackTree( TreeRoot ); dprintf( "\n" ); DebugPageHeapFreeBtree( TreeRoot ); return Success; } BOOLEAN DebugPageHeapResetStackNode( PVOID RemoteStackNode ) { PVOID Left, Right; if ( CheckInterrupted() ) return FALSE; WRITE_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, BusyCount, NULL ); WRITE_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, BusyBytes, NULL ); Left = FETCH_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, Left ); Right = FETCH_REMOTE_FIELD( RemoteStackNode, DPH_STACK_TRACE_NODE, Right ); if ( Left ) if ( ! DebugPageHeapResetStackNode( Left )) return FALSE; if ( Right ) if ( ! DebugPageHeapResetStackNode( Right )) return FALSE; return TRUE; } BOOLEAN DebugPageHeapResetHeapHogs( PVOID RemoteHeap ) { PVOID RemoteStackNode; dprintf( "\nDPH Heap at %08X, reset hogs to zero...", RemoteHeap ); // // Walk remote stack trace btree, reset all counts to zero. // RemoteStackNode = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pStackTraceRoot ); if ( RemoteStackNode != NULL ) { if ( ! DebugPageHeapResetStackNode( RemoteStackNode )) return FALSE; } dprintf( "done\n\n" ); return TRUE; } BOOLEAN DebugPageHeapExtensionShowHogs( PCSTR ArgumentString ) { PVOID RemoteHeapList; PVOID RemoteHeap; PVOID RemoteHeapToDump; BOOLEAN AnyDumps = FALSE; BOOLEAN Success; RemoteHeapToDump = (PVOID) strtoul( ArgumentString, NULL, 16 ); RemoteHeapList = (PVOID) GetExpression( "NTDLL!RtlpDebugPageHeapListHead" ); RemoteHeap = FetchRemotePVOID( RemoteHeapList ); if (( RemoteHeap == NULL ) || ( RemoteHeapToDump == NULL ) || ( strchr( ArgumentString, '?' ))) { #ifndef DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS dprintf( "\nUsage: !dphhogs [count] [reset]\n\n" " where is the heap base address or heap handle,\n" " [count] changes sort order to count versus bytes, or\n" " [reset] will force all allocation counts to zero.\n\n" ); #endif // DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS return DebugPageHeapExtensionShowHeapList(); } if ( strstr( ArgumentString, "count" )) DebugPageHeapSortByCount = TRUE; else DebugPageHeapSortByCount = FALSE; while ( RemoteHeap != NULL ) { if ((((ULONG)RemoteHeapToDump & 0xFFFF0000 ) == ((ULONG)RemoteHeap & 0xFFFF0000 )) || ((ULONG)RemoteHeapToDump == 0xFFFFFFFF )) { Success = FALSE; #ifdef DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS if ( DebugPageHeapExtensionLockRemoteHeap( RemoteHeap )) { #endif // DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS if ( strstr( ArgumentString, "reset" )) { Success = DebugPageHeapResetHeapHogs( RemoteHeap ); } else { Success = DebugPageHeapDumpThisHeapHogs( RemoteHeap ); } #ifdef DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS DebugPageHeapExtensionUnlockRemoteHeap( RemoteHeap ); } #endif // DPH_EXTENSION_BUILT_AS_SEPARATE_PROCESS if ( ! Success ) { return FALSE; } else { AnyDumps = TRUE; } } if ( CheckInterrupted() ) { return FALSE; } RemoteHeap = FETCH_REMOTE_FIELD( RemoteHeap, DPH_HEAP_ROOT, pNextHeapRoot ); } if ( ! AnyDumps ) { dprintf( "\nDebug page heap \"0x%x\" not found in process\n\n", RemoteHeapToDump ); DebugPageHeapExtensionShowHeapList(); return FALSE; } return TRUE; } #endif // DPH_CAPTURE_STACK_TRACE VOID DebugPageHeapExtensionHogs( PCSTR ArgumentString ) { #ifdef DPH_CAPTURE_STACK_TRACE DebugPageHeapExtensionShowHogs( ArgumentString ); #else dprintf( "\n!dphhogs only available on x86 checked builds\n\n" ); #endif }