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.
1149 lines
31 KiB
1149 lines
31 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
heappagx.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the page heap manager debug extensions.
|
|
|
|
Author:
|
|
|
|
Tom McGuire (TomMcg) 06-Jan-1995
|
|
Silviu Calinoiu (SilviuC) 22-Feb-2000
|
|
|
|
--*/
|
|
|
|
|
|
#define DEBUG_PAGE_HEAP 1
|
|
|
|
#include "precomp.h"
|
|
#include "heap.h"
|
|
|
|
|
|
|
|
__inline
|
|
BOOLEAN
|
|
CheckInterrupted(
|
|
VOID
|
|
)
|
|
{
|
|
if (CheckControlC()) {
|
|
dprintf ("\n...interrupted...\n" );
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define CHECK_FOR_CTRL_C() \
|
|
if (CheckControlC()) { \
|
|
dprintf ("\n...interrupted...\n" ); \
|
|
return; \
|
|
}
|
|
|
|
|
|
__inline
|
|
ULONG64
|
|
Read_PVOID (
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ULONG64 RemoteValue = 0;
|
|
ReadPointer( Address, &RemoteValue);
|
|
return RemoteValue;
|
|
}
|
|
|
|
__inline
|
|
ULONG
|
|
Read_ULONG(
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ULONG RemoteValue = 0;
|
|
ReadMemory( Address, &RemoteValue, sizeof( ULONG ), NULL );
|
|
return RemoteValue;
|
|
}
|
|
|
|
ULONG
|
|
ReturnFieldOffset(
|
|
PCHAR TypeName,
|
|
PCHAR FieldName)
|
|
{
|
|
ULONG off=0;
|
|
ULONG Result;
|
|
|
|
Result = GetFieldOffset(TypeName, FieldName, &off);
|
|
|
|
if (Result != 0) {
|
|
|
|
dprintf ("Error: Failed to get the offset for `%s!%s'.\n",
|
|
TypeName,
|
|
FieldName);
|
|
}
|
|
|
|
return off;
|
|
}
|
|
|
|
VOID
|
|
PageHeapLocateFaultAllocation(
|
|
ULONG64 RemoteHeap,
|
|
ULONG64 AddressOfFault
|
|
);
|
|
|
|
VOID
|
|
PageHeapReportAllocation(
|
|
ULONG64 RemoteHeap,
|
|
ULONG64 RemoteHeapNode,
|
|
PCHAR NodeType,
|
|
ULONG64 AddressOfFault
|
|
);
|
|
|
|
BOOLEAN
|
|
PageHeapExtensionShowHeapList(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PageHeapPrintFlagsMeaning (
|
|
ULONG64 HeapFlags
|
|
);
|
|
|
|
VOID
|
|
TraceDatabaseDump (
|
|
PCSTR Args,
|
|
BOOLEAN SortByCountField
|
|
);
|
|
|
|
VOID
|
|
TraceDatabaseBlockDump (
|
|
ULONG64 Address
|
|
);
|
|
|
|
VOID
|
|
FaultInjectionTracesDump (
|
|
PCSTR Args
|
|
);
|
|
|
|
#define PAGE_HEAP_HELP_TEXT " \n"
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
PageHeapHelp (
|
|
VOID
|
|
)
|
|
{
|
|
|
|
dprintf ("!heap -p Dump all page heaps. \n"
|
|
"!heap -p -h ADDR Detailed dump of page heap at ADDR. \n"
|
|
"!heap -p -a ADDR Figure out what heap block is at ADDR. \n"
|
|
"!heap -p -fi [N] Dump last N fault injection traces.\n"
|
|
" \n"
|
|
" +-----+---------------+--+ \n"
|
|
" | | | | Light page heap allocated block \n"
|
|
" +-----+---------------+--+ \n"
|
|
" ^ ^ ^ \n"
|
|
" | | 8 suffix bytes filled with 0xA0 \n"
|
|
" | user allocation (filled with E0 if zeroing not requested) \n"
|
|
" block header (starts with 0xABCDAAAA and ends with 0xDCBAAAAA).\n"
|
|
" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n"
|
|
" a `dds' on the StackTrace field gives the stacktrace of allocation. \n"
|
|
" \n"
|
|
" +-----+---------------+--+ \n"
|
|
" | | | | Light page heap freed block \n"
|
|
" +-----+---------------+--+ \n"
|
|
" ^ ^ ^ \n"
|
|
" | | 8 suffix bytes filled with 0xA0 \n"
|
|
" | user allocation (filled with F0 bytes) \n"
|
|
" block header (starts with 0xABCDAAA9 and ends with 0xDCBAAA9). \n"
|
|
" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n"
|
|
" a `dds' on the StackTrace field gives the stacktrace of allocation. \n"
|
|
" \n"
|
|
" +-----+---------+--+------ \n"
|
|
" | | | | ... N/A page Full page heap \n"
|
|
" +-----+---------+--+------ allocated block \n"
|
|
" ^ ^ ^ \n"
|
|
" | | 0-7 suffix bytes filled with 0xD0 \n"
|
|
" | user allocation (filled with C0 if zeroing not requested) \n"
|
|
" block header (starts with 0xABCDBBBB and ends with 0xDCBABBBB).\n"
|
|
" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n"
|
|
" a `dds' on the StackTrace field gives the stacktrace of allocation. \n"
|
|
" \n"
|
|
" +-----+---------+--+------ \n"
|
|
" | | | | ... N/A page Full page heap \n"
|
|
" +-----+---------+--+------ freed block \n"
|
|
" ^ ^ ^ \n"
|
|
" | | 0-7 suffix bytes filled with 0xD0 \n"
|
|
" | user allocation (filled with F0 bytes) \n"
|
|
" block header (starts with 0xABCDBBA and ends with 0xDCBABBBA).\n"
|
|
" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n"
|
|
" a `dds' on the StackTrace field gives the stacktrace of allocation. \n"
|
|
" \n"
|
|
"Note. The `-t', `-tc', `-ts' options are no longer supported. \n"
|
|
"Please use the UMDH tool provided with the debugger package \n"
|
|
"for equivalent functionality. \n"
|
|
" \n");
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapUseUmdh (
|
|
VOID
|
|
)
|
|
{
|
|
dprintf (" \n"
|
|
"This page heap debug extension option is no longer supported. \n"
|
|
"Please use the UMDH tool provided with the debugger package \n"
|
|
"for equivalent functionality. \n"
|
|
" \n");
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapExtensionFind(
|
|
PCSTR ArgumentString
|
|
)
|
|
{
|
|
ULONG64 RemoteHeapList;
|
|
ULONG64 RemoteHeap;
|
|
ULONG64 RemoteVirtualNode;
|
|
ULONG64 RemoteVirtualBase;
|
|
ULONG64 RemoteVirtualSize;
|
|
ULONG64 AddressOfFault;
|
|
BOOL GetResult;
|
|
ULONG Result;
|
|
ULONG Offset;
|
|
ULONG64 NextNode;
|
|
|
|
GetResult = GetExpressionEx (ArgumentString,
|
|
&AddressOfFault,
|
|
&ArgumentString);
|
|
|
|
if (GetResult == FALSE) {
|
|
dprintf ("\nFailed to convert `%s' to an address.\n",
|
|
ArgumentString);
|
|
return;
|
|
}
|
|
|
|
RemoteHeapList = (ULONG64) GetExpression ("NTDLL!RtlpDphPageHeapList");
|
|
RemoteHeap = Read_PVOID (RemoteHeapList);
|
|
|
|
if (RemoteHeap == 0) {
|
|
|
|
dprintf ("\nNo page heaps active in process (or bad symbols)\n\n");
|
|
AddressOfFault = 0;
|
|
}
|
|
|
|
if (( AddressOfFault == 0 ) || ( strchr( ArgumentString, '?' ))) {
|
|
|
|
PageHeapHelp();
|
|
return;
|
|
}
|
|
|
|
Result = GetFieldOffset ("NTDLL!_DPH_HEAP_ROOT",
|
|
"NextHeap",
|
|
&Offset);
|
|
|
|
if (Result != 0) {
|
|
dprintf ("\nFailed to get offset of `NextHeap' field.\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Find the heap that contains the range of virtual addresses that
|
|
// contain the AddressOfFault.
|
|
//
|
|
|
|
while (RemoteHeap != RemoteHeapList) {
|
|
|
|
//
|
|
// The heap header contains a linked list of virtual memory
|
|
// allocations.
|
|
//
|
|
|
|
InitTypeRead (RemoteHeap - Offset, ntdll!_DPH_HEAP_ROOT);
|
|
|
|
RemoteVirtualNode = ReadField (pVirtualStorageListHead);
|
|
|
|
while (RemoteVirtualNode != 0) {
|
|
|
|
InitTypeRead (RemoteVirtualNode, ntdll!_DPH_HEAP_BLOCK);
|
|
|
|
RemoteVirtualBase = ReadField (pVirtualBlock);
|
|
RemoteVirtualSize = ReadField (nVirtualBlockSize);
|
|
|
|
NextNode = ReadField (pNextAlloc);
|
|
|
|
if (RemoteVirtualBase == 0 || RemoteVirtualSize == 0) {
|
|
|
|
dprintf ("\nError: Heap 0x%p appears to have an invalid\n"
|
|
" virtual allocation list\n\n",
|
|
RemoteHeap);
|
|
}
|
|
|
|
if ((AddressOfFault >= RemoteVirtualBase) &&
|
|
(AddressOfFault <= 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.
|
|
//
|
|
|
|
PageHeapLocateFaultAllocation (RemoteHeap - Offset,
|
|
AddressOfFault);
|
|
return;
|
|
}
|
|
|
|
CHECK_FOR_CTRL_C();
|
|
|
|
RemoteVirtualNode = NextNode;
|
|
}
|
|
|
|
CHECK_FOR_CTRL_C();
|
|
|
|
//
|
|
// Not found in this heap. Continue to search in the next heap.
|
|
//
|
|
|
|
RemoteHeap = Read_PVOID (RemoteHeap);
|
|
|
|
}
|
|
|
|
//
|
|
// If we are here we did not find a virtual range.
|
|
//
|
|
|
|
dprintf ("\nCould not find a page heap containing virtual address %p\n",
|
|
AddressOfFault);
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapLocateFaultAllocation(
|
|
ULONG64 RemoteHeap,
|
|
ULONG64 AddressOfFault
|
|
)
|
|
{
|
|
ULONG64 ClosestHeapNode;
|
|
ULONG64 ClosestDifference;
|
|
ULONG64 RemoteHeapNode;
|
|
ULONG64 RemoteAllocBase;
|
|
ULONG64 RemoteAllocSize;
|
|
ULONG RemoteFreeListSize;
|
|
ULONG64 NextNode;
|
|
|
|
ClosestHeapNode = 0;
|
|
|
|
//
|
|
// First search the busy list for the containing allocation, if any.
|
|
//
|
|
|
|
InitTypeRead (RemoteHeap, NTDLL!_DPH_HEAP_ROOT);
|
|
|
|
RemoteHeapNode = ReadField (pBusyAllocationListHead);
|
|
|
|
while (RemoteHeapNode != 0) {
|
|
|
|
InitTypeRead (RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK);
|
|
|
|
RemoteAllocBase = ReadField (pVirtualBlock);
|
|
RemoteAllocSize = ReadField (nVirtualBlockSize);
|
|
|
|
NextNode = ReadField (pNextAlloc);
|
|
|
|
if ((AddressOfFault >= RemoteAllocBase) &&
|
|
(AddressOfFault < 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).
|
|
//
|
|
|
|
PageHeapReportAllocation (RemoteHeap,
|
|
RemoteHeapNode,
|
|
"allocated",
|
|
AddressOfFault );
|
|
|
|
return;
|
|
}
|
|
|
|
CHECK_FOR_CTRL_C();
|
|
|
|
RemoteHeapNode = NextNode;
|
|
}
|
|
|
|
//
|
|
// Failed to find containing allocation on busy list, so search free.
|
|
//
|
|
|
|
InitTypeRead (RemoteHeap, NTDLL!_DPH_HEAP_ROOT);
|
|
|
|
RemoteHeapNode = ReadField (pFreeAllocationListHead);
|
|
|
|
while (RemoteHeapNode != 0) {
|
|
|
|
InitTypeRead (RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK);
|
|
|
|
RemoteAllocBase = ReadField (pVirtualBlock);
|
|
RemoteAllocSize = ReadField (nVirtualBlockSize);
|
|
|
|
if ((AddressOfFault >= RemoteAllocBase) &&
|
|
(AddressOfFault < RemoteAllocBase + RemoteAllocSize)) {
|
|
|
|
//
|
|
// The fault appears to have occurred in this freed alloc's
|
|
// memory.
|
|
//
|
|
|
|
PageHeapReportAllocation (RemoteHeap,
|
|
RemoteHeapNode,
|
|
"freed",
|
|
AddressOfFault );
|
|
|
|
return;
|
|
}
|
|
|
|
CHECK_FOR_CTRL_C();
|
|
|
|
RemoteHeapNode = ReadField (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.
|
|
//
|
|
|
|
InitTypeRead (RemoteHeap, NTDLL!_DPH_HEAP_ROOT);
|
|
|
|
RemoteFreeListSize = (ULONG) ReadField (nFreeAllocations);
|
|
|
|
dprintf( "\nPAGEHEAP: %p references memory contained in the heap %p,\n"
|
|
" but does not reference an existing allocated or\n"
|
|
" recently freed heap block. It is possible that\n"
|
|
" the memory at %p 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
|
|
PageHeapReportAllocation(
|
|
ULONG64 RemoteHeap,
|
|
ULONG64 RemoteHeapNode,
|
|
PCHAR NodeType,
|
|
ULONG64 AddressOfFault
|
|
)
|
|
{
|
|
ULONG64 RemoteUserBase;
|
|
ULONG64 RemoteUserSize;
|
|
ULONG64 EndOfBlock;
|
|
ULONG64 PastTheBlock;
|
|
ULONG64 BeforeTheBlock;
|
|
|
|
InitTypeRead (RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK);
|
|
RemoteUserBase = ReadField (pUserAllocation);
|
|
RemoteUserSize = ReadField (nUserRequestedSize);
|
|
|
|
EndOfBlock = RemoteUserBase + RemoteUserSize - 1;
|
|
|
|
if (AddressOfFault > EndOfBlock) {
|
|
|
|
PastTheBlock = AddressOfFault - EndOfBlock;
|
|
|
|
dprintf( "\nPAGEHEAP: %p is %p bytes beyond the end of %s heap block at\n"
|
|
" %p of 0x%x bytes",
|
|
AddressOfFault,
|
|
PastTheBlock,
|
|
NodeType,
|
|
RemoteUserBase,
|
|
RemoteUserSize
|
|
);
|
|
|
|
}
|
|
else if (AddressOfFault >= RemoteUserBase) {
|
|
|
|
dprintf( "\nPAGEHEAP: %p references %s heap block at\n"
|
|
" %p of 0x%x bytes",
|
|
AddressOfFault,
|
|
NodeType,
|
|
RemoteUserBase,
|
|
RemoteUserSize
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BeforeTheBlock = (PCHAR) RemoteUserBase - (PCHAR) AddressOfFault;
|
|
|
|
dprintf( "\nPAGEHEAP: %p is %p bytes before the %s heap block at\n"
|
|
" %p of 0x%x bytes",
|
|
AddressOfFault,
|
|
BeforeTheBlock,
|
|
NodeType,
|
|
RemoteUserBase,
|
|
RemoteUserSize
|
|
);
|
|
|
|
}
|
|
|
|
{
|
|
ULONG64 Trace;
|
|
|
|
Trace = ReadField (StackTrace);
|
|
dprintf ("\n\n");
|
|
TraceDatabaseBlockDump (Trace);
|
|
}
|
|
}
|
|
|
|
|
|
#define FORMAT_TYPE_BUSY_LIST 0
|
|
#define FORMAT_TYPE_FREE_LIST 1
|
|
#define FORMAT_TYPE_VIRT_LIST 2
|
|
|
|
|
|
BOOLEAN
|
|
PageHeapDumpThisList(
|
|
ULONG64 RemoteList,
|
|
PCH ListName,
|
|
ULONG FormatType
|
|
)
|
|
{
|
|
ULONG64 RemoteNode;
|
|
ULONG64 RemoteBase;
|
|
ULONG64 RemoteSize;
|
|
ULONG64 RemoteUser;
|
|
ULONG64 RemoteUsiz;
|
|
ULONG64 RemoteFlag;
|
|
ULONG64 RemoteValu;
|
|
|
|
ULONG64 NextNode;
|
|
|
|
RemoteNode = RemoteList;
|
|
dprintf( "\n%s: \n", ListName);
|
|
|
|
switch (FormatType) {
|
|
|
|
case FORMAT_TYPE_BUSY_LIST:
|
|
|
|
dprintf( "Descriptor UserAddr UserSize VirtAddr VirtSize UserFlag UserValu\n" );
|
|
break;
|
|
|
|
case FORMAT_TYPE_FREE_LIST:
|
|
|
|
dprintf( "Descriptor UserAddr UserSize VirtAddr VirtSize\n" );
|
|
break;
|
|
}
|
|
|
|
while (RemoteNode) {
|
|
|
|
InitTypeRead (RemoteNode, NTDLL!_DPH_HEAP_BLOCK);
|
|
|
|
dprintf ("%p : ", RemoteNode);
|
|
|
|
RemoteBase = ReadField (pVirtualBlock );
|
|
RemoteSize = ReadField (nVirtualBlockSize );
|
|
RemoteUser = ReadField (pUserAllocation );
|
|
RemoteUsiz = ReadField (nUserRequestedSize );
|
|
RemoteFlag = ReadField (UserFlags );
|
|
RemoteValu = ReadField (UserValue );
|
|
|
|
NextNode = ReadField (pNextAlloc);
|
|
|
|
switch (FormatType) {
|
|
|
|
case FORMAT_TYPE_BUSY_LIST:
|
|
|
|
if (RemoteFlag || RemoteValu) {
|
|
|
|
dprintf ("%p %p %p %p %p %p\n",
|
|
RemoteUser,
|
|
RemoteUsiz,
|
|
RemoteBase,
|
|
RemoteSize,
|
|
RemoteFlag,
|
|
RemoteValu);
|
|
}
|
|
else {
|
|
|
|
dprintf ("%p %p %p %p\n",
|
|
RemoteUser,
|
|
RemoteUsiz,
|
|
RemoteBase,
|
|
RemoteSize);
|
|
}
|
|
|
|
break;
|
|
|
|
case FORMAT_TYPE_FREE_LIST:
|
|
|
|
dprintf ("%p %p %p %p\n",
|
|
RemoteUser,
|
|
RemoteUsiz,
|
|
RemoteBase,
|
|
RemoteSize);
|
|
|
|
break;
|
|
|
|
case FORMAT_TYPE_VIRT_LIST:
|
|
|
|
dprintf( "%p - %p (%p)\n",
|
|
RemoteBase,
|
|
(PCH)RemoteBase + RemoteSize,
|
|
RemoteSize);
|
|
|
|
break;
|
|
}
|
|
|
|
if (CheckInterrupted()) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Move on to the next node in the list.
|
|
//
|
|
|
|
RemoteNode = NextNode;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PageHeapDumpThisHeap(
|
|
ULONG64 RemoteHeap
|
|
)
|
|
{
|
|
ULONG64 RemoteNode;
|
|
ULONG64 VirtualList;
|
|
ULONG64 PoolList;
|
|
ULONG64 AvailableList;
|
|
ULONG64 FreeList;
|
|
ULONG64 BusyList;
|
|
|
|
InitTypeRead(RemoteHeap, NTDLL!_DPH_HEAP_ROOT);
|
|
|
|
dprintf ("\nPage heap @ %I64X with associated light heap @ %I64X :\n\n",
|
|
RemoteHeap,
|
|
ReadField (NormalHeap));
|
|
|
|
dprintf ("Signature: %I64X\n", ReadField(Signature));
|
|
dprintf ("HeapFlags: %I64X\n", ReadField(HeapFlags));
|
|
dprintf ("ExtraFlags: %I64X\n", ReadField(ExtraFlags));
|
|
dprintf ("NormalHeap: %I64X\n", ReadField(NormalHeap));
|
|
dprintf ("VirtualRanges: %I64X\n", ReadField(nVirtualStorageRanges));
|
|
dprintf ("VirtualCommit: %I64X\n", ReadField(nVirtualStorageBytes));
|
|
dprintf ("BusyAllocs: %I64X\n", ReadField(nBusyAllocations));
|
|
dprintf ("BusyVirtual: %I64X\n", ReadField(nBusyAllocationBytesCommitted));
|
|
dprintf ("BusyReadWrite: %I64X\n", ReadField(nBusyAllocationBytesAccessible));
|
|
dprintf ("FreeAllocs: %I64X\n", ReadField(nFreeAllocations));
|
|
dprintf ("FreeVirtual: %I64X\n", ReadField(nFreeAllocationBytesCommitted));
|
|
dprintf ("AvailAllocs: %I64X\n", ReadField(nAvailableAllocations));
|
|
dprintf ("AvailVirtual: %I64X\n", ReadField(nAvailableAllocationBytesCommitted));
|
|
dprintf ("NodePools: %I64X\n", ReadField(nNodePools));
|
|
dprintf ("NodeVirtual: %I64X\n", ReadField(nNodePoolBytes));
|
|
dprintf ("AvailNodes: %I64X\n", ReadField(nUnusedNodes));
|
|
dprintf ("Seed: %I64X\n", ReadField(Seed));
|
|
|
|
VirtualList = ReadField (pVirtualStorageListHead);
|
|
PoolList = ReadField (pNodePoolListHead);
|
|
AvailableList = ReadField (pAvailableAllocationListHead);
|
|
FreeList = ReadField (pFreeAllocationListHead);
|
|
BusyList = ReadField (pBusyAllocationListHead);
|
|
|
|
{
|
|
ULONG64 Trace;
|
|
|
|
dprintf ("\n");
|
|
Trace = ReadField (CreateStackTrace);
|
|
TraceDatabaseBlockDump (Trace);
|
|
}
|
|
|
|
if (! PageHeapDumpThisList(VirtualList,
|
|
"VirtualList",
|
|
FORMAT_TYPE_VIRT_LIST )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! PageHeapDumpThisList(PoolList,
|
|
"NodePoolList",
|
|
FORMAT_TYPE_VIRT_LIST )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! PageHeapDumpThisList(AvailableList,
|
|
"AvailableList",
|
|
FORMAT_TYPE_VIRT_LIST )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! PageHeapDumpThisList(FreeList,
|
|
"FreeList",
|
|
FORMAT_TYPE_FREE_LIST )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! PageHeapDumpThisList(BusyList,
|
|
"BusyList",
|
|
FORMAT_TYPE_BUSY_LIST )) {
|
|
return FALSE;
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapExtensionDump(
|
|
PCSTR ArgumentString
|
|
)
|
|
{
|
|
ULONG64 RemoteHeapList;
|
|
ULONG64 RemoteHeap;
|
|
ULONG64 RemoteHeapToDump;
|
|
BOOLEAN AnyDumps = FALSE;
|
|
BOOL GetResult;
|
|
ULONG Result;
|
|
ULONG Offset;
|
|
|
|
GetResult = GetExpressionEx (ArgumentString,
|
|
&RemoteHeapToDump,
|
|
&ArgumentString);
|
|
|
|
if (GetResult == FALSE) {
|
|
dprintf ("\nFailed to convert `%s' to an address.\n",
|
|
ArgumentString);
|
|
return;
|
|
}
|
|
|
|
RemoteHeapList = (ULONG64) GetExpression( "NTDLL!RtlpDphPageHeapList" );
|
|
RemoteHeap = Read_PVOID( RemoteHeapList );
|
|
|
|
if (( RemoteHeap == 0 ) ||
|
|
( RemoteHeapToDump == 0 ) ||
|
|
( strchr( ArgumentString, '?' ))) {
|
|
|
|
PageHeapHelp();
|
|
PageHeapExtensionShowHeapList();
|
|
return;
|
|
}
|
|
|
|
Result = GetFieldOffset ("NTDLL!_DPH_HEAP_ROOT",
|
|
"NextHeap",
|
|
&Offset);
|
|
|
|
if (Result != 0) {
|
|
dprintf ("\nFailed to get offset of `NextHeap' field.\n");
|
|
return;
|
|
}
|
|
|
|
while (RemoteHeap != RemoteHeapList) {
|
|
|
|
ULONG64 HeapAddress;
|
|
|
|
HeapAddress = RemoteHeap - Offset;
|
|
|
|
if ((((LONG_PTR)RemoteHeapToDump & 0xFFFF0000 ) == ((LONG_PTR)HeapAddress & 0xFFFF0000 )) ||
|
|
((LONG_PTR)RemoteHeapToDump == -1 )) {
|
|
|
|
AnyDumps = TRUE;
|
|
|
|
if (! PageHeapDumpThisHeap(HeapAddress))
|
|
return;
|
|
|
|
}
|
|
|
|
if (CheckInterrupted()) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Move forward in the list of Flink fields that chains
|
|
// all heaps.
|
|
//
|
|
|
|
RemoteHeap = Read_PVOID (RemoteHeap);
|
|
}
|
|
|
|
if (! AnyDumps) {
|
|
dprintf( "\nPage heap \"0x%p\" not found in process\n\n", RemoteHeapToDump );
|
|
PageHeapExtensionShowHeapList();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapDumpFaultInjectionSettings (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 Address;
|
|
ULONG Probability;
|
|
|
|
Probability = Read_ULONG (GetExpression ("NTDLL!RtlpDphFaultProbability"));
|
|
|
|
if (Probability) {
|
|
dprintf ("---------------------------------------------------------------\n");
|
|
dprintf ("Page heap fault injection is enabled! \n");
|
|
dprintf ("Heap allocations will fail randomly with probability %u/10000. \n", Probability);
|
|
dprintf ("---------------------------------------------------------------\n\n");
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PageHeapExtensionShowHeapList(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 RemoteHeapList;
|
|
ULONG64 RemoteHeap;
|
|
ULONG64 NormalHeap;
|
|
ULONG64 HeapFlags;
|
|
ULONG Result;
|
|
ULONG Offset;
|
|
|
|
PageHeapDumpFaultInjectionSettings ();
|
|
|
|
RemoteHeapList = (ULONG64)GetExpression( "NTDLL!RtlpDphPageHeapList" );
|
|
RemoteHeap = Read_PVOID( RemoteHeapList );
|
|
|
|
if (RemoteHeap == 0) {
|
|
|
|
dprintf( "\nNo page heaps active in process (or bad symbols)\n" );
|
|
return FALSE;
|
|
}
|
|
else {
|
|
|
|
Result = GetFieldOffset ("NTDLL!_DPH_HEAP_ROOT",
|
|
"NextHeap",
|
|
&Offset);
|
|
|
|
if (Result != 0) {
|
|
dprintf ("\nFailed to get offset of `NextHeap' field.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
dprintf ("Page heaps active in the process "
|
|
"(format: pageheap, lightheap, flags): \n\n");
|
|
|
|
while (RemoteHeap && (RemoteHeap != RemoteHeapList)) {
|
|
|
|
InitTypeRead (RemoteHeap - Offset, NTDLL!_DPH_HEAP_ROOT);
|
|
|
|
NormalHeap = ReadField (NormalHeap);
|
|
HeapFlags = ReadField (ExtraFlags);
|
|
|
|
dprintf (" %p , %p , %I64X (",
|
|
RemoteHeap - Offset,
|
|
NormalHeap,
|
|
HeapFlags);
|
|
|
|
PageHeapPrintFlagsMeaning (HeapFlags);
|
|
|
|
dprintf (")\n");
|
|
|
|
//
|
|
// Move forward in the list of Flink fields that chains
|
|
// all heaps.
|
|
//
|
|
|
|
RemoteHeap = Read_PVOID (RemoteHeap);
|
|
if (CheckInterrupted()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PageHeapIsActive(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 RemoteHeapList = (ULONG64)GetExpression ("NTDLL!RtlpDphPageHeapList" );
|
|
ULONG64 RemoteHeap = Read_PVOID( RemoteHeapList );
|
|
|
|
if (RemoteHeap == 0) {
|
|
|
|
return FALSE;
|
|
}
|
|
else {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PageHeapPrintFlagsMeaning (
|
|
ULONG64 HeapFlags
|
|
)
|
|
{
|
|
if ((HeapFlags & PAGE_HEAP_ENABLE_PAGE_HEAP)) {
|
|
dprintf ("pageheap ");
|
|
}
|
|
else {
|
|
dprintf ("lightheap ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_COLLECT_STACK_TRACES)) {
|
|
dprintf ("traces ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_NO_UMDH_SUPPORT)) {
|
|
dprintf ("no_umdh ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_CATCH_BACKWARD_OVERRUNS)) {
|
|
dprintf ("backwards ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_UNALIGNED_ALLOCATIONS)) {
|
|
dprintf ("unaligned ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_SIZE_RANGE)) {
|
|
dprintf ("size_range ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_DLL_RANGE)) {
|
|
dprintf ("dll_range ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_RANDOM_DECISION)) {
|
|
dprintf ("random ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_DLL_NAMES)) {
|
|
dprintf ("dlls ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_FAULT_INJECTION)) {
|
|
dprintf ("faults ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_CHECK_NO_SERIALIZE_ACCESS)) {
|
|
dprintf ("no_serialize ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_NO_LOCK_CHECKS)) {
|
|
dprintf ("no_lock_checks ");
|
|
}
|
|
|
|
if ((HeapFlags & PAGE_HEAP_USE_READONLY)) {
|
|
dprintf ("readonly ");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PageHeapExtension(
|
|
PCSTR ArgumentString
|
|
)
|
|
{
|
|
PCSTR Current;
|
|
|
|
//
|
|
// Is help requested?
|
|
//
|
|
|
|
if (strstr (ArgumentString, "?") != NULL) {
|
|
|
|
PageHeapHelp ();
|
|
}
|
|
|
|
//
|
|
// If page heap not active then return immediately.
|
|
//
|
|
|
|
if (! PageHeapIsActive()) {
|
|
dprintf ("Page heap is not active for this process. \n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Parse command line
|
|
//
|
|
|
|
if ((Current = strstr (ArgumentString, "-h")) != NULL) {
|
|
|
|
PageHeapExtensionDump (Current + strlen("-h"));
|
|
}
|
|
else if ((Current = strstr (ArgumentString, "-a")) != NULL) {
|
|
|
|
PageHeapExtensionFind (Current + strlen("-a"));
|
|
}
|
|
else if ((Current = strstr (ArgumentString, "-tc")) != NULL) {
|
|
|
|
PageHeapUseUmdh ();
|
|
}
|
|
else if ((Current = strstr (ArgumentString, "-ts")) != NULL) {
|
|
|
|
PageHeapUseUmdh ();
|
|
}
|
|
else if ((Current = strstr (ArgumentString, "-t")) != NULL) {
|
|
|
|
PageHeapUseUmdh ();
|
|
}
|
|
else if ((Current = strstr (ArgumentString, "-fi")) != NULL) {
|
|
|
|
FaultInjectionTracesDump (Current + strlen("-fi"));
|
|
}
|
|
else {
|
|
PageHeapExtensionShowHeapList ();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////// Trace database
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
VOID
|
|
TraceDatabaseBlockDump (
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ULONG64 TraceAddress;
|
|
ULONG64 ReturnAddress;
|
|
CHAR SymbolName[ 1024 ];
|
|
ULONG64 Displacement;
|
|
ULONG I;
|
|
ULONG64 BlockSize;
|
|
ULONG PvoidSize;
|
|
|
|
if (Address == 0) {
|
|
dprintf (" No trace\n");
|
|
return;
|
|
}
|
|
|
|
PvoidSize = IsPtr64() ? 8 : 4;
|
|
|
|
InitTypeRead (Address, NTDLL!_RTL_STACK_TRACE_ENTRY);
|
|
|
|
BlockSize = ReadField (Depth);
|
|
|
|
dprintf (" Trace @ %I64X \n",
|
|
Address);
|
|
|
|
for (I = 0; I < BlockSize; I += 1) {
|
|
|
|
TraceAddress = ReadField (BackTrace);
|
|
|
|
ReturnAddress = Read_PVOID (TraceAddress + I * PvoidSize);
|
|
|
|
GetSymbol (ReturnAddress, SymbolName, &Displacement);
|
|
|
|
dprintf (" %p %s+0x%p\n",
|
|
ReturnAddress,
|
|
SymbolName,
|
|
Displacement);
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////// Fault injection traces
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
VOID
|
|
FaultInjectionTracesDump (
|
|
PCSTR Args
|
|
)
|
|
{
|
|
ULONG64 TracesToDisplay = 0;
|
|
ULONG64 TraceAddress;
|
|
ULONG64 IndexAddress;
|
|
ULONG Index;
|
|
ULONG I;
|
|
const ULONG NO_OF_FAULT_INJECTION_TRACES = 128;
|
|
ULONG PvoidSize;
|
|
ULONG64 TraceBlock;
|
|
ULONG TracesFound = 0;
|
|
BOOLEAN Interrupted = FALSE;
|
|
ULONG64 FlagsAddress;
|
|
ULONG Flags;
|
|
|
|
if (Args) {
|
|
TracesToDisplay = GetExpression(Args);
|
|
|
|
if (TracesToDisplay == 0) {
|
|
TracesToDisplay = 4;
|
|
}
|
|
}
|
|
|
|
PvoidSize = IsPtr64() ? 8 : 4;
|
|
|
|
TraceAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphFaultStacks");
|
|
IndexAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphFaultStacksIndex");
|
|
FlagsAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphGlobalFlags");
|
|
|
|
Flags = Read_ULONG (FlagsAddress);
|
|
|
|
if (! (Flags & PAGE_HEAP_USE_FAULT_INJECTION)) {
|
|
|
|
dprintf ("Fault injection is not enabled for this process. \n");
|
|
dprintf ("Use `pageheap /enable PROGRAM /fault RATE' to enable it. \n");
|
|
return;
|
|
}
|
|
|
|
Index = Read_ULONG (IndexAddress);
|
|
|
|
for (I = 0; I < NO_OF_FAULT_INJECTION_TRACES; I += 1) {
|
|
|
|
Index -= 1;
|
|
Index &= (NO_OF_FAULT_INJECTION_TRACES - 1);
|
|
|
|
TraceBlock = Read_PVOID (TraceAddress + Index * PvoidSize);
|
|
|
|
if (TraceBlock != 0) {
|
|
TracesFound += 1;
|
|
|
|
dprintf ("\n");
|
|
TraceDatabaseBlockDump (TraceBlock);
|
|
|
|
if (TracesFound >= TracesToDisplay) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CheckControlC()) {
|
|
Interrupted = TRUE;
|
|
dprintf ("Interrupted \n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Interrupted == FALSE && TracesFound == 0) {
|
|
|
|
dprintf ("No fault injection traces found. \n");
|
|
}
|
|
}
|
|
|
|
|