2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platformMemory.h"
# include "console/dynamicTypes.h"
# include "console/engineAPI.h"
# include "core/stream/fileStream.h"
# include "core/strings/stringFunctions.h"
# include "console/console.h"
# include "platform/profiler.h"
# include "platform/threads/mutex.h"
# include "core/module.h"
// If profile paths are enabled, disable profiling of the
// memory manager as that would cause a cyclic dependency
// through the string table's allocation stuff used by the
// profiler (talk about a long sentence...)
# ifdef TORQUE_ENABLE_PROFILE_PATH
# undef PROFILE_START
# undef PROFILE_END
# undef PROFILE_SCOPE
# define PROFILE_START( x )
# define PROFILE_END()
# define PROFILE_SCOPE( x )
# endif
# ifdef TORQUE_MULTITHREAD
void * gMemMutex = NULL ;
# endif
//-------------------------------------- Make sure we don't have the define set
# ifdef new
# undef new
# endif
enum MemConstants {
Allocated = BIT ( 0 ) ,
Array = BIT ( 1 ) ,
DebugFlag = BIT ( 2 ) ,
Reallocated = BIT ( 3 ) , /// This flag is set if the memory has been allocated, then 'realloc' is called
GlobalFlag = BIT ( 4 ) ,
StaticFlag = BIT ( 5 ) ,
AllocatedGuard = 0xCEDEFEDE ,
FreeGuard = 0x5555FFFF ,
MaxAllocationAmount = 0xFFFFFFFF ,
TreeNodeAllocCount = 2048 ,
} ;
inline U32 flagToBit ( Memory : : EFlag flag )
{
using namespace Memory ;
U32 bit = 0 ;
switch ( flag )
{
case FLAG_Debug : bit = DebugFlag ; break ;
case FLAG_Global : bit = GlobalFlag ; break ;
case FLAG_Static : bit = StaticFlag ; break ;
}
return bit ;
}
enum RedBlackTokens {
Red = 0 ,
Black = 1
} ;
static U32 MinPageSize = 8 * 1024 * 1024 ;
# if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD)
# define LOG_PAGE_ALLOCS
# endif
U32 gNewNewTotal = 0 ;
U32 gImageAlloc = 0 ;
//---------------------------------------------------------------------------
namespace Memory
{
ConsoleFunctionGroupBegin ( Memory , " Memory manager utility functions. " ) ;
struct FreeHeader ;
/// Red/Black Tree Node - used to store queues of free blocks
struct TreeNode
{
U32 size ;
TreeNode * parent ;
TreeNode * left ;
TreeNode * right ;
U32 color ;
FreeHeader * queueHead ;
FreeHeader * queueTail ;
U32 unused ;
} ;
struct Header
{
// doubly linked list of allocated and free blocks -
// contiguous in memory.
# ifdef TORQUE_DEBUG_GUARD
U32 preguard [ 4 ] ;
# endif
Header * next ;
Header * prev ;
dsize_t size ;
U32 flags ;
# ifdef TORQUE_DEBUG_GUARD
# ifdef TORQUE_ENABLE_PROFILE_PATH
U32 unused [ 5 ] ;
# else
U32 unused [ 4 ] ;
# endif
U32 postguard [ 4 ] ;
# endif
} ;
struct AllocatedHeader
{
# ifdef TORQUE_DEBUG_GUARD
U32 preguard [ 4 ] ;
# endif
Header * next ;
Header * prev ;
dsize_t size ;
U32 flags ;
# ifdef TORQUE_DEBUG_GUARD
// an allocated header will only have this stuff if TORQUE_DEBUG_GUARD
U32 line ;
U32 allocNum ;
const char * fileName ;
# ifdef TORQUE_ENABLE_PROFILE_PATH
const char * profilePath ;
# endif
U32 realSize ;
U32 postguard [ 4 ] ;
# endif
void * getUserPtr ( )
{
return ( this + 1 ) ;
}
} ;
struct FreeHeader
{
# ifdef TORQUE_DEBUG_GUARD
U32 preguard [ 4 ] ;
# endif
Header * next ;
Header * prev ;
dsize_t size ;
U32 flags ;
// since a free header has at least one cache line (16 bytes)
// we can tag some more stuff on:
FreeHeader * nextQueue ; // of the same size
FreeHeader * prevQueue ; // doubly linked
TreeNode * treeNode ; // which tree node we're coming off of.
U32 guard ;
# ifdef TORQUE_DEBUG_GUARD
# ifdef TORQUE_ENABLE_PROFILE_PATH
U32 unused ;
# endif
U32 postguard [ 4 ] ;
# endif
} ;
struct PageRecord
{
dsize_t allocSize ;
PageRecord * prevPage ;
Header * headerList ; // if headerList is NULL, this is a treeNode page
void * basePtr ;
U32 unused [ 4 ] ; // even out the record to 32 bytes...
// so if we're on a 32-byte cache-line comp, the tree nodes
// will cache better
} ;
PageRecord * gPageList = NULL ;
TreeNode nil ;
TreeNode * NIL = & nil ;
TreeNode * gFreeTreeRoot = & nil ;
TreeNode * gTreeFreeList = NULL ;
U32 gInsertCount = 0 ;
U32 gRemoveCount = 0 ;
U32 gBreakAlloc = 0xFFFFFFFF ;
U32 gCurrAlloc = 0 ;
char gLogFilename [ 256 ] = " memlog.txt " ;
bool gEnableLogging = false ;
bool gNeverLogLeaks = 0 ;
bool gAlwaysLogLeaks = 0 ;
U32 gBytesAllocated = 0 ;
U32 gBlocksAllocated = 0 ;
U32 gPageBytesAllocated = 0 ;
struct HeapIterator
{
PageRecord * mCurrentPage ;
Header * mCurrentHeader ;
bool mAllocatedOnly ;
HeapIterator ( bool allocatedOnly = true )
: mCurrentPage ( gPageList ) ,
mAllocatedOnly ( allocatedOnly ) ,
mCurrentHeader ( NULL )
{
if ( mCurrentPage )
{
mCurrentHeader = mCurrentPage - > headerList ;
while ( ! mCurrentHeader & & mCurrentPage )
{
mCurrentPage = mCurrentPage - > prevPage ;
mCurrentHeader = mCurrentPage - > headerList ;
}
if ( mCurrentHeader & & mAllocatedOnly & & ! ( mCurrentHeader - > flags & Allocated ) )
+ + ( * this ) ; // Advance to first allocated record.
}
}
bool isValid ( ) const
{
return ( mCurrentHeader ! = NULL ) ;
}
HeapIterator & operator + + ( )
{
do
{
if ( ! mCurrentHeader )
{
if ( mCurrentPage )
mCurrentPage = mCurrentPage - > prevPage ;
if ( ! mCurrentPage )
break ;
mCurrentHeader = mCurrentPage - > headerList ;
}
else
mCurrentHeader = mCurrentHeader - > next ;
}
while ( ! mCurrentHeader | | ( mAllocatedOnly & & ! ( mCurrentHeader - > flags & Allocated ) ) ) ;
return * this ;
}
operator Header * ( ) const
{
return mCurrentHeader ;
}
Header * operator * ( ) const
{
return mCurrentHeader ;
}
Header * operator - > ( ) const
{
return mCurrentHeader ;
}
} ;
# ifdef TORQUE_DEBUG_GUARD
static bool checkGuard ( Header * header , bool alloc )
{
U32 guardVal = alloc ? AllocatedGuard : FreeGuard ;
for ( U32 i = 0 ; i < 4 ; i + + )
if ( header - > preguard [ i ] ! = guardVal | | header - > postguard [ i ] ! = guardVal )
{
Platform : : debugBreak ( ) ;
return false ;
}
return true ;
}
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void setGuard ( Header * header , bool alloc )
{
U32 guardVal = alloc ? AllocatedGuard : FreeGuard ;
for ( U32 i = 0 ; i < 4 ; i + + )
header - > preguard [ i ] = header - > postguard [ i ] = guardVal ;
}
# endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
# endif // TORQUE_DEBUG_GUARD
static void memoryError ( )
{
// free all the pages
PageRecord * walk = gPageList ;
while ( walk ) {
PageRecord * prev = walk - > prevPage ;
dRealFree ( walk ) ;
walk = prev ;
}
AssertFatal ( false , " Error allocating memory! Shutting down. " ) ;
Platform : : AlertOK ( " Torque Memory Error " , " Error allocating memory. Shutting down. \n " ) ;
Platform : : forceShutdown ( - 1 ) ;
}
PageRecord * allocPage ( dsize_t pageSize )
{
pageSize + = sizeof ( PageRecord ) ;
void * base = dRealMalloc ( pageSize ) ;
if ( base = = NULL )
memoryError ( ) ;
PageRecord * rec = ( PageRecord * ) base ;
rec - > basePtr = ( void * ) ( rec + 1 ) ;
rec - > allocSize = pageSize ;
rec - > prevPage = gPageList ;
gPageList = rec ;
rec - > headerList = NULL ;
return rec ;
}
TreeNode * allocTreeNode ( )
{
if ( ! gTreeFreeList )
{
PageRecord * newPage = allocPage ( TreeNodeAllocCount * sizeof ( TreeNode ) ) ;
TreeNode * walk = ( TreeNode * ) newPage - > basePtr ;
U32 i ;
gTreeFreeList = walk ;
for ( i = 0 ; i < TreeNodeAllocCount - 1 ; i + + , walk + + )
walk - > parent = walk + 1 ;
walk - > parent = NULL ;
}
TreeNode * ret = gTreeFreeList ;
gTreeFreeList = ret - > parent ;
return ret ;
}
void freeTreeNode ( TreeNode * tn )
{
tn - > parent = gTreeFreeList ;
gTreeFreeList = tn ;
}
static U32 validateTreeRecurse ( TreeNode * tree )
{
if ( tree = = NIL )
return 1 ;
// check my left tree
2013-08-04 21:26:01 +00:00
S32 lcount , rcount , nc = 0 ;
2012-09-19 15:15:01 +00:00
if ( tree - > color = = Red )
{
if ( tree - > left - > color = = Red | | tree - > right - > color = = Red )
Platform : : debugBreak ( ) ;
}
else
nc = 1 ;
FreeHeader * walk = tree - > queueHead ;
if ( ! walk )
Platform : : debugBreak ( ) ;
FreeHeader * prev = NULL ;
while ( walk )
{
if ( walk - > prevQueue ! = prev )
Platform : : debugBreak ( ) ;
if ( walk - > treeNode ! = tree )
Platform : : debugBreak ( ) ;
if ( walk - > size ! = tree - > size )
Platform : : debugBreak ( ) ;
if ( ! walk - > nextQueue & & walk ! = tree - > queueTail )
Platform : : debugBreak ( ) ;
prev = walk ;
walk = walk - > nextQueue ;
}
lcount = validateTreeRecurse ( tree - > left ) ;
rcount = validateTreeRecurse ( tree - > right ) ;
if ( lcount ! = rcount )
Platform : : debugBreak ( ) ;
return lcount + nc ;
}
static void validateParentageRecurse ( TreeNode * tree )
{
if ( tree - > left ! = NIL )
{
if ( tree - > left - > parent ! = tree )
Platform : : debugBreak ( ) ;
if ( tree - > left - > size > tree - > size )
Platform : : debugBreak ( ) ;
validateParentageRecurse ( tree - > left ) ;
}
if ( tree - > right ! = NIL )
{
if ( tree - > right - > parent ! = tree )
Platform : : debugBreak ( ) ;
if ( tree - > right - > size < tree - > size )
Platform : : debugBreak ( ) ;
validateParentageRecurse ( tree - > right ) ;
}
}
static void validateTree ( )
{
if ( gFreeTreeRoot = = NIL )
return ;
validateParentageRecurse ( gFreeTreeRoot ) ;
validateTreeRecurse ( gFreeTreeRoot ) ;
}
void validate ( )
{
# ifdef TORQUE_MULTITHREAD
if ( ! gMemMutex )
gMemMutex = Mutex : : createMutex ( ) ;
Mutex : : lockMutex ( gMemMutex ) ;
# endif
// first validate the free tree:
validateTree ( ) ;
// now validate all blocks:
for ( PageRecord * list = gPageList ; list ; list = list - > prevPage )
{
Header * prev = NULL ;
for ( Header * walk = list - > headerList ; walk ; walk = walk - > next )
{
# ifdef TORQUE_DEBUG_GUARD
checkGuard ( walk , walk - > flags & Allocated ) ;
# endif
if ( walk - > prev ! = prev )
Platform : : debugBreak ( ) ;
prev = walk ;
if ( walk - > next & & ( ( const char * ) ( walk - > next ) ! = ( const char * ) ( walk ) + sizeof ( Header ) + walk - > size ) )
Platform : : debugBreak ( ) ;
}
}
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
}
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void rotateLeft ( TreeNode * hdr )
{
TreeNode * temp = hdr - > right ;
hdr - > right = temp - > left ;
if ( temp - > left ! = NIL )
temp - > left - > parent = hdr ;
temp - > parent = hdr - > parent ;
if ( temp - > parent = = NIL )
gFreeTreeRoot = temp ;
else if ( hdr = = hdr - > parent - > left )
hdr - > parent - > left = temp ;
else
hdr - > parent - > right = temp ;
temp - > left = hdr ;
hdr - > parent = temp ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void rotateRight ( TreeNode * hdr )
{
TreeNode * temp = hdr - > left ;
hdr - > left = temp - > right ;
if ( temp - > right ! = NIL )
temp - > right - > parent = hdr ;
temp - > parent = hdr - > parent ;
if ( temp - > parent = = NIL )
gFreeTreeRoot = temp ;
else if ( hdr = = hdr - > parent - > left )
hdr - > parent - > left = temp ;
else
hdr - > parent - > right = temp ;
temp - > right = hdr ;
hdr - > parent = temp ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void treeInsert ( FreeHeader * fhdr )
{
# ifdef TORQUE_DEBUG_GUARD
checkGuard ( ( Header * ) fhdr , true ) ; // check to see that it's got allocated guards
setGuard ( ( Header * ) fhdr , false ) ;
# endif
//gInsertCount++;
TreeNode * newParent = NIL ;
TreeNode * walk = gFreeTreeRoot ;
while ( walk ! = NIL )
{
newParent = walk ;
if ( fhdr - > size < walk - > size )
walk = walk - > left ;
else if ( fhdr - > size > walk - > size )
walk = walk - > right ;
else // tag it on the end of the queue...
{
// insert it on this header...
walk - > queueTail - > nextQueue = fhdr ;
fhdr - > prevQueue = walk - > queueTail ;
walk - > queueTail = fhdr ;
fhdr - > nextQueue = NULL ;
fhdr - > treeNode = walk ;
return ;
}
}
TreeNode * hdr = allocTreeNode ( ) ;
hdr - > size = fhdr - > size ;
hdr - > queueHead = hdr - > queueTail = fhdr ;
fhdr - > nextQueue = fhdr - > prevQueue = NULL ;
fhdr - > treeNode = hdr ;
hdr - > left = NIL ;
hdr - > right = NIL ;
hdr - > parent = newParent ;
if ( newParent = = NIL )
gFreeTreeRoot = hdr ;
else if ( hdr - > size < newParent - > size )
newParent - > left = hdr ;
else
newParent - > right = hdr ;
// do red/black rotations
hdr - > color = Red ;
while ( hdr ! = gFreeTreeRoot & & ( hdr - > parent - > color = = Red ) )
{
TreeNode * parent = hdr - > parent ;
TreeNode * pparent = hdr - > parent - > parent ;
if ( parent = = pparent - > left )
{
TreeNode * temp = pparent - > right ;
if ( temp - > color = = Red )
{
parent - > color = Black ;
temp - > color = Black ;
pparent - > color = Red ;
hdr = pparent ;
}
else
{
if ( hdr = = parent - > right )
{
hdr = parent ;
rotateLeft ( hdr ) ;
parent = hdr - > parent ;
pparent = hdr - > parent - > parent ;
}
parent - > color = Black ;
pparent - > color = Red ;
rotateRight ( pparent ) ;
}
}
else
{
TreeNode * temp = pparent - > left ;
if ( temp - > color = = Red )
{
parent - > color = Black ;
temp - > color = Black ;
pparent - > color = Red ;
hdr = pparent ;
}
else
{
if ( hdr = = parent - > left )
{
hdr = parent ;
rotateRight ( hdr ) ;
parent = hdr - > parent ;
pparent = hdr - > parent - > parent ;
}
parent - > color = Black ;
pparent - > color = Red ;
rotateLeft ( pparent ) ;
}
}
}
gFreeTreeRoot - > color = Black ;
//validateTree();
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void treeRemove ( FreeHeader * hdr )
{
# ifdef TORQUE_DEBUG_GUARD
checkGuard ( ( Header * ) hdr , false ) ;
setGuard ( ( Header * ) hdr , true ) ;
# endif
//validateTree();
//gRemoveCount++;
FreeHeader * prev = hdr - > prevQueue ;
FreeHeader * next = hdr - > nextQueue ;
if ( prev )
prev - > nextQueue = next ;
else
hdr - > treeNode - > queueHead = next ;
if ( next )
next - > prevQueue = prev ;
else
hdr - > treeNode - > queueTail = prev ;
if ( prev | | next )
return ;
TreeNode * z = hdr - > treeNode ;
nil . color = Black ;
TreeNode * y , * x ;
if ( z - > left = = NIL | | z - > right = = NIL )
y = z ;
else
{
y = z - > right ;
while ( y - > left ! = NIL )
y = y - > left ;
}
if ( y - > left ! = NIL )
x = y - > left ;
else
x = y - > right ;
x - > parent = y - > parent ;
if ( y - > parent = = NIL )
gFreeTreeRoot = x ;
else if ( y = = y - > parent - > left )
y - > parent - > left = x ;
else
y - > parent - > right = x ;
U32 yColor = y - > color ;
if ( y ! = z )
{
// copy y's important fields into z (since we're going to free y)
if ( z - > parent - > left = = z )
z - > parent - > left = y ;
else if ( z - > parent - > right = = z )
z - > parent - > right = y ;
y - > left = z - > left ;
y - > right = z - > right ;
if ( y - > left ! = NIL )
y - > left - > parent = y ;
if ( y - > right ! = NIL )
y - > right - > parent = y ;
y - > parent = z - > parent ;
if ( z - > parent = = NIL )
gFreeTreeRoot = y ;
y - > color = z - > color ;
if ( x - > parent = = z )
x - > parent = y ;
//validateTree();
}
freeTreeNode ( z ) ;
if ( yColor = = Black )
{
while ( x ! = gFreeTreeRoot & & x - > color = = Black )
{
TreeNode * w ;
if ( x = = x - > parent - > left )
{
w = x - > parent - > right ;
if ( w - > color = = Red )
{
w - > color = Black ;
x - > parent - > color = Red ;
rotateLeft ( x - > parent ) ;
w = x - > parent - > right ;
}
if ( w - > left - > color = = Black & & w - > right - > color = = Black )
{
w - > color = Red ;
x = x - > parent ;
}
else
{
if ( w - > right - > color = = Black )
{
w - > left - > color = Black ;
rotateRight ( w ) ;
w = x - > parent - > right ;
}
w - > color = x - > parent - > color ;
x - > parent - > color = Black ;
w - > right - > color = Black ;
rotateLeft ( x - > parent ) ;
x = gFreeTreeRoot ;
}
}
else
{
w = x - > parent - > left ;
if ( w - > color = = Red )
{
w - > color = Black ;
x - > parent - > color = Red ;
rotateRight ( x - > parent ) ;
w = x - > parent - > left ;
}
if ( w - > left - > color = = Black & & w - > right - > color = = Black )
{
w - > color = Red ;
x = x - > parent ;
}
else
{
if ( w - > left - > color = = Black )
{
w - > right - > color = Black ;
rotateLeft ( w ) ;
w = x - > parent - > left ;
}
w - > color = x - > parent - > color ;
x - > parent - > color = Black ;
w - > left - > color = Black ;
rotateRight ( x - > parent ) ;
x = gFreeTreeRoot ;
}
}
}
x - > color = Black ;
}
//validateTree();
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static FreeHeader * treeFindSmallestGreaterThan ( dsize_t size )
{
TreeNode * bestMatch = NIL ;
TreeNode * walk = gFreeTreeRoot ;
while ( walk ! = NIL )
{
if ( size = = walk - > size )
return walk - > queueHead ;
else if ( size > walk - > size )
walk = walk - > right ;
else // size < walk->size
{
bestMatch = walk ;
walk = walk - > left ;
}
}
//validateTree();
if ( bestMatch ! = NIL )
return bestMatch - > queueHead ;
return NULL ;
}
# endif
/// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards
/// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled).
///
/// @note This function does not allow interior pointers!
void checkPtr ( void * ptr )
{
for ( HeapIterator iter ; iter . isValid ( ) ; + + iter )
{
AllocatedHeader * header = ( AllocatedHeader * ) * iter ;
if ( header - > getUserPtr ( ) = = ptr )
{
# ifdef TORQUE_DEBUG_GUARD
2014-03-15 10:51:36 +00:00
char buffer [ 1024 ] ;
2012-09-19 15:15:01 +00:00
if ( ! checkGuard ( * iter , true ) )
{
dSprintf ( buffer , sizeof ( buffer ) , " 0x%x is a valid heap pointer but has its guards corrupted " , ptr ) ;
Platform : : outputDebugString ( buffer ) ;
return ;
}
//dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr );
//Platform::outputDebugString( buffer );
2014-03-15 10:51:36 +00:00
# endif
2012-09-19 15:15:01 +00:00
return ;
}
}
char buffer [ 1024 ] ;
dSprintf ( buffer , sizeof ( buffer ) , " 0x%x is not a valid heap pointer " , ptr ) ;
Platform : : outputDebugString ( buffer ) ;
Platform : : debugBreak ( ) ;
}
/// Dump info on all memory blocks that are still allocated.
/// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP.
void ensureAllFreed ( )
{
# ifndef TORQUE_DISABLE_MEMORY_MANAGER
U32 numLeaks = 0 ;
U32 bytesLeaked = 0 ;
for ( HeapIterator iter ; iter . isValid ( ) ; + + iter )
{
AllocatedHeader * header = ( AllocatedHeader * ) * iter ;
if ( ! ( header - > flags & GlobalFlag ) )
{
// Note: can't spill profile paths here since they by
// now are all invalid (they're on the now freed string table)
# ifdef TORQUE_DEBUG_GUARD
Platform : : outputDebugString ( " MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i) " ,
header - > getUserPtr ( ) ,
header - > allocNum ,
( header - > flags & StaticFlag ? " (static) " : " " ) ,
header - > fileName , header - > line , header - > realSize , header - > size ) ;
numLeaks + + ;
bytesLeaked + = header - > size ;
# endif
}
}
if ( numLeaks )
Platform : : outputDebugString ( " NUM LEAKS: %i (%i bytes) " , numLeaks , bytesLeaked ) ;
# endif
}
struct MemDumpLog
{
U32 size ;
U32 count ;
U32 depthTotal ;
U32 maxDepth ;
U32 minDepth ;
} ;
void logDumpTraverse ( MemDumpLog * sizes , TreeNode * header , U32 depth )
{
if ( header = = NIL )
return ;
MemDumpLog * mySize = sizes ;
while ( mySize - > size < header - > size )
mySize + + ;
U32 cnt = 0 ;
for ( FreeHeader * walk = header - > queueHead ; walk ; walk = walk - > nextQueue )
cnt + + ;
mySize - > count + = cnt ;
mySize - > depthTotal + = depth * cnt ;
mySize - > maxDepth = depth > mySize - > maxDepth ? depth : mySize - > maxDepth ;
mySize - > minDepth = depth < mySize - > minDepth ? depth : mySize - > minDepth ;
logDumpTraverse ( sizes , header - > left , depth + 1 ) ;
logDumpTraverse ( sizes , header - > right , depth + 1 ) ;
}
# ifdef TORQUE_DEBUG
DefineConsoleFunction ( validateMemory , void , ( ) , ,
" @brief Used to validate memory space for the game. \n \n "
" @ingroup Debugging " )
{
validate ( ) ;
}
# endif
DefineConsoleFunction ( freeMemoryDump , void , ( ) , ,
" @brief Dumps some useful statistics regarding free memory. \n \n "
" Dumps an analysis of \' free chunks \' of memory. "
" Does not print how much memory is free. \n \n "
" @ingroup Debugging " )
{
U32 startSize = 16 ;
MemDumpLog memSizes [ 20 ] ;
U32 i ;
for ( i = 0 ; i < 20 ; i + + )
{
memSizes [ i ] . size = startSize < < i ;
memSizes [ i ] . count = 0 ;
memSizes [ i ] . depthTotal = 0 ;
memSizes [ i ] . maxDepth = 0 ;
memSizes [ i ] . minDepth = 1000 ;
}
memSizes [ 19 ] . size = MaxAllocationAmount ;
logDumpTraverse ( memSizes , gFreeTreeRoot , 1 ) ;
MemDumpLog fullMem ;
fullMem . count = 0 ;
fullMem . depthTotal = 0 ;
fullMem . maxDepth = 0 ;
fullMem . minDepth = 1000 ;
for ( i = 0 ; i < 20 ; i + + )
{
if ( memSizes [ i ] . count )
Con : : printf ( " Size: %d - Free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g " ,
memSizes [ i ] . size , memSizes [ i ] . count , memSizes [ i ] . maxDepth , memSizes [ i ] . minDepth ,
F32 ( memSizes [ i ] . depthTotal ) / F32 ( memSizes [ i ] . count ) ) ;
fullMem . count + = memSizes [ i ] . count ;
fullMem . depthTotal + = memSizes [ i ] . depthTotal ;
fullMem . maxDepth = memSizes [ i ] . maxDepth > fullMem . maxDepth ? memSizes [ i ] . maxDepth : fullMem . maxDepth ;
fullMem . minDepth = memSizes [ i ] . minDepth < fullMem . minDepth ? memSizes [ i ] . minDepth : fullMem . minDepth ;
}
Con : : printf ( " Total free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g " ,
fullMem . count , fullMem . maxDepth , fullMem . minDepth , F32 ( fullMem . depthTotal ) / F32 ( fullMem . count ) ) ;
}
# ifdef TORQUE_DEBUG_GUARD
void flagCurrentAllocs ( EFlag flag )
{
# ifdef TORQUE_ENABLE_PROFILE_PATH
if ( gProfiler & & ! gProfiler - > isEnabled ( ) )
{
gProfiler - > enable ( true ) ;
// warm it up
//gProfiler->dumpToConsole();
}
# endif
U32 bit = flagToBit ( flag ) ;
for ( HeapIterator iter ; iter . isValid ( ) ; + + iter )
iter - > flags | = bit ;
}
DefineEngineFunction ( flagCurrentAllocs , void , ( ) , ,
" @brief Flags all current memory allocations. \n \n "
" Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). "
" Helpful in detecting memory leaks and analyzing memory usage. \n \n "
" @ingroup Debugging " )
{
flagCurrentAllocs ( ) ;
}
void dumpUnflaggedAllocs ( const char * file , EFlag flag )
{
countUnflaggedAllocs ( file , NULL , flag ) ;
}
S32 countUnflaggedAllocs ( const char * filename , S32 * outUnflaggedRealloc , EFlag flag )
{
S32 unflaggedAllocCount = 0 ;
S32 unflaggedReAllocCount = 0 ;
FileStream fws ;
bool useFile = filename & & * filename ;
if ( useFile )
useFile = fws . open ( filename , Torque : : FS : : File : : Write ) ;
char buffer [ 1024 ] ;
U32 bit = flagToBit ( flag ) ;
PageRecord * walk ;
for ( walk = gPageList ; walk ; walk = walk - > prevPage )
{
for ( Header * probe = walk - > headerList ; probe ; probe = probe - > next )
{
if ( probe - > flags & Allocated )
{
AllocatedHeader * pah = ( AllocatedHeader * ) probe ;
if ( ! ( pah - > flags & bit ) )
{
// If you want to extract further information from an unflagged
// memory allocation, do the following:
// U8 *foo = (U8 *)pah;
// foo += sizeof(Header);
// FooObject *obj = (FooObject *)foo;
dSprintf ( buffer , 1023 , " %s%s \t %d \t %d \t %d \r \n " ,
pah - > flags & Reallocated ? " [R] " : " " ,
pah - > fileName ! = NULL ? pah - > fileName : " Undetermined " ,
pah - > line , pah - > realSize , pah - > allocNum ) ;
if ( pah - > flags & Reallocated )
unflaggedReAllocCount + + ;
else
unflaggedAllocCount + + ;
if ( useFile )
{
fws . write ( dStrlen ( buffer ) , buffer ) ;
fws . write ( 2 , " \r \n " ) ;
}
else
{
if ( pah - > flags & Reallocated )
Con : : warnf ( buffer ) ;
else
Con : : errorf ( buffer ) ;
}
# ifdef TORQUE_ENABLE_PROFILE_PATH
static char line [ 4096 ] ;
dSprintf ( line , sizeof ( line ) , " %s \r \n real size=%d " ,
pah - > profilePath ? pah - > profilePath : " unknown " ,
pah - > realSize ) ;
if ( useFile )
{
fws . write ( dStrlen ( line ) , line ) ;
fws . write ( 2 , " \r \n " ) ;
}
else
{
if ( pah - > flags & Reallocated )
Con : : warnf ( line ) ;
else
Con : : errorf ( line ) ;
}
# endif
}
}
}
}
if ( useFile )
fws . close ( ) ;
if ( outUnflaggedRealloc ! = NULL )
* outUnflaggedRealloc = unflaggedReAllocCount ;
return unflaggedAllocCount ;
}
DefineEngineFunction ( dumpUnflaggedAllocs , void , ( const char * fileName ) , ( " " ) ,
" @brief Dumps all unflagged memory allocations. \n \n "
" Dumps all memory allocations that were made after a call to flagCurrentAllocs(). "
" Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage. \n \n "
" @param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). "
" If left blank, data will be dumped to the console. \n \n "
" @tsexample \n "
" dumpMemSnapshot(); // dumps info to console \n "
" dumpMemSnapshot( \" C:/Torque/profilerlog1.txt \" ); // dumps info to file \n "
" @endtsexample \n \n "
" @note Available in debug builds only. "
" In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function. \n \n "
" @ingroup Debugging " )
{
dumpUnflaggedAllocs ( fileName ) ;
}
static void initLog ( )
{
static const char * sInitString = " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) --- \r \n " ;
FileStream fws ;
fws . open ( gLogFilename , Torque : : FS : : File : : Write ) ;
fws . write ( dStrlen ( sInitString ) , sInitString ) ;
fws . close ( ) ;
}
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void logAlloc ( const AllocatedHeader * hdr , S32 memSize )
{
FileStream fws ;
fws . open ( gLogFilename , Torque : : FS : : File : : ReadWrite ) ;
fws . setPosition ( fws . getStreamSize ( ) ) ;
char buffer [ 1024 ] ;
dSprintf ( buffer , 1023 , " alloc: %s %d %d %d \r \n " ,
hdr - > fileName ! = NULL ? hdr - > fileName : " Undetermined " ,
hdr - > line , memSize , hdr - > allocNum ) ;
fws . write ( dStrlen ( buffer ) , buffer ) ;
fws . close ( ) ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void logRealloc ( const AllocatedHeader * hdr , S32 memSize )
{
FileStream fws ;
fws . open ( gLogFilename , Torque : : FS : : File : : ReadWrite ) ;
fws . setPosition ( fws . getStreamSize ( ) ) ;
char buffer [ 1024 ] ;
dSprintf ( buffer , 1023 , " realloc: %s %d %d %d \r \n " ,
hdr - > fileName ! = NULL ? hdr - > fileName : " Undetermined " ,
hdr - > line , memSize , hdr - > allocNum ) ;
fws . write ( dStrlen ( buffer ) , buffer ) ;
fws . close ( ) ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void logFree ( const AllocatedHeader * hdr )
{
FileStream fws ;
fws . open ( gLogFilename , Torque : : FS : : File : : ReadWrite ) ;
fws . setPosition ( fws . getStreamSize ( ) ) ;
char buffer [ 1024 ] ;
dSprintf ( buffer , 1023 , " free: %s %d %d \r \n " ,
hdr - > fileName ! = NULL ? hdr - > fileName : " Undetermined " ,
hdr - > line , hdr - > allocNum ) ;
fws . write ( dStrlen ( buffer ) , buffer ) ;
fws . close ( ) ;
}
# endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
# endif
void enableLogging ( const char * fileName )
{
dStrcpy ( gLogFilename , fileName ) ;
if ( ! gEnableLogging )
{
gEnableLogging = true ;
# ifdef TORQUE_DEBUG_GUARD
initLog ( ) ;
# endif
}
}
void disableLogging ( )
{
gLogFilename [ 0 ] = ' \0 ' ;
gEnableLogging = false ;
}
// CodeReview - this is never called so commented out to save warning.
// Do we want to re-enable it? Might be nice to get leak tracking on
// exit...or maybe that is just a problematical feature we shouldn't
// worry about.
//static void shutdown()
//{
//#ifdef TORQUE_MULTITHREAD
// Mutex::destroyMutex(gMemMutex);
// gMemMutex = NULL;
//#endif
//
//#ifdef TORQUE_DEBUG_GUARD
//
// // write out leaks and such
// const U32 maxNumLeaks = 1024;
// U32 numLeaks = 0;
//
// AllocatedHeader* pLeaks[maxNumLeaks];
// for (PageRecord * walk = gPageList; walk; walk = walk->prevPage)
// for(Header *probe = walk->headerList; probe; probe = probe->next)
// if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL)
// pLeaks[numLeaks++] = (AllocatedHeader *) probe;
//
// if (numLeaks && !gNeverLogLeaks)
// {
// if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected. Write to memoryLeaks.log?") == true)
// {
// char buffer[1024];
// FileStream logFile;
// logFile.open("memoryLeaks.log", Torque::FS::File::Write);
//
// for (U32 i = 0; i < numLeaks; i++)
// {
// dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum);
// logFile.write(dStrlen(buffer), buffer);
// }
// logFile.close();
// }
// }
//#endif
//
// // then free all the memory pages
// for (PageRecord * walk = gPageList; walk; )
// {
// PageRecord *prev = walk->prevPage;
// dRealFree(walk);
// walk = prev;
// }
//}
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static Header * allocMemPage ( dsize_t pageSize )
{
pageSize + = sizeof ( Header ) ;
if ( pageSize < MinPageSize )
pageSize = MinPageSize ;
PageRecord * base = allocPage ( pageSize ) ;
Header * rec = ( Header * ) base - > basePtr ;
base - > headerList = rec ;
rec - > size = pageSize - sizeof ( Header ) ;
rec - > next = NULL ;
rec - > prev = NULL ;
rec - > flags = 0 ;
# ifdef TORQUE_DEBUG_GUARD
setGuard ( rec , true ) ;
# endif
# ifdef LOG_PAGE_ALLOCS
gPageBytesAllocated + = pageSize ;
// total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string
const U32 StrSize = 256 ;
char strBytesAllocated [ StrSize ] ;
if ( gBytesAllocated > 0 )
dSprintf ( strBytesAllocated , sizeof ( strBytesAllocated ) , " %i " , gBytesAllocated ) ;
else
dStrncpy ( strBytesAllocated , " unknown - enable TORQUE_DEBUG_GUARD " , StrSize ) ;
# ifndef TORQUE_MULTITHREAD // May deadlock.
// NOTE: This code may be called within Con::_printf, and if that is the case
// this will infinitly recurse. This is the reason for the code in Con::_printf
// that sets Con::active to false. -patw
if ( Con : : isActive ( ) )
Con : : errorf ( " PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i) " , strBytesAllocated , gPageBytesAllocated ) ;
# endif
# endif
return rec ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void checkUnusedAlloc ( FreeHeader * header , U32 size )
{
//validate();
if ( header - > size > = size + sizeof ( Header ) + 16 )
{
U8 * basePtr = ( U8 * ) header ;
basePtr + = sizeof ( Header ) ;
FreeHeader * newHeader = ( FreeHeader * ) ( basePtr + size ) ;
newHeader - > next = header - > next ;
newHeader - > prev = ( Header * ) header ;
header - > next = ( Header * ) newHeader ;
if ( newHeader - > next )
newHeader - > next - > prev = ( Header * ) newHeader ;
newHeader - > flags = 0 ;
newHeader - > size = header - > size - size - sizeof ( Header ) ;
header - > size = size ;
# ifdef TORQUE_DEBUG_GUARD
setGuard ( ( Header * ) newHeader , true ) ;
# endif
treeInsert ( newHeader ) ;
}
}
# endif
# if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static bool gReentrantGuard = false ;
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void * alloc ( dsize_t size , bool array , const char * fileName , const U32 line )
{
AssertFatal ( size < MaxAllocationAmount , " Memory::alloc - tried to allocate > MaxAllocationAmount! " ) ;
# ifdef TORQUE_MULTITHREAD
if ( ! gMemMutex & & ! gReentrantGuard )
{
gReentrantGuard = true ;
gMemMutex = Mutex : : createMutex ( ) ;
gReentrantGuard = false ;
}
if ( ! gReentrantGuard )
Mutex : : lockMutex ( gMemMutex ) ;
# endif
AssertFatal ( size < MaxAllocationAmount , " Size error. " ) ;
//validate();
if ( size = = 0 )
{
# ifdef TORQUE_MULTITHREAD
if ( ! gReentrantGuard )
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return NULL ;
}
# ifndef TORQUE_ENABLE_PROFILE_PATH
// Note: will cause crash if profile path is on
PROFILE_START ( MemoryAlloc ) ;
# endif
# ifdef TORQUE_DEBUG_GUARD
// if we're guarding, round up to the nearest DWORD
size = ( ( size + 3 ) & ~ 0x3 ) ;
# else
// round up size to nearest 16 byte boundary (cache lines and all...)
size = ( ( size + 15 ) & ~ 0xF ) ;
# endif
FreeHeader * header = treeFindSmallestGreaterThan ( size ) ;
if ( header )
treeRemove ( header ) ;
else
header = ( FreeHeader * ) allocMemPage ( size ) ;
// ok, see if there's enough room in the block to make another block
// for this to happen it has to have enough room for a header
// and 16 more bytes.
U8 * basePtr = ( U8 * ) header ;
basePtr + = sizeof ( Header ) ;
checkUnusedAlloc ( header , size ) ;
AllocatedHeader * retHeader = ( AllocatedHeader * ) header ;
retHeader - > flags = array ? ( Allocated | Array ) : Allocated ;
# ifdef TORQUE_DEBUG_GUARD
retHeader - > line = line ;
retHeader - > fileName = fileName ;
retHeader - > allocNum = gCurrAlloc ;
retHeader - > realSize = size ;
# ifdef TORQUE_ENABLE_PROFILE_PATH
retHeader - > profilePath = gProfiler ? gProfiler - > getProfilePath ( ) : " pre " ;
# endif
gBytesAllocated + = size ;
gBlocksAllocated + + ;
//static U32 skip = 0;
//if ((++skip % 1000) == 0)
// Con::printf("new=%i, newnew=%i, imagenew=%i",gBytesAllocated,gNewNewTotal,gImageAlloc);
if ( gEnableLogging )
logAlloc ( retHeader , size ) ;
# endif
if ( gCurrAlloc = = gBreakAlloc & & gBreakAlloc ! = 0xFFFFFFFF )
Platform : : debugBreak ( ) ;
gCurrAlloc + + ;
# ifndef TORQUE_ENABLE_PROFILE_PATH
PROFILE_END ( ) ;
# endif
//validate();
# ifdef TORQUE_DEBUG
// fill the block with the fill value. although this is done in free(), that won't fill
// newly allocated MM memory (which hasn't been freed yet). We use a different fill value
// to diffentiate filled freed memory from filled new memory; this may aid debugging.
# ifndef TORQUE_ENABLE_PROFILE_PATH
PROFILE_START ( stompMem1 ) ;
# endif
dMemset ( basePtr , 0xCF , size ) ;
# ifndef TORQUE_ENABLE_PROFILE_PATH
PROFILE_END ( ) ;
# endif
# endif
if ( gCurrAlloc = = gBreakAlloc & & gBreakAlloc ! = 0xFFFFFFFF )
Platform : : debugBreak ( ) ;
gCurrAlloc + + ;
# ifdef TORQUE_MULTITHREAD
if ( ! gReentrantGuard )
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return basePtr ;
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void free ( void * mem , bool array )
{
// validate();
if ( ! mem )
return ;
# ifdef TORQUE_MULTITHREAD
if ( ! gMemMutex )
gMemMutex = Mutex : : createMutex ( ) ;
if ( mem ! = gMemMutex )
Mutex : : lockMutex ( gMemMutex ) ;
else
gMemMutex = NULL ;
# endif
PROFILE_START ( MemoryFree ) ;
AllocatedHeader * hdr = ( ( AllocatedHeader * ) mem ) - 1 ;
AssertFatal ( hdr - > flags & Allocated , avar ( " Not an allocated block! " ) ) ;
AssertFatal ( ( ( bool ) ( ( hdr - > flags & Array ) = = Array ) ) = = array , avar ( " Array alloc mismatch. " ) ) ;
gBlocksAllocated - - ;
# ifdef TORQUE_DEBUG_GUARD
gBytesAllocated - = hdr - > realSize ;
if ( gEnableLogging )
logFree ( hdr ) ;
# endif
hdr - > flags = 0 ;
// fill the block with the fill value
# ifdef TORQUE_DEBUG
# ifndef TORQUE_ENABLE_PROFILE_PATH
PROFILE_START ( stompMem2 ) ;
# endif
dMemset ( mem , 0xCE , hdr - > size ) ;
# ifndef TORQUE_ENABLE_PROFILE_PATH
PROFILE_END ( ) ;
# endif
# endif
// see if we can merge hdr with the block after it.
Header * next = hdr - > next ;
if ( next & & next - > flags = = 0 )
{
treeRemove ( ( FreeHeader * ) next ) ;
hdr - > size + = next - > size + sizeof ( Header ) ;
hdr - > next = next - > next ;
if ( next - > next )
next - > next - > prev = ( Header * ) hdr ;
}
// see if we can merge hdr with the block before it.
Header * prev = hdr - > prev ;
if ( prev & & prev - > flags = = 0 )
{
treeRemove ( ( FreeHeader * ) prev ) ;
prev - > size + = hdr - > size + sizeof ( Header ) ;
prev - > next = hdr - > next ;
if ( hdr - > next )
hdr - > next - > prev = prev ;
hdr = ( AllocatedHeader * ) prev ;
}
// throw this puppy into the tree!
treeInsert ( ( FreeHeader * ) hdr ) ;
PROFILE_END ( ) ;
// validate();
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
}
# endif
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
static void * realloc ( void * mem , dsize_t size , const char * fileName , const U32 line )
{
//validate();
if ( ! size ) {
free ( mem , false ) ;
return NULL ;
}
if ( ! mem )
return alloc ( size , false , fileName , line ) ;
# ifdef TORQUE_MULTITHREAD
if ( ! gMemMutex )
gMemMutex = Mutex : : createMutex ( ) ;
Mutex : : lockMutex ( gMemMutex ) ;
# endif
AllocatedHeader * hdr = ( ( AllocatedHeader * ) mem ) - 1 ;
# ifdef TORQUE_DEBUG_GUARD
checkGuard ( ( Header * ) hdr , true ) ;
# endif
AssertFatal ( ( hdr - > flags & Allocated ) = = Allocated , " Bad block flags. " ) ;
size = ( size + 0xF ) & ~ 0xF ;
U32 oldSize = hdr - > size ;
if ( oldSize = = size )
{
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return mem ;
}
PROFILE_START ( MemoryRealloc ) ;
FreeHeader * next = ( FreeHeader * ) hdr - > next ;
# ifdef TORQUE_DEBUG_GUARD
// adjust header size and allocated bytes size
hdr - > realSize + = size - oldSize ;
gBytesAllocated + = size - oldSize ;
if ( gEnableLogging )
logRealloc ( hdr , size ) ;
// Add reallocated flag, note header changes will not persist if the realloc
// decides tofree, and then perform a fresh allocation for the memory. The flag will
// be manually set again after this takes place, down at the bottom of this fxn.
hdr - > flags | = Reallocated ;
// Note on Above ^
// A more useful/robust implementation can be accomplished by storing an additional
// AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure
// itself to create a sort of reallocation history. This will be, essentially,
// a allocation header stack for each allocation. Each time the memory is reallocated
// it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain
// it to the reallocation history chain, and the dump output changed to display
// reallocation history. It is also important to clean up this chain during 'free'
// using dRealFree (Since memory for the chain was allocated via dRealMalloc).
//
// See patw for details.
# endif
if ( next & & ! ( next - > flags & Allocated ) & & next - > size + hdr - > size + sizeof ( Header ) > = size )
{
// we can merge with the next dude.
treeRemove ( next ) ;
hdr - > size + = sizeof ( Header ) + next - > size ;
hdr - > next = next - > next ;
if ( next - > next )
next - > next - > prev = ( Header * ) hdr ;
checkUnusedAlloc ( ( FreeHeader * ) hdr , size ) ;
//validate();
PROFILE_END ( ) ;
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return mem ;
}
else if ( size < oldSize )
{
checkUnusedAlloc ( ( FreeHeader * ) hdr , size ) ;
PROFILE_END ( ) ;
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return mem ;
}
# ifdef TORQUE_DEBUG_GUARD
// undo above adjustment because we're going though alloc instead
hdr - > realSize - = size - oldSize ;
gBytesAllocated - = size - oldSize ;
# endif
void * ret = alloc ( size , false , fileName , line ) ;
dMemcpy ( ret , mem , oldSize ) ;
free ( mem , false ) ;
PROFILE_END ( ) ;
// Re-enable the 'Reallocated' flag so that this allocation can be ignored by
// a non-strict run of the flag/dumpunflagged.
hdr = ( ( AllocatedHeader * ) ret ) - 1 ;
hdr - > flags | = Reallocated ;
# ifdef TORQUE_MULTITHREAD
Mutex : : unlockMutex ( gMemMutex ) ;
# endif
return ret ;
}
# endif
dsize_t getMemoryUsed ( )
{
U32 size = 0 ;
PageRecord * walk ;
for ( walk = gPageList ; walk ; walk = walk - > prevPage ) {
for ( Header * probe = walk - > headerList ; probe ; probe = probe - > next )
if ( probe - > flags & Allocated ) {
size + = probe - > size ;
}
}
return size ;
}
# ifdef TORQUE_DEBUG_GUARD
2013-08-04 21:26:01 +00:00
DefineEngineFunction ( dumpAlloc , void , ( S32 allocNum ) , ,
2012-09-19 15:15:01 +00:00
" @brief Dumps information about the given allocated memory block. \n \n "
" @param allocNum Memory block to dump information about. "
" @note Available in debug builds only. "
" In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function. \n \n "
" @ingroup Debugging " )
{
PageRecord * walk ;
for ( walk = gPageList ; walk ; walk = walk - > prevPage )
for ( Header * probe = walk - > headerList ; probe ; probe = probe - > next )
if ( probe - > flags & Allocated )
{
AllocatedHeader * pah = ( AllocatedHeader * ) probe ;
if ( pah - > allocNum = = allocNum )
{
Con : : printf ( " file: %s \n "
" line: %i \n "
" size: %i \n "
" allocNum: %i \n "
" reallocated: %s " ,
pah - > fileName ! = NULL ? pah - > fileName : " Undetermined " ,
pah - > line ,
pah - > realSize ,
pah - > allocNum ,
pah - > flags & Reallocated ? " yes " : " no "
) ;
// Dump the profile path, if we have one.
# ifdef TORQUE_ENABLE_PROFILE_PATH
if ( pah - > profilePath & & pah - > profilePath [ 0 ] )
Con : : printf ( " profilepath: %s " , pah - > profilePath ) ;
# endif
}
}
}
DefineEngineFunction ( dumpMemSnapshot , void , ( const char * fileName ) , ,
" @brief Dumps a snapshot of current memory to a file. \n \n "
" The total memory used will also be output to the console. \n "
" This function will attempt to create the file if it does not already exist. \n "
" @param fileName Name and path of file to save profiling stats to. Must use forward slashes (/) \n "
" @tsexample \n "
" dumpMemSnapshot( \" C:/Torque/ProfilerLogs/profilerlog1.txt \" ); \n "
" @endtsexample \n \n "
" @note Available in debug builds only. "
" In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function. \n \n "
" @ingroup Debugging " )
{
FileStream fws ;
fws . open ( fileName , Torque : : FS : : File : : Write ) ;
char buffer [ 2048 ] ;
PageRecord * walk ;
for ( walk = gPageList ; walk ; walk = walk - > prevPage ) {
for ( Header * probe = walk - > headerList ; probe ; probe = probe - > next )
if ( probe - > flags & Allocated ) {
AllocatedHeader * pah = ( AllocatedHeader * ) probe ;
dSprintf ( buffer , sizeof ( buffer ) , " %s%s \t %d \t %d \t %d \r \n " ,
pah - > flags & Reallocated ? " [R] " : " " ,
pah - > fileName ! = NULL ? pah - > fileName : " Undetermined " ,
pah - > line , pah - > realSize , pah - > allocNum ) ;
fws . write ( dStrlen ( buffer ) , buffer ) ;
// Dump the profile path, if we have one.
# ifdef TORQUE_ENABLE_PROFILE_PATH
if ( pah - > profilePath )
{
dSprintf ( buffer , sizeof ( buffer ) , " %s \r \n \r \n " , pah - > profilePath ) ;
fws . write ( dStrlen ( buffer ) , buffer ) ;
}
# endif
}
}
Con : : errorf ( " total memory used: %d " , getMemoryUsed ( ) ) ;
fws . close ( ) ;
}
# endif
dsize_t getMemoryAllocated ( )
{
return 0 ;
}
void getMemoryInfo ( void * ptr , Info & info )
{
# ifndef TORQUE_DISABLE_MEMORY_MANAGER
AllocatedHeader * header = ( ( AllocatedHeader * ) ptr ) - 1 ;
info . mAllocSize = header - > size ;
# ifdef TORQUE_DEBUG_GUARD
info . mAllocNumber = header - > allocNum ;
info . mLineNumber = header - > line ;
info . mFileName = header - > fileName ;
# endif
info . mIsArray = header - > flags & Array ;
info . mIsGlobal = header - > flags & GlobalFlag ;
info . mIsStatic = header - > flags & StaticFlag ;
# endif
}
void setBreakAlloc ( U32 breakAlloc )
{
gBreakAlloc = breakAlloc ;
}
ConsoleFunctionGroupEnd ( Memory ) ;
} // namespace Memory
void setMinimumAllocUnit ( U32 allocUnit )
{
AssertFatal ( isPow2 ( allocUnit ) & & allocUnit > ( 2 < < 20 ) ,
" Error, allocunit must be a power of two, and greater than 2 megs " ) ;
MinPageSize = allocUnit ;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
# if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
// Manage our own memory, add overloaded memory operators and functions
void * FN_CDECL operator new ( dsize_t size , const char * fileName , const U32 line )
{
return Memory : : alloc ( size , false , fileName , line ) ;
}
void * FN_CDECL operator new [ ] ( dsize_t size , const char * fileName , const U32 line )
{
return Memory : : alloc ( size , true , fileName , line ) ;
}
void * FN_CDECL operator new ( dsize_t size )
{
return Memory : : alloc ( size , false , NULL , 0 ) ;
}
void * FN_CDECL operator new [ ] ( dsize_t size )
{
return Memory : : alloc ( size , true , NULL , 0 ) ;
}
void FN_CDECL operator delete ( void * mem )
{
Memory : : free ( mem , false ) ;
}
void FN_CDECL operator delete [ ] ( void * mem )
{
Memory : : free ( mem , true ) ;
}
void * dMalloc_r ( dsize_t in_size , const char * fileName , const dsize_t line )
{
return Memory : : alloc ( in_size , false , fileName , line ) ;
}
void dFree ( void * in_pFree )
{
Memory : : free ( in_pFree , false ) ;
}
void * dRealloc_r ( void * in_pResize , dsize_t in_size , const char * fileName , const dsize_t line )
{
return Memory : : realloc ( in_pResize , in_size , fileName , line ) ;
}
AFTER_MODULE_INIT ( Sim )
{
Con : : addVariable ( " $Memory::numBlocksAllocated " , TypeS32 , & Memory : : gBlocksAllocated ,
" Total number of memory blocks currently allocated. \n \n "
" @ingroup Debugging " ) ;
Con : : addVariable ( " $Memory::numBytesAllocated " , TypeS32 , & Memory : : gBytesAllocated ,
" Total number of bytes currently allocated. \n \n "
" @ingroup Debugging " ) ;
}
# else
// Don't manage our own memory
void * dMalloc_r ( dsize_t in_size , const char * fileName , const dsize_t line )
{
return malloc ( in_size ) ;
}
void dFree ( void * in_pFree )
{
free ( in_pFree ) ;
}
void * dRealloc_r ( void * in_pResize , dsize_t in_size , const char * fileName , const dsize_t line )
{
return realloc ( in_pResize , in_size ) ;
}
# endif