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.
 
 
 
 
 
 

1370 lines
43 KiB

// Ruler
// 1 2 3 4 5 6 7 8
//345678901234567890123456789012345678901234567890123456789012345678901234567890
/********************************************************************/
/* */
/* The standard layout. */
/* */
/* The standard layout for 'cpp' files in this code is as */
/* follows: */
/* */
/* 1. Include files. */
/* 2. Constants local to the class. */
/* 3. Data structures local to the class. */
/* 4. Data initializations. */
/* 5. Static functions. */
/* 6. Class functions. */
/* */
/* The constructor is typically the first function, class */
/* member functions appear in alphabetical order with the */
/* destructor appearing at the end of the file. Any section */
/* or function this is not required is simply omitted. */
/* */
/********************************************************************/
#include "InterfacePCH.hpp"
#include "DebugHeap.hpp"
#include "Heap.hpp"
void Failure( char* a);
/********************************************************************/
/* */
/* Constants local to the class. */
/* */
/* The constants supplied here try to make the layout of the */
/* the caches easier to understand and update. Additionally, */
/* there are also various guard related constants. */
/* */
/********************************************************************/
CONST SBIT32 FindCacheSize = 2048;
CONST SBIT32 FindCacheThreshold = 0;
CONST SBIT32 FindSize = 1024;
CONST SBIT32 Stride1 = 4;
CONST SBIT32 Stride2 = 1024;
CONST int GuardMask = (sizeof(int)-1);
CONST int GuardSize = sizeof(int);
/********************************************************************/
/* */
/* The description of the heap. */
/* */
/* A heap is a collection of fixed sized allocation caches. */
/* An allocation cache consists of an allocation size, the */
/* number of pre-built allocations to cache, a chunk size and */
/* a parent page size which is sub-divided to create elements */
/* for this cache. A heap consists of two arrays of caches. */
/* Each of these arrays has a stride (i.e. 'Stride1' and */
/* 'Stride2') which is typically the smallest common factor of */
/* all the allocation sizes in the array. */
/* */
/********************************************************************/
STATIC ROCKALL::CACHE_DETAILS Caches1[] =
{
//
// Bucket Size Of Bucket Parent
// Size Cache Chunks Page Size
//
{ 4, 0, 32, 32 },
{ 8, 0, 32, 32 },
{ 12, 0, 64, 64 },
{ 16, 0, 64, 64 },
{ 20, 0, 64, 64 },
{ 24, 0, 128, 128 },
{ 32, 0, 64, 64 },
{ 40, 0, 128, 128 },
{ 48, 0, 256, 256 },
{ 64, 0, 128, 128 },
{ 80, 0, 512, 512 },
{ 96, 0, 512, 512 },
{ 128, 0, 256, 256 },
{ 160, 0, 512, 512 },
{ 192, 0, 1024, 1024 },
{ 224, 0, 512, 512 },
{ 256, 0, 512, 512 },
{ 320, 0, 1024, 1024 },
{ 384, 0, 2048, 2048 },
{ 448, 0, 4096, 4096 },
{ 512, 0, 1024, 1024 },
{ 576, 0, 4096, 4096 },
{ 640, 0, 8192, 8192 },
{ 704, 0, 4096, 4096 },
{ 768, 0, 4096, 4096 },
{ 832, 0, 8192, 8192 },
{ 896, 0, 8192, 8192 },
{ 960, 0, 4096, 4096 },
{ 0,0,0,0 }
};
STATIC ROCKALL::CACHE_DETAILS Caches2[] =
{
//
// Bucket Size Of Bucket Parent
// Size Cache Chunks Page Size
//
{ 1024, 0, 2048, 2048 },
{ 2048, 0, 4096, 4096 },
{ 3072, 0, 65536, 65536 },
{ 4096, 0, 8192, 8192 },
{ 5120, 0, 65536, 65536 },
{ 6144, 0, 65536, 65536 },
{ 7168, 0, 65536, 65536 },
{ 8192, 0, 65536, 65536 },
{ 9216, 0, 65536, 65536 },
{ 10240, 0, 65536, 65536 },
{ 12288, 0, 65536, 65536 },
{ 16384, 0, 65536, 65536 },
{ 21504, 0, 65536, 65536 },
{ 32768, 0, 65536, 65536 },
{ 65536, 0, 65536, 65536 },
{ 65536, 0, 65536, 65536 },
{ 0,0,0,0 }
};
/********************************************************************/
/* */
/* The description bit vectors. */
/* */
/* All heaps keep track of allocations using bit vectors. An */
/* allocation requires 2 bits to keep track of its state. The */
/* following array supplies the size of the available bit */
/* vectors measured in 32 bit words. */
/* */
/********************************************************************/
STATIC int NewPageSizes[] = { 1,4,0 };
/********************************************************************/
/* */
/* Class constructor. */
/* */
/* The overall structure and layout of the heap is controlled */
/* by the various constants and calls made in this function. */
/* There is a significant amount of flexibility available to */
/* a heap which can lead to them having dramatically different */
/* properties. */
/* */
/********************************************************************/
DEBUG_HEAP::DEBUG_HEAP
(
int MaxFreeSpace,
bool Recycle,
bool SingleImage,
bool ThreadSafe
) :
//
// Call the constructors for the contained classes.
//
ROCKALL
(
Caches1,
Caches2,
FindCacheSize,
FindCacheThreshold,
FindSize,
MaxFreeSpace,
NewPageSizes,
Recycle,
SingleImage,
Stride1,
Stride2,
ThreadSafe
)
{
//
// We make much use of the guard value in the
// debug heap so here we try to claim the
// address but not commit it so we will cause
// an access violation if the program ever
// tries to access it.
//
VirtualAlloc
(
((void*) GuardValue),
GuardSize,
MEM_RESERVE,
PAGE_NOACCESS
);
//
// We verify various values and ensure the heap
// is not corrupt.
//
if
(
(MaxFreeSpace < 0)
||
(ROCKALL::Corrupt())
)
{ Failure( "Heap initialization failed to complete" ); }
}
/********************************************************************/
/* */
/* Memory deallocation. */
/* */
/* We make sure the memory is allocated and that the guard */
/* words have not been damanged. If so we reset the contents */
/* of the allocation and delete the allocation. */
/* */
/********************************************************************/
bool DEBUG_HEAP::Delete( void *Address,int Size )
{
AUTO DEBUG_HEADER *Header =
(
(Address == ((void*) AllocationFailure))
? ((DEBUG_HEADER*) Address)
: ComputeHeaderAddress( Address )
);
//
// A well known practice is to try to delete
// a null pointer. This is really a very poor
// style but we support it in any case.
//
if ( Header != ((void*) AllocationFailure) )
{
AUTO int TotalSize;
//
// Ask for the details of the allocation. This
// will fail if the memory is not allocated.
//
if ( ROCKALL::Verify( ((void*) Header),& TotalSize ) )
{
REGISTER int NewSize = (Size + sizeof(DEBUG_GUARD));
//
// Test the guard words to make sure they have
// not been damaged.
//
TestGuardWords( Header,TotalSize );
//
// Delete the user information by writing
// guard words over the allocation. This
// should cause the application to crash
// if the area is read and also allows us
// to check to see if it is written later.
//
ResetGuardWords( Header,TotalSize );
//
// Delete the allocation. This really ought
// to work given we have already checked that
// the allocation is valid unless there is a
// race condition.
//
if ( ! ROCKALL::Delete( ((void*) Header),NewSize ) )
{ Failure( "Delete requested failed due to race" ); }
//
// We ensure that the heap has not become corrupt
// during the deletion process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "Delete failed to complete" ); }
}
else
{ Failure( "Delete requested on unallocated memory" ); }
}
return true;
}
/********************************************************************/
/* */
/* Delete all allocations. */
/* */
/* We check to make sure the heap is not corrupt and force */
/* the return of all heap space back to the operating system. */
/* */
/********************************************************************/
void DEBUG_HEAP::DeleteAll( bool Recycle )
{
AUTO bool Active;
AUTO void *Address = NULL;
AUTO int Space;
//
// Walk the heap and check all the allocations
// to make sure the guard words have not been
// overwritten.
//
while ( ROCKALL::Walk( & Active,& Address,& Space ) )
{
//
// We inspect the guard words to make sure
// they have not been overwritten.
//
if ( Active )
{ TestGuardWords( ((DEBUG_HEADER*) Address),Space ); }
else
{ UnmodifiedGuardWords( ((DEBUG_HEADER*) Address),Space ); }
}
//
// Delete the heap and force all the allocated
// memory to be returned to the operating system
// regardless of what the user requested. Any
// attempt to access the deallocated memory will
// be trapped by the operating system.
//
ROCKALL::DeleteAll( (Recycle && false) );
//
// We ensure that the heap has not become corrupt
// during the deletion process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "DeleteAll failed to complete" ); }
}
/********************************************************************/
/* */
/* Memory allocation details. */
/* */
/* Extract information about a memory allocation and just for */
/* good measure check the guard words at the same time. */
/* */
/********************************************************************/
bool DEBUG_HEAP::Details( void *Address,int *Space )
{ return Verify( Address,Space ); }
/********************************************************************/
/* */
/* Print a list of heap leaks. */
/* */
/* We walk the heap and output a list of active heap */
/* allocations to the debug window, */
/* */
/********************************************************************/
void DEBUG_HEAP::HeapLeaks( void )
{
AUTO bool Active;
AUTO void *Address = NULL;
AUTO int Space;
//
// Walk the heap and find all the active and
// available spece. We would normally expect
// this to be proportional to the size of the
// heap.
//
while ( ROCKALL::Walk( & Active,& Address,& Space ) )
{
CONST INT DebugBufferSize = 8192;
#ifndef OUTPUT_FREE_SPACE
//
// We report all active heap allocations
// just so the user knows there are leaks.
//
if ( Active )
{
#endif
AUTO CHAR Buffer[ DebugBufferSize ];
//
// Format the string to be printed.
//
(void) sprintf
(
Buffer,
"Memory leak \t%d \t0x%x \t%d\n",
Active,
(((SBIT32) Address) + sizeof(DEBUG_HEADER)),
Space
);
//
// Force null termination.
//
Buffer[ (DebugBufferSize-1) ] = '\0';
//
// Write the string to the debug window.
//
OutputDebugString( Buffer );
#ifndef OUTPUT_FREE_SPACE
}
#endif
}
}
/********************************************************************/
/* */
/* Multiple memory deallocations. */
/* */
/* We make sure all the memory is allocated and that the guard */
/* words have not been damaged. If so we reset the contents */
/* of the allocations and then delete all the allocations. */
/* */
/********************************************************************/
bool DEBUG_HEAP::MultipleDelete
(
int Actual,
void *Array[],
int Size
)
{
REGISTER int Count;
REGISTER int NewSize = (Size + sizeof(DEBUG_GUARD));
//
// Examine each memory allocation and delete it
// after carefully checking it.
//
for ( Count=0;Count < Actual;Count ++ )
{
AUTO int TotalSize;
AUTO VOID *Address = Array[ Count ];
AUTO DEBUG_HEADER *Header =
(
(Address == ((void*) AllocationFailure))
? ((DEBUG_HEADER*) Address)
: ComputeHeaderAddress( Address )
);
//
// Ask for the details of the allocation. This
// will fail if the memory is not allocated.
//
if ( ROCKALL::Verify( ((void*) Header),& TotalSize ) )
{
//
// Test the guard words to make sure they have
// not been damaged.
//
TestGuardWords( Header,TotalSize );
//
// Delete the user information by writing
// guard words over the allocation. This
// should cause the application to crash
// if the area is read and also allows us
// to check to see if it is written later.
//
ResetGuardWords( Header,TotalSize );
//
// Update the address in the array to the
// address originally allocated.
//
Array[ Count ] = ((VOID*) Header);
}
else
{ Failure( "Delete requested on unallocated memory" ); }
}
//
// Delete the allocation. This really ought
// to work given we have already checked that
// the allocations are valid unless there is a
// race condition.
//
if ( ! ROCKALL::MultipleDelete( Actual,Array,NewSize ) )
{ Failure( "Delete requested failed due to race" ); }
//
// We ensure that the heap has not become corrupt
// during the deletion process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "MultipleDelete failed to complete" ); }
return true;
}
/********************************************************************/
/* */
/* Multiple memory allocations. */
/* */
/* Allocate a collection of memory elements and setup the */
/* guard information so we can check they have not been */
/* damaged later. */
/* */
/********************************************************************/
bool DEBUG_HEAP::MultipleNew
(
int *Actual,
void *Array[],
int Requested,
int Size,
int *Space,
bool Zero
)
{
REGISTER bool Result = false;
//
// The requested number of elements and the size
// must be greater than zero. We require the
// caller to allocate a positive amount of memory.
//
if ( (Requested > 0) && (Size >= 0) )
{
AUTO int TotalSize;
REGISTER int NewSize =
(((Size + sizeof(DEBUG_GUARD)) + GuardMask) & ~GuardMask);
//
// Allocate the memory plus some additional
// memory for the guard words.
//
Result =
(
ROCKALL::MultipleNew
(
Actual,
Array,
Requested,
NewSize,
& TotalSize
)
);
//
// If we were able to allocate some memory then
// set the guard words so we can detect any
// corruption later.
//
if ( (*Actual) > 0 )
{
REGISTER int Count;
//
// If the real size is requested then return
// it to the caller.
//
if ( Space != NULL )
{ (*Space) = (TotalSize - sizeof(DEBUG_GUARD)); }
//
// Set the guard words so we can see if
// someone damages any allocation. If the
// caller requested the size information
// then we must assume that it could be
// used so we need to adjust the number
// of guard words.
//
for ( Count=0;Count < (*Actual);Count ++ )
{
REGISTER void **Current = & Array[ Count ];
//
// Set up the guard words and ensure
// the allocation has not been written
// since being freed.
//
SetGuardWords
(
((DEBUG_HEADER*) (*Current)),
((Space == NULL) ? Size : (*Space)),
TotalSize
);
//
// Compute the external address and place
// it back in the array.
//
(*Current) = ComputeDataAddress( ((DEBUG_HEADER*) (*Current)) );
//
// Zero the memory if the needed.
//
if ( Zero )
{
ZeroMemory
(
(*Current),
((Space == NULL) ? Size : (*Space))
);
}
}
}
//
// We ensure that the heap has not become corrupt
// during the allocation process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "Multiple new failed to complete" ); }
}
else
{ Failure( "Allocation size must greater than zero" ); }
return Result;
}
/********************************************************************/
/* */
/* Memory allocation. */
/* */
/* We add some space on to the original allocation size for */
/* various information and then call the allocator. We then */
/* set the guard words so we can check for overruns. */
/* */
/********************************************************************/
void *DEBUG_HEAP::New( int Size,int *Space,bool Zero )
{
REGISTER void *Address = ((void*) AllocationFailure);
//
// The size must be greater than or equal to zero.
// We do not know how to allocate a negative amount
// of memory.
//
if ( Size >= 0 )
{
AUTO int TotalSize;
REGISTER int NewSize =
(((Size + sizeof(DEBUG_GUARD)) + GuardMask) & ~GuardMask);
//
// Allocate the memory plus some additional
// memory for the guard words.
//
Address = ROCKALL::New( NewSize,& TotalSize,false );
//
// If we were able to allocate some memory then
// set the guard words so we can detect any
// corruption later.
//
if ( Address != ((void*) AllocationFailure) )
{
//
// If the real size is requested then return it
// to the caller.
//
if ( Space != NULL )
{ (*Space) = (TotalSize - sizeof(DEBUG_GUARD)); }
//
// Set the guard words so we can see if
// someone damages any allocation. If the
// caller requested the size information
// then we must assume that it could be
// used so we need to adjust the number
// of guard words.
//
SetGuardWords
(
((DEBUG_HEADER*) Address),
((Space == NULL) ? Size : (*Space)),
TotalSize
);
//
// Compute the external address and place
// it back in the variable.
//
Address = ComputeDataAddress( ((DEBUG_HEADER*) Address) );
//
// Zero the allocation if needed.
//
if ( Zero )
{
ZeroMemory
(
Address,
((Space == NULL) ? Size : (*Space))
);
}
}
//
// We ensure that the heap has not become corrupt
// during the allocation process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "New failed to complete" ); }
}
else
{ Failure( "Allocation size can not be negative" ); }
return Address;
}
/********************************************************************/
/* */
/* Memory area allocation. */
/* */
/* We need to allocate some new memory from the operating */
/* system and prepare it for use in the debugging heap. */
/* */
/********************************************************************/
void *DEBUG_HEAP::NewArea( int AlignMask,int Size,bool User )
{
REGISTER void *Memory = ROCKALL::NewArea( AlignMask,Size,User );
//
// If we managed to get a new page then write
// the guard value over it to allow us to
// verify it has not been overwritten later.
//
if ( Memory != ((void*) AllocationFailure) )
{
REGISTER int Count;
//
// Write the guard value into all of the new
// heap page to allow it to be checked for
// corruption.
//
for ( Count=0;Count < Size;Count += GuardSize )
{ (((int*) Memory)[ (Count / GuardSize) ]) = GuardValue; }
}
return Memory;
}
/********************************************************************/
/* */
/* Memory reallocation. */
/* */
/* We need to resize an allocation. We ensure the original */
/* allocation was undamaged and then expand it. We also */
/* update the guard words to reflect the changes. */
/* */
/********************************************************************/
void *DEBUG_HEAP::Resize
(
void *Address,
int NewSize,
int Move,
int *Space,
bool NoDelete,
bool Zero
)
{
AUTO DEBUG_HEADER *Header =
(
(Address == ((void*) AllocationFailure))
? ((DEBUG_HEADER*) Address)
: ComputeHeaderAddress( Address )
);
//
// A well known practice is to try to resize a null
// pointer. This is really a very poor style but we
// support it in any case.
//
if ( Header != ((void*) AllocationFailure) )
{
AUTO int TotalSize;
//
// The new size must be greater than or equal to
// zero. We do not know how to allocate a negative
// amount of memory.
//
if ( NewSize >= 0 )
{
REGISTER int Size =
(((NewSize + sizeof(DEBUG_GUARD)) + GuardMask) & ~GuardMask);
//
// Ask for the details of the allocation. This
// will fail if the memory is not allocated.
//
if ( ROCKALL::Verify( ((void*) Header),& TotalSize ) )
{
REGISTER void *OriginalAddress = ((void*) Header);
REGISTER int OriginalSize = TotalSize;
//
// Test the guard words to make sure they have
// not been damaged.
//
TestGuardWords( Header,TotalSize );
//
// Reallocate the memory plus some additional
// memory for the guard words.
//
Address =
(
ROCKALL::Resize
(
OriginalAddress,
Size,
Move,
& TotalSize,
true,
false
)
);
//
// If we were able to allocate some memory
// then set the guard words so we can detect
// any corruption later.
//
if ( Address != ((void*) AllocationFailure) )
{
REGISTER SBIT32 SpaceUsed = Header -> Size;
//
// Delete the user information by writing
// guard words over the allocation. This
// should cause the application to crash
// if the area is read and allows us to
// check to see if it is written later.
//
if ( (! NoDelete) && (Address != OriginalAddress) )
{
ResetGuardWords( Header,OriginalSize );
if ( ! ROCKALL::Delete( OriginalAddress ) )
{ Failure( "Delete failed due to race" ); }
}
//
// If the real size is requested then
// return it to the caller.
//
if ( Space != NULL )
{ (*Space) = (TotalSize - sizeof(DEBUG_GUARD)); }
//
// Update the guard words so we can see
// if someone damages the allocation. If
// the caller requested the size information
// then we must assume that it could be
// used so we need to adjust the guard words.
//
UpdateGuardWords
(
((DEBUG_HEADER*) Address),
((Space == NULL) ? NewSize : (*Space)),
TotalSize
);
//
// Compute the external address and place
// it back in the variable.
//
Address = ComputeDataAddress( ((DEBUG_HEADER*) Address) );
//
// Zero the memory if the needed.
//
if ( Zero )
{
REGISTER SBIT32 ActualSize =
((Space == NULL) ? Size : (*Space));
REGISTER SBIT32 Difference =
(ActualSize - SpaceUsed);
//
// If the new size is larger than
// old size then zero the end of the
// new allocation.
//
if ( Difference > 0 )
{
REGISTER CHAR *Array = ((CHAR*) Address);
ZeroMemory( & Array[ SpaceUsed ],Difference );
}
}
}
}
else
{ Failure( "Resize requested on unallocated memory" ); }
}
else
{ Failure( "Allocation size must be positive" ); }
}
else
{ Address = New( NewSize,Space,Zero ); }
//
// We ensure that the heap has not become corrupt
// during the reallocation process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "Resize failed to complete" ); }
return Address;
}
/********************************************************************/
/* */
/* Reset the guard words. */
/* */
/* We need to reset the guard words just before we delete a */
/* memory allocation. */
/* */
/********************************************************************/
void DEBUG_HEAP::ResetGuardWords( DEBUG_HEADER *Header,int TotalSize )
{
REGISTER int Count;
//
// Write guard words over the allocated space as
// the allocation is about to be freed.
//
for ( Count=0;Count < TotalSize;Count += GuardSize )
{ (((int*) Header)[ (Count / GuardSize) ]) = GuardValue; }
}
/********************************************************************/
/* */
/* Set the guard words. */
/* */
/* We need to set the guard words just after an allocation so */
/* we can check them later. */
/* */
/********************************************************************/
void DEBUG_HEAP::SetGuardWords( DEBUG_HEADER *Header,int Size,int TotalSize )
{
//
// We check that the information supplied seems
// to make sense before setting up the guard words.
//
if
(
((((int) Header) & GuardMask) == 0)
&&
((TotalSize & GuardMask) == 0)
&&
((Size + ((int) sizeof(DEBUG_GUARD))) <= TotalSize)
&&
(Size >= 0)
)
{
REGISTER int Count;
//
// We know that the entire allocation should be
// set to the guard value so check that it has
// not been overwritten.
//
for ( Count=0;Count < TotalSize;Count += GuardSize )
{
if ( (((int*) Header)[ (Count / GuardSize) ]) != GuardValue )
{ Failure( "Guard words have been damaged" ); }
}
//
// Write the header information.
//
Header -> Size = Size;
}
else
{ Failure( "Guard word area is too small or unaligned" ); }
}
/********************************************************************/
/* */
/* Test the guard words. */
/* */
/* We need to test the guard words a various times to ensure */
/* are still valid. */
/* */
/********************************************************************/
void DEBUG_HEAP::TestGuardWords( DEBUG_HEADER *Header,int TotalSize )
{
//
// We check that the information supplied seems
// to make sense before testing the guard words.
//
if
(
((((int) Header) & GuardMask) == 0)
&&
((TotalSize & GuardMask) == 0)
&&
((Header -> Size + ((int) sizeof(DEBUG_GUARD))) <= TotalSize)
&&
(Header -> Size >= 0)
)
{
REGISTER int Count;
REGISTER char *DataArea = ((char*) ComputeDataAddress( Header ));
REGISTER int EndIndex = ((Header -> Size + GuardMask) & ~GuardMask);
REGISTER int EndSize = (TotalSize - sizeof(DEBUG_HEADER) - GuardSize);
REGISTER char *MidGuard = & DataArea[ (EndIndex - GuardSize) ];
REGISTER DEBUG_TRAILER *Trailer = ((DEBUG_TRAILER*) MidGuard);
//
// Test the guard word just before the allocation
// to see if it has been overwritten.
//
if ( Header -> StartGuard != GuardValue )
{ Failure( "Leading guard word has been damaged" ); }
//
// Test the guard bytes just after the allocation
// to see if they have been overwritten.
//
for ( Count=Header -> Size;(Count & GuardMask) != 0;Count ++ )
{
REGISTER int ByteIndex = (Count & GuardMask);
//
// Test each byte up to the next word boundary.
//
if
(
Trailer -> MidGuard[ ByteIndex ]
!=
((char*) & GuardValue)[ ByteIndex ]
)
{ Failure( "Trailing guard byte has been damaged" ); }
}
//
// Test the guard words following the allocation
// to see if they have been overwritten.
//
for ( Count=(EndSize - Count);Count >= 0;Count -= GuardSize )
{
if ( Trailer -> EndGuard[ (Count / GuardSize) ] != GuardValue )
{ Failure( "Trailing guard word has been damaged" ); }
}
}
else
{ Failure( "Guard information has been damaged" ); }
}
/********************************************************************/
/* */
/* Memory Trunction. */
/* */
/* We truncate the heap and make sure that this does not */
/* corrupt the heap in some way. */
/* */
/********************************************************************/
bool DEBUG_HEAP::Truncate( int MaxFreeSpace )
{
REGISTER bool Result;
//
// We truncate the heap and release all available
// memory regardless of what the caller requested.
//
Result = ROCKALL::Truncate( 0 );
//
// We verify various values and ensure the heap
// is not corrupt.
//
if
(
(MaxFreeSpace < 0)
||
(ROCKALL::Corrupt())
)
{ Failure( "Heap truncation failed to complete" ); }
return Result;
}
/********************************************************************/
/* */
/* Unmodified guard words. */
/* */
/* We need to inspect the guard words to ensure they have not */
/* changed after being freed. */
/* */
/********************************************************************/
void DEBUG_HEAP::UnmodifiedGuardWords( DEBUG_HEADER *Header,int TotalSize )
{
REGISTER int Count;
//
// We know that the entire allocation should be
// set to the guard value so check that it has
// not been overwritten.
//
for ( Count=0;Count < TotalSize;Count += GuardSize )
{
if ( (((int*) Header)[ (Count / GuardSize) ]) != GuardValue )
{ Failure( "Guard words on unallocated memory have been damaged" ); }
}
}
/********************************************************************/
/* */
/* Update the guard words. */
/* */
/* We need to update the guard words after a resize so we can */
/* check them later. */
/* */
/********************************************************************/
void DEBUG_HEAP::UpdateGuardWords( DEBUG_HEADER *Header,int Size,int TotalSize )
{
//
// We check that the information supplied seems
// to make sense before setting up the guard words.
//
if
(
((((int) Header) & GuardMask) == 0)
&&
((TotalSize & GuardMask) == 0)
&&
((Size + ((int) sizeof(DEBUG_GUARD))) <= TotalSize)
&&
(Size >= 0)
)
{
//
// We only copy the smaller of the new size
// and the old size. So check just the
// correct number of guard words.
//
if ( Header -> Size > Size )
{
REGISTER int Count;
REGISTER char *DataArea = ((char*) ComputeDataAddress( Header ));
REGISTER int EndIndex = ((Size + GuardMask) & ~GuardMask);
REGISTER int EndSize = (TotalSize - sizeof(DEBUG_HEADER) - GuardSize);
REGISTER char *MidGuard = & DataArea[ (EndIndex - GuardSize) ];
REGISTER DEBUG_TRAILER *Trailer = ((DEBUG_TRAILER*) MidGuard);
//
// Update the guard bytes just after the
// allocation.
//
for ( Count=Size;(Count & GuardMask) != 0;Count ++ )
{
REGISTER int ByteIndex = (Count & GuardMask);
Trailer -> MidGuard[ ByteIndex ] =
((char*) & GuardValue)[ ByteIndex ];
}
//
// Write guard words over part of the space
// as the allocation is being shrunk.
//
for ( Count=(EndSize - Count);Count >= 0;Count -= GuardSize )
{ Trailer -> EndGuard[ (Count / GuardSize) ] = GuardValue; }
//
// Update the header information.
//
Header -> Size = Size;
//
// We know that the entire allocation should
// be set to the guard value so check that it
// has not been overwritten.
//
TestGuardWords( Header,TotalSize );
}
else
{
//
// We know that the entire allocation should be
// set to the guard value so check that it has
// not been overwritten.
//
TestGuardWords( Header,TotalSize );
//
// Update the header information.
//
Header -> Size = Size;
}
}
else
{ Failure( "Guard word information area is damaged" ); }
}
/********************************************************************/
/* */
/* Verify memory allocation details. */
/* */
/* Extract information about a memory allocation and just for */
/* good measure check the guard words at the same time. */
/* */
/********************************************************************/
bool DEBUG_HEAP::Verify( void *Address,int *Space )
{
AUTO bool Result;
AUTO int TotalSize;
AUTO DEBUG_HEADER *Header =
(
(Address == ((void*) AllocationFailure))
? ((DEBUG_HEADER*) Address)
: ComputeHeaderAddress( Address )
);
//
// Extract information about the memory allocation.
//
Result =
(
ROCKALL::Verify
(
((void*) Header),
& TotalSize
)
);
//
// If we managed to extract the information then
// check the guard words for good measure.
//
if ( Result )
{
//
// If we are about to return the actual
// amount of spce available then we must
// update the size of the guard area.
//
if ( Space == NULL )
{
//
// Test the guard words to make sure they have
// not been damaged.
//
TestGuardWords( Header,TotalSize );
}
else
{
//
// Compute the amount of available space.
//
(*Space) = (TotalSize - sizeof(DEBUG_GUARD));
//
// Test the guard words to make sure they have
// not been damaged.
//
UpdateGuardWords( Header,(*Space),TotalSize );
}
}
//
// We ensure that the heap has not become corrupt
// during the verification process.
//
if ( ROCKALL::Corrupt() )
{ Failure( "Heap verify failed to complete" ); }
return Result;
}
/********************************************************************/
/* */
/* Walk the heap. */
/* */
/* We have been asked to walk the heap. It is hard to know */
/* why anybody might want to do this given the rest of the */
/* functionality available. Nonetheless, we just do what is */
/* required to keep everyone happy. */
/* */
/********************************************************************/
bool DEBUG_HEAP::Walk( bool *Active,void **Address,int *Space )
{
AUTO DEBUG_HEADER *Header =
(
((*Address) == ((void*) AllocationFailure))
? ((DEBUG_HEADER*) (*Address))
: ComputeHeaderAddress( (*Address) )
);
//
// Walk the heap.
//
if ( ROCKALL::Walk( Active,((VOID**) & Header),Space ) )
{
//
// We inspect the guard words to make sure
// they have not been overwritten.
//
if ( (*Active) )
{ TestGuardWords( Header,(*Space) ); }
else
{ UnmodifiedGuardWords( Header,(*Space) ); }
//
// Compute the new heap address.
//
(*Address) = ComputeDataAddress( Header );
//
// Compute the amount of available space.
//
(*Space) -= sizeof(DEBUG_GUARD);
return true;
}
else
{ return false; }
}
/********************************************************************/
/* */
/* Class destructor. */
/* */
/* Destory the current instance of the class. */
/* */
/********************************************************************/
DEBUG_HEAP::~DEBUG_HEAP( void )
{
AUTO bool Active;
AUTO void *Address = NULL;
AUTO int Space;
//
// Walk the heap and check all the allocations
// to make sure the guard words have not been
// overwritten.
//
while ( ROCKALL::Walk( & Active,& Address,& Space ) )
{
//
// We inspect the guard words to make sure
// they have not been overwritten.
//
if ( Active )
{ TestGuardWords( ((DEBUG_HEADER*) Address),Space ); }
else
{ UnmodifiedGuardWords( ((DEBUG_HEADER*) Address),Space ); }
}
//
// We ensure that the heap has not become corrupt
// during the its lifetime.
//
if ( ROCKALL::Corrupt() )
{ Failure( "Destructor failed to complete" ); }
}