1756 lines
36 KiB
C
1756 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
heapleak.c
|
||
|
||
Abstract:
|
||
|
||
Garbage collection leak detection
|
||
|
||
Author:
|
||
|
||
Adrian Marinescu (adrmarin) 04-24-2000
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ntrtlp.h"
|
||
#include "heap.h"
|
||
#include "heappriv.h"
|
||
|
||
|
||
//
|
||
// heap walking contexts.
|
||
//
|
||
|
||
#define CONTEXT_START_GLOBALS 11
|
||
#define CONTEXT_START_HEAP 1
|
||
#define CONTEXT_END_HEAP 2
|
||
#define CONTEXT_START_SEGMENT 3
|
||
#define CONTEXT_END_SEGMENT 4
|
||
#define CONTEXT_FREE_BLOCK 5
|
||
#define CONTEXT_BUSY_BLOCK 6
|
||
#define CONTEXT_LOOKASIDE_BLOCK 7
|
||
#define CONTEXT_VIRTUAL_BLOCK 8
|
||
#define CONTEXT_END_BLOCKS 9
|
||
#define CONTEXT_ERROR 10
|
||
|
||
typedef BOOLEAN (*HEAP_ITERATOR_CALLBACK)(
|
||
IN ULONG Context,
|
||
IN PHEAP HeapAddress,
|
||
IN PHEAP_SEGMENT SegmentAddress,
|
||
IN PHEAP_ENTRY EntryAddress,
|
||
IN ULONG_PTR Data
|
||
);
|
||
|
||
//
|
||
// Garbage collector structures
|
||
//
|
||
|
||
typedef enum _USAGE_TYPE {
|
||
|
||
UsageUnknown,
|
||
UsageModule,
|
||
UsageHeap,
|
||
UsageOther
|
||
|
||
} USAGE_TYPE;
|
||
|
||
typedef struct _HEAP_BLOCK {
|
||
|
||
LIST_ENTRY Entry;
|
||
ULONG_PTR BlockAddress;
|
||
ULONG_PTR Size;
|
||
LONG Count;
|
||
|
||
} HEAP_BLOCK, *PHEAP_BLOCK;
|
||
|
||
typedef struct _BLOCK_DESCR {
|
||
|
||
USAGE_TYPE Type;
|
||
ULONG_PTR Heap;
|
||
LONG Count;
|
||
HEAP_BLOCK Blocks[1];
|
||
|
||
}BLOCK_DESCR, *PBLOCK_DESCR;
|
||
|
||
typedef struct _MEMORY_MAP {
|
||
|
||
ULONG_PTR Granularity;
|
||
ULONG_PTR Offset;
|
||
ULONG_PTR MaxAddress;
|
||
|
||
CHAR FlagsBitmap[256 / 8];
|
||
|
||
union{
|
||
|
||
struct _MEMORY_MAP * Details[ 256 ];
|
||
PBLOCK_DESCR Usage[ 256 ];
|
||
};
|
||
|
||
struct _MEMORY_MAP * Parent;
|
||
|
||
} MEMORY_MAP, *PMEMORY_MAP;
|
||
|
||
//
|
||
// Process leak detection flags
|
||
//
|
||
|
||
#define INSPECT_LEAKS 1
|
||
#define BREAK_ON_LEAKS 2
|
||
|
||
ULONG RtlpShutdownProcessFlags = 0;
|
||
|
||
//
|
||
// Allocation routines. It creates a temporary heap for the temporary
|
||
// leak detection structures
|
||
//
|
||
|
||
HANDLE RtlpLeakHeap;
|
||
|
||
#define RtlpLeakAllocateBlock(Size) RtlAllocateHeap(RtlpLeakHeap, 0, Size)
|
||
|
||
|
||
//
|
||
// Local data declarations
|
||
//
|
||
|
||
MEMORY_MAP RtlpProcessMemoryMap;
|
||
LIST_ENTRY RtlpBusyList;
|
||
LIST_ENTRY RtlpLeakList;
|
||
|
||
ULONG RtlpLeaksCount = 0;
|
||
|
||
|
||
ULONG_PTR RtlpLDPreviousPage = 0;
|
||
ULONG_PTR RtlpLDCrtPage = 0;
|
||
LONG RtlpLDNumBlocks = 0;
|
||
PHEAP_BLOCK RtlpTempBlocks = NULL;
|
||
ULONG_PTR RtlpCrtHeapAddress = 0;
|
||
ULONG_PTR RtlpLeakHeapAddress = 0;
|
||
ULONG_PTR RtlpPreviousStartAddress = 0;
|
||
|
||
//
|
||
// Debugging facility
|
||
//
|
||
|
||
ULONG_PTR RtlpBreakAtAddress = MAXULONG_PTR;
|
||
|
||
|
||
//
|
||
// Walking heap routines. These are general purposes routines that
|
||
// receive a callback function to handle a specific operation
|
||
//
|
||
|
||
|
||
BOOLEAN
|
||
RtlpReadHeapSegment(
|
||
IN PHEAP Heap,
|
||
IN ULONG SegmentIndex,
|
||
IN PHEAP_SEGMENT Segment,
|
||
IN HEAP_ITERATOR_CALLBACK HeapCallback
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to walk a heap segment. For each block
|
||
from the segment is invoked the HeapCallback function.
|
||
|
||
Arguments:
|
||
|
||
Heap - The heap being walked
|
||
|
||
SegmentIndex - The index of this segment
|
||
|
||
Segment - The segment to be walked
|
||
|
||
HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
|
||
|
||
Return Value:
|
||
|
||
TRUE if succeeds.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PHEAP_ENTRY PrevEntry, Entry, NextEntry;
|
||
PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
|
||
ULONG_PTR UnCommittedRangeAddress = 0;
|
||
SIZE_T UnCommittedRangeSize = 0;
|
||
|
||
//
|
||
// Ask the callback if we're required to walk this segment. Return otherwise.
|
||
//
|
||
|
||
if (!(*HeapCallback)( CONTEXT_START_SEGMENT,
|
||
Heap,
|
||
Segment,
|
||
0,
|
||
0
|
||
)) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Prepare to read the uncommitted ranges. we need to jump
|
||
// to the next uncommitted range for each last block
|
||
//
|
||
|
||
UnCommittedRange = Segment->UnCommittedRanges;
|
||
|
||
if (UnCommittedRange) {
|
||
|
||
UnCommittedRangeAddress = (ULONG_PTR)UnCommittedRange->Address;
|
||
UnCommittedRangeSize = UnCommittedRange->Size;
|
||
}
|
||
|
||
//
|
||
// Walk the segment, block by block
|
||
//
|
||
|
||
Entry = (PHEAP_ENTRY)Segment->BaseAddress;
|
||
|
||
PrevEntry = 0;
|
||
|
||
while (Entry < Segment->LastValidEntry) {
|
||
|
||
ULONG EntryFlags = Entry->Flags;
|
||
|
||
//
|
||
// Determine the next block entry. Size is in heap granularity and
|
||
// sizeof(HEAP_ENTRY) == HEAP_GRANULARITY.
|
||
//
|
||
|
||
NextEntry = Entry + Entry->Size;
|
||
|
||
(*HeapCallback)( (Entry->Flags & HEAP_ENTRY_BUSY ?
|
||
CONTEXT_BUSY_BLOCK :
|
||
CONTEXT_FREE_BLOCK),
|
||
Heap,
|
||
Segment,
|
||
Entry,
|
||
Entry->Size
|
||
);
|
||
|
||
PrevEntry = Entry;
|
||
Entry = NextEntry;
|
||
|
||
//
|
||
// Check whether this is the last entry
|
||
//
|
||
|
||
if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
|
||
|
||
if ((ULONG_PTR)Entry == UnCommittedRangeAddress) {
|
||
|
||
//
|
||
// Here we need to skip the uncommited range and jump
|
||
// to the next valid block
|
||
//
|
||
|
||
PrevEntry = 0;
|
||
Entry = (PHEAP_ENTRY)(UnCommittedRangeAddress + UnCommittedRangeSize);
|
||
|
||
UnCommittedRange = UnCommittedRange->Next;
|
||
|
||
if (UnCommittedRange) {
|
||
|
||
UnCommittedRangeAddress = UnCommittedRange->Address;
|
||
UnCommittedRangeSize = UnCommittedRange->Size;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We finished the search because we exausted the uncommitted
|
||
// ranges
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Return to our caller.
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
RtlpReadHeapData(
|
||
IN PHEAP Heap,
|
||
IN HEAP_ITERATOR_CALLBACK HeapCallback
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to walk a heap. This means:
|
||
- walking all segments
|
||
- walking the virtual blocks
|
||
- walking the lookaside
|
||
|
||
Arguments:
|
||
|
||
Heap - The heap being walked
|
||
|
||
HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
|
||
|
||
Return Value:
|
||
|
||
TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG SegmentCount;
|
||
PLIST_ENTRY Head, Next;
|
||
PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
|
||
|
||
//
|
||
// Flush the lookaside first
|
||
//
|
||
|
||
if (Lookaside != NULL) {
|
||
|
||
ULONG i;
|
||
PVOID Block;
|
||
|
||
Heap->FrontEndHeap = NULL;
|
||
Heap->FrontEndHeapType = 0;
|
||
|
||
for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
|
||
|
||
while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
|
||
|
||
RtlFreeHeap( Heap, 0, Block );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check whether we're required to walk this heap
|
||
//
|
||
|
||
if (!(*HeapCallback)( CONTEXT_START_HEAP,
|
||
Heap,
|
||
0,
|
||
0,
|
||
0
|
||
)) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Start walking through the segments
|
||
//
|
||
|
||
for (SegmentCount = 0; SegmentCount < HEAP_MAXIMUM_SEGMENTS; SegmentCount++) {
|
||
|
||
PHEAP_SEGMENT Segment = Heap->Segments[SegmentCount];
|
||
|
||
if (Segment) {
|
||
|
||
//
|
||
// Call the appropriate routine to walk a valid segment
|
||
//
|
||
|
||
RtlpReadHeapSegment( Heap,
|
||
SegmentCount,
|
||
Segment,
|
||
HeapCallback
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Start walking the virtual block list
|
||
//
|
||
|
||
Head = &Heap->VirtualAllocdBlocks;
|
||
|
||
Next = Head->Flink;
|
||
|
||
while (Next != Head) {
|
||
|
||
PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
|
||
|
||
VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
|
||
|
||
(*HeapCallback)( CONTEXT_VIRTUAL_BLOCK,
|
||
Heap,
|
||
0,
|
||
NULL,
|
||
(ULONG_PTR)VirtualAllocBlock
|
||
);
|
||
|
||
Next = Next->Flink;
|
||
}
|
||
|
||
if (!(*HeapCallback)( CONTEXT_END_BLOCKS,
|
||
Heap,
|
||
0,
|
||
0,
|
||
0
|
||
)) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpReadProcessHeaps(
|
||
IN HEAP_ITERATOR_CALLBACK HeapCallback
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to walk the existing heaps in the current process
|
||
|
||
Arguments:
|
||
|
||
HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
|
||
|
||
Return Value:
|
||
|
||
TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PPEB ProcessPeb = NtCurrentPeb();
|
||
|
||
if (!(*HeapCallback)( CONTEXT_START_GLOBALS,
|
||
0,
|
||
0,
|
||
0,
|
||
(ULONG_PTR)ProcessPeb
|
||
)) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Walk the heaps from the process PEB
|
||
//
|
||
|
||
for (i = 0; i < ProcessPeb->NumberOfHeaps; i++) {
|
||
|
||
RtlpReadHeapData ( (PHEAP)(ProcessPeb->ProcessHeaps[ i ]),
|
||
HeapCallback
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpInitializeMap (
|
||
IN PMEMORY_MAP MemMap,
|
||
IN PMEMORY_MAP Parent
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initialize a memory map structure
|
||
|
||
Arguments:
|
||
|
||
MemMap - Map being initializated
|
||
Parent - The upper level map
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Clear the memory map data
|
||
//
|
||
|
||
RtlZeroMemory(MemMap, sizeof(*MemMap));
|
||
|
||
//
|
||
// Save the upper level map
|
||
//
|
||
|
||
MemMap->Parent = Parent;
|
||
|
||
//
|
||
// Determine the granularity from the parent's granularity
|
||
//
|
||
|
||
if (Parent) {
|
||
|
||
MemMap->Granularity = Parent->Granularity / 256;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpSetBlockInfo (
|
||
IN PMEMORY_MAP MemMap,
|
||
IN ULONG_PTR Base,
|
||
IN ULONG_PTR Size,
|
||
IN PBLOCK_DESCR BlockDescr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine will set a given block descriptor for a range
|
||
in the memory map.
|
||
|
||
Arguments:
|
||
|
||
MemMap - The memory map
|
||
|
||
Base - base address for the range to be set.
|
||
|
||
Size - size in bytes of the zone
|
||
|
||
BlockDescr - The pointer to the BLOCK_DESCR structure to be set
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR Start, End;
|
||
ULONG_PTR i;
|
||
|
||
//
|
||
// Check wheter we got a valid range
|
||
//
|
||
|
||
if (((Base + Size - 1) < MemMap->Offset) ||
|
||
(Base > MemMap->MaxAddress)
|
||
) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Determine the starting index to be set
|
||
//
|
||
|
||
if (Base > MemMap->Offset) {
|
||
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
||
} else {
|
||
Start = 0;
|
||
}
|
||
|
||
//
|
||
// Determine the ending index to be set
|
||
//
|
||
|
||
End = (Base - MemMap->Offset + Size - 1) / MemMap->Granularity;
|
||
|
||
if (End > 255) {
|
||
|
||
End = 255;
|
||
}
|
||
|
||
for (i = Start; i <= End; i++) {
|
||
|
||
//
|
||
// Check whether this is the lowes memory map level
|
||
//
|
||
|
||
if (MemMap->Granularity == PAGE_SIZE) {
|
||
|
||
//
|
||
// This is the last level in the memory map, so we can apply
|
||
// the block descriptor here
|
||
//
|
||
|
||
if (BlockDescr) {
|
||
|
||
//
|
||
// Check if we already have a block descriptor here
|
||
//
|
||
|
||
if (MemMap->Usage[i] != NULL) {
|
||
if (MemMap->Usage[i] != BlockDescr) {
|
||
|
||
DbgPrint("Error\n");
|
||
}
|
||
}
|
||
|
||
//
|
||
// Assign the given descriptor
|
||
//
|
||
|
||
MemMap->Usage[i] = BlockDescr;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We didn't recedive a block descriptor. We set
|
||
// then the given flag
|
||
//
|
||
|
||
MemMap->FlagsBitmap[i / 8] |= 1 << (i % 8);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This isn't the lowest map level. We recursively call
|
||
// this function for the next detail range
|
||
//
|
||
|
||
if (!MemMap->Details[i]) {
|
||
|
||
//
|
||
// Allocate a new map
|
||
//
|
||
|
||
MemMap->Details[i] = RtlpLeakAllocateBlock( sizeof(*MemMap) );
|
||
|
||
if (!MemMap->Details[i]) {
|
||
|
||
DbgPrint("Error allocate\n");
|
||
}
|
||
|
||
//
|
||
// Initialize the map and link it with the current one
|
||
//
|
||
|
||
RtlpInitializeMap(MemMap->Details[i], MemMap);
|
||
MemMap->Details[i]->Offset = MemMap->Offset + MemMap->Granularity * i;
|
||
MemMap->Details[i]->MaxAddress = MemMap->Offset + MemMap->Granularity * (i+1) - 1;
|
||
}
|
||
|
||
RtlpSetBlockInfo(MemMap->Details[i], Base, Size, BlockDescr);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
PBLOCK_DESCR
|
||
RtlpGetBlockInfo (
|
||
IN PMEMORY_MAP MemMap,
|
||
IN ULONG_PTR Base
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function will return the appropriate Block descriptor
|
||
for a given base address
|
||
|
||
Arguments:
|
||
|
||
MemMap - The memory map
|
||
|
||
Base - The base address for the descriptor we are looking for
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR Start;
|
||
PBLOCK_DESCR BlockDescr = NULL;
|
||
|
||
//
|
||
// Validate the range
|
||
//
|
||
|
||
if ((Base < MemMap->Offset) ||
|
||
(Base > MemMap->MaxAddress)
|
||
) {
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Determine the appropriate index for lookup
|
||
//
|
||
|
||
if (Base > MemMap->Offset) {
|
||
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
||
} else {
|
||
Start = 0;
|
||
}
|
||
|
||
//
|
||
// If this is the lowest map level we'll return that entry
|
||
//
|
||
|
||
if (MemMap->Granularity == PAGE_SIZE) {
|
||
|
||
return MemMap->Usage[ Start ];
|
||
|
||
} else {
|
||
|
||
//
|
||
// We need a lower detail level call
|
||
//
|
||
|
||
if (MemMap->Details[ Start ]) {
|
||
|
||
return RtlpGetBlockInfo( MemMap->Details[Start], Base );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We didn't find something for this address, we'll return NULL then
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlpGetMemoryFlag (
|
||
IN PMEMORY_MAP MemMap,
|
||
IN ULONG_PTR Base
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the flag for a given base address
|
||
|
||
Arguments:
|
||
|
||
MemMap - The memory map
|
||
|
||
Base - The base address we want to know the flag
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR Start;
|
||
PBLOCK_DESCR BlockDescr = NULL;
|
||
|
||
//
|
||
// Validate the base address
|
||
//
|
||
|
||
if ((Base < MemMap->Offset) ||
|
||
(Base > MemMap->MaxAddress)
|
||
) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Determine the appropriate index for the given base address
|
||
//
|
||
|
||
if (Base > MemMap->Offset) {
|
||
|
||
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
||
|
||
} else {
|
||
|
||
Start = 0;
|
||
}
|
||
|
||
if (MemMap->Granularity == PAGE_SIZE) {
|
||
|
||
//
|
||
// Return the bit value if are in the case of
|
||
// the lowest detail level
|
||
//
|
||
|
||
return (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Lookup in the detailed map
|
||
//
|
||
|
||
if (MemMap->Details[Start]) {
|
||
|
||
return RtlpGetMemoryFlag(MemMap->Details[Start], Base);
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpInitializeLeakDetection ()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initialize the leak detection structures
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR AddressRange = PAGE_SIZE;
|
||
ULONG_PTR PreviousAddressRange = PAGE_SIZE;
|
||
|
||
//
|
||
// Initialize the global memory map
|
||
//
|
||
|
||
RtlpInitializeMap(&RtlpProcessMemoryMap, NULL);
|
||
|
||
//
|
||
// Initialize the lists
|
||
//
|
||
|
||
InitializeListHead( &RtlpBusyList );
|
||
InitializeListHead( &RtlpLeakList );
|
||
|
||
//
|
||
// Determine the granularity for the highest memory map level
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
AddressRange = AddressRange * 256;
|
||
|
||
if (AddressRange < PreviousAddressRange) {
|
||
|
||
RtlpProcessMemoryMap.MaxAddress = MAXULONG_PTR;
|
||
|
||
RtlpProcessMemoryMap.Granularity = PreviousAddressRange;
|
||
|
||
break;
|
||
}
|
||
|
||
PreviousAddressRange = AddressRange;
|
||
}
|
||
|
||
RtlpTempBlocks = RtlpLeakAllocateBlock(PAGE_SIZE);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlpPushPageDescriptor(
|
||
IN ULONG_PTR Page,
|
||
IN ULONG_PTR NumPages
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine binds the temporary block data into a block descriptor
|
||
structure and push it to the memory map
|
||
|
||
Arguments:
|
||
|
||
Page - The start page that wil contain this data
|
||
|
||
NumPages - The number of pages to be set
|
||
|
||
Return Value:
|
||
|
||
TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBLOCK_DESCR PBlockDescr;
|
||
PBLOCK_DESCR PreviousDescr;
|
||
|
||
//
|
||
// Check whether we already have a block descriptor there
|
||
//
|
||
|
||
PreviousDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Page * PAGE_SIZE );
|
||
|
||
if (PreviousDescr) {
|
||
|
||
DbgPrint("Conflicting descriptors %08lx\n", PreviousDescr);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// We need to allocate a block descriptor structure and initializate it
|
||
// with the acquired data.
|
||
//
|
||
|
||
PBlockDescr = (PBLOCK_DESCR)RtlpLeakAllocateBlock(sizeof(BLOCK_DESCR) + (RtlpLDNumBlocks - 1) * sizeof(HEAP_BLOCK));
|
||
|
||
if (!PBlockDescr) {
|
||
|
||
DbgPrint("Unable to allocate page descriptor\n");
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
PBlockDescr->Type = UsageHeap;
|
||
PBlockDescr->Count = RtlpLDNumBlocks;
|
||
PBlockDescr->Heap = RtlpCrtHeapAddress;
|
||
|
||
//
|
||
// Copy the temporary block buffer
|
||
//
|
||
|
||
RtlCopyMemory(PBlockDescr->Blocks, RtlpTempBlocks, RtlpLDNumBlocks * sizeof(HEAP_BLOCK));
|
||
|
||
//
|
||
// If this page doesn't bnelong to the temporary heap, we insert all these blocks
|
||
// in the busy list
|
||
//
|
||
|
||
if (RtlpCrtHeapAddress != RtlpLeakHeapAddress) {
|
||
|
||
LONG i;
|
||
|
||
for (i = 0; i < RtlpLDNumBlocks; i++) {
|
||
|
||
InitializeListHead( &PBlockDescr->Blocks[i].Entry );
|
||
|
||
//
|
||
// We might have a blockin more different pages. but We'll
|
||
// insert only ones in the list
|
||
//
|
||
|
||
if (PBlockDescr->Blocks[i].BlockAddress != RtlpPreviousStartAddress) {
|
||
|
||
InsertTailList(&RtlpLeakList, &PBlockDescr->Blocks[i].Entry);
|
||
|
||
PBlockDescr->Blocks[i].Count = 0;
|
||
|
||
//
|
||
// Save the last block address
|
||
//
|
||
|
||
RtlpPreviousStartAddress = PBlockDescr->Blocks[i].BlockAddress;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the memory map with this block descriptor
|
||
//
|
||
|
||
RtlpSetBlockInfo(&RtlpProcessMemoryMap, Page * PAGE_SIZE, NumPages * PAGE_SIZE, PBlockDescr);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlpRegisterHeapBlocks (
|
||
IN ULONG Context,
|
||
IN PHEAP Heap OPTIONAL,
|
||
IN PHEAP_SEGMENT Segment OPTIONAL,
|
||
IN PHEAP_ENTRY Entry OPTIONAL,
|
||
IN ULONG_PTR Data OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the callback routine invoked while parsing the
|
||
process heaps. Depending on the context it is invoked
|
||
it performs different tasks.
|
||
|
||
Arguments:
|
||
|
||
Context - The context this callback is being invoked
|
||
|
||
Heap - The Heap structure
|
||
|
||
Segment - The current Segment (if any)
|
||
|
||
Entry - The current block entry (if any)
|
||
|
||
Data - Additional data
|
||
|
||
Return Value:
|
||
|
||
TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Check whether we need to break at this address
|
||
//
|
||
|
||
if ((ULONG_PTR)Entry == RtlpBreakAtAddress) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (Context == CONTEXT_START_HEAP) {
|
||
|
||
//
|
||
// The only thing we need to do in this case
|
||
// is to set the global current heap address
|
||
//
|
||
|
||
RtlpCrtHeapAddress = (ULONG_PTR)Heap;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// For a new segment, we mark the flag for the whole
|
||
// reserved space for the segment the flag to TRUE
|
||
//
|
||
|
||
if (Context == CONTEXT_START_SEGMENT) {
|
||
|
||
RtlpSetBlockInfo(&RtlpProcessMemoryMap, (ULONG_PTR)Segment->BaseAddress, Segment->NumberOfPages * PAGE_SIZE, NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (Context == CONTEXT_ERROR) {
|
||
|
||
DbgPrint("HEAP %p (Seg %p) At %p Error: %s\n",
|
||
Heap,
|
||
Segment,
|
||
Entry,
|
||
Data
|
||
);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (Context == CONTEXT_END_BLOCKS) {
|
||
|
||
if (RtlpLDPreviousPage) {
|
||
|
||
RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
|
||
}
|
||
|
||
RtlpLDPreviousPage = 0;
|
||
RtlpLDNumBlocks = 0;
|
||
|
||
} else if (Context == CONTEXT_BUSY_BLOCK) {
|
||
|
||
ULONG_PTR EndPage;
|
||
|
||
//
|
||
// EnrtySize is assuming is the same as heap granularity
|
||
//
|
||
|
||
EndPage = (((ULONG_PTR)(Entry + Entry->Size)) - 1)/ PAGE_SIZE;
|
||
|
||
//
|
||
// Check whether we received a valid block
|
||
//
|
||
|
||
if ((Context == CONTEXT_BUSY_BLOCK) &&
|
||
!RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)Entry)) {
|
||
|
||
DbgPrint("%p address isn't from the heap\n", Entry);
|
||
}
|
||
|
||
//
|
||
// Determine the starting page that contains the block
|
||
//
|
||
|
||
RtlpLDCrtPage = ((ULONG_PTR)Entry) / PAGE_SIZE;
|
||
|
||
if (RtlpLDCrtPage != RtlpLDPreviousPage) {
|
||
|
||
//
|
||
// We moved to an other page, so we need to save the previous
|
||
// information before going further
|
||
//
|
||
|
||
if (RtlpLDPreviousPage) {
|
||
|
||
RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
|
||
}
|
||
|
||
//
|
||
// Reset the temporary data. We're starting a new page now
|
||
//
|
||
|
||
RtlpLDPreviousPage = RtlpLDCrtPage;
|
||
RtlpLDNumBlocks = 0;
|
||
}
|
||
|
||
//
|
||
// Add this block to the current list
|
||
//
|
||
|
||
RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Size = Entry->Size * sizeof(HEAP_ENTRY);
|
||
|
||
RtlpLDNumBlocks++;
|
||
|
||
if (EndPage != RtlpLDCrtPage) {
|
||
|
||
//
|
||
// The block ends on a different page. We can then save the
|
||
// starting page and all others but the last one
|
||
//
|
||
|
||
RtlpPushPageDescriptor(RtlpLDCrtPage, 1);
|
||
|
||
RtlpLDNumBlocks = 0;
|
||
|
||
RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Size = Entry->Size * sizeof(HEAP_ENTRY);
|
||
|
||
RtlpLDNumBlocks += 1;
|
||
|
||
if (EndPage - RtlpLDCrtPage > 1) {
|
||
|
||
RtlpPushPageDescriptor(RtlpLDCrtPage + 1, EndPage - RtlpLDCrtPage - 1);
|
||
}
|
||
|
||
RtlpLDPreviousPage = EndPage;
|
||
}
|
||
|
||
} else if (Context == CONTEXT_VIRTUAL_BLOCK) {
|
||
|
||
PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock = (PHEAP_VIRTUAL_ALLOC_ENTRY)Data;
|
||
ULONG_PTR EndPage;
|
||
|
||
//
|
||
// EnrtySize is assuming is the same as heap granularity
|
||
//
|
||
|
||
EndPage = ((ULONG_PTR)Data + VirtualAllocBlock->CommitSize - 1)/ PAGE_SIZE;
|
||
|
||
RtlpLDCrtPage = (Data) / PAGE_SIZE;
|
||
|
||
if (RtlpLDCrtPage != RtlpLDPreviousPage) {
|
||
|
||
//
|
||
// Save the previous data if we're moving to a new page
|
||
//
|
||
|
||
if (RtlpLDPreviousPage) {
|
||
|
||
RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
|
||
}
|
||
|
||
RtlpLDPreviousPage = RtlpLDCrtPage;
|
||
RtlpLDNumBlocks = 0;
|
||
}
|
||
|
||
//
|
||
// Initialize the block descriptor structure as we are
|
||
// starting a new page
|
||
//
|
||
|
||
RtlpLDNumBlocks = 0;
|
||
|
||
RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
|
||
RtlpTempBlocks[RtlpLDNumBlocks].Size = VirtualAllocBlock->CommitSize;
|
||
|
||
RtlpLDNumBlocks += 1;
|
||
|
||
RtlpPushPageDescriptor(RtlpLDCrtPage, EndPage - RtlpLDCrtPage + 1);
|
||
|
||
RtlpLDPreviousPage = 0;
|
||
|
||
} else if ( Context == CONTEXT_LOOKASIDE_BLOCK ) {
|
||
|
||
PBLOCK_DESCR PBlockDescr;
|
||
LONG i;
|
||
|
||
//
|
||
// Check whether we received a valid block
|
||
//
|
||
|
||
if (!RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)Entry)) {
|
||
|
||
DbgPrint("%p address isn't from the heap\n", Entry);
|
||
}
|
||
|
||
PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, (ULONG_PTR)Entry );
|
||
|
||
if (!PBlockDescr) {
|
||
|
||
DbgPrint("Error finding block from lookaside %p\n", Entry);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Find the block in the block descriptor
|
||
//
|
||
|
||
for (i = 0; i < PBlockDescr->Count; i++) {
|
||
|
||
if ((PBlockDescr->Blocks[i].BlockAddress <= (ULONG_PTR)Entry) &&
|
||
(PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > (ULONG_PTR)Entry)) {
|
||
|
||
PBlockDescr->Blocks[i].Count = -10000;
|
||
|
||
//
|
||
// Remove the block from the busy list
|
||
//
|
||
|
||
RemoveEntryList(&PBlockDescr->Blocks[i].Entry);
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// A block from lookaside should be busy for the heap structures.
|
||
// If we didn't find the block in the block list, something went
|
||
// wrong. We make some noise here.
|
||
//
|
||
|
||
DbgPrint("Error, block %p from lookaside not found in allocated block list\n", Entry);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
PHEAP_BLOCK
|
||
RtlpGetHeapBlock (
|
||
IN ULONG_PTR Address
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function performs a lookup for the block descriptor
|
||
for a given address. The address can point somewhere inside the
|
||
block.
|
||
|
||
|
||
Arguments:
|
||
|
||
Address - The lookup address.
|
||
|
||
Return Value:
|
||
|
||
Returns a pointer to the heap descriptor structure if found.
|
||
This is not NULL if the given address belongs to any busy heap block.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBLOCK_DESCR PBlockDescr;
|
||
LONG i;
|
||
|
||
//
|
||
// Find the block descriptor for the given address
|
||
//
|
||
|
||
PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Address );
|
||
|
||
if ( (PBlockDescr != NULL)
|
||
&&
|
||
(PBlockDescr->Heap != RtlpLeakHeapAddress)) {
|
||
|
||
//
|
||
// Search through the blocks
|
||
//
|
||
|
||
for (i = 0; i < PBlockDescr->Count; i++) {
|
||
|
||
if ((PBlockDescr->Blocks[i].BlockAddress <= Address) &&
|
||
(PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > Address)) {
|
||
|
||
//
|
||
// Search again if the caller didn't pass a start address
|
||
//
|
||
|
||
if (PBlockDescr->Blocks[i].BlockAddress != Address) {
|
||
|
||
return RtlpGetHeapBlock(PBlockDescr->Blocks[i].BlockAddress);
|
||
}
|
||
|
||
//
|
||
// we found a block here.
|
||
//
|
||
|
||
return &(PBlockDescr->Blocks[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpDumpEntryHeader ( )
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Writes the table header
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
DbgPrint("Entry User Heap Size PrevSize Flags\n");
|
||
DbgPrint("------------------------------------------------------------\n");
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpDumpEntryFlagDescription(
|
||
IN ULONG Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function writes a description string for the given block flag
|
||
|
||
Arguments:
|
||
|
||
Flags - Block flags
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
if (Flags & HEAP_ENTRY_BUSY) DbgPrint("busy "); else DbgPrint("free ");
|
||
if (Flags & HEAP_ENTRY_EXTRA_PRESENT) DbgPrint("extra ");
|
||
if (Flags & HEAP_ENTRY_FILL_PATTERN) DbgPrint("fill ");
|
||
if (Flags & HEAP_ENTRY_VIRTUAL_ALLOC) DbgPrint("virtual ");
|
||
if (Flags & HEAP_ENTRY_LAST_ENTRY) DbgPrint("last ");
|
||
if (Flags & HEAP_ENTRY_SETTABLE_FLAGS) DbgPrint("user_flag ");
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpDumpEntryInfo(
|
||
IN ULONG_PTR HeapAddress,
|
||
IN PHEAP_ENTRY Entry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function logs a heap block information
|
||
|
||
Arguments:
|
||
|
||
HeapAddress - The heap that contains the entry to be displayied
|
||
|
||
Entry - The block entry
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DbgPrint("%p %p %p %8lx %8lx ",
|
||
Entry,
|
||
(Entry + 1),
|
||
HeapAddress,
|
||
Entry->Size << HEAP_GRANULARITY_SHIFT,
|
||
Entry->PreviousSize << HEAP_GRANULARITY_SHIFT
|
||
);
|
||
|
||
RtlpDumpEntryFlagDescription(Entry->Flags);
|
||
|
||
DbgPrint("\n");
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlpScanHeapAllocBlocks ( )
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function does:
|
||
- Scan all busy blocks and update the references to all other blocks
|
||
- Build the list with leaked blocks
|
||
- Reports the leaks
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
Return TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY Next;
|
||
|
||
//
|
||
// walk the busy list
|
||
//
|
||
|
||
Next = RtlpBusyList.Flink;
|
||
|
||
while (Next != &RtlpBusyList) {
|
||
|
||
PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
|
||
|
||
PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + sizeof(HEAP_ENTRY));
|
||
|
||
//
|
||
// Move to the next block in the list
|
||
//
|
||
|
||
Next = Next->Flink;
|
||
|
||
//
|
||
// Iterate through block space and update
|
||
// the references for every block found here
|
||
//
|
||
|
||
while ((ULONG_PTR)CrtAddress < Block->BlockAddress + Block->Size) {
|
||
|
||
PHEAP_BLOCK pBlock = RtlpGetHeapBlock( *CrtAddress );
|
||
|
||
if (pBlock) {
|
||
|
||
//
|
||
// We found a block. we increment then the reference count
|
||
//
|
||
|
||
if (pBlock->Count == 0) {
|
||
|
||
RemoveEntryList(&pBlock->Entry);
|
||
InsertTailList(&RtlpBusyList, &pBlock->Entry);
|
||
}
|
||
|
||
pBlock->Count += 1;
|
||
|
||
if (pBlock->BlockAddress == RtlpBreakAtAddress) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
//
|
||
// Go to the next possible pointer
|
||
//
|
||
|
||
CrtAddress++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now walk the leak list, and report leaks.
|
||
// Also any pointer found here will be dereferenced and added to
|
||
// the end of list.
|
||
//
|
||
|
||
Next = RtlpLeakList.Flink;
|
||
|
||
while (Next != &RtlpLeakList) {
|
||
|
||
PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
|
||
PBLOCK_DESCR PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Block->BlockAddress );
|
||
PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + sizeof(HEAP_ENTRY));
|
||
|
||
if (PBlockDescr) {
|
||
|
||
//
|
||
// First time we need to display the header
|
||
//
|
||
|
||
if (RtlpLeaksCount == 0) {
|
||
|
||
RtlpDumpEntryHeader();
|
||
}
|
||
|
||
//
|
||
// Display the information for this block
|
||
//
|
||
|
||
RtlpDumpEntryInfo( PBlockDescr->Heap, (PHEAP_ENTRY)Block->BlockAddress);
|
||
|
||
RtlpLeaksCount += 1;
|
||
}
|
||
|
||
//
|
||
// Go to the next item from the leak list
|
||
//
|
||
|
||
Next = Next->Flink;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
RtlpScanProcessVirtualMemory()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function scan the whole process virtual address space and lookup
|
||
for possible references to busy blocks
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
Return TRUE if succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR lpAddress = 0;
|
||
MEMORY_BASIC_INFORMATION Buffer;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Loop through virtual memory zones, we'll skip the heap space here
|
||
//
|
||
|
||
while ( NT_SUCCESS( Status ) ) {
|
||
|
||
Status = ZwQueryVirtualMemory( NtCurrentProcess(),
|
||
(PVOID)lpAddress,
|
||
MemoryBasicInformation,
|
||
&Buffer,
|
||
sizeof(Buffer),
|
||
NULL );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// If the page can be written, it might contain pointers to heap blocks
|
||
// We'll exclude at this point the heap address space. We scan the heaps
|
||
// later.
|
||
//
|
||
|
||
if ((Buffer.AllocationProtect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
|
||
&&
|
||
(Buffer.State & MEM_COMMIT)
|
||
&&
|
||
!(Buffer.Protect & PAGE_GUARD)
|
||
&&
|
||
!RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)lpAddress)) {
|
||
|
||
PULONG_PTR Pointers = (PULONG_PTR)lpAddress;
|
||
ULONG_PTR i, Count;
|
||
|
||
//
|
||
// compute the number of possible pointers
|
||
//
|
||
|
||
Count = Buffer.RegionSize / sizeof(ULONG_PTR);
|
||
|
||
try {
|
||
|
||
//
|
||
// Loop through pages and check any possible pointer reference
|
||
//
|
||
|
||
for (i = 0; i < Count; i++) {
|
||
|
||
//
|
||
// Check whether we have a pointer to a busy heap block
|
||
//
|
||
|
||
PHEAP_BLOCK pBlock = RtlpGetHeapBlock(*Pointers);
|
||
|
||
if (pBlock) {
|
||
|
||
if (pBlock->BlockAddress == RtlpBreakAtAddress) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (pBlock->Count == 0) {
|
||
|
||
RemoveEntryList(&pBlock->Entry);
|
||
InsertTailList(&RtlpBusyList, &pBlock->Entry);
|
||
}
|
||
|
||
pBlock->Count += 1;
|
||
}
|
||
|
||
//
|
||
// Move to the next pointer
|
||
//
|
||
|
||
Pointers++;
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Nothing more to do
|
||
//
|
||
}
|
||
}
|
||
|
||
//
|
||
// Move to the next VM range to query
|
||
//
|
||
|
||
lpAddress += Buffer.RegionSize;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now update the references provided by the busy blocks
|
||
//
|
||
|
||
RtlpScanHeapAllocBlocks( );
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
RtlDetectHeapLeaks ()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine detects and display the leaks found in the current process
|
||
|
||
NOTE: The caller must make sure no other thread can change some heap data
|
||
while a tread is executing this one. In general this function is supposed
|
||
to be called from LdrShutdownProcess.
|
||
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Check if the global flag has the leak detection enabled
|
||
//
|
||
|
||
if (RtlpShutdownProcessFlags & (INSPECT_LEAKS | BREAK_ON_LEAKS)) {
|
||
RtlpLeaksCount = 0;
|
||
|
||
//
|
||
// Create a temporary heap that will be used for any alocation
|
||
// of these functions.
|
||
//
|
||
|
||
RtlpLeakHeap = RtlCreateHeap(HEAP_NO_SERIALIZE | HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
|
||
|
||
if (RtlpLeakHeap) {
|
||
|
||
PPEB ProcessPeb = NtCurrentPeb();
|
||
|
||
HeapDebugPrint( ("Inspecting leaks at process shutdown ...\n") );
|
||
|
||
RtlpInitializeLeakDetection();
|
||
|
||
//
|
||
// The last heap from the heap list is our temporary heap
|
||
//
|
||
|
||
RtlpLeakHeapAddress = (ULONG_PTR)ProcessPeb->ProcessHeaps[ ProcessPeb->NumberOfHeaps - 1 ];
|
||
|
||
//
|
||
// Scan all process heaps, build the memory map and
|
||
// the busy block list
|
||
//
|
||
|
||
RtlpReadProcessHeaps( RtlpRegisterHeapBlocks );
|
||
|
||
//
|
||
// Scan the process virtual memory and the busy blocks
|
||
// At the end build the list with leaked blocks and report them
|
||
//
|
||
|
||
RtlpScanProcessVirtualMemory();
|
||
|
||
//
|
||
// Destroy the temporary heap
|
||
//
|
||
|
||
RtlDestroyHeap(RtlpLeakHeap);
|
||
|
||
RtlpLeakHeap = NULL;
|
||
|
||
//
|
||
// Report the final statement about the process leaks
|
||
//
|
||
|
||
if (RtlpLeaksCount) {
|
||
|
||
HeapDebugPrint(("%ld leaks detected.\n", RtlpLeaksCount));
|
||
|
||
if (RtlpShutdownProcessFlags & BREAK_ON_LEAKS) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
} else {
|
||
|
||
HeapDebugPrint( ("No leaks detected.\n"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|