|
|
// 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" ); } }
|