Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1863 lines
42 KiB

//
// Copyright (c) 2001 Microsoft Corporation
//
// Module Name
//
// gc.c
//
// Abstract
//
// Implementation of APIs used for garbage collection in UMDH. These
// APIs are declared in the header file gc.h
//
// Garbage Collection: The automatic reclamation of heap-allocated
// storage after its last use by a program.
//
// Garbage collection is done in two steps-
// 1. Identify the garbage
// 2. Reclaim the garbage
//
// Since UMDH is not an in-process tool, only part 1 is accomplished
// in this implementation.
//
// Garbage Collection Algorithm:
//
// It uses Mark-Sweep (not exactly) to identify live objects from
// garbage.
//
// 1. Grovel through the entire process virtual memory and identify
// the live objects by incrementing the reference counts of the
// heap objects.
//
// 2. Create a list for those heap objects (garbage) whose reference
// count is zero.
//
// 3. Identify the heap objects (not in garbage) referenced by these
// objects from the garbage and decrement the count by one. If the
// reference count drops to zero, add the heap object to the list
// of objects in garbage.
//
// 4. Continue till all the objects in the garbage are traversed and
// reference counts are incremented/decremented accordingly.
//
// 5. Dump the list of objects in garbage.
//
// To improve the number of leaks detected by this algorithm, reference
// counts of the heap objects are not incremented, if the object
// reference is from invalid stack regions (those regions of the stack
// which are read/write but above the stack pointer) when grovelling
// through the virtual memory in step one.
//
//
// Authors
//
// Narayana Batchu (nbatchu) 21-Jun-01
//
// Revision History
//
// NBatchu 21-Jun-01 Initial version
// NBatchu 24-Sep-01 Performance optimizations
//
//
// Wish List
//
//
// [-] Producing stack traces along with the leak table
//
// [-] Sorting the leaks by the stack traces (TraceIndex)
//
// [-] Adding the logic to detect circular reference counting. When blocks
// are circularly referenced, then this algorithm would not be able to
// detect leaks
//
// [-] Adding code for ia64, to filter out invalid stack regions. As of now
// this is implemented for x86 machines only
//
//
// Bugs
//
// [-] Partial copy error when reading process virtual memory - as of now
// we are ignoring those errors
//
#include <ntos.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <heap.h>
#include <heappriv.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <tlhelp32.h>
#include "miscellaneous.h"
#include "gc.h"
#define BLOCK_CAPACITY 512
#define HEAP_CAPACITY 8
#define MAX_INDEX 0xffffffff
#define MAX_THREADS 128
#define MAX_HEAP_BLOCK_SIZE 4096
#define MAX_VIRTUAL_BLOCK_SIZE (64*1024)
//
// Handle to the process
//
HANDLE g_hProcess;
//
// InitializeHeapBlock
//
// Initializes the HEAP_BLOCK structure
//
// Arguments
//
// Block Pointer to a HEAP_BLOCK to be initialized
//
// ReturnValue
//
VOID
InitializeHeapBlock(
PHEAP_BLOCK Block
)
{
if (NULL == Block) {
return;
}
Block->BlockAddress = 0;
Block->BlockSize = 0;
Block->RefCount = 0;
Block->TraceIndex = 0;
}
//
// SetHeapBlock
//
// Sets the fields of HEAP_BLOCK structure
//
// Arguments
//
// Block Pointer to a HEAP_BLOCK whose fields to be set
//
// ReturnValue
//
VOID
SetHeapBlock(
PHEAP_BLOCK Block,
ULONG_PTR BlockAddress,
ULONG BlockSize,
USHORT TraceIndex
)
{
if (NULL == Block) {
return;
}
Block->BlockAddress = BlockAddress;
Block->BlockSize = BlockSize;
Block->RefCount = 0;
Block->TraceIndex = TraceIndex;
}
//
// InitializeBlockList
//
// Initializes the BLOCK_LIST structure
//
// Arguments
//
// BlockList Pointer to a BLOCK_LIST to be initialized
//
// ReturnValue
//
BOOL
InitializeBlockList(
PBLOCK_LIST BlockList
)
{
BOOL fSuccess = TRUE;
if (NULL == BlockList) {
goto Exit;
}
BlockList->HeapAddress = 0;
BlockList->BlockCount = 0;
BlockList->Capacity = BLOCK_CAPACITY;
BlockList->ListSorted = TRUE;
BlockList->Blocks = (PHEAP_BLOCK)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
BlockList->Capacity * sizeof(HEAP_BLOCK));
if (NULL == BlockList->Blocks) {
BlockList->Capacity = 0;
Error (__FILE__,
__LINE__,
"HeapAlloc failed while allocating more memory");
fSuccess = FALSE;
}
Exit:
return fSuccess;
}
//
// FreeBlockList
//
// Frees the memory allocted for the Blocks field (if any)
// while initializing this BLOCK_LIST structure.
//
// Arguments
//
// BlockList Pointer to a BLOCK_LIST
//
// ReturnValue
//
VOID
FreeBlockList(
PBLOCK_LIST BlockList
)
{
if (NULL == BlockList) {
return;
}
BlockList->HeapAddress = 0;
BlockList->BlockCount = 0;
BlockList->Capacity = 0;
BlockList->ListSorted = TRUE;
if (NULL != BlockList->Blocks) {
HeapFree(GetProcessHeap(), 0, BlockList->Blocks);
BlockList->Blocks = NULL;
}
}
//
// IntializeHeapList
//
// Initializes HEAP_LIST structure
//
// Arguments
//
// HeapList Pointer to a HEAP_LIST
//
// ReturnValue
//
BOOL
InitializeHeapList(
PHEAP_LIST HeapList
)
{
ULONG Index;
BOOL fSuccess = TRUE;
if (NULL == HeapList) {
goto Exit;
}
HeapList->Capacity = HEAP_CAPACITY;
HeapList->HeapCount = 0;
HeapList->Heaps =
(PBLOCK_LIST)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(BLOCK_LIST) * HeapList->Capacity);
if (NULL == HeapList->Heaps) {
HeapList->Capacity = 0;
Error (__FILE__,
__LINE__,
"HeapAlloc failed while allocating more memory");
fSuccess = FALSE;
}
Exit:
return fSuccess;
}
//
// FreeHeapList
//
// Frees the memory allocted for the Heaps field (if any)
// while initializing this HEAP_LIST structure.
//
// Arguments
//
// BlockList Pointer to a HEAP_LIST
//
// ReturnValue
//
VOID
FreeHeapList(
PHEAP_LIST HeapList
)
{
ULONG Index;
if (NULL == HeapList) {
return;
}
HeapList->Capacity = 0;
HeapList->HeapCount = 0;
if (NULL != HeapList->Heaps) {
for (Index=0; Index<HeapList->HeapCount; Index++) {
FreeBlockList(&HeapList->Heaps[Index]);
}
HeapFree(GetProcessHeap(), 0, HeapList->Heaps);
HeapList->Heaps = NULL;
}
}
//
// InitializeAddressList
//
// Initializes a ADDRESS_LIST object
//
// Arguments
//
// AddressList Pointer to ADDRESS_LIST structure
//
// ReturnValue
//
VOID
InitializeAddressList(
PADDRESS_LIST AddressList
)
{
if (NULL == AddressList) {
return;
}
AddressList->Address = 0;
InitializeListHead(&(AddressList->Next));
}
//
// FreeAddressList
//
// Frees the memory allocated for the linked list
//
// Arguments
//
// AddressList Pointer to ADDRESS_LIST to be freed
//
// ReturnValue
//
VOID
FreeAddressList(
PADDRESS_LIST AddressList
)
{
PLIST_ENTRY NextEntry;
PLIST_ENTRY Entry;
PADDRESS_LIST List;
if (NULL == AddressList) {
return;
}
//
// Walk through the list and free up the memory
//
NextEntry = &AddressList->Next;
while (!IsListEmpty(NextEntry)) {
Entry = RemoveHeadList(NextEntry);
List = CONTAINING_RECORD(Entry, ADDRESS_LIST, Next);
HeapFree(GetProcessHeap(), 0, List);
}
}
//
// IncreaseBlockListCapacity
//
// Increases the storing capacity for the BLOCK_LIST
// structure. Every time this function is called the storing
// capacity doubles. There is a trade off between the number
// of times HeapReAlloc is called and the amount of memory
// is allocated.
//
// Arguments
//
// BlockList Pointer to a BLOCK_LIST object
//
// ReturnValue
//
// BOOL Returns TRUE if successful in increasing the
// capacity of BlockList.
//
BOOL
IncreaseBlockListCapacity(
PBLOCK_LIST BlockList
)
{
BOOL fSuccess = FALSE;
ULONG NewCapacity;
PVOID NewBlockList;
if (NULL == BlockList) {
goto Exit;
}
NewCapacity = BlockList->Capacity * 2;
if (0 == NewCapacity) {
fSuccess = InitializeBlockList(BlockList);
goto Exit;
}
NewBlockList = HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
BlockList->Blocks,
NewCapacity * sizeof(HEAP_BLOCK));
if (NULL != NewBlockList) {
BlockList->Blocks = (PHEAP_BLOCK)NewBlockList;
BlockList->Capacity = NewCapacity;
fSuccess = TRUE;
}
else {
Error (__FILE__,
__LINE__,
"HeapReAlloc failed while allocating more memory");
}
Exit:
return fSuccess;
}
//
// IncreaseHeapListCapacity
//
// Increases the storing capacity for the HEAP_LIST
// structure. Every time this function is called the storing
// capacity doubles. There is a trade off between the number
// of times HeapReAlloc is called and the amount of memory
// is allocated.
//
// Arguments
//
// BlockList Pointer to a HEAP_LIST object
//
// ReturnValue
//
// BOOL Returns TRUE if successful in increasing the
// capacity of HeapList.
//
BOOL
IncreaseHeapListCapacity(
PHEAP_LIST HeapList
)
{
BOOL fSuccess = FALSE;
ULONG NewCapacity;
PVOID NewHeapList;
if (NULL == HeapList) {
goto Exit;
}
NewCapacity = HeapList->Capacity * 2;
if (0 == NewCapacity) {
fSuccess = InitializeHeapList(HeapList);
goto Exit;
}
NewHeapList = HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
HeapList->Heaps,
NewCapacity * sizeof(BLOCK_LIST));
if (NULL != NewHeapList) {
HeapList->Heaps = (PBLOCK_LIST)NewHeapList;
HeapList->Capacity = NewCapacity;
fSuccess = TRUE;
}
else {
Error(__FILE__,
__LINE__,
"HeapReAlloc failed while allocating more memory");
}
Exit:
return fSuccess;
}
//
// InsertHeapBlock
//
// Inserts HEAP_BLOCK object into BLOCK_LIST. BLOCK_LIST is
// an array of HEAP_BLOCKs belonging to a particular heap.
//
// Arguments
//
// BlockList Pointer to BLOCK_LIST. HEAP_BLOCK is inserted
// into this list.
//
// Block Pointer to HEAP_BLOCK to be inserted in.
//
// ReturnValue
//
// ULONG Returns the Index at which HEAP_BLOCK is inserted
// in BLOCK_LIST
//
ULONG
InsertHeapBlock(
PBLOCK_LIST BlockList,
PHEAP_BLOCK Block
)
{
ULONG Index = MAX_INDEX;
BOOL Result;
if (NULL == BlockList || NULL == Block) {
goto Exit;
}
Index = BlockList->BlockCount;
if (Index >= BlockList->Capacity) {
//
// Try to increase block list capacity.
//
if (!IncreaseBlockListCapacity(BlockList)) {
goto Exit;
}
}
BlockList->Blocks[Index].BlockAddress = Block->BlockAddress;
BlockList->Blocks[Index].BlockSize = Block->BlockSize;
BlockList->Blocks[Index].RefCount = Block->RefCount;
BlockList->Blocks[Index].TraceIndex = Block->TraceIndex;
BlockList->BlockCount += 1;
BlockList->ListSorted = FALSE;
Exit:
return Index;
}
//
// InsertBlockList
//
// Inserts BLOCK_LIST object into HEAP_LIST. HEAP_LIST is
// an array of BLOCK_LISTs belonging to a particular process.
// And BLOCK_LIST is an array of HEAP_BLOCKs belonging to a
// particular heap.
//
// Arguments
//
// BlockList Pointer to BLOCK_LIST. HEAP_BLOCK is inserted
// into this list.
//
// Block Pointer to HEAP_BLOCK to be inserted in.
//
// ReturnValue
//
// ULONG Returns the Index at which HEAP_BLOCK is inserted
// in BLOCK_LIST
//
ULONG
InsertBlockList(
PHEAP_LIST HeapList,
PBLOCK_LIST BlockList
)
{
ULONG I, Index = MAX_INDEX;
PBLOCK_LIST NewBlockList;
if (NULL == HeapList || NULL == BlockList) {
goto Exit;
}
if (0 == BlockList->BlockCount) {
goto Exit;
}
Index = HeapList->HeapCount;
if (Index >= HeapList->Capacity) {
//
// Increase the heap list capacity since we hit the limit.
//
if (!IncreaseHeapListCapacity(HeapList)) {
goto Exit;
}
}
HeapList->Heaps[Index].Blocks = BlockList->Blocks;
NewBlockList = &HeapList->Heaps[Index];
//
// Copy the values stored in BlockList to NewBlockList.
//
NewBlockList->BlockCount = BlockList->BlockCount;
NewBlockList->Capacity = BlockList->Capacity;
NewBlockList->HeapAddress = BlockList->HeapAddress;
NewBlockList->ListSorted = BlockList->ListSorted;
//
// Increment the HeapCount
//
HeapList->HeapCount += 1;
Exit:
return Index;
}
//
// GetThreadHandles
//
// Enumerates all the threads in the system and filters only the
// threads in the process we are concerned
//
// Arguments
//
// ProcessId Process ID
//
// ThreadHandles Array of Handles, which receive the handles to
// the enumerated threads
//
// Count Array count
//
// ReturnValue
//
// DWORD Returns the number of thread handles opened
//
DWORD
GetThreadHandles(
DWORD ProcessId,
LPHANDLE ThreadHandles,
ULONG Count
)
{
HANDLE ThreadSnap = NULL;
BOOL Result = FALSE;
THREADENTRY32 ThreadEntry = {0};
ULONG I, Index = 0;
// SilviuC: These APIs xxxtoolhelpxxx are crappy. Keep them for now but
// you should get yourself familiarized with NT apis that do the same. For
// instance take a look in sdktools\systrack where there is code that gets
// stack information for each thread.
//
// Take a snapshot of all the threads in the system
//
ThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (NULL == ThreadSnap) {
Error (__FILE__,
__LINE__,
"CreateToolhelp32Snapshot failed with error : %ld\n",
GetLastError());
goto Exit;
}
//
// Fill in the size for ThreadEntry before using it
//
ThreadEntry.dwSize = sizeof(THREADENTRY32);
//
// Walk through snap shot of threads and look for the threads
// whose process ids match the process id of the process we
// are looking for.
//
Result = Thread32First(ThreadSnap, &ThreadEntry);
while (Result) {
if (ThreadEntry.th32OwnerProcessID == ProcessId) {
HANDLE ThreadHandle = OpenThread(THREAD_GET_CONTEXT,
FALSE,
ThreadEntry.th32ThreadID);
if (NULL == ThreadHandle) {
Error (__FILE__,
__LINE__,
"OpenThread failed with error : %ld\n",
GetLastError());
}
else {
if (NULL != ThreadHandles && Index < Count) {
ThreadHandles[Index] = ThreadHandle;
}
Index += 1;
}
}
Result = Thread32Next(ThreadSnap, &ThreadEntry);
}
Exit:
//
// Clean up the snapshot object
//
if (NULL != ThreadSnap) {
CloseHandle (ThreadSnap);
}
return Index;
}
//
// GetThreadContexts
//
// Gets the thread contexts of all the threads in the process
//
// Arguments
//
// ThreadContexts Array of CONTEXT structures to store thread
// stack/context information
//
// ThreadHandles Array of thread handles
//
// Count Array count
//
// ReturnValue
//
// BOOL Returns true if successful
//
BOOL
GetThreadContexts(
PCONTEXT ThreadContexts,
LPHANDLE ThreadHandles,
ULONG Count
)
{
ULONG Index;
BOOL Result;
for (Index = 0; Index < Count; Index += 1) {
ZeroMemory(&ThreadContexts[Index], sizeof(CONTEXT));
ThreadContexts[Index].ContextFlags =
CONTEXT_INTEGER | CONTEXT_CONTROL;
Result = GetThreadContext (ThreadHandles[Index],
&ThreadContexts[Index]);
if (FALSE == Result) {
Error (__FILE__,
__LINE__,
"GetThreadContext Failed with error : %ld\n",
GetLastError());
}
}
return TRUE;
}
//
// StackFilteredAddress
//
// Each thread in the process has its own stack and each stack
// has a read/write region that is not valid (this region is
// above the stack pointer). This function filters out this
// region by incrementing the start address of the block to
// the end of stack pointer, so that we dont search those
// regions of the stack which dont contain valid data.
//
// As af now, this function is implemented for X86 machines only.
// For IA64 machines, the register names (in the CONTEXT structure)
// are different than X86 machines and different header files need
// to be added to make it compile and work.
//
// Arguments
//
// Address Address to the block
//
// Size Size of the block pointed to 'Address'
//
// ThreadContexts Array to CONTEXTs of all the threads in
// the process
//
// Count Array count
//
// ReturnValue
//
// ULONG_PTR Returns new address to the end of the
// valid stack region
//
ULONG_PTR
StackFilteredAddress(
ULONG_PTR Address,
SIZE_T Size,
PCONTEXT ThreadContexts,
ULONG Count
)
{
ULONG Index;
ULONG_PTR FilteredAddress = Address;
//
// SilviuC: It is easy to get the same kind of stuff for IA64. If I am not
// mistaken the field is called Sp.
//
#ifdef X86
for (Index = 0; Index < Count; Index += 1) {
if (ThreadContexts[Index].Esp >= Address &&
ThreadContexts[Index].Esp <= Address + Size) {
FilteredAddress = ThreadContexts[Index].Esp;
break;
}
}
#endif
return FilteredAddress;
}
//
// SortByBlockAddress
//
// Sorts HEAP_BLOCKs belonging to a particular BLOCK_LIST
// by comparing the BlockAddresses.
//
// Compare function required by qsort (uses quick sort to sort
// the elements in the array).
//
// More info about the arguments and the return values could be
// found in MSDN.
//
int __cdecl
SortByBlockAddress (
const PHEAP_BLOCK Block1,
const PHEAP_BLOCK Block2
)
{
int iCompare;
if (Block1->BlockAddress > Block2->BlockAddress) {
iCompare = +1;
}
else if (Block1->BlockAddress < Block2->BlockAddress) {
iCompare = -1;
}
else {
iCompare = 0;
}
return iCompare;
}
int __cdecl
SortByTraceIndex (
const PHEAP_BLOCK Block1,
const PHEAP_BLOCK Block2
)
{
int iCompare;
//
// Sort such that items with identical TraceIndex are adjacent.
// (That this results in ascending order is irrelevant).
//
if (Block1->TraceIndex > Block2->TraceIndex) {
iCompare = +1;
}
else if (Block1->TraceIndex < Block2->TraceIndex) {
iCompare = -1;
}
else {
iCompare = 0;
}
if (0 == iCompare) {
//
// For two items with identical TraceIndex, sort into ascending
// order by BytesAllocated.
//
if (Block1->BlockSize > Block2->BlockSize) {
iCompare = 1;
}
else if (Block1->BlockSize < Block2->BlockSize) {
iCompare = -1;
}
else {
iCompare = 0;
}
}
return iCompare;
}
//
// SortHeaps
//
// Sorts all the heaps in the HEAP_LIST.
// Each heap is sorted by increasing value of HEAP_BLOCK
// addresses. The top most entry for each heap would be
// having the min address value
//
// Arguments
//
// HeapList Pointer to HEAP_LIST
//
// Return Value
//
VOID
SortHeaps(
PHEAP_LIST HeapList,
int (__cdecl *compare )(const void *elem1, const void *elem2 )
)
{
ULONG HeapCount;
ULONG Index;
if (NULL == HeapList) {
return;
}
HeapCount = HeapList->HeapCount;
for (Index = 0; Index < HeapCount; Index += 1) {
//
// Sort the BLOCK_LIST only if it contains heap objects
//
if (0 != HeapList->Heaps[Index].BlockCount) {
qsort (HeapList->Heaps[Index].Blocks,
HeapList->Heaps[Index].BlockCount,
sizeof(HEAP_BLOCK),
compare);
}
HeapList->Heaps[Index].ListSorted = TRUE;
}
}
//
// GetHeapBlock
//
// Finds a HEAP_BLOCK whose range contains the the address
// pointed to by Address
//
// Arguments
//
// Address Address as ULONG_PTR
//
// HeapList Pointer to HEAP_LIST to be searched.
//
// ReturnValue
//
// PHEAP_BLOCK Returns the pointer to the HEAP_BLOCK that
// contains the address.
//
PHEAP_BLOCK
GetHeapBlock (
ULONG_PTR Address,
PHEAP_LIST HeapList
)
{
PHEAP_BLOCK Block = NULL;
ULONG I,J;
ULONG Start, Mid, End;
PBLOCK_LIST BlockList;
//
// Since most of the memory is null (zero), this check would
// improve the performance
//
if (0 == Address ||
NULL == HeapList ||
0 == HeapList->HeapCount) {
goto Exit;
}
for (I = 0; I < HeapList->HeapCount; I += 1) {
//
// Ignore if the heap contains no objects
//
if (0 == HeapList->Heaps[I].BlockCount) {
continue;
}
//
// Binary search the address in the sorted list of heap blocks for
// the current heap.
//
Start = 0;
End = HeapList->Heaps[I].BlockCount - 1;
BlockList = &HeapList->Heaps[I];
while (Start <= End) {
Mid = (Start + End)/2;
if (Address < BlockList->Blocks[Mid].BlockAddress) {
End = Mid - 1;
}
else if (Address >= BlockList->Blocks[Mid].BlockAddress +
BlockList->Blocks[Mid].BlockSize) {
Start = Mid + 1;
}
else {
Block = &BlockList->Blocks[Mid];
break;
}
if (Mid == Start || Mid == End) {
break;
}
}
if (NULL != Block) {
break;
}
}
Exit:
return Block;
}
//
// InsertAddress
//
// Inserts a node in the linked list. The new node has the
// Address stored. This node is inserted at the end of the
// linked list.
//
// Arguments
//
// Address Address of a block in the heap
//
// List Pointer to a ADDRESS_LIST
//
// ReturnValue
//
VOID
InsertAddress(
ULONG_PTR Address,
PADDRESS_LIST List
)
{
PADDRESS_LIST NewList;
NewList = (PADDRESS_LIST) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof (ADDRESS_LIST));
if (NULL == NewList) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed to allocate memory");
return;
}
NewList->Address = Address;
InsertTailList(&(List->Next), &(NewList->Next));
}
//
// DumpLeakList
//
// Dumps the leak list to a file or console. Parses through
// each of the HEAP_BLOCK and dumps those blocks whose RefCount
// is 0 (zero).
//
// Arguments
//
// File Output file
//
// HeapList Pointer to a HEAP_LIST
//
// ReturnValue
//
VOID
DumpLeakList(
FILE * File,
PHEAP_LIST HeapList
)
{
ULONG I,J;
ULONG Count = 1;
USHORT RefTraceIndex = 0;
ULONG TotalBytes = 0;
PHEAP_BLOCK HeapBlock;
SortHeaps(HeapList, SortByTraceIndex);
//
// Now walk the heap list, and report leaks.
//
fprintf(
File,
"\n\n*- - - - - - - - - - Leaks detected - - - - - - - - - -\n\n"
);
for (I = 0; I < HeapList->HeapCount; I += 1) {
for (J = 0; J < HeapList->Heaps[I].BlockCount; J += 1) {
HeapBlock = &(HeapList->Heaps[I].Blocks[J]);
//
// Merge the leaks whose trace index is same (i.e. whose
// allocation stack trace is same)
//
if (RefTraceIndex == HeapBlock->TraceIndex && 0 == HeapBlock->RefCount) {
Count += 1;
TotalBytes += HeapBlock->BlockSize;
}
//
// Display them if
// 1. They are from different stack traces and there are leaks
// OR
// 2. This is the last Block in the list and there are leaks.
//
if ((RefTraceIndex != HeapBlock->TraceIndex) ||
((I+1) == HeapList->HeapCount && (J+1) == HeapList->Heaps[I].BlockCount)) {
if (0 != RefTraceIndex && 0 != TotalBytes) {
fprintf(
File,
"0x%x bytes leaked by: BackTrace%05d (in 0x%04x allocations)\n",
TotalBytes,
RefTraceIndex,
Count
);
}
//
// Update trace index, count and total bytes
//
RefTraceIndex = HeapBlock->TraceIndex;
Count = (0 == HeapBlock->RefCount) ? 1 : 0;
TotalBytes = (0 == HeapBlock->RefCount) ? HeapList->Heaps[I].Blocks[J].BlockSize : 0;
}
}
}
fprintf(
File,
"\n*- - - - - - - - - - End of Leaks - - - - - - - - - -\n\n"
);
return;
}
//
// ScanHeapFreeBlocks
//
// Scans the free list and updates the references to any of the
// busy blocks. When the refernce count of the busy blocks drops
// to zero, it is appended to the end of the free list
//
// Arguments
//
// HeapList Pointer to HEAP_LIST
//
// FreeList Pointer to ADDRESS_LIST that contains addresses to
// free heap blocks
//
// ReturnValue
//
// BOOL Returns true if successful
//
BOOL
ScanHeapFreeBlocks(
PHEAP_LIST HeapList,
PADDRESS_LIST FreeList
)
{
BOOL Result;
ULONG Count, i;
PULONG_PTR Pointer;
ULONG_PTR FinalAddress;
PHEAP_BLOCK CurrentBlock;
PVOID HeapBlock;
ULONG HeapBlockSize = 0;
BOOL Success = TRUE;
PLIST_ENTRY FirstEntry;
PLIST_ENTRY NextEntry;
PADDRESS_LIST AddressList;
//
// Allocate a chunk of memory for reading heap objects
//
HeapBlock = (PVOID) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
MAX_HEAP_BLOCK_SIZE);
if (NULL == HeapBlock) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed to allocate memory");
Success = FALSE;
goto Exit;
}
HeapBlockSize = MAX_HEAP_BLOCK_SIZE;
//
// Walk the free list by deleting the entries read
//
FirstEntry = &(FreeList->Next);
while (!IsListEmpty(FirstEntry)) {
NextEntry = RemoveHeadList(FirstEntry);
AddressList = CONTAINING_RECORD(NextEntry,
ADDRESS_LIST,
Next);
CurrentBlock = GetHeapBlock(AddressList->Address,
HeapList);
assert(NULL != CurrentBlock);
if (NULL == CurrentBlock) {
Error (__FILE__,
__LINE__,
"GetHeapBlock returned NULL. May be because of reading stale memory");
continue;
}
if (HeapBlockSize < CurrentBlock->BlockSize) {
if (NULL != HeapBlock) {
HeapFree(GetProcessHeap(), 0, HeapBlock);
}
HeapBlock = (PVOID) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
CurrentBlock->BlockSize);
if (NULL == HeapBlock) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed to allocate memory");
Success = FALSE;
goto Exit;
}
HeapBlockSize = CurrentBlock->BlockSize;
}
//
// Read the contents of the freed heap block
// from the target process.
//
Result = UmdhReadAtVa(__FILE__,
__LINE__,
g_hProcess,
(PVOID)CurrentBlock->BlockAddress,
HeapBlock,
CurrentBlock->BlockSize);
if (Result) {
FinalAddress = (ULONG_PTR)HeapBlock+CurrentBlock->BlockSize;
Pointer = (PULONG_PTR) HeapBlock;
while ((ULONG_PTR)Pointer < FinalAddress) {
//
// Check whether we have a pointer to a
// busy heap block
//
PHEAP_BLOCK Block = GetHeapBlock(*Pointer,HeapList);
if (NULL != Block) {
//
// We found a block. we decrement the reference
// count
//
if (0 == Block->RefCount) {
//
// This should never happen!!
//
Error (__FILE__,
__LINE__,
"Something wrong! Should not get a block whose "
"RefCount is already 0 @ %p",
Block->BlockAddress);
}
else if (1 == Block->RefCount) {
//
// Queue the newly found free block at the end of
// the list of freed heap blocks. The block has become
// eligible for this because `HeapBlock' contained the
// last remaining reference to `Block'.
//
InsertAddress(Block->BlockAddress, FreeList);
Block->RefCount = 0;
}
else {
Block->RefCount -= 1;
}
}
//
// Move to the next pointer
//
Pointer += 1;
}
}
}
Exit:
//
// Free the memory allocated at HeapBlock
//
if (NULL != HeapBlock) {
HeapFree (GetProcessHeap(), 0, HeapBlock);
HeapBlock = NULL;
}
return Success;
}
//
// ScanProcessVirtualMemory
//
// Scans the virtual memory and updates the RefCount of the heap
// blocks. This also takes care excluding the invalid stack
// regions that might contain valid pointers.
//
// Arguments
//
// Pid Process ID
//
// FreeList Pointer to a ADDRESS_LIST that holds the address of
// all free heap blocks
//
// HeapList Pointer to HEAP_LIST
//
// ReturnValue
//
// BOOL Returns true if successful in scanning through the
// virtual memory
BOOL
ScanProcessVirtualMemory(
ULONG Pid,
PADDRESS_LIST FreeList,
PHEAP_LIST HeapList
)
{
ULONG_PTR Address = 0;
MEMORY_BASIC_INFORMATION Buffer;
PVOID VirtualBlock;
ULONG VirtualBlockSize;
SYSTEM_INFO SystemInfo;
LPVOID MinAddress;
LPVOID MaxAddress;
LPHANDLE ThreadHandles;
PCONTEXT ThreadContexts;
ULONG ThreadCount;
ULONG Index;
SIZE_T dwBytesRead = 1;
BOOL Success = TRUE;
//
// Enumerate all the threads in the process and get their
// stack information
//
//
// Get the count of threads in the process
//
ThreadCount = GetThreadHandles (Pid, NULL, 0);
//
// Allocate memory for ThreadHandles
//
ThreadHandles = (LPHANDLE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
ThreadCount * sizeof(HANDLE));
if (NULL == ThreadHandles) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed for ThreadHandles");
ThreadCount = 0;
}
//
// Get the handles to the threads in the process
//
GetThreadHandles(Pid, ThreadHandles, ThreadCount);
//
// Allocate memory for ThreadContexts
//
ThreadContexts = (PCONTEXT)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
ThreadCount * sizeof(CONTEXT));
if (NULL == ThreadContexts) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed for ThreadContexts");
ThreadCount = 0;
}
GetThreadContexts (ThreadContexts, ThreadHandles, ThreadCount);
//
// We need to know maximum and minimum address space that we can
// grovel. SYSTEM_INFO has this information.
//
GetSystemInfo(&SystemInfo);
MinAddress = SystemInfo.lpMinimumApplicationAddress;
MaxAddress = SystemInfo.lpMaximumApplicationAddress;
//
// Loop through virtual memory zones
//
Address = (ULONG_PTR)MinAddress;
//
// Allocate chunk of memory for virtual block
//
VirtualBlock = (PVOID) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
MAX_VIRTUAL_BLOCK_SIZE);
if (NULL == VirtualBlock) {
Error (__FILE__,
__LINE__,
"HeapAlloc failed to allocate memory");
Success = FALSE;
goto Exit;
}
VirtualBlockSize = MAX_VIRTUAL_BLOCK_SIZE;
//
// dwBytesRead equals 1 when we enter the loop for the first time due
// to previous initialization at the start of the function.
//
while (0 != dwBytesRead && Address < (ULONG_PTR)MaxAddress) {
dwBytesRead = VirtualQueryEx (g_hProcess,
(PVOID)Address,
&Buffer,
sizeof(Buffer));
if (0 != dwBytesRead) {
DWORD dwFlags = (PAGE_READWRITE |
PAGE_EXECUTE_READWRITE |
PAGE_WRITECOPY |
PAGE_EXECUTE_WRITECOPY);
//
// If the page can be written, it might contain pointers
// to heap blocks.
//
if ((Buffer.AllocationProtect & dwFlags) &&
(Buffer.State & MEM_COMMIT)) {
PULONG_PTR Pointer;
ULONG_PTR FinalAddress;
ULONG_PTR FilteredAddress;
SIZE_T NewRegionSize;
BOOL Result;
int j;
SIZE_T BytesRead = 0;
FilteredAddress = StackFilteredAddress(Address,
Buffer.RegionSize,
ThreadContexts,
ThreadCount);
NewRegionSize = Buffer.RegionSize -
(SIZE_T)( (ULONG_PTR)FilteredAddress - (ULONG_PTR)Address);
if (VirtualBlockSize < NewRegionSize) {
if (NULL != VirtualBlock) {
HeapFree(GetProcessHeap(), 0, VirtualBlock);
VirtualBlock = NULL;
VirtualBlockSize = 0;
}
VirtualBlock = (PVOID) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
NewRegionSize);
if (NULL == VirtualBlock) {
Error (
__FILE__,
__LINE__,
"HeapAlloc failed to allocate memory"
);
Success = FALSE;
goto Exit;
}
VirtualBlockSize = (ULONG)NewRegionSize;
}
Result = ReadProcessMemory(g_hProcess,
(PVOID)FilteredAddress,
VirtualBlock,
NewRegionSize,
&BytesRead);
assert(NewRegionSize == BytesRead);
FinalAddress = (ULONG_PTR)VirtualBlock + BytesRead;
Pointer = (PULONG_PTR) VirtualBlock;
//
// Loop through pages and check any possible
// pointer reference
//
while ((ULONG_PTR)Pointer < FinalAddress) {
PHEAP_BLOCK Block;
//
// Check whether we have a pointer to a
// busy heap block
//
Block = GetHeapBlock(*Pointer,HeapList);
if (NULL != Block) {
Block->RefCount += 1;
}
//
// Move to the next pointer
//
Pointer += 1;
}
}
//
// Move to the next VM range to query
//
Address += Buffer.RegionSize;
}
}
//
// Create a linked list of free heap blocks
//
{
ULONG i, j;
for (i=0; i<HeapList->HeapCount; i++)
for (j=0; j<HeapList->Heaps[i].BlockCount; j++)
if (0 == HeapList->Heaps[i].Blocks[j].RefCount)
InsertAddress(HeapList->Heaps[i].Blocks[j].BlockAddress,
FreeList);
}
Exit:
//
// Close the ThreadHandles opened
//
for (Index = 0; Index < ThreadCount; Index += 1) {
CloseHandle (ThreadHandles[Index]);
ThreadHandles[Index] = NULL;
}
//
// Cleanup the memory allocated for ThreadHandles
//
if (NULL != ThreadHandles) {
HeapFree(GetProcessHeap(), 0, ThreadHandles);
ThreadHandles = NULL;
}
//
// Cleanup the memory allocated for ThreadContexts
//
if (NULL != ThreadContexts) {
HeapFree(GetProcessHeap(), 0, ThreadContexts);
ThreadContexts = NULL;
}
//
// Free up the memory allocated for VirtualBlock
//
if (NULL != VirtualBlock ) {
HeapFree (GetProcessHeap(), 0, VirtualBlock);
VirtualBlock = NULL;
}
return Success;
}
VOID
DetectLeaks(
PHEAP_LIST HeapList,
ULONG Pid,
FILE * OutFile
)
{
ADDRESS_LIST FreeList;
//
// Initialize the linked list
//
InitializeAddressList(&FreeList);
//
// Get a handle to the process
//
g_hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ |
PROCESS_SUSPEND_RESUME,
FALSE,
Pid);
if (NULL == g_hProcess) {
Error (__FILE__,
__LINE__,
"OpenProcess (%u) failed with error %u",
Pid,
GetLastError()
);
goto Exit;
}
//
// Sort Heaps
//
SortHeaps(HeapList, SortByBlockAddress);
//
// Scan through virtual memory zones
//
ScanProcessVirtualMemory(Pid, &FreeList, HeapList);
//
// Update references provided by the free blocks
//
ScanHeapFreeBlocks(HeapList, &FreeList);
//
// Dump the list of leaked blocks
//
DumpLeakList(OutFile, HeapList);
Exit:
//
// Close the process handle
//
if (NULL != g_hProcess) {
CloseHandle(g_hProcess);
g_hProcess = NULL;
}
//
// Free the memory associated with FreeList
//
FreeAddressList(&FreeList);
}