Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1158 lines
31 KiB

#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 <address>\n\n"
" where <address> 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 <address>\n\n"
" where <address> 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 <address> [count] [reset]\n\n"
" where <address> 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
}