|
|
// 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 "Heap.hpp"
#include "RockallDebugBackEnd.hpp"
#include "RockallDebugFrontEnd.hpp"
/********************************************************************/ /* */ /* Constants local to the class. */ /* */ /* The constants supplied here try to make the layout of the */ /* the caches easier to understand and update. */ /* */ /********************************************************************/
CONST SBIT32 FindCacheSize = 2048; CONST SBIT32 FindCacheThreshold = 0; CONST SBIT32 FindSize = 1024;
/********************************************************************/ /* */ /* 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. */ /* */ /********************************************************************/
ROCKALL_DEBUG_FRONT_END::ROCKALL_DEBUG_FRONT_END ( CACHE_DETAILS *Caches1, CACHE_DETAILS *Caches2, int MaxFreeSpace, ROCKALL_BACK_END *RockallBackEnd, bool Recycle, bool SingleImage, int Stride1, int Stride2, bool ThreadSafe ) : //
// Call the constructors for the contained classes.
//
ROCKALL_FRONT_END ( Caches1, Caches2, FindCacheSize, FindCacheThreshold, FindSize, ((MaxFreeSpace == 0) ? MaxFreeSpace : 0), NewPageSizes, RockallBackEnd, 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 ensure
// an access violation if the program ever
// tries to access it.
//
VirtualAlloc ( ((void*) GuardValue), GuardSize, MEM_RESERVE, PAGE_NOACCESS ); }
/********************************************************************/ /* */ /* 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 ROCKALL_DEBUG_FRONT_END::Delete( void *Address,int Size ) { //
// 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 ( Address != ((void*) AllocationFailure) ) { //
// 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.
//
DeleteGuard( Address );
return true; } else { return false; } }
/********************************************************************/ /* */ /* 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 ROCKALL_DEBUG_FRONT_END::DeleteAll( bool Recycle ) { AUTO bool Active; AUTO void *Address = NULL; AUTO int Space;
//
// Walk the heap to verify all the allocations
// so that we know that the heap is undamaged.
//
while ( WalkGuard( & Active,& 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_FRONT_END::DeleteAll( (Recycle && false) ); }
/********************************************************************/ /* */ /* Memory allocation details. */ /* */ /* Extract information about a memory allocation and just for */ /* good measure check the guard words at the same time. */ /* */ /********************************************************************/
bool ROCKALL_DEBUG_FRONT_END::Details( void *Address,int *Space ) { return Verify( Address,Space ); }
/********************************************************************/ /* */ /* Exception processing. */ /* */ /* Although it is very hard to make Rockall crash it is */ /* technically possible. When (or should I say if) this */ /* we call the following function (which may be overloadded). */ /* */ /********************************************************************/
void ROCKALL_DEBUG_FRONT_END::Exception( char *Message ) { DebugPrint ( "EXCEPTION CAUGHT: %s\n" "ROCKALL TOTAL HEAP FAILURE: You have toasted the heap - Wow !!!!\n", Message ); }
/********************************************************************/ /* */ /* 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 the allocations. */ /* */ /********************************************************************/
bool ROCKALL_DEBUG_FRONT_END::MultipleDelete ( int Actual, void *Array[], int Size ) { REGISTER bool Result = true; REGISTER SBIT32 Count;
//
// We would realy like to use the multiple
// delete functionality of Rockall here but
// it is too much effort. So we simply call
// the standard debug delete on each entry
// in the array. Although this is not as
// fast it does give more transparent results.
//
for ( Count=0;Count < Actual;Count ++ ) { //
// Delete each memory allocation after
// carefully checking it.
//
if ( ! Delete( Array[ Count ],Size ) ) { Result = false; } }
return Result; }
/********************************************************************/ /* */ /* 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 ROCKALL_DEBUG_FRONT_END::MultipleNew ( int *Actual, void *Array[], int Requested, int Size, int *Space, bool Zero ) { //
// We would realy like to use the multiple
// new functionality of Rockall here but
// it is too much effort. So we simply call
// the standard debug new on each entry
// in the array. Although this is not as
// fast it does give more transparent results.
//
for ( (*Actual)=0;(*Actual) < Requested;(*Actual) ++ ) { REGISTER void *Current = New( Size,Space,Zero );
//
// We add each sucessful memory allocation to
// into the array.
//
if ( Current != ((void*) AllocationFailure) ) { Array[ (*Actual) ] = Current; } else { break; } }
return ((*Actual) == Requested); }
/********************************************************************/ /* */ /* 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 *ROCKALL_DEBUG_FRONT_END::New( int Size,int *Space,bool Zero ) { AUTO 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 ) { //
// We need to allocate some space plus an extra
// bit for the guard words so we can detect any
// corruption later.
//
if ( NewGuard( & Address,Size,Space ) ) { //
// Zero the allocation if requested. We do
// this based on whether we are returning the
// space information. If not we only zero
// size requested. Otherwise we have to zero
// the entire area.
//
if ( Zero ) { ZeroMemory ( Address, ((Space == NULL) ? Size : (*Space)) ); } } } else { UserError( Address,NULL,"Allocation size can not be negative" ); }
return Address; }
/********************************************************************/ /* */ /* 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 *ROCKALL_DEBUG_FRONT_END::Resize ( void *Address, int NewSize, int Move, int *Space, bool NoDelete, bool Zero ) { REGISTER void *NewAddress = ((void*) AllocationFailure);
//
// 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 ( Address != ((void*) AllocationFailure) ) { //
// 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 ) { AUTO int ActualSize;
//
// Ask for the details of the allocation. This
// will fail if the memory is not allocated.
//
if ( VerifyGuard( Address,& ActualSize,Space ) ) { //
// We always move an allocation if we are
// allowed to as this has the best chance
// of shaking out various types of bugs.
//
if ( Move != 0 ) { //
// We need to make sure we were able
// to allocate the new memory otherwise
// the copy will fail.
//
if ( NewGuard( & NewAddress,NewSize,Space ) ) { REGISTER SBIT32 Smallest = ((ActualSize < NewSize) ? ActualSize : NewSize); REGISTER SBIT32 Largest = (((Space == NULL)) ? NewSize : (*Space));
//
// Copy the contents of the old allocation
// to the new allocation.
//
memcpy ( ((void*) NewAddress), ((void*) Address), ((int) Smallest) );
//
// Zero the allocation if requested. We do
// this based on whether we are returning the
// space information. If not we only zero
// size requested. Otherwise we have to zero
// the entire area.
//
if ( Zero ) { ZeroMemory ( (((char*) NewAddress) + Smallest), (Largest - Smallest) ); }
//
// Delete the existing memory allocation
// and clean up.
//
DeleteGuard( Address ); } } } else { UserError( Address,NULL,"Resize on unallocated address" ); } } else { UserError( Address,NULL,"Allocation size must be positive" ); } } else { NewAddress = New( NewSize,Space,Zero ); }
return NewAddress; }
/********************************************************************/ /* */ /* Verify memory allocation details. */ /* */ /* Extract information about a memory allocation and just for */ /* good measure check the guard words at the same time. */ /* */ /********************************************************************/
bool ROCKALL_DEBUG_FRONT_END::Verify( void *Address,int *Space ) { AUTO int Size;
//
// Verify that the supplied address is an area
// of allocated memory. If not just exit as this
// is only a request for information.
//
return VerifyGuard( Address,& Size,Space ); }
/********************************************************************/ /* */ /* 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 ROCKALL_DEBUG_FRONT_END::Walk( bool *Active,void **Address,int *Space ) { //
// Walk the heap.
//
return WalkGuard( Active,Address,Space ); }
/********************************************************************/ /* */ /* Class destructor. */ /* */ /* Destory the current instance of the class. */ /* */ /********************************************************************/
ROCKALL_DEBUG_FRONT_END::~ROCKALL_DEBUG_FRONT_END( void ) { /* void */ }
|