mirror of https://github.com/lianthony/NT4.0
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.
2131 lines
78 KiB
2131 lines
78 KiB
#if defined(TARGET_i386)
|
|
#define STACK_TRACE_DATABASE_SUPPORT 1
|
|
#elif defined(TARGET_MIPS) || defined(TARGET_ALPHA) || defined(TARGET_PPC)
|
|
#define STACK_TRACE_DATABASE_SUPPORT 0
|
|
#elif i386
|
|
#define STACK_TRACE_DATABASE_SUPPORT 1
|
|
#else
|
|
#define STACK_TRACE_DATABASE_SUPPORT 0
|
|
#endif
|
|
|
|
#define STOP_ON_ALLOC 1
|
|
#define STOP_ON_REALLOC 2
|
|
#define STOP_ON_FREE 3
|
|
|
|
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 DumpHeapTraceBuffer;
|
|
BOOLEAN DumpStackBackTrace;
|
|
BOOLEAN SetStopOnBreakPoint;
|
|
BOOLEAN RemoveStopOnBreakPoint;
|
|
BOOLEAN EnableHeapChecking;
|
|
BOOLEAN EnableHeapValidateOnCall;
|
|
BOOLEAN DisableHeapChecking;
|
|
BOOLEAN DisableHeapValidateOnCall;
|
|
BOOLEAN ToggleAPICallTracing;
|
|
ULONG HeapToDump;
|
|
ULONG HeapEntryToDump;
|
|
ULONG ReservedSize;
|
|
ULONG CommittedSize;
|
|
ULONG AllocatedSize;
|
|
ULONG FreeSize;
|
|
ULONG OverheadSize;
|
|
ULONG NumberOfHeaps;
|
|
ULONG HeapIndex;
|
|
PHEAP *HeapsList;
|
|
ULONG StopOnOperation;
|
|
PVOID StopOnAddress;
|
|
HEAP_STOP_ON_TAG StopOnTag;
|
|
WCHAR StopOnTagName[ 24 ];
|
|
|
|
ULONG FreeListCounts[ HEAP_MAXIMUM_FREELISTS ];
|
|
ULONG TotalFreeSize;
|
|
PHEAP HeapAddress;
|
|
HEAP Heap;
|
|
ULONG SegmentNumber;
|
|
PHEAP_SEGMENT SegmentAddress;
|
|
HEAP_SEGMENT Segments[ HEAP_MAXIMUM_SEGMENTS ];
|
|
} HEAP_STATE, *PHEAP_STATE;
|
|
|
|
BOOL
|
|
ConvertTagNameToIndex(
|
|
IN PHEAP_STATE State
|
|
);
|
|
|
|
BOOL
|
|
GetHeapTagEntry(
|
|
IN PHEAP Heap,
|
|
IN USHORT TagIndex,
|
|
OUT PHEAP_TAG_ENTRY TagEntry
|
|
);
|
|
|
|
VOID
|
|
WalkHEAP(
|
|
IN PHEAP_STATE State
|
|
);
|
|
|
|
VOID
|
|
WalkHEAP_SEGMENT(
|
|
IN PHEAP_STATE State
|
|
);
|
|
|
|
BOOL
|
|
ValidateHeapHeader(
|
|
IN PVOID HeapAddress,
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
BOOL
|
|
ValidateHeapEntry(
|
|
IN PHEAP_STATE State,
|
|
IN PHEAP_ENTRY PrevEntryAddress,
|
|
IN PHEAP_ENTRY PrevEntry,
|
|
IN PHEAP_ENTRY EntryAddress,
|
|
IN PHEAP_ENTRY Entry
|
|
);
|
|
|
|
VOID
|
|
DumpHeapEntry(
|
|
IN PHEAP_STATE State,
|
|
IN PHEAP_ENTRY EntryAddress,
|
|
IN PHEAP_ENTRY Entry
|
|
);
|
|
|
|
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
VOID
|
|
DumpStackBackTraceIndex(
|
|
IN PHEAP_STATE State,
|
|
IN USHORT BackTraceIndex
|
|
);
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
|
|
// Following in traceext.c
|
|
|
|
VOID
|
|
DumpTraceBuffer(
|
|
PVOID TraceAddrToDump,
|
|
LPSTR HeaderString
|
|
);
|
|
|
|
|
|
BOOLEAN HeapExtInitialized;
|
|
|
|
PULONG pNtGlobalFlag;
|
|
|
|
PBOOLEAN pRtlpHeapInvalidBreakPoint;
|
|
PVOID *pRtlpHeapInvalidBadAddress;
|
|
|
|
PHEAP pRtlpGlobalTagHeap;
|
|
HEAP RtlpGlobalTagHeap;
|
|
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
PSTACK_TRACE_DATABASE *pRtlpStackTraceDataBase;
|
|
PSTACK_TRACE_DATABASE RtlpStackTraceDataBase;
|
|
STACK_TRACE_DATABASE StackTraceDataBase;
|
|
BOOLEAN HaveCopyOfStackTraceDataBase;
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
|
|
PHEAP_STOP_ON_VALUES pRtlpHeapStopOn;
|
|
|
|
BOOLEAN RtlpHeapInvalidBreakPoint;
|
|
PVOID RtlpHeapInvalidBadAddress;
|
|
|
|
VOID
|
|
HeapExtension(
|
|
PCSTR lpArgumentString,
|
|
PPEB ProcessPeb
|
|
)
|
|
{
|
|
BOOL b, GotHeapsList, ArgumentsSpecified;
|
|
PHEAP *pHeapsList;
|
|
ULONG NtGlobalFlag;
|
|
LPSTR p;
|
|
ULONG i;
|
|
ULONG DashBArgumentState;
|
|
ULONG AddressToDump;
|
|
HEAP_STATE State;
|
|
UCHAR ArgumentBuffer[ 16 ];
|
|
ULONG TagIndex;
|
|
PHEAP_TAG_ENTRY pTagEntry;
|
|
HEAP_TAG_ENTRY TagEntry;
|
|
PHEAP_PSEUDO_TAG_ENTRY pPseudoTagEntry;
|
|
HEAP_PSEUDO_TAG_ENTRY PseudoTagEntry;
|
|
BOOLEAN HeapHeaderModified;
|
|
BOOLEAN RtlpHeapInvalidBreakPoint;
|
|
PVOID RtlpHeapInvalidBadAddress;
|
|
|
|
if (!HeapExtInitialized) {
|
|
pNtGlobalFlag = (PULONG)GetExpression( "NTDLL!NtGlobalFlag" );
|
|
if (pNtGlobalFlag == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!NtGlobalFlag.\n" );
|
|
}
|
|
|
|
pRtlpHeapInvalidBreakPoint = (PBOOLEAN)GetExpression( "NTDLL!RtlpHeapInvalidBreakPoint" );
|
|
if (pRtlpHeapInvalidBreakPoint == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapInvalidBreakPoint.\n" );
|
|
}
|
|
|
|
pRtlpHeapInvalidBadAddress = (PVOID *)GetExpression( "NTDLL!RtlpHeapInvalidBadAddress" );
|
|
if (pRtlpHeapInvalidBadAddress == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapInvalidBadAddress.\n" );
|
|
}
|
|
|
|
pRtlpGlobalTagHeap = (PHEAP)GetExpression( "NTDLL!RtlpGlobalTagHeap" );
|
|
if (pRtlpGlobalTagHeap == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpGlobalTagHeap.\n" );
|
|
}
|
|
|
|
pRtlpHeapStopOn = (PHEAP_STOP_ON_VALUES)GetExpression( "NTDLL!RtlpHeapStopOn" );
|
|
if (pRtlpHeapStopOn == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpHeapStopOn\n" );
|
|
}
|
|
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
pRtlpStackTraceDataBase = (PSTACK_TRACE_DATABASE *)GetExpression( "NTDLL!RtlpStackTraceDataBase" );
|
|
if (pRtlpStackTraceDataBase == NULL) {
|
|
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpStackTraceDataBase\n" );
|
|
}
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
|
|
HeapExtInitialized = TRUE;
|
|
}
|
|
RtlpGlobalTagHeap.Signature = 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)lpArgumentString;
|
|
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 'c':
|
|
State.DumpHeapTraceBuffer = TRUE;
|
|
break;
|
|
|
|
case 'C':
|
|
State.ToggleAPICallTracing = 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, "%lx", &AddressToDump );
|
|
}
|
|
|
|
if ((p = strpbrk( p, " " )) == NULL) {
|
|
p = "";
|
|
}
|
|
}
|
|
else {
|
|
p++;
|
|
}
|
|
}
|
|
|
|
if (AddressToDump == (ULONG)-1) {
|
|
AddressToDump = (ULONG)ProcessPeb->ProcessHeap;
|
|
}
|
|
|
|
GotHeapsList = FALSE;
|
|
State.NumberOfHeaps = ProcessPeb->NumberOfHeaps;
|
|
pHeapsList = (PHEAP *)ProcessPeb->ProcessHeaps;
|
|
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( PHEAP ) );
|
|
if (State.HeapsList == NULL) {
|
|
dprintf( "Unable to allocate memory to hold ProcessHeaps array\n" );
|
|
}
|
|
else {
|
|
//
|
|
// Read the array of heap pointers
|
|
//
|
|
|
|
if (!ReadMemory( (DWORD)pHeapsList,
|
|
State.HeapsList,
|
|
State.NumberOfHeaps * sizeof( PHEAP ),
|
|
NULL
|
|
)
|
|
) {
|
|
dprintf( "%08lx: Unable to read ProcessHeaps array\n", pHeapsList );
|
|
}
|
|
else {
|
|
GotHeapsList = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GotHeapsList) {
|
|
retryArgs:
|
|
if (!ArgumentsSpecified) {
|
|
if (pRtlpHeapInvalidBreakPoint != NULL) {
|
|
b = ReadMemory( (DWORD)pRtlpHeapInvalidBreakPoint,
|
|
&RtlpHeapInvalidBreakPoint,
|
|
sizeof( RtlpHeapInvalidBreakPoint ),
|
|
NULL
|
|
);
|
|
if (b && RtlpHeapInvalidBreakPoint) {
|
|
RtlpHeapInvalidBadAddress = NULL;
|
|
if (pRtlpHeapInvalidBadAddress != NULL) {
|
|
b = ReadMemory( (DWORD)pRtlpHeapInvalidBadAddress,
|
|
&RtlpHeapInvalidBadAddress,
|
|
sizeof( RtlpHeapInvalidBadAddress ),
|
|
NULL
|
|
);
|
|
if (b) {
|
|
AddressToDump = (ULONG)RtlpHeapInvalidBadAddress;
|
|
}
|
|
}
|
|
|
|
dprintf( "Stop inside heap manager...validating heap address 0x%x\n", AddressToDump );
|
|
State.ValidateHeap = TRUE;
|
|
State.DumpStackBackTrace = TRUE;
|
|
ArgumentsSpecified = TRUE;
|
|
goto retryArgs;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (AddressToDump != 0) {
|
|
for (State.HeapIndex=0;
|
|
State.HeapIndex<State.NumberOfHeaps;
|
|
State.HeapIndex++
|
|
) {
|
|
if (AddressToDump-1 == State.HeapIndex ||
|
|
AddressToDump == (ULONG)State.HeapsList[ State.HeapIndex ]
|
|
) {
|
|
State.HeapToDump = (ULONG)State.HeapsList[ State.HeapIndex ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (State.HeapToDump == 0) {
|
|
if (AddressToDump & 0x80000000) {
|
|
State.HeapToDump = AddressToDump;
|
|
}
|
|
else {
|
|
State.HeapToDump = (ULONG)-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;
|
|
}
|
|
}
|
|
|
|
i = (ULONG)State.EnableHeapChecking + (ULONG)State.EnableHeapValidateOnCall +
|
|
(ULONG)State.DisableHeapChecking + (ULONG)State.DisableHeapValidateOnCall +
|
|
(ULONG)State.ToggleAPICallTracing;
|
|
if (i > 1) {
|
|
dprintf( "HEAPEXT: -d, -D, -e, -E and -C flags are mutually exclusive\n" );
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) {
|
|
if (pRtlpHeapStopOn == NULL) {
|
|
dprintf( "HEAPEXT: Unable to %s heap breakpoint due to missing or invalid NTDLL symbols.\n",
|
|
State.SetStopOnBreakPoint ? "set" : "remove"
|
|
);
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (State.HeapToDump == 0) {
|
|
dprintf( "HEAPEXT: Must specify either heap index or heap address to -b command.\n" );
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pNtGlobalFlag == NULL ||
|
|
!ReadMemory( (DWORD)pNtGlobalFlag,
|
|
&NtGlobalFlag,
|
|
sizeof( NtGlobalFlag ),
|
|
NULL
|
|
)
|
|
) {
|
|
if (i == 1) {
|
|
dprintf( "HEAPEXT: Unable to access value of NTDLL!NtGlobalFlag.\n" );
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (State.ShowHelp) {
|
|
dprintf( "usage: !heap [address] [-? | -h] [-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( " -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( " -c displays the recent API calls made for the specified heap(s).\n" );
|
|
dprintf( " -C enables or disables (i.e. toggles) API call tracing 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" );
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
|
|
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_ENABLE_CALL_TRACING |
|
|
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_ENABLE_CALL_TRACING) {
|
|
dprintf( " maintain log of heap API calls\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {
|
|
dprintf( " disable coalescing of free blocks\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (State.DumpGlobalTags) {
|
|
dprintf( "Global Tags defined for each DLL that makes an untagged allocation.\n" );
|
|
if (RtlpGlobalTagHeap.Signature != HEAP_SIGNATURE) {
|
|
b = ReadMemory( (DWORD)(pRtlpGlobalTagHeap),
|
|
&RtlpGlobalTagHeap,
|
|
sizeof( RtlpGlobalTagHeap ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "HEAPEXT: Unable to read RtlpGlobalTagHeap\n" );
|
|
if (State.HeapsList != NULL) {
|
|
free( State.HeapsList );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
pTagEntry = RtlpGlobalTagHeap.TagEntries;
|
|
if (pTagEntry == NULL) {
|
|
dprintf( " no global tags currently defined.\n" );
|
|
}
|
|
else {
|
|
dprintf( " Tag Name Allocs Frees Diff Allocated\n" );
|
|
for (TagIndex=1; TagIndex<RtlpGlobalTagHeap.NextAvailableTagIndex; TagIndex++) {
|
|
pTagEntry += 1;
|
|
b = ReadMemory( (DWORD)pTagEntry,
|
|
&TagEntry,
|
|
sizeof( TagEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "%04x: unable to read HEAP_TAG_ENTRY at %x\n", TagIndex, pTagEntry );
|
|
break;
|
|
}
|
|
else
|
|
if (TagEntry.Allocs != 0 ||
|
|
TagEntry.Frees != 0 ||
|
|
TagEntry.Size != 0
|
|
) {
|
|
dprintf( "%04x: %-20.20ws %8d %8d %6d %8d\n",
|
|
TagEntry.TagIndex,
|
|
TagEntry.TagName,
|
|
TagEntry.Allocs,
|
|
TagEntry.Frees,
|
|
TagEntry.Allocs - TagEntry.Frees,
|
|
TagEntry.Size << HEAP_GRANULARITY_SHIFT
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk the list of heaps
|
|
//
|
|
while (!State.ExitDumpLoop &&
|
|
!CheckControlC() &&
|
|
(!GotHeapsList || (State.HeapIndex < State.NumberOfHeaps ))
|
|
) {
|
|
memset( &State.FreeListCounts, 0, sizeof( State.FreeListCounts ) );
|
|
State.TotalFreeSize = 0;
|
|
if (!GotHeapsList) {
|
|
State.HeapAddress = (PHEAP)State.HeapToDump;
|
|
State.ExitDumpLoop = TRUE;
|
|
}
|
|
else {
|
|
State.HeapAddress = State.HeapsList[ State.HeapIndex ];
|
|
}
|
|
|
|
b = ReadMemory( (DWORD)State.HeapAddress, &State.Heap, sizeof( State.Heap ), NULL );
|
|
if (State.HeapIndex == 0) {
|
|
dprintf( "Index Address Name Debugging options enabled\n" );
|
|
}
|
|
|
|
dprintf( "%3u: %08x ", State.HeapIndex + 1, State.HeapAddress );
|
|
if (!b) {
|
|
dprintf( " - heap headers inaccessable, skipping\n" );
|
|
}
|
|
else
|
|
if (!ArgumentsSpecified) {
|
|
if (!GetHeapTagEntry( &State.Heap, 0, &TagEntry )) {
|
|
TagEntry.TagName[ 0 ] = UNICODE_NULL;
|
|
}
|
|
dprintf( " %-14.14ws", TagEntry.TagName );
|
|
|
|
if (State.Heap.Flags & HEAP_TAIL_CHECKING_ENABLED) {
|
|
dprintf( " tail checking" );
|
|
}
|
|
|
|
if (State.Heap.Flags & HEAP_FREE_CHECKING_ENABLED) {
|
|
dprintf( " free checking" );
|
|
}
|
|
|
|
if (State.Heap.Flags & HEAP_VALIDATE_PARAMETERS_ENABLED) {
|
|
dprintf( " validate parameters" );
|
|
}
|
|
|
|
if (State.Heap.Flags & HEAP_VALIDATE_ALL_ENABLED) {
|
|
dprintf( " validate on call" );
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
}
|
|
else
|
|
if ((ULONG)State.HeapAddress == State.HeapToDump ||
|
|
State.HeapToDump == 0 ||
|
|
State.HeapToDump == (ULONG)-1
|
|
) {
|
|
dprintf( "\n" );
|
|
for (i=0; i<HEAP_MAXIMUM_SEGMENTS; i++) {
|
|
if (State.Heap.Segments[ i ] != NULL) {
|
|
b = ReadMemory( (DWORD)State.Heap.Segments[ i ],
|
|
&State.Segments[ i ],
|
|
sizeof( HEAP_SEGMENT ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " Unabled to read HEAP_SEGMENT structure at %08x\n", State.Heap.Segments[ i ] );
|
|
}
|
|
else {
|
|
dprintf( " Segment at %08x to %08x (%08x bytes committed)\n",
|
|
i == 0 ? (DWORD)State.HeapAddress : (DWORD)State.Heap.Segments[ i ],
|
|
State.Segments[ i ].LastValidEntry,
|
|
(DWORD)(State.Segments[ i ].LastValidEntry) -
|
|
(DWORD)(i == 0 ? (DWORD)State.HeapAddress : (DWORD)State.Heap.Segments[ i ]) -
|
|
(State.Segments[ i ].NumberOfUnCommittedPages * PAGE_SIZE)
|
|
);
|
|
|
|
if (State.HeapToDump == (ULONG)-1) {
|
|
if (AddressToDump >= (ULONG)State.Heap.Segments[ i ] &&
|
|
AddressToDump < (ULONG)State.Segments[ i ].LastValidEntry
|
|
) {
|
|
State.HeapToDump = (ULONG)State.HeapAddress;
|
|
if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) {
|
|
State.StopOnAddress = (PVOID)AddressToDump;
|
|
}
|
|
else {
|
|
State.HeapEntryToDump = AddressToDump;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (State.HeapToDump == (ULONG)-1) {
|
|
State.HeapIndex += 1;
|
|
continue;
|
|
}
|
|
|
|
if (State.SetStopOnBreakPoint || State.RemoveStopOnBreakPoint) {
|
|
PULONG pul;
|
|
|
|
switch( State.StopOnOperation) {
|
|
case STOP_ON_ALLOC:
|
|
if (State.StopOnTagName[0] == UNICODE_NULL) {
|
|
pul = &pRtlpHeapStopOn->AllocAddress;
|
|
}
|
|
else {
|
|
pul = &pRtlpHeapStopOn->AllocTag.HeapAndTagIndex;
|
|
}
|
|
break;
|
|
|
|
case STOP_ON_REALLOC:
|
|
if (State.StopOnTagName[0] == UNICODE_NULL) {
|
|
pul = &pRtlpHeapStopOn->ReAllocAddress;
|
|
}
|
|
else {
|
|
pul = &pRtlpHeapStopOn->ReAllocTag.HeapAndTagIndex;
|
|
}
|
|
break;
|
|
|
|
case STOP_ON_FREE:
|
|
if (State.StopOnTagName[0] == UNICODE_NULL) {
|
|
pul = &pRtlpHeapStopOn->FreeAddress;
|
|
}
|
|
else {
|
|
pul = &pRtlpHeapStopOn->FreeTag.HeapAndTagIndex;
|
|
}
|
|
break;
|
|
default:
|
|
pul = NULL;
|
|
break;
|
|
}
|
|
|
|
if (pul != NULL) {
|
|
if (State.StopOnTagName[0] == UNICODE_NULL) {
|
|
if (State.RemoveStopOnBreakPoint) {
|
|
State.StopOnAddress = 0;
|
|
}
|
|
b = WriteMemory( (DWORD)pul,
|
|
&State.StopOnAddress,
|
|
sizeof( State.StopOnAddress ),
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
if (!ConvertTagNameToIndex( &State )) {
|
|
dprintf( "HEAPEXT: Unable to convert tag name %ws to an index\n", State.StopOnTagName );
|
|
b = TRUE;
|
|
}
|
|
else {
|
|
b = WriteMemory( (DWORD)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, &State.Heap );
|
|
}
|
|
|
|
HeapHeaderModified = FALSE;
|
|
if (State.EnableHeapChecking || State.EnableHeapValidateOnCall) {
|
|
if (!(State.Heap.Flags & HEAP_TAIL_CHECKING_ENABLED)) {
|
|
State.Heap.AlignRound += CHECK_HEAP_TAIL_SIZE;
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->AlignRound,
|
|
&State.Heap.AlignRound,
|
|
sizeof( State.Heap.AlignRound ),
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
b = TRUE;
|
|
}
|
|
|
|
|
|
if (b) {
|
|
HeapHeaderModified = TRUE;
|
|
State.Heap.Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED |
|
|
HEAP_TAIL_CHECKING_ENABLED |
|
|
HEAP_FREE_CHECKING_ENABLED;
|
|
if (State.EnableHeapValidateOnCall) {
|
|
State.Heap.Flags |= HEAP_VALIDATE_ALL_ENABLED;
|
|
}
|
|
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->Flags,
|
|
(LPCVOID)&State.Heap.Flags,
|
|
sizeof( State.Heap.Flags ),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if (!b) {
|
|
dprintf( "HEAPEXT: Unable to enable heap checking for heap %x\n", State.HeapAddress );
|
|
ReadMemory( (DWORD)State.HeapAddress, &State.Heap, sizeof( State.Heap ), NULL );
|
|
}
|
|
else {
|
|
if (State.EnableHeapValidateOnCall) {
|
|
dprintf( "HEAPEXT: Enabled validate on call heap checking for heap %x\n", State.HeapAddress );
|
|
}
|
|
else {
|
|
dprintf( "HEAPEXT: Enabled heap checking for heap %x\n", State.HeapAddress );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (State.DisableHeapChecking || State.DisableHeapValidateOnCall) {
|
|
if (State.DisableHeapValidateOnCall) {
|
|
if (State.Heap.Flags & HEAP_VALIDATE_ALL_ENABLED) {
|
|
State.Heap.Flags &= ~HEAP_VALIDATE_ALL_ENABLED;
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->Flags,
|
|
(LPCVOID)&State.Heap.Flags,
|
|
sizeof( State.Heap.Flags ),
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
if (State.Heap.Flags & HEAP_TAIL_CHECKING_ENABLED) {
|
|
HeapHeaderModified = TRUE;
|
|
State.Heap.AlignRound -= CHECK_HEAP_TAIL_SIZE;
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->AlignRound,
|
|
(LPCVOID)&State.Heap.AlignRound,
|
|
sizeof( State.Heap.AlignRound ),
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
b = TRUE;
|
|
}
|
|
|
|
if (b) {
|
|
State.Heap.Flags &= ~(HEAP_VALIDATE_PARAMETERS_ENABLED |
|
|
HEAP_VALIDATE_ALL_ENABLED |
|
|
HEAP_TAIL_CHECKING_ENABLED |
|
|
HEAP_FREE_CHECKING_ENABLED
|
|
);
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->Flags,
|
|
(LPCVOID)&State.Heap.Flags,
|
|
sizeof( State.Heap.Flags ),
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!b) {
|
|
dprintf( "HEAPEXT: Unable to disable heap checking for heap %x\n", State.HeapAddress );
|
|
ReadMemory( (DWORD)State.HeapAddress, &State.Heap, sizeof( State.Heap ), NULL );
|
|
}
|
|
else {
|
|
if (State.DisableHeapValidateOnCall) {
|
|
dprintf( "HEAPEXT: Disabled validate on call heap checking for heap %x\n", State.HeapAddress );
|
|
}
|
|
else {
|
|
dprintf( "HEAPEXT: Disabled heap checking for heap %x\n", State.HeapAddress );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (State.ToggleAPICallTracing) {
|
|
State.Heap.Flags ^= HEAP_CREATE_ENABLE_TRACING;
|
|
b = WriteMemory( (DWORD)&State.HeapAddress->Flags,
|
|
(LPCVOID)&State.Heap.Flags,
|
|
sizeof( State.Heap.Flags ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "HEAPEXT: Unable to toggle API call tracing for heap %x\n", State.HeapAddress );
|
|
ReadMemory( (DWORD)State.HeapAddress, &State.Heap, sizeof( State.Heap ), NULL );
|
|
}
|
|
else {
|
|
HeapHeaderModified = TRUE;
|
|
if (State.Heap.Flags & HEAP_CREATE_ENABLE_TRACING) {
|
|
dprintf( "HEAPEXT: Enabled API call tracing for heap %x\n", State.HeapAddress );
|
|
}
|
|
else {
|
|
dprintf( "HEAPEXT: Disabled API call tracing for heap %x\n", State.HeapAddress );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (State.DumpHeapTags) {
|
|
pTagEntry = State.Heap.TagEntries;
|
|
if (pTagEntry == NULL) {
|
|
dprintf( " no tags currently defined for this heap.\n" );
|
|
}
|
|
else {
|
|
dprintf( " Tag Name Allocs Frees Diff Allocated\n" );
|
|
for (TagIndex=1; TagIndex<State.Heap.NextAvailableTagIndex; TagIndex++) {
|
|
pTagEntry += 1;
|
|
b = ReadMemory( (DWORD)pTagEntry,
|
|
&TagEntry,
|
|
sizeof( TagEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "%04x: unable to read HEAP_TAG_ENTRY at %x\n", TagIndex, pTagEntry );
|
|
}
|
|
else
|
|
if (TagEntry.Allocs != 0 ||
|
|
TagEntry.Frees != 0 ||
|
|
TagEntry.Size != 0
|
|
) {
|
|
dprintf( "%04x: %-20.20ws %8d %8d %6d %8d\n",
|
|
TagEntry.TagIndex,
|
|
TagEntry.TagName,
|
|
TagEntry.Allocs,
|
|
TagEntry.Frees,
|
|
TagEntry.Allocs - TagEntry.Frees,
|
|
TagEntry.Size << HEAP_GRANULARITY_SHIFT
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (State.DumpHeapPseudoTags) {
|
|
pPseudoTagEntry = State.Heap.PseudoTagEntries;
|
|
if (pPseudoTagEntry == NULL) {
|
|
dprintf( " no pseudo tags currently defined for this heap.\n" );
|
|
}
|
|
else {
|
|
dprintf( " Tag Name Allocs Frees Diff Allocated\n" );
|
|
for (TagIndex=1; TagIndex<HEAP_NUMBER_OF_PSEUDO_TAG; TagIndex++) {
|
|
pPseudoTagEntry += 1;
|
|
b = ReadMemory( (DWORD)pPseudoTagEntry,
|
|
&PseudoTagEntry,
|
|
sizeof( PseudoTagEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "%04x: unable to read HEAP_PSEUDO_TAG_ENTRY at %x\n", TagIndex, pPseudoTagEntry );
|
|
}
|
|
else
|
|
if (PseudoTagEntry.Allocs != 0 ||
|
|
PseudoTagEntry.Frees != 0 ||
|
|
PseudoTagEntry.Size != 0
|
|
) {
|
|
if (TagIndex == 0) {
|
|
dprintf( "%04x: Objects>%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",
|
|
PseudoTagEntry.Allocs,
|
|
PseudoTagEntry.Frees,
|
|
PseudoTagEntry.Allocs - PseudoTagEntry.Frees,
|
|
PseudoTagEntry.Size << HEAP_GRANULARITY_SHIFT
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (State.Heap.TraceBuffer != NULL && State.DumpHeapTraceBuffer) {
|
|
DumpTraceBuffer( State.Heap.TraceBuffer, "Heap API call trace" );
|
|
}
|
|
|
|
if (HeapHeaderModified && (State.Heap.HeaderValidateCopy != NULL)) {
|
|
b = WriteMemory( (DWORD)State.Heap.HeaderValidateCopy,
|
|
&State.Heap,
|
|
FIELD_OFFSET( HEAP, Reserved ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( "HEAPEXT: Unable to update header validation copy at %x\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 );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
ConvertTagNameToIndex(
|
|
IN PHEAP_STATE State
|
|
)
|
|
{
|
|
ULONG TagIndex;
|
|
PHEAP_TAG_ENTRY pTagEntry;
|
|
HEAP_TAG_ENTRY TagEntry;
|
|
PHEAP_PSEUDO_TAG_ENTRY pPseudoTagEntry;
|
|
HEAP_PSEUDO_TAG_ENTRY PseudoTagEntry;
|
|
BOOL b;
|
|
PWSTR s;
|
|
|
|
if (State->RemoveStopOnBreakPoint) {
|
|
State->StopOnTag.HeapAndTagIndex = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!_wcsnicmp( State->StopOnTagName, L"Objects", 7 )) {
|
|
pPseudoTagEntry = State->Heap.PseudoTagEntries;
|
|
if (pPseudoTagEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
s = &State->StopOnTagName[ 7 ];
|
|
if (*s == L'>') {
|
|
State->StopOnTag.HeapIndex = State->Heap.ProcessHeapsListIndex;
|
|
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)
|
|
) {
|
|
State->StopOnTag.HeapIndex = State->Heap.ProcessHeapsListIndex;
|
|
State->StopOnTag.TagIndex = (State->StopOnTag.TagIndex >> HEAP_GRANULARITY_SHIFT) |
|
|
HEAP_PSEUDO_TAG_FLAG;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
pTagEntry = State->Heap.TagEntries;
|
|
if (pTagEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (TagIndex=1; TagIndex<State->Heap.NextAvailableTagIndex; TagIndex++) {
|
|
pTagEntry += 1;
|
|
b = ReadMemory( (DWORD)pTagEntry,
|
|
&TagEntry,
|
|
sizeof( TagEntry ),
|
|
NULL
|
|
);
|
|
if (b && !_wcsicmp( State->StopOnTagName, TagEntry.TagName )) {
|
|
State->StopOnTag.TagIndex = TagEntry.TagIndex;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetHeapTagEntry(
|
|
IN PHEAP Heap,
|
|
IN USHORT TagIndex,
|
|
OUT PHEAP_TAG_ENTRY TagEntry
|
|
)
|
|
{
|
|
BOOL b;
|
|
PHEAP_TAG_ENTRY pTagEntries;
|
|
PHEAP_PSEUDO_TAG_ENTRY pPseudoTagEntries;
|
|
|
|
b = FALSE;
|
|
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
|
|
TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
|
|
pPseudoTagEntries = Heap->PseudoTagEntries;
|
|
if (pPseudoTagEntries == 0) {
|
|
return FALSE;
|
|
}
|
|
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;
|
|
b = ReadMemory( (DWORD)(pPseudoTagEntries + TagIndex),
|
|
TagEntry,
|
|
sizeof( *pPseudoTagEntries ),
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
if (TagIndex & HEAP_GLOBAL_TAG) {
|
|
if (RtlpGlobalTagHeap.Signature != HEAP_SIGNATURE) {
|
|
b = ReadMemory( (DWORD)(pRtlpGlobalTagHeap),
|
|
&RtlpGlobalTagHeap,
|
|
sizeof( RtlpGlobalTagHeap ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
return b;
|
|
}
|
|
}
|
|
TagIndex &= ~HEAP_GLOBAL_TAG;
|
|
if (TagIndex < RtlpGlobalTagHeap.NextAvailableTagIndex) {
|
|
pTagEntries = RtlpGlobalTagHeap.TagEntries;
|
|
if (pTagEntries == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
b = ReadMemory( (DWORD)(pTagEntries + TagIndex),
|
|
TagEntry,
|
|
sizeof( *TagEntry ),
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
else
|
|
if (TagIndex < Heap->NextAvailableTagIndex) {
|
|
pTagEntries = Heap->TagEntries;
|
|
if (pTagEntries == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
b = ReadMemory( (DWORD)(pTagEntries + TagIndex),
|
|
TagEntry,
|
|
sizeof( *TagEntry ),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
WalkHEAP(
|
|
IN PHEAP_STATE State
|
|
)
|
|
{
|
|
BOOL b;
|
|
PVOID FreeListHead;
|
|
ULONG i;
|
|
PLIST_ENTRY Head, Next;
|
|
HEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocEntry;
|
|
HEAP_TAG_ENTRY TagEntry;
|
|
PHEAP_FREE_ENTRY FreeEntryAddress;
|
|
HEAP_FREE_ENTRY FreeEntry;
|
|
PHEAP_UCR_SEGMENT UCRSegment;
|
|
HEAP_UCR_SEGMENT CapturedUCRSegment;
|
|
ULONG AlignRound;
|
|
|
|
AlignRound = State->Heap.AlignRound - sizeof( HEAP_ENTRY );
|
|
if (State->Heap.Flags & HEAP_TAIL_CHECKING_ENABLED) {
|
|
AlignRound -= CHECK_HEAP_TAIL_SIZE;
|
|
}
|
|
|
|
dprintf( " Flags: %08x\n", State->Heap.Flags );
|
|
dprintf( " ForceFlags: %08x\n", State->Heap.ForceFlags );
|
|
dprintf( " Granularity: %u bytes\n", AlignRound + 1 );
|
|
dprintf( " Segment Reserve: %08x\n", State->Heap.SegmentReserve );
|
|
dprintf( " Segment Commit: %08x\n", State->Heap.SegmentCommit );
|
|
dprintf( " DeCommit Block Thres:%08x\n", State->Heap.DeCommitFreeBlockThreshold );
|
|
dprintf( " DeCommit Total Thres:%08x\n", State->Heap.DeCommitTotalFreeThreshold );
|
|
dprintf( " Total Free Size: %08x\n", State->Heap.TotalFreeSize );
|
|
dprintf( " Max. Allocation Size:%08x\n", State->Heap.MaximumAllocationSize );
|
|
dprintf( " Lock Variable at: %08x\n", State->Heap.LockVariable );
|
|
dprintf( " Next TagIndex: %04x\n", State->Heap.NextAvailableTagIndex );
|
|
dprintf( " Maximum TagIndex: %04x\n", State->Heap.MaximumTagIndex );
|
|
dprintf( " Tag Entries: %08x\n", State->Heap.TagEntries );
|
|
dprintf( " PsuedoTag Entries: %08x\n", State->Heap.PseudoTagEntries );
|
|
if (State->Heap.TraceBuffer) {
|
|
dprintf( " Trace Buffer at: %08x\n", State->Heap.TraceBuffer );
|
|
}
|
|
dprintf( " Virtual Alloc List: %08x\n", &State->HeapAddress->VirtualAllocdBlocks );
|
|
|
|
Head = &State->HeapAddress->VirtualAllocdBlocks;
|
|
Next = State->Heap.VirtualAllocdBlocks.Flink;
|
|
while (Next != Head) {
|
|
if (!ReadMemory( (DWORD)Next,
|
|
&VirtualAllocEntry,
|
|
sizeof( VirtualAllocEntry ),
|
|
NULL
|
|
)
|
|
) {
|
|
dprintf( " Unable to read _HEAP_VIRTUAL_ALLOC_ENTRY structure at %x\n", Next );
|
|
break;
|
|
}
|
|
|
|
if (State->DumpHeapEntries) {
|
|
dprintf( " %08x: %08x [%02x] - busy (%x)",
|
|
Next,
|
|
VirtualAllocEntry.CommitSize,
|
|
VirtualAllocEntry.CommitSize - VirtualAllocEntry.BusyBlock.Size,
|
|
VirtualAllocEntry.BusyBlock.Flags
|
|
);
|
|
|
|
if (VirtualAllocEntry.BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN) {
|
|
dprintf( ", tail fill" );
|
|
}
|
|
if (VirtualAllocEntry.ExtraStuff.Settable) {
|
|
dprintf( " (Handle %08x)", VirtualAllocEntry.ExtraStuff.Settable );
|
|
}
|
|
|
|
if (VirtualAllocEntry.ExtraStuff.TagIndex) {
|
|
if (GetHeapTagEntry( &State->Heap, VirtualAllocEntry.ExtraStuff.TagIndex, &TagEntry )) {
|
|
dprintf( " (%ws)", TagEntry.TagName );
|
|
}
|
|
else {
|
|
dprintf( " (Tag %x)", VirtualAllocEntry.ExtraStuff.TagIndex );
|
|
}
|
|
}
|
|
|
|
if (VirtualAllocEntry.BusyBlock.Flags & HEAP_ENTRY_SETTABLE_FLAGS) {
|
|
dprintf( ", user flags (%x)", (VirtualAllocEntry.BusyBlock.Flags & HEAP_ENTRY_SETTABLE_FLAGS) >> 5 );
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
DumpStackBackTraceIndex( State, VirtualAllocEntry.ExtraStuff.AllocatorBackTraceIndex );
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
}
|
|
|
|
if (VirtualAllocEntry.Entry.Flink == Next) {
|
|
dprintf( " **** List is hosed\n");
|
|
break;
|
|
}
|
|
|
|
Next = VirtualAllocEntry.Entry.Flink;
|
|
}
|
|
|
|
dprintf( " UCR FreeList: %08x\n", State->Heap.UnusedUnCommittedRanges );
|
|
UCRSegment = State->Heap.UCRSegments;
|
|
while (UCRSegment != NULL) {
|
|
b = ReadMemory( (DWORD)UCRSegment,
|
|
&CapturedUCRSegment,
|
|
sizeof( CapturedUCRSegment ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " Unabled to read HEAP_UCR_SEGMENT structure at %08x\n", UCRSegment );
|
|
break;
|
|
}
|
|
else {
|
|
dprintf( " UCRSegment - %08x: %08x . %08x\n",
|
|
UCRSegment,
|
|
CapturedUCRSegment.CommittedSize,
|
|
CapturedUCRSegment.ReservedSize
|
|
);
|
|
}
|
|
|
|
if (State->ComputeSummary) {
|
|
State->OverheadSize += CapturedUCRSegment.CommittedSize;
|
|
}
|
|
|
|
UCRSegment = CapturedUCRSegment.Next;
|
|
}
|
|
|
|
dprintf( " FreeList Usage: %08x %08x %08x %08x\n",
|
|
State->Heap.u.FreeListsInUseUlong[0],
|
|
State->Heap.u.FreeListsInUseUlong[1],
|
|
State->Heap.u.FreeListsInUseUlong[2],
|
|
State->Heap.u.FreeListsInUseUlong[3]
|
|
);
|
|
|
|
if (State->ComputeSummary) {
|
|
State->OverheadSize += sizeof( State->Heap );
|
|
dprintf( "Committed Allocated Free OverHead\n" );
|
|
dprintf( "% 8x % 8x % 8x % 8x\r",
|
|
State->CommittedSize,
|
|
State->AllocatedSize,
|
|
State->FreeSize,
|
|
State->OverheadSize
|
|
);
|
|
}
|
|
|
|
for (i=0; i<HEAP_MAXIMUM_FREELISTS; i++) {
|
|
FreeListHead = &State->HeapAddress->FreeLists[ i ];
|
|
if (State->Heap.FreeLists[ i ].Flink != State->Heap.FreeLists[ i ].Blink ||
|
|
State->Heap.FreeLists[ i ].Flink != FreeListHead
|
|
) {
|
|
dprintf( " FreeList[ %02x ] at %08x: %08x . %08x\n",
|
|
i,
|
|
FreeListHead,
|
|
State->Heap.FreeLists[ i ].Blink,
|
|
State->Heap.FreeLists[ i ].Flink
|
|
);
|
|
|
|
Next = State->Heap.FreeLists[ i ].Flink;
|
|
while (Next != FreeListHead) {
|
|
FreeEntryAddress = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
|
|
b = ReadMemory( (DWORD)FreeEntryAddress,
|
|
&FreeEntry,
|
|
sizeof( FreeEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " Unabled to read HEAP_ENTRY structure at %08x\n", FreeEntryAddress );
|
|
break;
|
|
}
|
|
|
|
if (State->DumpHeapFreeLists) {
|
|
dprintf( " %08x: %05x . %05x [%02x] - free\n",
|
|
FreeEntryAddress,
|
|
FreeEntry.PreviousSize << HEAP_GRANULARITY_SHIFT,
|
|
FreeEntry.Size << HEAP_GRANULARITY_SHIFT,
|
|
FreeEntry.Flags
|
|
);
|
|
}
|
|
|
|
Next = FreeEntry.FreeList.Flink;
|
|
|
|
if (CheckControlC()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (i=0; i<HEAP_MAXIMUM_SEGMENTS; i++) {
|
|
if (State->Heap.Segments[ i ] != NULL) {
|
|
State->SegmentNumber = i;
|
|
State->SegmentAddress = State->Heap.Segments[ i ];
|
|
WalkHEAP_SEGMENT( State );
|
|
}
|
|
|
|
if (State->ExitDumpLoop || CheckControlC()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((ULONG)State->HeapAddress == State->HeapToDump) {
|
|
State->ExitDumpLoop = TRUE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
WalkHEAP_SEGMENT(
|
|
IN PHEAP_STATE State
|
|
)
|
|
{
|
|
PHEAP_SEGMENT Segment;
|
|
BOOL b;
|
|
BOOLEAN DumpEntry;
|
|
PHEAP_ENTRY EntryAddress, PrevEntryAddress, NextEntryAddress;
|
|
HEAP_ENTRY Entry, PrevEntry;
|
|
PHEAP_UNCOMMMTTED_RANGE UnCommittedRanges;
|
|
PHEAP_UNCOMMMTTED_RANGE Buffer, UnCommittedRange, UnCommittedRangeEnd;
|
|
|
|
Segment = &State->Segments[ State->SegmentNumber ];
|
|
if (State->ComputeSummary) {
|
|
State->OverheadSize += sizeof( *Segment );
|
|
dprintf( "% 8x % 8x % 8x % 8x\r",
|
|
State->CommittedSize,
|
|
State->AllocatedSize,
|
|
State->FreeSize,
|
|
State->OverheadSize
|
|
);
|
|
}
|
|
|
|
if (State->DumpHeapSegments) {
|
|
dprintf( " Segment%02u at %08x:\n", State->SegmentNumber, State->SegmentAddress );
|
|
dprintf( " Flags: %08x\n", Segment->Flags );
|
|
dprintf( " Base: %08x\n", Segment->BaseAddress );
|
|
dprintf( " First Entry: %08x\n", Segment->FirstEntry );
|
|
dprintf( " Last Entry: %08x\n", Segment->LastValidEntry );
|
|
dprintf( " Total Pages: %08x\n", Segment->NumberOfPages );
|
|
dprintf( " Total UnCommit: %08x\n", Segment->NumberOfUnCommittedPages );
|
|
dprintf( " Largest UnCommit:%08x\n", Segment->LargestUnCommittedRange );
|
|
dprintf( " UnCommitted Ranges: (%u)\n", Segment->NumberOfUnCommittedRanges );
|
|
}
|
|
|
|
Buffer = malloc( Segment->NumberOfUnCommittedRanges * sizeof( *UnCommittedRange ) );
|
|
if (Buffer == NULL) {
|
|
dprintf( " unable to allocate memory for reading uncommitted ranges\n" );
|
|
return;
|
|
}
|
|
|
|
UnCommittedRanges = Segment->UnCommittedRanges;
|
|
UnCommittedRange = Buffer;
|
|
while (UnCommittedRanges != NULL) {
|
|
b = ReadMemory( (DWORD)UnCommittedRanges,
|
|
UnCommittedRange,
|
|
sizeof( *UnCommittedRange ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " unable to read uncommited range structure at %x\n",
|
|
UnCommittedRanges
|
|
);
|
|
free( Buffer );
|
|
return;
|
|
}
|
|
|
|
if (State->DumpHeapSegments) {
|
|
dprintf( " %08x: %08x\n", UnCommittedRange->Address, UnCommittedRange->Size );
|
|
}
|
|
|
|
UnCommittedRanges = UnCommittedRange->Next;
|
|
UnCommittedRange->Next = (UnCommittedRange+1);
|
|
UnCommittedRange += 1;
|
|
|
|
if (CheckControlC()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (State->DumpHeapSegments) {
|
|
dprintf( "\n" );
|
|
}
|
|
|
|
State->CommittedSize += ( Segment->NumberOfPages -
|
|
Segment->NumberOfUnCommittedPages
|
|
) * PAGE_SIZE;
|
|
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 %x\n", State->SegmentNumber, State->HeapAddress );
|
|
}
|
|
|
|
UnCommittedRangeEnd = UnCommittedRange;
|
|
UnCommittedRange = Buffer;
|
|
if (Segment->BaseAddress == State->HeapAddress) {
|
|
EntryAddress = &((PHEAP)State->HeapAddress)->Entry;
|
|
}
|
|
else {
|
|
EntryAddress = &State->Heap.Segments[ State->SegmentNumber ]->Entry;
|
|
}
|
|
|
|
PrevEntryAddress = NULL;
|
|
while (EntryAddress < Segment->LastValidEntry) {
|
|
b = ReadMemory( (DWORD)EntryAddress, &Entry, sizeof( Entry ), NULL );
|
|
if (!b) {
|
|
dprintf( " unable to read heap entry at %08x\n", EntryAddress );
|
|
break;
|
|
}
|
|
|
|
NextEntryAddress = EntryAddress + Entry.Size;
|
|
if (State->DumpHeapEntries) {
|
|
DumpEntry = TRUE;
|
|
}
|
|
else
|
|
if (PrevEntryAddress != NULL &&
|
|
(State->HeapEntryToDump == (ULONG)PrevEntryAddress ||
|
|
(State->HeapEntryToDump > (ULONG)PrevEntryAddress &&
|
|
State->HeapEntryToDump <= (ULONG)NextEntryAddress
|
|
)
|
|
)
|
|
) {
|
|
DumpEntry = TRUE;
|
|
}
|
|
else {
|
|
DumpEntry = FALSE;
|
|
}
|
|
|
|
if (DumpEntry) {
|
|
DumpHeapEntry( State, EntryAddress, &Entry );
|
|
}
|
|
|
|
if (!(Entry.Flags & HEAP_ENTRY_BUSY)) {
|
|
State->TotalFreeSize += Entry.Size;
|
|
}
|
|
|
|
if (State->ComputeSummary) {
|
|
if (Entry.Flags & HEAP_ENTRY_BUSY) {
|
|
State->AllocatedSize += Entry.Size << HEAP_GRANULARITY_SHIFT;
|
|
State->AllocatedSize -= Entry.UnusedBytes;
|
|
State->OverheadSize += Entry.UnusedBytes;
|
|
}
|
|
else {
|
|
State->FreeSize += Entry.Size << HEAP_GRANULARITY_SHIFT;
|
|
}
|
|
}
|
|
|
|
if (State->ValidateHeap) {
|
|
if (!ValidateHeapEntry( State,
|
|
PrevEntryAddress,
|
|
&PrevEntry,
|
|
EntryAddress,
|
|
&Entry
|
|
)
|
|
) {
|
|
if (State->DumpHeapEntries) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Entry.Size == 0 || CheckControlC()) {
|
|
break;
|
|
}
|
|
|
|
PrevEntryAddress = EntryAddress;
|
|
PrevEntry = Entry;
|
|
EntryAddress = NextEntryAddress;
|
|
if (Entry.Flags & HEAP_ENTRY_LAST_ENTRY) {
|
|
if (State->ComputeSummary) {
|
|
dprintf( "% 8x % 8x % 8x % 8x\r",
|
|
State->CommittedSize,
|
|
State->AllocatedSize,
|
|
State->FreeSize,
|
|
State->OverheadSize
|
|
);
|
|
}
|
|
|
|
if ((ULONG)EntryAddress == UnCommittedRange->Address) {
|
|
if (DumpEntry) {
|
|
dprintf( " %08x: %08x - uncommitted bytes.\n",
|
|
UnCommittedRange->Address,
|
|
UnCommittedRange->Size
|
|
);
|
|
}
|
|
|
|
PrevEntryAddress = NULL;
|
|
EntryAddress = (PHEAP_ENTRY)
|
|
((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size);
|
|
|
|
UnCommittedRange += 1;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
free( Buffer );
|
|
if (State->ComputeSummary) {
|
|
dprintf( "% 8x % 8x % 8x % 8x\r",
|
|
State->CommittedSize,
|
|
State->AllocatedSize,
|
|
State->FreeSize,
|
|
State->OverheadSize
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct {
|
|
ULONG Offset;
|
|
LPSTR Description;
|
|
} FieldOffsets[] = {
|
|
FIELD_OFFSET( HEAP, Entry ), "Entry",
|
|
FIELD_OFFSET( HEAP, Signature ), "Signature",
|
|
FIELD_OFFSET( HEAP, Flags ), "Flags",
|
|
FIELD_OFFSET( HEAP, ForceFlags ), "ForceFlags",
|
|
FIELD_OFFSET( HEAP, VirtualMemoryThreshold ), "VirtualMemoryThreshold",
|
|
FIELD_OFFSET( HEAP, SegmentReserve ), "SegmentReserve",
|
|
FIELD_OFFSET( HEAP, SegmentCommit ), "SegmentCommit",
|
|
FIELD_OFFSET( HEAP, DeCommitFreeBlockThreshold ), "DeCommitFreeBlockThreshold",
|
|
FIELD_OFFSET( HEAP, DeCommitTotalFreeThreshold ), "DeCommitTotalFreeThreshold",
|
|
FIELD_OFFSET( HEAP, TotalFreeSize ), "TotalFreeSize",
|
|
FIELD_OFFSET( HEAP, MaximumAllocationSize ), "MaximumAllocationSize",
|
|
FIELD_OFFSET( HEAP, ProcessHeapsListIndex ), "ProcessHeapsListIndex",
|
|
FIELD_OFFSET( HEAP, HeaderValidateLength ), "HeaderValidateLength",
|
|
FIELD_OFFSET( HEAP, HeaderValidateCopy ), "HeaderValidateCopy",
|
|
FIELD_OFFSET( HEAP, NextAvailableTagIndex ), "NextAvailableTagIndex",
|
|
FIELD_OFFSET( HEAP, MaximumTagIndex ), "MaximumTagIndex",
|
|
FIELD_OFFSET( HEAP, TagEntries ), "TagEntries",
|
|
FIELD_OFFSET( HEAP, UCRSegments ), "UCRSegments",
|
|
FIELD_OFFSET( HEAP, UnusedUnCommittedRanges ), "UnusedUnCommittedRanges",
|
|
FIELD_OFFSET( HEAP, AlignRound ), "AlignRound",
|
|
FIELD_OFFSET( HEAP, AlignMask ), "AlignMask",
|
|
FIELD_OFFSET( HEAP, VirtualAllocdBlocks ), "VirtualAllocdBlocks",
|
|
FIELD_OFFSET( HEAP, Segments ), "Segments",
|
|
FIELD_OFFSET( HEAP, u ), "FreeListsInUse",
|
|
FIELD_OFFSET( HEAP, FreeListsInUseTerminate ), "FreeListsInUseTerminate",
|
|
FIELD_OFFSET( HEAP, AllocatorBackTraceIndex ), "AllocatorBackTraceIndex",
|
|
FIELD_OFFSET( HEAP, TraceBuffer ), "TraceBuffer",
|
|
FIELD_OFFSET( HEAP, EventLogMask ), "EventLogMask",
|
|
FIELD_OFFSET( HEAP, PseudoTagEntries ), "PseudoTagEntries",
|
|
FIELD_OFFSET( HEAP, FreeLists ), "FreeLists",
|
|
FIELD_OFFSET( HEAP, LockVariable ), "LockVariable",
|
|
FIELD_OFFSET( HEAP, Reserved ), "Reserved",
|
|
sizeof( HEAP ), "Uncommitted Ranges",
|
|
0xFFFF, NULL
|
|
};
|
|
|
|
BOOL
|
|
ValidateHeapHeader(
|
|
IN PVOID HeapAddress,
|
|
IN PHEAP Heap
|
|
)
|
|
{
|
|
PVOID CurrentHeaderValidate;
|
|
PVOID PreviousHeaderValidate;
|
|
ULONG i, n, nEqual;
|
|
BOOL b;
|
|
|
|
if (Heap->Signature != HEAP_SIGNATURE) {
|
|
dprintf( "Heap at %x contains invalid signature.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
n = Heap->HeaderValidateLength;
|
|
if (n == 0 || Heap->HeaderValidateCopy == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
b = FALSE;
|
|
CurrentHeaderValidate = malloc( n );
|
|
if (CurrentHeaderValidate != NULL) {
|
|
PreviousHeaderValidate = malloc( n );
|
|
if (PreviousHeaderValidate != NULL) {
|
|
b = ReadMemory( (DWORD)HeapAddress,
|
|
CurrentHeaderValidate,
|
|
n,
|
|
NULL
|
|
);
|
|
if (b) {
|
|
b = ReadMemory( (DWORD)Heap->HeaderValidateCopy,
|
|
PreviousHeaderValidate,
|
|
n,
|
|
NULL
|
|
);
|
|
if (b) {
|
|
nEqual = RtlCompareMemory( CurrentHeaderValidate,
|
|
PreviousHeaderValidate,
|
|
n
|
|
);
|
|
if (nEqual != n) {
|
|
dprintf( "HEAPEXT: Heap %x - headers modified (%x is %x instead of %x)\n",
|
|
HeapAddress,
|
|
(PCHAR)HeapAddress + nEqual,
|
|
*(PULONG)((PCHAR)CurrentHeaderValidate + nEqual),
|
|
*(PULONG)((PCHAR)PreviousHeaderValidate + nEqual)
|
|
);
|
|
for (i=0; FieldOffsets[ i ].Description != NULL; i++) {
|
|
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[ CHECK_HEAP_TAIL_SIZE ] = {
|
|
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 PHEAP_ENTRY PrevEntryAddress,
|
|
IN PHEAP_ENTRY PrevEntry,
|
|
IN PHEAP_ENTRY EntryAddress,
|
|
IN PHEAP_ENTRY Entry
|
|
)
|
|
{
|
|
UCHAR EntryTail[ CHECK_HEAP_TAIL_SIZE ];
|
|
ULONG FreeFill[ 256 ];
|
|
PVOID FreeAddress;
|
|
ULONG Size, cb, cbEqual;
|
|
BOOL b;
|
|
|
|
if (PrevEntryAddress == NULL && Entry->PreviousSize != 0) {
|
|
dprintf( " PreviousSize field is non-zero when it should be zero to mark first entry\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if (PrevEntryAddress != NULL && Entry->PreviousSize != PrevEntry->Size) {
|
|
dprintf( " PreviousSize field does not match size in previous entry\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if (Entry->Flags & HEAP_ENTRY_BUSY) {
|
|
if (Entry->Flags & HEAP_ENTRY_FILL_PATTERN) {
|
|
Size = (Entry->Size << HEAP_GRANULARITY_SHIFT) - Entry->UnusedBytes;
|
|
b = ReadMemory( (DWORD)(EntryAddress+1) + Size,
|
|
EntryTail,
|
|
sizeof( EntryTail ),
|
|
NULL
|
|
);
|
|
if (b) {
|
|
cbEqual = RtlCompareMemory( EntryTail,
|
|
CheckHeapFillPattern,
|
|
CHECK_HEAP_TAIL_SIZE
|
|
);
|
|
if (cbEqual != CHECK_HEAP_TAIL_SIZE) {
|
|
dprintf( " Heap block at %x modified at %x past requested size of %x (%x * 8 - %x)\n",
|
|
EntryAddress,
|
|
(DWORD)EntryAddress + Size + cbEqual,
|
|
Size, Entry->Size, Entry->UnusedBytes
|
|
);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
dprintf( " Unable to read tail of heap block at %x\n", EntryAddress );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (Entry->Flags & HEAP_ENTRY_FILL_PATTERN) {
|
|
Size = (Entry->Size - 2) << HEAP_GRANULARITY_SHIFT;
|
|
if (Entry->Flags & HEAP_ENTRY_EXTRA_PRESENT &&
|
|
Size > sizeof( HEAP_FREE_ENTRY_EXTRA )
|
|
) {
|
|
Size -= sizeof( HEAP_FREE_ENTRY_EXTRA );
|
|
}
|
|
FreeAddress = (PHEAP_FREE_ENTRY)EntryAddress + 1;
|
|
while (Size != 0) {
|
|
if (Size > sizeof( FreeFill )) {
|
|
cb = sizeof( FreeFill );
|
|
}
|
|
else {
|
|
cb = Size;
|
|
}
|
|
b = ReadMemory( (DWORD)FreeAddress,
|
|
FreeFill,
|
|
cb,
|
|
NULL
|
|
);
|
|
if (b) {
|
|
cbEqual = RtlCompareMemoryUlong( FreeFill, cb, FREE_HEAP_FILL );
|
|
if (cbEqual != cb) { \
|
|
dprintf( " Free Heap block %x modified at %x after it was freed\n",
|
|
EntryAddress,
|
|
(PCHAR)FreeAddress + cbEqual
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
dprintf( " Unable to portion of free heap block at %x\n", EntryAddress );
|
|
return FALSE;
|
|
}
|
|
|
|
Size -= cb;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpHeapEntry(
|
|
IN PHEAP_STATE State,
|
|
IN PHEAP_ENTRY EntryAddress,
|
|
IN PHEAP_ENTRY Entry
|
|
)
|
|
{
|
|
BOOL b;
|
|
HEAP_ENTRY_EXTRA EntryExtra;
|
|
HEAP_TAG_ENTRY TagEntry;
|
|
HEAP_FREE_ENTRY_EXTRA FreeExtra;
|
|
PVOID p;
|
|
USHORT BackTraceIndex;
|
|
|
|
dprintf( " %08x: %05x . %05x [%02x]",
|
|
EntryAddress,
|
|
Entry->PreviousSize << HEAP_GRANULARITY_SHIFT,
|
|
Entry->Size << HEAP_GRANULARITY_SHIFT,
|
|
Entry->Flags
|
|
);
|
|
BackTraceIndex = 0;
|
|
if (Entry->Flags & HEAP_ENTRY_BUSY) {
|
|
dprintf( " - busy (%x)",
|
|
(Entry->Size << HEAP_GRANULARITY_SHIFT) - Entry->UnusedBytes
|
|
);
|
|
if (Entry->Flags & HEAP_ENTRY_FILL_PATTERN) {
|
|
dprintf( ", tail fill" );
|
|
}
|
|
if (Entry->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
p = (EntryAddress + Entry->Size - 1);
|
|
b = ReadMemory( (DWORD)p, &EntryExtra, sizeof( EntryExtra ), NULL );
|
|
if (!b) {
|
|
dprintf( " - unable to read heap entry extra at %08x", p );
|
|
}
|
|
else {
|
|
BackTraceIndex = EntryExtra.AllocatorBackTraceIndex;
|
|
if (EntryExtra.Settable) {
|
|
dprintf( " (Handle %08x)", EntryExtra.Settable );
|
|
}
|
|
if (EntryExtra.TagIndex) {
|
|
if (GetHeapTagEntry( &State->Heap, EntryExtra.TagIndex, &TagEntry )) {
|
|
dprintf( " (%ws)", TagEntry.TagName );
|
|
}
|
|
else {
|
|
dprintf( " (Tag %x)", EntryExtra.TagIndex );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (Entry->SmallTagIndex) {
|
|
if (GetHeapTagEntry( &State->Heap, Entry->SmallTagIndex, &TagEntry )) {
|
|
dprintf( " (%ws)", TagEntry.TagName );
|
|
}
|
|
else {
|
|
dprintf( " (Tag %x)", Entry->SmallTagIndex );
|
|
}
|
|
}
|
|
|
|
if (Entry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) {
|
|
dprintf( ", user flags (%x)", (Entry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) >> 5 );
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
}
|
|
else {
|
|
if (Entry->Flags & HEAP_ENTRY_FILL_PATTERN) {
|
|
dprintf( " free fill" );
|
|
}
|
|
|
|
if (Entry->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
p = ((PHEAP_FREE_ENTRY_EXTRA)(EntryAddress + Entry->Size)) - 1;
|
|
b = ReadMemory( (DWORD)p, &FreeExtra, sizeof( FreeExtra ), NULL );
|
|
if (!b) {
|
|
dprintf( " - unable to read heap free extra at %08x", p );
|
|
}
|
|
else {
|
|
BackTraceIndex = FreeExtra.FreeBackTraceIndex;
|
|
if (GetHeapTagEntry( &State->Heap, FreeExtra.TagIndex, &TagEntry )) {
|
|
dprintf( " (%ws)", TagEntry.TagName );
|
|
}
|
|
else {
|
|
dprintf( " (Tag %x at %x)", FreeExtra.TagIndex, p );
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
}
|
|
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
DumpStackBackTraceIndex( State, BackTraceIndex );
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
return;
|
|
}
|
|
|
|
|
|
#if STACK_TRACE_DATABASE_SUPPORT
|
|
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[ 64 ];
|
|
ULONG Displacement;
|
|
|
|
ULONG NumberOfEntriesAdded;
|
|
PRTL_STACK_TRACE_ENTRY *EntryIndexArray; // Indexed by [-1 .. -NumberOfEntriesAdded]
|
|
|
|
if (State->DumpStackBackTrace &&
|
|
BackTraceIndex != 0 &&
|
|
pRtlpStackTraceDataBase != NULL
|
|
) {
|
|
if (!HaveCopyOfStackTraceDataBase) {
|
|
b = ReadMemory( (DWORD)pRtlpStackTraceDataBase,
|
|
&RtlpStackTraceDataBase,
|
|
sizeof( RtlpStackTraceDataBase ),
|
|
NULL
|
|
);
|
|
if (!b || RtlpStackTraceDataBase == NULL) {
|
|
State->DumpStackBackTrace = FALSE;
|
|
return;
|
|
}
|
|
|
|
b = ReadMemory( (DWORD)RtlpStackTraceDataBase,
|
|
&StackTraceDataBase,
|
|
sizeof( StackTraceDataBase ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
State->DumpStackBackTrace = FALSE;
|
|
return;
|
|
}
|
|
|
|
HaveCopyOfStackTraceDataBase = TRUE;
|
|
}
|
|
|
|
if (BackTraceIndex < StackTraceDataBase.NumberOfEntriesAdded) {
|
|
b = ReadMemory( (DWORD)(StackTraceDataBase.EntryIndexArray - BackTraceIndex),
|
|
&pBackTraceEntry,
|
|
sizeof( pBackTraceEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " unable to read stack back trace index (%x) entry at %x\n",
|
|
BackTraceIndex,
|
|
(DWORD)(StackTraceDataBase.EntryIndexArray - BackTraceIndex)
|
|
);
|
|
return;
|
|
}
|
|
|
|
b = ReadMemory( (DWORD)pBackTraceEntry,
|
|
&BackTraceEntry,
|
|
sizeof( BackTraceEntry ),
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
dprintf( " unable to read stack back trace entry at %x\n",
|
|
BackTraceIndex,
|
|
pBackTraceEntry
|
|
);
|
|
return;
|
|
}
|
|
|
|
dprintf( " Stack trace (%u) at %x:\n", BackTraceIndex, pBackTraceEntry );
|
|
for (i=0; i<BackTraceEntry.Depth; i++) {
|
|
GetSymbol( (LPVOID)BackTraceEntry.BackTrace[ i ],
|
|
Symbol,
|
|
&Displacement
|
|
);
|
|
dprintf( " %08x: %s", BackTraceEntry.BackTrace[ i ], Symbol );
|
|
if (Displacement != 0) {
|
|
dprintf( "+0x%x", Displacement );
|
|
}
|
|
dprintf( "\n" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // STACK_TRACE_DATABASE_SUPPORT
|
|
|
|
#if 0
|
|
int
|
|
_CRTAPI1
|
|
_wtoi(
|
|
const wchar_t *nptr
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Value;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
RtlInitUnicodeString( &UnicodeString, nptr );
|
|
Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &Value );
|
|
if (NT_SUCCESS( Status )) {
|
|
return (int)Value;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif
|