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.
 
 
 
 
 
 

1290 lines
40 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 "CallStack.hpp"
#include "DebugHeap.hpp"
#include "Globallock.hpp"
#include "RockallDebugBackEnd.hpp"
/********************************************************************/
/* */
/* 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 MaxContents = 32;
CONST SBIT32 DebugBufferSize = 256;
CONST SBIT32 SkipFunctions = 2;
CONST SBIT32 Stride1 = 4;
CONST SBIT32 Stride2 = 1024;
/********************************************************************/
/* */
/* 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_FRONT_END::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 },
{ 28, 0, 256, 256 },
{ 32, 0, 64, 64 },
{ 40, 0, 128, 128 },
{ 48, 0, 256, 256 },
{ 56, 0, 512, 512 },
{ 64, 0, 128, 128 },
{ 80, 0, 512, 512 },
{ 96, 0, 512, 512 },
{ 112, 0, 1024, 1024 },
{ 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, 8192, 8192 },
{ 640, 0, 4096, 4096 },
{ 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_FRONT_END::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 }
};
/********************************************************************/
/* */
/* Static data structures. */
/* */
/* The static data structures are initialized and prepared for */
/* use here. */
/* */
/********************************************************************/
#pragma init_seg(compiler)
STATIC ROCKALL_DEBUG_BACK_END RockallDebugBackEnd( true,false );
/********************************************************************/
/* */
/* 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,
//
// Additional debug flags.
//
bool FunctionTrace,
bool TrapOnUserError
) :
//
// Call the constructors for the contained classes.
//
ROCKALL_DEBUG_FRONT_END
(
Caches1,
Caches2,
MaxFreeSpace,
& RockallDebugBackEnd,
Recycle,
SingleImage,
Stride1,
Stride2,
ThreadSafe
)
{
//
// We will only enable the symbols if they are
// requested by the user. If not we will zero
// the class pointer.
//
if ( FunctionTrace )
{
//
// We will try to allocate some space so we can
// support the annoation of memory allocations
// will call traces.
//
CallStack = ((CALL_STACK*) SpecialNew( sizeof(CALL_STACK) ));
//
// We ensure that we were able to allocate the
// required space.
//
if ( CallStack != NULL )
{ PLACEMENT_NEW( CallStack,CALL_STACK ); }
}
else
{ CallStack = NULL; }
//
// We know that Rockall can survive a wide variety
// of user errors. Nonetheless, we can optionally
// raise an exception whn there is an error.
//
ExitOnError = TrapOnUserError;
}
/********************************************************************/
/* */
/* Compute the heap address. */
/* */
/* Compute the heap address from the user address. */
/* */
/********************************************************************/
void *DEBUG_HEAP::ComputeHeapAddress( void *Address )
{ return ((void*) (((char*) Address) - sizeof(HEADER))); }
/********************************************************************/
/* */
/* Compute the user address. */
/* */
/* Compute the user address from the heap address. */
/* */
/********************************************************************/
void *DEBUG_HEAP::ComputeUserAddress( void *Address )
{ return ((void*) (((char*) Address) + sizeof(HEADER))); }
/********************************************************************/
/* */
/* Compute the user space. */
/* */
/* Compute the user space from the supplied size. */
/* */
/********************************************************************/
int DEBUG_HEAP::ComputeUserSpace( int Space )
{ return (Space - sizeof(HEADER_AND_TRAILER)); }
/********************************************************************/
/* */
/* Delete the guard words. */
/* */
/* When we delete a memory allocation we overwrite it with */
/* guard words to make it really unpleasant for anyone who */
/* reads it and easy to spot when anyone write to it. */
/* */
/********************************************************************/
void DEBUG_HEAP::DeleteGuard( void *Address )
{
AUTO HEADER *Header;
AUTO TRAILER *Trailer;
AUTO int Space;
//
// Although we are about to delete the memory
// allocation there is still a chance that it
// got corrupted. So we need to verify that
// it is still undamaged.
//
if ( VerifyHeaderAndTrailer( Address,& Header,& Space,& Trailer,false ) )
{
//
// We need to overwrite all of the allocation
// to ensure that if the code tries to read
// any existing data that it is overwritten.
//
WriteGuardWords( ((void*) Header),Space );
//
// 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_FRONT_END::Delete( ((void*) Header),Space ) )
{ UserError( Address,NULL,"Delete failed due to race" ); }
}
}
/********************************************************************/
/* */
/* 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 ( WalkGuard( & Active,& Address,& Space ) )
{
AUTO CHAR Contents[ ((MaxContents + 4) * 2) ];
#ifndef OUTPUT_FREE_SPACE
//
// We report all active heap allocations
// just so the user knows there are leaks.
//
if ( Active )
{
#endif
AUTO HEADER *Header = ((HEADER*) ComputeHeapAddress( Address ) );
AUTO SBIT32 Count;
//
// Format the contents string into hexadecimal
// ready for output.
//
for
(
Count=0;
((Count < MaxContents) && (Count < Header -> Size));
Count += sizeof(SBIT32)
)
{
REGISTER CHAR *Value =
(((CHAR*) Header) + Count + sizeof(HEADER));
//
// Format each byte into hexadecimal.
//
sprintf
(
& Contents[ (Count * 2) ],
"%08x",
(*((SBIT32*) Value))
);
}
//
// Terminate the string. If it was too long
// then add the postfix "..." to the end.
//
if ( Count < MaxContents )
{ Contents[ (Count * 2) ] = '\0'; }
else
{
REGISTER CHAR *End = & Contents[ (Count * 2) ];
End[0] = '.';
End[1] = '.';
End[2] = '.';
End[3] = '\0';
}
//
// Format the message to be printed.
//
DebugPrint
(
"\nDetails of Memory Leak\n"
"Active : %d\n"
"Address : 0x%x\n"
"Bytes : %d\n"
"Contents : 0x%s\n",
Active,
((SBIT32) Address),
Header -> Size,
Contents
);
//
// We will generate a call trace if this
// is enabled.
//
if ( CallStack != NULL )
{
//
// Even when enabled there is a chance
// that the symbol subsystem could
// not walk the stack.
//
if ( Header -> Count > 0 )
{
AUTO CHAR Buffer[ DebugBufferSize ];
//
// We add the call stack information
// if there is enough space.
//
CallStack -> FormatCallStack
(
Buffer,
Header -> Functions,
DebugBufferSize,
Header -> Count
);
//
// Format the message to be printed.
//
DebugPrint
(
"Origin : (See 'Call Stack')\n"
"\n"
"Call Stack at Allocation:\n"
"%s\n",
Buffer
);
}
else
{
//
// Explain why there is no 'Call Stack'.
//
DebugPrint
(
"Origin : Unknown ('StackWalk' in 'ImageHlp.DLL' "
"was unable to walk the call stack)\n"
);
}
}
else
{
//
// Explain why there is no 'Call Stack'.
//
DebugPrint( "Origin : 'Call Stack' is Disabled\n" );
}
#ifndef OUTPUT_FREE_SPACE
}
#endif
}
}
/********************************************************************/
/* */
/* New guard words. */
/* */
/* When we make a memory allocation we verify that the guard */
/* words are still unmodified. We then setup the debug */
/* information so it describes the allocation. */
/* */
/********************************************************************/
bool DEBUG_HEAP::NewGuard( void **Address,int Size,int *Space )
{
AUTO int ActualSize;
AUTO SBIT32 MinimumSize =
(((Size + sizeof(HEADER_AND_TRAILER)) + GuardMask) & ~GuardMask);
AUTO HEADER *Header =
((HEADER*) ROCKALL_FRONT_END::New( MinimumSize,& ActualSize,false ));
//
// We need to be sure that the memory allocation
// was sucessful.
//
if ( ((void*) Header) != ((void*) AllocationFailure) )
{
//
// We need to compute the address of the area
// available to the caller and return the space
// available if requested.
//
(*Address) = ComputeUserAddress( ((void*) Header) );
if ( Space != NULL )
{ (*Space) = ComputeUserSpace( ActualSize ); }
//
// We need to make sure that the memory has
// not been damaged in any way.
//
if ( ! VerifyGuardWords( ((void*) Header),ActualSize ) )
{
//
// Complain about the damaged guard words
// and repair it so processing can continue.
//
UserError( (*Address),NULL,"Area damaged since deletion" );
WriteGuardWords( ((void*) Header),ActualSize );
}
//
// We now set up the header information that
// describes the memory allocation.
//
Header -> Count = 0;
Header -> Size = ((Space == NULL) ? Size : (*Space));
//
// We will extract the current call stack if
// needed and store it in the memory allocation.
//
if ( CallStack != NULL )
{
Header -> Count =
(
CallStack -> GetCallStack
(
Header -> Functions,
MaxFunctions,
SkipFunctions
)
);
}
return true;
}
else
{ return false; }
}
/********************************************************************/
/* */
/* Verify the supplied address. */
/* */
/* We verify that the supplied address appaers to be a valid */
/* debug memory allocation. If not we complain and exit. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyAddress
(
void *Address,
HEADER **Header,
int *Space,
bool Verify
)
{
//
// Lets be really paranoid and make sure that
// this heap knows about the supplied address.
//
if ( ROCKALL_FRONT_END::KnownArea( Address ) )
{
REGISTER void *NewAddress = ComputeHeapAddress( Address );
//
// Ask for the details of the allocation. This
// will fail if the memory is not allocated.
//
if ( ROCKALL_FRONT_END::Verify( ((void*) NewAddress),Space ) )
{
//
// Lets be even more paranoid and make sure
// that the address is correctly aligned
// and memory allocation is large enough to
// contain the necessary debug information.
//
if
(
((((int) NewAddress) & GuardMask) == 0)
&&
((*Space) >= sizeof(HEADER_AND_TRAILER))
&&
(((*Space) & GuardMask) == 0)
)
{
//
// When we have established that the address
// seems to be valid we can return it to
// the caller.
//
(*Header) = ((HEADER*) NewAddress);
return true;
}
else
{
//
// When the address is refers to something
// that does not appear to be from the debug
// heap we complain about it to the user.
//
UserError( Address,NULL,"Address unsuitable for debugging" );
return false;
}
}
else
{
//
// When the address is refers to something
// that does not appear to be from Rockall
// heap we complain about it to the user.
//
if ( ! Verify )
{ UserError( Address,NULL,"Address not allocated" ); }
return false;
}
}
else
{
//
// When the address is clearly bogus we complain
// about it to the user.
//
if ( ! Verify )
{ UserError( Address,NULL,"Address falls outside the heap" ); }
return false;
}
}
/********************************************************************/
/* */
/* Verify the guard words. */
/* */
/* When we verify a memory allocation we ensure that the */
/* guard words are all undamaged. If we find a problem we */
/* complain and repair the damage. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyGuard( void *Address,int *Size,int *Space )
{
AUTO HEADER *Header;
AUTO TRAILER *Trailer;
//
// We would like to verify that the allocated
// area is still undamaged and extract various
// information about it.
//
if ( VerifyHeaderAndTrailer( Address,& Header,Space,& Trailer,true ) )
{
//
// We know that Rockall typically allocates
// a few more bytes than requested. However,
// when we are debugging we pretend that this
// is not the case and fill the extra space
// with guard words. However, if we are asked
// the actual size then the game it is up and
// we update the necessary fields.
//
if ( Space != NULL )
{
//
// Compute the available user space and
// update the internal sizes.
//
Header -> Size = ComputeUserSpace( (*Space) );
(*Space) = Header -> Size;
}
//
// We need to return what we believe is the the
// size of the user area and the total amount of
// user available space.
//
(*Size) = Header -> Size;
return true;
}
else
{ return false; }
}
/********************************************************************/
/* */
/* Verify a string of guard words. */
/* */
/* We need to verify the guard words a various times to ensure */
/* they have not been damaged. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyGuardWords( void *Address,int Size )
{
REGISTER SBIT32 Size1 = (((long) Address) & GuardMask);
REGISTER SBIT32 Size2 = ((GuardSize - Size1) & GuardMask);
REGISTER SBIT32 Size3 = ((Size - Size2) / GuardSize);
REGISTER SBIT32 Size4 = (Size - Size2 - (Size3 * GuardSize));
REGISTER SBIT32 *Word = ((SBIT32*) (((long) Address) & ~GuardMask));
//
// Although a guard word area typically starts
// on a word aligned boundary it can sometimes
// start on a byte aligned boundary.
//
if ( Size2 > 0 )
{
REGISTER SBIT32 Mask = ~((1 << (Size1 * 8)) - 1);
//
// Examine the partial word and make sure
// the guard bytes are unmodified.
//
if ( ((*(Word ++)) & Mask) != (GuardValue & Mask) )
{ return false; }
}
//
// When there is a collection of aligned guard words
// we can quickly verify them.
//
if ( Size3 > 0 )
{
//
// Verify each guard word is unmodified.
//
for ( Size3 --;Size3 >= 0;Size3 -- )
{
if ( Word[ Size3 ] != GuardValue )
{ return false; }
}
}
//
// Although a guard word area typically ends
// on a word aligned boundary it can sometimes
// end on a byte aligned boundary.
//
if ( Size4 > 0 )
{
REGISTER SBIT32 Mask = ((1 << ((GuardSize - Size4) * 8)) - 1);
//
// Examine the partial word and make sure
// the guard bytes are unmodified.
//
if ( ((*(Word ++)) & Mask) != (GuardValue & Mask) )
{ return false; }
}
return true;
}
/********************************************************************/
/* */
/* Verify the header. */
/* */
/* We verify that the suppied address appears to map to a */
/* valid debug header. If not we complain and exit. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyHeader
(
void *Address,
HEADER **Header,
int *Space,
bool Verify
)
{
//
// We check that the address supplied seems
// to make sense before examining the header
// and testing the guard words.
//
if ( VerifyAddress( Address,Header,Space,Verify ) )
{
REGISTER int MaxSpace =
((int) ((*Header) -> Size + sizeof(HEADER_AND_TRAILER)));
//
// We are now fairly confident that the
// address is (or was at one time) a valid
// debug mory allocation. So lets examine
// the header to see if it still seems valid.
//
if
(
((*Header) -> Count >= 0)
&&
((*Header) -> Count <= MaxFunctions)
&&
((*Header) -> Size >= 0)
&&
(MaxSpace <= (*Space))
)
{
REGISTER int Count = ((*Header) -> Count);
REGISTER void *GuardWords = & (*Header) -> Functions[ Count ];
REGISTER int NumberOfGuardWords = (MaxLeadingGuardWords - Count);
REGISTER int Size = (NumberOfGuardWords * sizeof(GuardWords));
//
// Verify that the leading guard words
// just after the header have not been
// damaged.
//
if ( ! VerifyGuardWords( GuardWords,Size ) )
{
//
// We complain about damaged guard
// words and then repair them to prevent
// further complaints.
//
UserError( Address,(*Header),"Leading guard words corrupt" );
WriteGuardWords( GuardWords,Size );
}
}
else
{
//
// When the header has been damaged we
// complain about it to the user and then
// try to repair it to prevent further
// complaints.
//
UserError( Address,NULL,"Leading guard information corrupt" );
WriteGuardWords( ((void*) Header),sizeof(HEADER) );
//
// We select safe default settings.
//
(*Header) -> Count = 0;
(*Header) -> Size = ((*Space) - sizeof(HEADER_AND_TRAILER));
}
return true;
}
else
{ return false; }
}
/********************************************************************/
/* */
/* Verify a memory allocation. */
/* */
/* We need to verify that the supplied address is an undamaged */
/* memory allocation. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyHeaderAndTrailer
(
void *Address,
HEADER **Header,
int *Space,
TRAILER **Trailer,
bool Verify
)
{
//
// We need to know the space occupied by the
// allocation to compute the details of the
// trailer. So if the space parameter is null
// we use a local temporary value.
//
if ( Space != NULL )
{
//
// We need to verify the entire memory allocation
// and ensure it is fit for use.
//
return
(
VerifyHeader( Address,Header,Space,Verify )
&&
VerifyTrailer( (*Header),(*Space),Trailer )
);
}
else
{
AUTO int Temporary;
//
// We need to verify the entire memory allocation
// and ensure it is fit for use.
//
return
(
VerifyHeader( Address,Header,& Temporary,Verify )
&&
VerifyTrailer( (*Header),Temporary,Trailer )
);
}
}
/********************************************************************/
/* */
/* Verify the trailer. */
/* */
/* We need to verify the guard words a various times to ensure */
/* they have not been damaged. */
/* */
/********************************************************************/
bool DEBUG_HEAP::VerifyTrailer
(
HEADER *Header,
int Space,
TRAILER **Trailer
)
{
REGISTER SBIT32 Size = (Space - (sizeof(HEADER) + Header -> Size ));
//
// Compute the address of the user area and the
// the trailing guard words.
//
(*Trailer) =
((TRAILER*) (((char*) Header) + sizeof(HEADER) + Header -> Size));
//
// Verify that the trailing guard words
// just after the user area are not damaged.
//
if ( ! VerifyGuardWords( ((void*) (*Trailer)),Size ) )
{
REGISTER void *Address = ComputeUserAddress( ((void*) Header) );
//
// We complain about damaged guard words
// and then repair them to prevent further
// complaints.
//
UserError( Address,Header,"Trailing guard words corrupt" );
WriteGuardWords( ((void*) (*Trailer)),Size );
}
return true;
}
/********************************************************************/
/* */
/* Walk the heap. */
/* */
/* When we verify each memory allocation as we walk the heap */
/* and ensure the guard words are all undamaged. If we find a */
/* problem we complain and repair the damage. */
/* */
/********************************************************************/
bool DEBUG_HEAP::WalkGuard( bool *Active,void **Address,int *Space )
{
//
// We may need to convert the supplied user
// address into a heap address so we can walk
// the heap.
//
if ( (*Address) != ((void*) AllocationFailure) )
{ (*Address) = ComputeHeapAddress( (*Address) ); }
//
// Walk the heap.
//
if ( ROCKALL_FRONT_END::Walk( Active,Address,Space ) )
{
REGISTER void *NewAddress = ComputeUserAddress( (*Address) );
//
// We inspect the guard words to make sure
// they have not been overwritten.
//
if ( (*Active) )
{
AUTO HEADER *Header;
AUTO TRAILER *Trailer;
//
// Although we are about to delete the memory
// allocation there is still a chance that it
// got corrupted. So we need to verify that
// it is still undamaged.
//
VerifyHeaderAndTrailer( NewAddress,& Header,Space,& Trailer,false );
}
else
{
//
// We need to make sure that the memory has
// not been damaged in any way.
//
if ( ! VerifyGuardWords( (*Address),(*Space) ) )
{
//
// We need to overwrite the entire alloction
// as it has been damaged in some way.
//
UserError( (*Address),NULL,"Area damaged after deletion" );
WriteGuardWords( (*Address),(*Space) );
}
}
//
// Compute the new heap address and adjust
// the reported size.
//
(*Address) = NewAddress;
(*Space) = ComputeUserSpace( (*Space) );
return true;
}
else
{ return false; }
}
/********************************************************************/
/* */
/* Write a string of guard words. */
/* */
/* We need write a string of guard words whenever we allocate */
/* memory or detect some corruption. */
/* */
/********************************************************************/
void DEBUG_HEAP::WriteGuardWords( void *Address,int Size )
{
REGISTER SBIT32 Size1 = (((long) Address) & GuardMask);
REGISTER SBIT32 Size2 = ((GuardSize - Size1) & GuardMask);
REGISTER SBIT32 Size3 = ((Size - Size2) / GuardSize);
REGISTER SBIT32 Size4 = (Size - Size2 - (Size3 * GuardSize));
REGISTER SBIT32 *Word = ((SBIT32*) (((long) Address) & ~GuardMask));
//
// Although a guard word area typically starts
// on a word aligned boundary it can sometimes
// start on a byte aligned boundary.
//
if ( Size2 > 0 )
{
REGISTER SBIT32 Mask = ~((1 << (Size1 * 8)) - 1);
//
// Write the partial guard word but keep any
// existing data in related bytes.
//
(*(Word ++)) = (((*Word) & ~Mask) | (GuardValue & Mask));
}
//
// When there is a collection of aligned guard words
// we can quickly write them.
//
if ( Size3 > 0 )
{
//
// Write each guard word.
//
for ( Size3 --;Size3 >= 0;Size3 -- )
{ Word[ Size3 ] = ((SBIT32) GuardValue); }
}
//
// Although a guard word area typically ends
// on a word aligned boundary it can sometimes
// end on a byte aligned boundary.
//
if ( Size4 > 0 )
{
REGISTER SBIT32 Mask = ((1 << ((GuardSize - Size4) * 8)) - 1);
//
// Write the partial guard word but keep any
// existing data in related bytes.
//
(*(Word ++)) = (((*Word) & ~Mask) | (GuardValue & Mask));
}
}
/********************************************************************/
/* */
/* Abort on user error. */
/* */
/* When we encounter an error we output all of the information */
/* and throw an exception. */
/* */
/********************************************************************/
void DEBUG_HEAP::UserError( void *Address,void *Details,char *Message )
{
REGISTER HEADER *Header = ((HEADER*) Details);
STATIC GLOBALLOCK Globallock;
//
// Claim a lock so that multiple threads have
// to wait to output any heap statistics.
//
Globallock.ClaimLock();
//
// When we have an description of the
// allocation we can complain about it
//
if ( Header != NULL )
{
AUTO CHAR Contents[ ((MaxContents + 4) * 2) ];
AUTO SBIT32 Count;
//
// Format the contents string into hexadecimal
// ready for output.
//
for
(
Count=0;
((Count < MaxContents) && (Count < Header -> Size));
Count += sizeof(SBIT32)
)
{
REGISTER CHAR *Value =
(((CHAR*) Header) + Count + sizeof(HEADER));
//
// Format each byte into hexadecimal.
//
sprintf
(
& Contents[ (Count * 2) ],
"%08x",
(*((SBIT32*) Value))
);
}
//
// Terminate the string. If it was too long
// then add the postfix "..." to the end.
//
if ( Count < MaxContents )
{ Contents[ (Count * 2) ] = '\0'; }
else
{
REGISTER CHAR *End = & Contents[ (Count * 2) ];
End[0] = '.';
End[1] = '.';
End[2] = '.';
End[3] = '\0';
}
//
// Format the message to be printed.
//
DebugPrint
(
"\nDetails of Heap Corruption\n"
"Address : 0x%x\n"
"Bytes : %d\n"
"Contents : 0x%s\n"
"Message : %s\n",
Address,
Header -> Size,
Contents,
Message
);
//
// We will generate a call trace if this
// is enabled.
//
if ( CallStack != NULL )
{
//
// Even when enabled there is a chance
// that the symbol subsystem could
// not walk the stack.
//
if ( Header -> Count > 0 )
{
AUTO CHAR Buffer[ DebugBufferSize ];
//
// We add the call stack information
// if there is enough space.
//
CallStack -> FormatCallStack
(
Buffer,
Header -> Functions,
DebugBufferSize,
Header -> Count
);
//
// Format the message to be printed.
//
DebugPrint
(
"Origin : (See 'Call Stack')\n\n"
"Call Stack at Allocation:\n"
"%s\n",
Buffer
);
}
else
{
//
// Explain why there is no 'Call Stack'.
//
DebugPrint
(
"Origin : Unknown ('StackWalk' in 'ImageHlp.DLL' "
"was unable to walk the call stack)\n"
);
}
}
else
{
//
// Explain why there is no 'Call Stack'.
//
DebugPrint( "Origin : 'Call Stack' is Disabled\n" );
}
}
else
{
//
// Format the message to be printed.
//
DebugPrint
(
"\nDetails of Heap Corruption\n"
"Address : 0x%x\n"
"Bytes : (unknown)\n"
"Contents : (unknown)\n"
"Message : %s\n\n",
Address,
Message
);
}
//
// Relesae the lock.
//
Globallock.ReleaseLock();
//
// Terminate the application (if enabled).
//
if ( ExitOnError )
{ Failure( Message ); }
}
/********************************************************************/
/* */
/* Class destructor. */
/* */
/* Destory the current instance of the class. */
/* */
/********************************************************************/
DEBUG_HEAP::~DEBUG_HEAP( void )
{
AUTO bool Active;
AUTO void *Address = NULL;
AUTO int Space;
//
// Output a warning message related to debug heaps
// and the inflated size of allocations.
//
DebugPrint
(
"\n"
"REMINDER: The heap at 0x%x is a 'DEBUG_HEAP'.\n"
"REMINDER: All allocations are inflated by %d bytes.\n"
"\n",
this,
sizeof(HEADER_AND_TRAILER)
);
//
// Walk the heap to verify all the allocations
// so that we know that the heap is undamaged.
//
while ( WalkGuard( & Active,& Address,& Space ) );
//
// Destroy the symbols if they are active.
//
if ( CallStack != NULL )
{ PLACEMENT_DELETE( CallStack,CALL_STACK ); }
}