1495 lines
39 KiB
C
1495 lines
39 KiB
C
|
|
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
heapleak.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Adrian Marinescu (adrmarin) 04/17/2000
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "heap.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
ULONG64 AddressToFind;
|
|
|
|
ULONG64 HeapAddressFind;
|
|
ULONG64 SegmentAddressFind;
|
|
ULONG64 HeapEntryFind;
|
|
ULONG64 HeapEntryFindSize;
|
|
BOOLEAN Lookaside;
|
|
|
|
BOOLEAN VerifyBlocks;
|
|
|
|
ULONG64 DumpOptions = 0xffffff;
|
|
|
|
#define HEAP_DUMP_FREE_LISTS 1
|
|
#define HEAP_DUMP_GFLAGS 2
|
|
#define HEAP_DUMP_GTAGS 4
|
|
#define HEAP_DUMP_FREE_LISTS_DETAILS 8
|
|
|
|
BOOLEAN ScanVM;
|
|
|
|
ULONG
|
|
GetFieldSize (
|
|
IN LPCSTR Type,
|
|
IN LPCSTR Field
|
|
)
|
|
{
|
|
FIELD_INFO flds = {(PUCHAR)Field, NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL};
|
|
|
|
SYM_DUMP_PARAM Sym = {
|
|
sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT, 0,
|
|
NULL, NULL, NULL, 1, &flds
|
|
};
|
|
|
|
ULONG RetVal;
|
|
|
|
RetVal = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
|
|
|
|
return flds.size;
|
|
}
|
|
|
|
|
|
ULONG
|
|
ReadLongValue(LPTSTR Symbol)
|
|
{
|
|
ULONG64 Address;
|
|
ULONG Value = 0;
|
|
|
|
Address = GetExpression( Symbol );
|
|
|
|
if ( (Address == 0) ||
|
|
!ReadMemory( Address, &Value, sizeof( Value ), NULL )) {
|
|
|
|
dprintf( "HEAPEXT: Unable to read %s\n", Symbol );
|
|
}
|
|
|
|
return Value;
|
|
}
|
|
|
|
|
|
|
|
#define CheckAndPrintFlag(x)\
|
|
if (Flags & (x)) dprintf(#x" ");
|
|
|
|
void
|
|
DumpFlagDescription(ULONG Flags)
|
|
{
|
|
CheckAndPrintFlag(HEAP_NO_SERIALIZE);
|
|
CheckAndPrintFlag(HEAP_GROWABLE);
|
|
CheckAndPrintFlag(HEAP_GENERATE_EXCEPTIONS);
|
|
CheckAndPrintFlag(HEAP_ZERO_MEMORY);
|
|
CheckAndPrintFlag(HEAP_REALLOC_IN_PLACE_ONLY);
|
|
CheckAndPrintFlag(HEAP_TAIL_CHECKING_ENABLED);
|
|
CheckAndPrintFlag(HEAP_FREE_CHECKING_ENABLED);
|
|
CheckAndPrintFlag(HEAP_DISABLE_COALESCE_ON_FREE);
|
|
CheckAndPrintFlag(HEAP_CREATE_ALIGN_16);
|
|
CheckAndPrintFlag(HEAP_CREATE_ENABLE_TRACING);
|
|
}
|
|
|
|
void
|
|
DumpEntryFlagDescription(ULONG Flags)
|
|
{
|
|
if (Flags & HEAP_ENTRY_BUSY) dprintf("busy "); else dprintf("free ");
|
|
if (Flags & HEAP_ENTRY_EXTRA_PRESENT) dprintf("extra ");
|
|
if (Flags & HEAP_ENTRY_FILL_PATTERN) dprintf("fill ");
|
|
if (Flags & HEAP_ENTRY_VIRTUAL_ALLOC) dprintf("virtual ");
|
|
if (Flags & HEAP_ENTRY_LAST_ENTRY) dprintf("last ");
|
|
if (Flags & HEAP_ENTRY_SETTABLE_FLAGS) dprintf("user_flag ");
|
|
}
|
|
|
|
void
|
|
DumpEntryHeader()
|
|
{
|
|
dprintf("Entry User Heap Segment Size PrevSize Flags\n");
|
|
dprintf("----------------------------------------------------------------------\n");
|
|
}
|
|
|
|
void
|
|
DumpEntryInfo(
|
|
IN ULONG64 HeapAddress,
|
|
IN ULONG64 SegmentAddress,
|
|
ULONG64 EntryAddress
|
|
)
|
|
{
|
|
ULONG SizeOfEntry;
|
|
ULONG PreviousSize;
|
|
ULONG Flags;
|
|
ULONG Size;
|
|
UCHAR SegmentIndex;
|
|
UCHAR SmallTagIndex;
|
|
|
|
SizeOfEntry = GetTypeSize("_HEAP_ENTRY"); // same as granularity
|
|
InitTypeRead(EntryAddress, _HEAP_ENTRY);
|
|
|
|
PreviousSize = (ULONG)ReadField(PreviousSize) * SizeOfEntry;
|
|
Size = (ULONG) ReadField(Size) * SizeOfEntry;
|
|
Flags = (ULONG) ReadField(Flags);
|
|
SegmentIndex = (UCHAR) ReadField(SegmentIndex);
|
|
SmallTagIndex = (UCHAR) ReadField(SmallTagIndex);
|
|
|
|
if (SegmentIndex != 0xff) {
|
|
|
|
dprintf("%p %p %p %p %8lx %8lx ",
|
|
EntryAddress,
|
|
EntryAddress + SizeOfEntry,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
Size,
|
|
PreviousSize
|
|
);
|
|
|
|
DumpEntryFlagDescription(Flags);
|
|
|
|
if (Lookaside) {
|
|
dprintf(" - lookaside ");
|
|
}
|
|
|
|
} else {
|
|
|
|
ULONG64 SubSegment = ReadField(SubSegment);
|
|
ULONG64 BlockSize;
|
|
|
|
GetFieldValue(SubSegment, "ntdll!_HEAP_SUBSEGMENT", "BlockSize", BlockSize);
|
|
|
|
Size = (ULONG)BlockSize * SizeOfEntry;
|
|
|
|
dprintf("%p %p %p %p %8lx - ",
|
|
EntryAddress,
|
|
EntryAddress + SizeOfEntry,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
Size
|
|
);
|
|
|
|
if (SmallTagIndex) {
|
|
|
|
dprintf("LFH_BUSY; ");
|
|
} else {
|
|
dprintf("LFH_FREE; ");
|
|
}
|
|
DumpEntryFlagDescription(Flags);
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
void
|
|
DumpHeapStructure (ULONG64 HeapAddress)
|
|
{
|
|
ULONG AlignRound, Offset;
|
|
|
|
if (InitTypeRead(HeapAddress, _HEAP)) {
|
|
|
|
return;
|
|
}
|
|
|
|
GetFieldOffset("_HEAP", "VirtualAllocdBlocks", &Offset);
|
|
AlignRound = (ULONG)ReadField(AlignRound) - GetTypeSize( "_HEAP_ENTRY" );
|
|
|
|
if ((ULONG)ReadField(Flags) & HEAP_TAIL_CHECKING_ENABLED) {
|
|
|
|
AlignRound -= CHECK_HEAP_TAIL_SIZE;
|
|
}
|
|
|
|
AlignRound += 1;
|
|
|
|
dprintf( " Flags: %08x ", (ULONG)ReadField(Flags) );
|
|
DumpFlagDescription((ULONG)ReadField(Flags)); dprintf("\n");
|
|
|
|
dprintf( " ForceFlags: %08x ", (ULONG)ReadField(ForceFlags) );
|
|
DumpFlagDescription((ULONG)ReadField(ForceFlags)); dprintf("\n");
|
|
|
|
dprintf( " Granularity: %u bytes\n", AlignRound );
|
|
dprintf( " Segment Reserve: %08x\n", (ULONG)ReadField(SegmentReserve));
|
|
dprintf( " Segment Commit: %08x\n", (ULONG)ReadField(SegmentCommit) );
|
|
dprintf( " DeCommit Block Thres:%08x\n", (ULONG)ReadField(DeCommitFreeBlockThreshold) );
|
|
dprintf( " DeCommit Total Thres:%08x\n", (ULONG)ReadField(DeCommitTotalFreeThreshold) );
|
|
dprintf( " Total Free Size: %08x\n", (ULONG)ReadField(TotalFreeSize) );
|
|
dprintf( " Max. Allocation Size:%08x\n", (ULONG)ReadField(MaximumAllocationSize) );
|
|
dprintf( " Lock Variable at: %p\n", ReadField(LockVariable) );
|
|
dprintf( " Next TagIndex: %04x\n", (ULONG)ReadField(NextAvailableTagIndex) );
|
|
dprintf( " Maximum TagIndex: %04x\n", (ULONG)ReadField(MaximumTagIndex) );
|
|
dprintf( " Tag Entries: %08x\n", (ULONG)ReadField(TagEntries) );
|
|
dprintf( " PsuedoTag Entries: %08x\n", (ULONG)ReadField(PseudoTagEntries) );
|
|
dprintf( " Virtual Alloc List: %p\n", HeapAddress + Offset);
|
|
|
|
if (DumpOptions & (HEAP_DUMP_FREE_LISTS | HEAP_DUMP_FREE_LISTS_DETAILS)) {
|
|
|
|
ULONG i, ListSize, FreeListOffset;
|
|
|
|
dprintf( " FreeList Usage: %08x %08x %08x %08x\n",
|
|
(ULONG)ReadField(u.FreeListsInUseUlong[0]),
|
|
(ULONG)ReadField(u.FreeListsInUseUlong[1]),
|
|
(ULONG)ReadField(u.FreeListsInUseUlong[2]),
|
|
(ULONG)ReadField(u.FreeListsInUseUlong[3])
|
|
);
|
|
|
|
GetFieldOffset ("_HEAP", "FreeLists", &Offset);
|
|
ListSize = GetTypeSize("LIST_ENTRY");
|
|
GetFieldOffset ("_HEAP_FREE_ENTRY", "FreeList", &FreeListOffset);
|
|
|
|
for (i=0; i<HEAP_MAXIMUM_FREELISTS; i++) {
|
|
|
|
ULONG64 Flink, Blink, Next;
|
|
ULONG64 FreeListHead;
|
|
ULONG Count = 0;
|
|
ULONG MinSize = 0, MaxSize = 0;
|
|
|
|
FreeListHead = HeapAddress + Offset + ListSize * i;
|
|
|
|
GetFieldValue(FreeListHead, "LIST_ENTRY", "Flink", Flink);
|
|
GetFieldValue(FreeListHead, "LIST_ENTRY", "Blink", Blink);
|
|
|
|
if (Flink != FreeListHead) {
|
|
|
|
dprintf( " FreeList[ %02x ] at %08p",
|
|
i,
|
|
FreeListHead
|
|
);
|
|
|
|
if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
Next = Flink;
|
|
while (Next != FreeListHead) {
|
|
|
|
ULONG Size, PrevSize, Flags;
|
|
ULONG64 FreeEntryAddress;
|
|
|
|
FreeEntryAddress = Next - FreeListOffset;
|
|
|
|
if (InitTypeRead ( FreeEntryAddress, _HEAP_FREE_ENTRY)) {
|
|
|
|
if (Count) {
|
|
dprintf( " Total: %ld blocks (%08lx, %08lx) * Error reading %p\n",
|
|
Count,
|
|
MinSize,
|
|
MaxSize,
|
|
FreeEntryAddress
|
|
);
|
|
} else {
|
|
dprintf( " No blocks in list. Error reading %p\n",
|
|
FreeEntryAddress
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Size = (ULONG)ReadField(Size) * AlignRound;
|
|
PrevSize = (ULONG)ReadField(PreviousSize) * AlignRound;
|
|
Flags = (ULONG)ReadField(Flags);
|
|
|
|
if (!Count) {
|
|
|
|
MinSize = MaxSize = Size;
|
|
|
|
} else {
|
|
|
|
if (Size < MinSize) MinSize = Size;
|
|
if (Size > MaxSize) MaxSize = Size;
|
|
}
|
|
|
|
if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
|
|
|
|
dprintf( " %08p: %05x . %05x [%02x] - ",
|
|
FreeEntryAddress,
|
|
PrevSize,
|
|
Size,
|
|
Flags
|
|
);
|
|
|
|
DumpEntryFlagDescription(Flags);
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
Count += 1;
|
|
|
|
ReadPointer(Next, &Next);
|
|
|
|
if (CheckControlC()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Count) {
|
|
|
|
if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
|
|
|
|
dprintf(" ");
|
|
}
|
|
|
|
dprintf( " * Total %ld block(s) (%08lx, %08lx)\n",
|
|
Count,
|
|
MinSize,
|
|
MaxSize
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DumpGlobals()
|
|
{
|
|
ULONG64 pNtGlobalFlag, NtGlobalFlag = 0;
|
|
ULONG64 pNtTempGlobal, NtTempGlobal = 0;
|
|
|
|
NtGlobalFlag = ReadLongValue("NTDLL!NtGlobalFlag");
|
|
|
|
if ((NtGlobalFlag & (FLG_HEAP_ENABLE_TAIL_CHECK |
|
|
FLG_HEAP_ENABLE_FREE_CHECK |
|
|
FLG_HEAP_VALIDATE_PARAMETERS |
|
|
FLG_HEAP_VALIDATE_ALL |
|
|
FLG_HEAP_ENABLE_TAGGING |
|
|
FLG_USER_STACK_TRACE_DB |
|
|
FLG_HEAP_DISABLE_COALESCING )) != 0 ) {
|
|
|
|
dprintf( "NtGlobalFlag enables following debugging aids for new heaps:" );
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) {
|
|
dprintf( " tail checking\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) {
|
|
dprintf( " free checking\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) {
|
|
dprintf( " validate parameters\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) {
|
|
dprintf( " validate on call\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_ENABLE_TAGGING) {
|
|
dprintf( " heap tagging\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
|
|
dprintf( " stack back traces\n" );
|
|
}
|
|
|
|
if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {
|
|
dprintf( " disable coalescing of free blocks\n" );
|
|
}
|
|
}
|
|
|
|
NtTempGlobal = ReadLongValue( "NTDLL!RtlpDisableHeapLookaside" );
|
|
|
|
if (NtTempGlobal) {
|
|
|
|
dprintf( "The process has the following heap extended settings %08lx:\n", (ULONG)NtTempGlobal );
|
|
|
|
if (NtTempGlobal & 1) {
|
|
|
|
dprintf(" - Lookasides disabled\n");
|
|
}
|
|
|
|
if (NtTempGlobal & 2) {
|
|
|
|
dprintf(" - Large blocks cache disabled\n");
|
|
}
|
|
|
|
if (NtTempGlobal & 8) {
|
|
|
|
dprintf(" - Low Fragmentation Heap activated for all heaps\n");
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
pNtTempGlobal = GetExpression( "NTDLL!RtlpAffinityState" );
|
|
|
|
if ( pNtTempGlobal ) {
|
|
|
|
ULONG64 TempValue;
|
|
ULONG64 AffinitySwaps;
|
|
ULONG64 AffinityResets;
|
|
ULONG64 AffinityAllocs;
|
|
|
|
GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "Limit", TempValue);
|
|
|
|
if (TempValue) {
|
|
|
|
dprintf("Affinity manager status:\n");
|
|
dprintf(" - Virtual affinity limit %I64ld\n", TempValue);
|
|
|
|
GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "CrtLimit", TempValue);
|
|
dprintf(" - Current entries in use %ld\n", (LONG)TempValue);
|
|
|
|
GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinitySwaps", AffinitySwaps);
|
|
GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinityResets", AffinityResets);
|
|
GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinityAllocs", AffinityAllocs);
|
|
|
|
dprintf(" - Statistics: Swaps=%I64ld, Resets=%I64ld, Allocs=%I64ld\n\n",
|
|
AffinitySwaps,
|
|
AffinityResets,
|
|
AffinityAllocs
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN HeapFindRoutine(
|
|
IN ULONG Context,
|
|
IN ULONG64 HeapAddress,
|
|
IN ULONG64 SegmentAddress,
|
|
IN ULONG64 EntryAddress,
|
|
IN ULONG64 Data
|
|
)
|
|
{
|
|
switch (Context) {
|
|
|
|
case CONTEXT_START_HEAP:
|
|
|
|
//
|
|
// we found a block, we won't need then to search in other heaps
|
|
//
|
|
|
|
if (HeapEntryFind) {
|
|
ScanLevel = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case CONTEXT_FREE_BLOCK:
|
|
case CONTEXT_BUSY_BLOCK:
|
|
case CONTEXT_LOOKASIDE_BLOCK:
|
|
case CONTEXT_VIRTUAL_BLOCK:
|
|
|
|
if ((AddressToFind >= EntryAddress) &&
|
|
(AddressToFind < (EntryAddress + Data))) {
|
|
|
|
if (HeapEntryFind == 0) {
|
|
|
|
HeapAddressFind = HeapAddress;
|
|
SegmentAddressFind = SegmentAddress;
|
|
}
|
|
|
|
if (Context == CONTEXT_LOOKASIDE_BLOCK) {
|
|
|
|
Lookaside = TRUE;
|
|
|
|
ScanLevel = 0;
|
|
}
|
|
|
|
HeapEntryFind = EntryAddress;
|
|
HeapEntryFindSize = Data;
|
|
}
|
|
|
|
break;
|
|
|
|
case CONTEXT_ERROR:
|
|
|
|
dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
Data
|
|
);
|
|
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SearchVMReference (
|
|
HANDLE hProcess,
|
|
ULONG64 Base,
|
|
ULONG64 EndAddress
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T BufferLen;
|
|
ULONG_PTR lpAddress = 0;
|
|
MEMORY_BASIC_INFORMATION Buffer;
|
|
PVOID MemoryBuffer;
|
|
|
|
if ( hProcess ) {
|
|
|
|
MemoryBuffer = malloc(PageSize);
|
|
|
|
if (!MemoryBuffer) {
|
|
|
|
dprintf("Not enough memory. Abording.\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
dprintf("Search VM for address range %p - %p : ",Base, EndAddress);
|
|
|
|
BufferLen = sizeof(Buffer);
|
|
|
|
while (BufferLen) {
|
|
|
|
BufferLen = VirtualQueryEx( hProcess,
|
|
(LPVOID)lpAddress,
|
|
&Buffer,
|
|
sizeof(Buffer)
|
|
);
|
|
|
|
if (BufferLen) {
|
|
|
|
if ((Buffer.AllocationProtect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) /*&&
|
|
(Buffer.Type == MEM_PRIVATE)*/) {
|
|
|
|
ULONG64 NumPages;
|
|
ULONG i, j;
|
|
|
|
NumPages = Buffer.RegionSize / PageSize;
|
|
|
|
for (i = 0; i < NumPages; i++) {
|
|
|
|
if (ReadMemory( (ULONG64)(lpAddress + i * PageSize),
|
|
MemoryBuffer,
|
|
PageSize,
|
|
NULL
|
|
)) {
|
|
|
|
ULONG_PTR * Pointers = (ULONG_PTR *)MemoryBuffer;
|
|
|
|
for (j = 0; j < PageSize/sizeof(ULONG_PTR); j++) {
|
|
|
|
ULONG_PTR Address = lpAddress + i * PageSize + j * sizeof(ULONG_PTR);
|
|
|
|
if ((*Pointers >= Base) &&
|
|
(*Pointers <= EndAddress)) {
|
|
|
|
dprintf("%08lx (%08lx), ",
|
|
Address,
|
|
*Pointers
|
|
);
|
|
}
|
|
|
|
Pointers += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lpAddress += Buffer.RegionSize;
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
free(MemoryBuffer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
HeapStat(LPCTSTR szArguments);
|
|
|
|
VOID
|
|
HeapFindBlock(LPCTSTR szArguments)
|
|
{
|
|
ULONG64 Process;
|
|
ULONG64 ThePeb;
|
|
HANDLE hProcess;
|
|
|
|
|
|
if (!InitializeHeapExtension()) {
|
|
|
|
return;
|
|
}
|
|
|
|
HeapEntryFind = 0;
|
|
HeapEntryFindSize = 0;
|
|
HeapAddressFind = 0;
|
|
SegmentAddressFind = 0;
|
|
Lookaside = FALSE;
|
|
AddressToFind = 0;
|
|
|
|
GetPebAddress( 0, &ThePeb);
|
|
GetCurrentProcessHandle( &hProcess );
|
|
|
|
ScanVM = FALSE;
|
|
|
|
{
|
|
LPSTR p = (LPSTR)szArguments;
|
|
|
|
while (p && *p) {
|
|
|
|
if (*p == '-') {
|
|
|
|
p++;
|
|
|
|
if (toupper(*p) == 'V') {
|
|
|
|
ScanVM = TRUE;
|
|
}
|
|
|
|
} else if (isxdigit(*p)) {
|
|
|
|
sscanf( p, "%I64lx", &AddressToFind );
|
|
|
|
while ((*p) && isxdigit(*p)) {
|
|
|
|
p++;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
}
|
|
|
|
if (AddressToFind == 0) {
|
|
|
|
dprintf("Syntax:\n!heap -x [-v] address\n");
|
|
return;
|
|
}
|
|
|
|
ScanProcessHeaps( 0,
|
|
ThePeb,
|
|
HeapFindRoutine
|
|
);
|
|
|
|
if (HeapEntryFind) {
|
|
|
|
DumpEntryHeader();
|
|
|
|
DumpEntryInfo(HeapAddressFind, SegmentAddressFind, HeapEntryFind);
|
|
|
|
dprintf("\n");
|
|
|
|
if (ScanVM) {
|
|
|
|
SearchVMReference(hProcess, HeapEntryFind, HeapEntryFind + HeapEntryFindSize - 1);
|
|
}
|
|
} else {
|
|
|
|
if (ScanVM) {
|
|
SearchVMReference(hProcess, AddressToFind, AddressToFind);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Heap stat implementation
|
|
//
|
|
|
|
typedef struct _SIZE_INFO {
|
|
ULONG Busy;
|
|
ULONG Free;
|
|
ULONG FrontHeapAllocs;
|
|
ULONG FrontHeapFrees;
|
|
}SIZE_INFO, *PSIZE_INFO;
|
|
|
|
typedef struct _HEAP_STATS {
|
|
|
|
ULONG HeapIndex;
|
|
ULONG64 HeapAddress;
|
|
ULONG64 ReservedMemory;
|
|
ULONG64 CommitedMemory;
|
|
ULONG64 VirtualBytes;
|
|
ULONG64 FreeSpace;
|
|
ULONG Flags;
|
|
ULONG FreeListLength;
|
|
ULONG VirtualBlocks;
|
|
ULONG UncommitedRanges;
|
|
ULONG64 LockContention;
|
|
ULONG FrontEndHeapType;
|
|
ULONG64 FrontEndHeap;
|
|
|
|
BOOLEAN ScanningSubSegment;
|
|
|
|
}HEAP_STATS, *PHEAP_STATS;
|
|
|
|
HEAP_STATS CrtHeapStat;
|
|
PSIZE_INFO SizeInfo = NULL;
|
|
ULONG BucketSize;
|
|
ULONG LargestBucketIndex = 0;
|
|
ULONG64 StatHeapAddress;
|
|
ULONG DumpBlocksSize = 0;
|
|
|
|
ULONG
|
|
FASTCALL
|
|
HeapStatSizeToSizeIndex(ULONG64 Size)
|
|
{
|
|
ULONG Index = (ULONG)(Size / BucketSize);
|
|
|
|
if (Index >= LargestBucketIndex) {
|
|
|
|
Index = LargestBucketIndex - 1;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
|
|
VOID
|
|
HeapStatDumpBlocks()
|
|
{
|
|
ULONG Index;
|
|
ULONG64 Busy = 0, Free = 0, FEBusy = 0, FEFree = 0;
|
|
|
|
|
|
dprintf("\n Default heap Front heap \n");
|
|
dprintf(" Range (bytes) Busy Free Busy Free \n");
|
|
dprintf("----------------------------------------------- \n");
|
|
if (SizeInfo == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
for ( Index = 0; Index < LargestBucketIndex; Index++ ) {
|
|
|
|
if (SizeInfo[Index].Busy
|
|
||
|
|
SizeInfo[Index].Free
|
|
||
|
|
SizeInfo[Index].FrontHeapAllocs
|
|
||
|
|
SizeInfo[Index].FrontHeapFrees) {
|
|
|
|
dprintf("%6ld - %6ld %6ld %6ld %6ld %6ld\n",
|
|
Index * BucketSize,
|
|
(Index + 1) * BucketSize,
|
|
SizeInfo[Index].Busy,
|
|
SizeInfo[Index].Free,
|
|
SizeInfo[Index].FrontHeapAllocs,
|
|
SizeInfo[Index].FrontHeapFrees
|
|
);
|
|
|
|
FEBusy += SizeInfo[Index].FrontHeapAllocs;
|
|
FEFree += SizeInfo[Index].FrontHeapFrees;
|
|
Busy += SizeInfo[Index].Busy;
|
|
Free += SizeInfo[Index].Free;
|
|
}
|
|
}
|
|
|
|
dprintf("----------------------------------------------- \n");
|
|
dprintf(" Total %6I64d %6I64d %6I64d %6I64d \n",
|
|
Busy,
|
|
Free,
|
|
FEBusy,
|
|
FEFree
|
|
);
|
|
}
|
|
|
|
VOID
|
|
DumpBlock (
|
|
IN ULONG64 BlockAddress,
|
|
IN UCHAR Flag
|
|
)
|
|
{
|
|
UCHAR Buffer[33];
|
|
PULONG pLongArray = (PULONG)Buffer;
|
|
ULONG i;
|
|
|
|
memchr(Buffer, '?', sizeof( Buffer ));
|
|
|
|
if (!ReadMemory( BlockAddress, &Buffer, sizeof( Buffer ), NULL )) {
|
|
|
|
dprintf("%p ?\n", BlockAddress);
|
|
|
|
return;
|
|
}
|
|
|
|
dprintf("%p %c %08lx %08lx %08lx %08lx ",
|
|
BlockAddress,
|
|
Flag,
|
|
pLongArray[0],
|
|
pLongArray[1],
|
|
pLongArray[2],
|
|
pLongArray[3]
|
|
);
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
if (!isprint(Buffer[i])) {
|
|
|
|
Buffer[i] = '.';
|
|
}
|
|
}
|
|
|
|
Buffer[32] = 0;
|
|
|
|
dprintf("%s\n", Buffer);
|
|
}
|
|
|
|
VOID
|
|
CollectHeapInfo(
|
|
ULONG64 HeapAddress
|
|
)
|
|
{
|
|
ULONG64 TempValue;
|
|
ULONG64 FrontEndHeapType;
|
|
|
|
memset(&CrtHeapStat, 0, sizeof(CrtHeapStat));
|
|
|
|
CrtHeapStat.HeapAddress = HeapAddress;
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "TotalFreeSize", CrtHeapStat.FreeSpace);
|
|
CrtHeapStat.FreeSpace *= HeapEntrySize;
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "NonDedicatedListLength", CrtHeapStat.FreeListLength);
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "Flags", TempValue);
|
|
CrtHeapStat.Flags = (ULONG)TempValue;
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "Signature", TempValue);
|
|
|
|
if (TempValue != 0xeeffeeff) {
|
|
dprintf("Error: Heap %p has an invalid signature %08lx\n", HeapAddress, 0xeeffeeff);
|
|
}
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "LockVariable", TempValue) == 0) {
|
|
|
|
if (TempValue != 0) {
|
|
|
|
GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION", "DebugInfo", TempValue);
|
|
GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", CrtHeapStat.LockContention);
|
|
|
|
} else {
|
|
|
|
CrtHeapStat.LockContention = 0xbad;
|
|
}
|
|
}
|
|
|
|
CrtHeapStat.FrontEndHeapType = 0;
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "Lookaside", TempValue)) {
|
|
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeapType", FrontEndHeapType)) {
|
|
|
|
dprintf("Front-end heap type info is not available\n");
|
|
|
|
} else {
|
|
|
|
CrtHeapStat.FrontEndHeapType = (ULONG)FrontEndHeapType;
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", CrtHeapStat.FrontEndHeap);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (TempValue) {
|
|
|
|
CrtHeapStat.FrontEndHeapType = 1;
|
|
}
|
|
|
|
CrtHeapStat.FrontEndHeap = TempValue;
|
|
}
|
|
}
|
|
|
|
typedef struct _BUCKET_INFO {
|
|
|
|
ULONG TotalBlocks;
|
|
ULONG SubSegmentCounts;
|
|
ULONG BlockUnits;
|
|
ULONG UseAffinity;
|
|
LONG Conversions;
|
|
|
|
} BUCKET_INFO, *PBUCKET_INFO;
|
|
|
|
VOID
|
|
DumpLowfHeap(
|
|
ULONG64 HeapAddress
|
|
)
|
|
{
|
|
ULONG64 TempValue, CrtAddress;
|
|
ULONG64 Head;
|
|
ULONG TempOffset, i;
|
|
ULONG64 Next;
|
|
ULONG Counter, TempSize;
|
|
ULONG Values[20];
|
|
PBUCKET_INFO BucketInfo;
|
|
ULONG CacheSize = GetFieldSize("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks") / sizeof(ULONG);
|
|
|
|
if (CacheSize > 20) {
|
|
|
|
CacheSize = 20;
|
|
}
|
|
|
|
InitTypeRead(HeapAddress, _LFH_HEAP);
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "Lock.DebugInfo", TempValue) == 0) {
|
|
|
|
if (TempValue != 0) {
|
|
|
|
ULONG64 Contention;
|
|
|
|
GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", Contention);
|
|
|
|
dprintf(" Lock contention %7ld\n", (ULONG) Contention);
|
|
}
|
|
}
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "SubSegmentZones.Flink", Head);
|
|
|
|
Counter = 0;
|
|
|
|
ReadPtr(Head, &Next);
|
|
|
|
while (Next != Head) {
|
|
|
|
Counter += 1;
|
|
|
|
if (ReadPtr(Next, &Next)) {
|
|
|
|
dprintf("ERROR Cannot read SubSegmentZones list at %p\n", Next);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
dprintf(" Metadata usage %7ld\n", Counter * 1024);
|
|
|
|
dprintf(" Statistics:\n");
|
|
dprintf(" Segments created %7ld\n", ReadField(SegmentCreate));
|
|
dprintf(" Segments deleted %7ld\n", ReadField(SegmentDelete));
|
|
dprintf(" Segments reused %7ld\n", ReadField(SegmentInsertInFree));
|
|
dprintf(" Conversions %7ld\n", ReadField(Conversions));
|
|
dprintf(" ConvertedSpace %7ld\n\n", ReadField(ConvertedSpace));
|
|
|
|
GetFieldOffset("ntdll!_LFH_HEAP", "UserBlockCache", &TempOffset);
|
|
|
|
CrtAddress = TempValue = HeapAddress + TempOffset;
|
|
|
|
InitTypeRead(TempValue, _USER_MEMORY_CACHE);
|
|
dprintf(" Block cache:\n");
|
|
dprintf(" Free blocks %7ld\n", ReadField(FreeBlocks));
|
|
dprintf(" Sequence %7ld\n", ReadField(Sequence));
|
|
dprintf(" Cache blocks");
|
|
|
|
for (i = 0; i < CacheSize; i++) {
|
|
|
|
ULONG64 Depth;
|
|
|
|
GetFieldValue(TempValue, "ntdll!_SLIST_HEADER", "Depth", Depth);
|
|
|
|
dprintf(" %6ld", (ULONG)Depth);
|
|
TempValue += GetTypeSize("_SLIST_HEADER");
|
|
}
|
|
|
|
GetFieldOffset("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks", &TempOffset);
|
|
dprintf("\n Available ");
|
|
|
|
|
|
TempValue = CrtAddress + TempOffset;
|
|
|
|
if (ReadMemory( TempValue, &Values, CacheSize * sizeof(ULONG), NULL )) {
|
|
|
|
for (i = 0; i < CacheSize; i++) {
|
|
|
|
dprintf(" %6ld", Values[i]);
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
GetFieldOffset("ntdll!_LFH_HEAP", "Buckets", &TempOffset);
|
|
|
|
CrtAddress = HeapAddress + TempOffset;
|
|
TempSize = GetTypeSize("_HEAP_BUCKET");
|
|
|
|
BucketInfo = (PBUCKET_INFO)malloc(128 * sizeof(BUCKET_INFO));
|
|
|
|
if (BucketInfo) {
|
|
|
|
dprintf(" Buckets info:\n");
|
|
dprintf(" Size Blocks Seg Aff Conv\n");
|
|
dprintf("-----------------------------\n");
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
|
|
InitTypeRead(CrtAddress + (i * TempSize), _HEAP_BUCKET);
|
|
|
|
BucketInfo[i].TotalBlocks = (ULONG)ReadField(Counters.TotalBlocks);
|
|
BucketInfo[i].BlockUnits = (ULONG)ReadField(BlockUnits);
|
|
BucketInfo[i].Conversions = (ULONG)ReadField(Conversions);
|
|
BucketInfo[i].SubSegmentCounts = (ULONG)ReadField(Counters.SubSegmentCounts);
|
|
BucketInfo[i].UseAffinity = (ULONG)ReadField(UseAffinity);
|
|
|
|
if (BucketInfo[i].TotalBlocks) {
|
|
|
|
dprintf("%6ld %7ld %5ld %ld %4ld\n",
|
|
BucketInfo[i].BlockUnits*HeapEntrySize,
|
|
BucketInfo[i].TotalBlocks,
|
|
BucketInfo[i].SubSegmentCounts,
|
|
BucketInfo[i].UseAffinity,
|
|
BucketInfo[i].Conversions
|
|
);
|
|
}
|
|
}
|
|
dprintf("-----------------------------\n");
|
|
|
|
free(BucketInfo);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpHeapInfo(
|
|
ULONG64 HeapAddress
|
|
)
|
|
{
|
|
ULONG64 TempValue;
|
|
|
|
dprintf("\n%2ld: Heap %p\n",
|
|
CrtHeapStat.HeapIndex,
|
|
CrtHeapStat.HeapAddress);
|
|
|
|
dprintf(" Flags %08lx - ", CrtHeapStat.Flags);
|
|
DumpFlagDescription(CrtHeapStat.Flags);
|
|
dprintf("\n");
|
|
|
|
dprintf(" Reserved %I64d (k)\n", CrtHeapStat.ReservedMemory/1024);
|
|
dprintf(" Commited %I64d (k)\n", CrtHeapStat.CommitedMemory/1024);
|
|
dprintf(" Virtual bytes %I64d (k)\n", CrtHeapStat.VirtualBytes/1024);
|
|
dprintf(" Free space %I64d (k)\n", CrtHeapStat.FreeSpace/1024);
|
|
|
|
|
|
if (CrtHeapStat.CommitedMemory) {
|
|
dprintf(" External fragmentation %ld%% (%ld free blocks)\n",
|
|
(ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
|
|
CrtHeapStat.FreeListLength
|
|
);
|
|
|
|
}
|
|
|
|
if (CrtHeapStat.VirtualBytes) {
|
|
dprintf(" Virtual address fragmentation %ld%% (%ld uncommited ranges)\n",
|
|
(ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
|
|
CrtHeapStat.UncommitedRanges
|
|
);
|
|
}
|
|
|
|
|
|
dprintf(" Virtual blocks %ld\n", CrtHeapStat.VirtualBlocks);
|
|
dprintf(" Lock contention %ld\n", CrtHeapStat.LockContention);
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "LastSegmentIndex", TempValue);
|
|
dprintf(" Segments %ld\n", (ULONG)TempValue + 1);
|
|
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "LargeBlocksIndex", TempValue);
|
|
|
|
if (TempValue) {
|
|
|
|
ULONG PerfOffset;
|
|
|
|
InitTypeRead(TempValue, _HEAP_INDEX);
|
|
|
|
dprintf(" %ld hash table for the free list\n", (ULONG) ReadField(ArraySize));
|
|
dprintf(" Commits %ld\n", (ULONG) ReadField(Committs));
|
|
dprintf(" Decommitts %ld\n", (ULONG) ReadField(Decommitts));
|
|
}
|
|
|
|
switch (CrtHeapStat.FrontEndHeapType) {
|
|
case 1:
|
|
|
|
dprintf("\n Lookaside heap %p\n", CrtHeapStat.FrontEndHeap);
|
|
break;
|
|
case 2:
|
|
|
|
dprintf("\n Low fragmentation heap %p\n", CrtHeapStat.FrontEndHeap);
|
|
|
|
DumpLowfHeap(CrtHeapStat.FrontEndHeap);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
HeapStatRoutine(
|
|
IN ULONG Context,
|
|
IN ULONG64 HeapAddress,
|
|
IN ULONG64 SegmentAddress,
|
|
IN ULONG64 EntryAddress,
|
|
IN ULONG64 Data
|
|
)
|
|
{
|
|
ULONG SizeIndex;
|
|
|
|
switch (Context) {
|
|
|
|
case CONTEXT_START_HEAP:
|
|
{
|
|
if (DumpBlocksSize == 0) {
|
|
dprintf("Walking the heap %p ", HeapAddress);
|
|
}
|
|
|
|
CollectHeapInfo(HeapAddress);
|
|
|
|
//
|
|
// Allow scanning the heap
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case CONTEXT_END_HEAP:
|
|
|
|
if (DumpBlocksSize == 0) {
|
|
dprintf("\r");
|
|
}
|
|
if (SizeInfo == NULL) {
|
|
|
|
dprintf("%p %08lx %7I64d %6I64d %6I64d %6I64d %5ld %5ld %4ld %6lx ",
|
|
CrtHeapStat.HeapAddress,
|
|
CrtHeapStat.Flags,
|
|
(CrtHeapStat.ReservedMemory / 1024),
|
|
(CrtHeapStat.CommitedMemory / 1024),
|
|
(CrtHeapStat.VirtualBytes / 1024),
|
|
(CrtHeapStat.FreeSpace / 1024),
|
|
CrtHeapStat.FreeListLength,
|
|
CrtHeapStat.UncommitedRanges,
|
|
CrtHeapStat.VirtualBlocks,
|
|
CrtHeapStat.LockContention
|
|
);
|
|
|
|
switch (CrtHeapStat.FrontEndHeapType) {
|
|
case 1:
|
|
dprintf("L ");
|
|
break;
|
|
case 2:
|
|
dprintf("LFH");
|
|
break;
|
|
default:
|
|
dprintf(" ");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
//
|
|
// Report external fragmentation is the heap uses more than 1M
|
|
// The threshold to report is 10%
|
|
//
|
|
|
|
if ((CrtHeapStat.CommitedMemory > 1024*1024)
|
|
&&
|
|
CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory > 10) {
|
|
|
|
dprintf(" External fragmentation %ld %% (%ld free blocks)\n",
|
|
(ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
|
|
CrtHeapStat.FreeListLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// Report virtual address fragmentation is the heap has more than 100 UCR
|
|
// The threshold to report is 10%
|
|
//
|
|
|
|
if (CrtHeapStat.UncommitedRanges > 100
|
|
&&
|
|
(CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes > 10) {
|
|
|
|
dprintf(" Virtual address fragmentation %ld %% (%ld uncommited ranges)\n",
|
|
(ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
|
|
CrtHeapStat.UncommitedRanges
|
|
);
|
|
}
|
|
|
|
//
|
|
// Make noise about lock contention. The value is 1M, and it's arbitrary.
|
|
// Over a long run this value can be legitimate
|
|
//
|
|
|
|
if (CrtHeapStat.LockContention > 1024*1024) {
|
|
|
|
dprintf(" Lock contention %ld \n",
|
|
CrtHeapStat.LockContention);
|
|
}
|
|
} else {
|
|
|
|
DumpHeapInfo(StatHeapAddress);
|
|
HeapStatDumpBlocks();
|
|
}
|
|
break;
|
|
|
|
case CONTEXT_START_SEGMENT:
|
|
|
|
{
|
|
ULONG64 NumberOfPages, NumberOfUnCommittedPages, LargestUnCommittedRange, NumberOfUnCommittedRanges;
|
|
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedPages", NumberOfUnCommittedPages);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "LargestUnCommittedRange", LargestUnCommittedRange);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedRanges", NumberOfUnCommittedRanges);
|
|
|
|
CrtHeapStat.ReservedMemory += NumberOfPages * PageSize;
|
|
CrtHeapStat.CommitedMemory += (NumberOfPages - NumberOfUnCommittedPages) * PageSize;
|
|
CrtHeapStat.UncommitedRanges += (ULONG)NumberOfUnCommittedRanges;
|
|
CrtHeapStat.VirtualBytes += NumberOfPages * PageSize - LargestUnCommittedRange;
|
|
|
|
}
|
|
|
|
if ((SizeInfo != NULL)
|
|
&&
|
|
(DumpBlocksSize == 0)
|
|
) {
|
|
|
|
dprintf(".");
|
|
}
|
|
|
|
if (VerifyBlocks) {
|
|
|
|
dprintf(".");
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Do not walk the blocks. We need to return FALSE
|
|
//
|
|
|
|
return (SizeInfo != NULL);
|
|
|
|
case CONTEXT_START_SUBSEGMENT:
|
|
|
|
CrtHeapStat.ScanningSubSegment = TRUE;
|
|
break;
|
|
|
|
case CONTEXT_END_SUBSEGMENT:
|
|
CrtHeapStat.ScanningSubSegment = FALSE;
|
|
break;
|
|
|
|
case CONTEXT_FREE_BLOCK:
|
|
if (SizeInfo) {
|
|
|
|
if (CrtHeapStat.ScanningSubSegment) {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
|
|
|
|
} else {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].Free += 1;
|
|
}
|
|
}
|
|
|
|
if (Data == DumpBlocksSize) {
|
|
|
|
DumpBlock(EntryAddress + HeapEntrySize, 'F');
|
|
}
|
|
break;
|
|
case CONTEXT_BUSY_BLOCK:
|
|
|
|
if (SizeInfo) {
|
|
|
|
if (CrtHeapStat.ScanningSubSegment) {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapAllocs += 1;
|
|
|
|
} else {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
|
|
}
|
|
}
|
|
if (Data == DumpBlocksSize) {
|
|
DumpBlock(EntryAddress + HeapEntrySize, 'B');
|
|
}
|
|
break;
|
|
|
|
case CONTEXT_LOOKASIDE_BLOCK:
|
|
|
|
if (SizeInfo) {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy -= 1;
|
|
}
|
|
if (Data == DumpBlocksSize) {
|
|
|
|
DumpBlock(EntryAddress + HeapEntrySize, 'f');
|
|
}
|
|
break;
|
|
case CONTEXT_VIRTUAL_BLOCK:
|
|
|
|
if (SizeInfo) {
|
|
|
|
SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
|
|
}
|
|
CrtHeapStat.VirtualBlocks += 1;
|
|
break;
|
|
|
|
case CONTEXT_ERROR:
|
|
|
|
dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
Data
|
|
);
|
|
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
HeapStat(LPCTSTR szArguments)
|
|
{
|
|
ULONG64 Process;
|
|
ULONG64 ThePeb;
|
|
HANDLE hProcess;
|
|
|
|
int LastCommand = ' ';
|
|
|
|
|
|
if (!InitializeHeapExtension()) {
|
|
|
|
return;
|
|
}
|
|
|
|
HeapEntryFind = 0;
|
|
HeapEntryFindSize = 0;
|
|
HeapAddressFind = 0;
|
|
SegmentAddressFind = 0;
|
|
Lookaside = FALSE;
|
|
StatHeapAddress = 0;
|
|
BucketSize = 1024;
|
|
DumpBlocksSize = 0;
|
|
VerifyBlocks = FALSE;
|
|
|
|
GetPebAddress( 0, &ThePeb);
|
|
GetCurrentProcessHandle( &hProcess );
|
|
|
|
ScanVM = FALSE;
|
|
|
|
{
|
|
LPSTR p = (LPSTR)szArguments;
|
|
|
|
while (p && *p) {
|
|
|
|
if (*p == '-') {
|
|
|
|
p++;
|
|
|
|
LastCommand = toupper(*p);
|
|
|
|
if (LastCommand == 'V') {
|
|
|
|
VerifyBlocks = TRUE;
|
|
}
|
|
|
|
} else if (isxdigit(*p)) {
|
|
|
|
ULONG64 HexInput;
|
|
|
|
sscanf( p, "%I64lx", &HexInput );
|
|
|
|
while ((*p) && isxdigit(*p)) {
|
|
|
|
p++;
|
|
}
|
|
|
|
switch (LastCommand) {
|
|
case 'B':
|
|
|
|
BucketSize = (ULONG)HexInput;
|
|
|
|
break;
|
|
case 'D':
|
|
|
|
DumpBlocksSize = (ULONG)HexInput;
|
|
|
|
break;
|
|
default:
|
|
|
|
if (StatHeapAddress != 0) {
|
|
|
|
dprintf("Parameter error: unexpected second heap address %I64d\n", HexInput);
|
|
|
|
} else {
|
|
StatHeapAddress = HexInput;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
}
|
|
|
|
if (StatHeapAddress == 0) {
|
|
|
|
DumpGlobals();
|
|
|
|
if (PointerSize == 8) {
|
|
dprintf(" ");
|
|
}
|
|
dprintf(" Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast \n");
|
|
|
|
if (PointerSize == 8) {
|
|
dprintf(" ");
|
|
}
|
|
|
|
dprintf(" (k) (k) (k) (k) length blocks cont. heap \n");
|
|
|
|
if (PointerSize == 8) {
|
|
dprintf("--------");
|
|
}
|
|
dprintf("-----------------------------------------------------------------------------\n");
|
|
|
|
ScanProcessHeaps( 0,
|
|
ThePeb,
|
|
HeapStatRoutine
|
|
);
|
|
|
|
|
|
if (PointerSize == 8) {
|
|
dprintf("--------");
|
|
}
|
|
dprintf("-----------------------------------------------------------------------------\n");
|
|
|
|
} else {
|
|
|
|
//
|
|
// Do not handle blocks over 512k, which is close to virtual alloc limit
|
|
//
|
|
|
|
LargestBucketIndex = (512*1024)/BucketSize;
|
|
|
|
SizeInfo = malloc(LargestBucketIndex * sizeof(SIZE_INFO));
|
|
|
|
if (SizeInfo == NULL) {
|
|
|
|
dprintf("cannot allocate thye statistics buffer\n");
|
|
return;
|
|
}
|
|
|
|
memset(SizeInfo, 0, LargestBucketIndex * sizeof(SIZE_INFO));
|
|
|
|
ScanProcessHeaps( StatHeapAddress,
|
|
ThePeb,
|
|
HeapStatRoutine
|
|
);
|
|
|
|
free(SizeInfo);
|
|
SizeInfo = NULL;
|
|
}
|
|
}
|
|
|
|
|