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