1269 lines
28 KiB
C++
1269 lines
28 KiB
C++
|
//
|
||
|
// rvMemSys.cpp - Memory management system
|
||
|
// Date: 12/17/04
|
||
|
// Created by: Dwight Luetscher
|
||
|
//
|
||
|
|
||
|
#include "../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#ifdef _RV_MEM_SYS_SUPPORT
|
||
|
|
||
|
static memoryStats_t mem_total_allocs = { 0, 0x0fffffff, -1, 0 };
|
||
|
static memoryStats_t mem_frame_allocs;
|
||
|
static memoryStats_t mem_frame_frees;
|
||
|
|
||
|
rvHeapArena mainHeapArena;
|
||
|
rvHeapArena *currentHeapArena; // this is the main heap arena that all other heaps use
|
||
|
rvHeap defaultHeap; // this is the default system heap
|
||
|
|
||
|
static rvHeap *systemHeapArray[MAX_SYSTEM_HEAPS]; // array of pointers to rvHeaps that are common to idLib, Game, and executable
|
||
|
|
||
|
#if defined(_WIN32) && !defined(_XENON)
|
||
|
static LPVOID sharedMem = NULL; // pointer to shared memory
|
||
|
static HANDLE hMapObject = NULL; // handle to file mapping
|
||
|
#endif
|
||
|
|
||
|
// Descriptions that go with each tag. When updating the tag enum in Heap.h please
|
||
|
// update this list as well.
|
||
|
// (also update the list in Heap.cpp)
|
||
|
char *TagNames[] = {
|
||
|
"none",
|
||
|
"New operation",
|
||
|
"default",
|
||
|
"Lexer",
|
||
|
"Parser",
|
||
|
"AAS routing",
|
||
|
"Class",
|
||
|
"Script program",
|
||
|
"Collision Model",
|
||
|
"CVar",
|
||
|
"Decl System",
|
||
|
"File System",
|
||
|
"Images",
|
||
|
"Materials",
|
||
|
"Models",
|
||
|
"Fonts",
|
||
|
"Main renderer",
|
||
|
"Vertex data",
|
||
|
"Sound",
|
||
|
"Window",
|
||
|
"Event loop",
|
||
|
"Math - Matrices and vectors",
|
||
|
"Animation",
|
||
|
"Dynamic Blocks",
|
||
|
"Strings",
|
||
|
"GUI",
|
||
|
"Effects",
|
||
|
"Entities",
|
||
|
"Physics",
|
||
|
"AI",
|
||
|
"Network",
|
||
|
"Not Used"
|
||
|
};
|
||
|
|
||
|
template<int X>
|
||
|
class TagTableCheck
|
||
|
{
|
||
|
private:
|
||
|
TagTableCheck();
|
||
|
};
|
||
|
|
||
|
template<>
|
||
|
class TagTableCheck<1>
|
||
|
{
|
||
|
};
|
||
|
|
||
|
// An error here means you need to synchronize TagNames and Mem_Alloc_Types_t
|
||
|
TagTableCheck<sizeof(TagNames)/sizeof(char*) == MA_MAX> TagTableCheckedHere;
|
||
|
// An error here means there are too many tags. No more than 32!
|
||
|
TagTableCheck<MA_DO_NOT_USE<32> TagMaxCheckedHere;
|
||
|
|
||
|
#ifndef ENABLE_INTEL_SMP
|
||
|
MemScopedTag* MemScopedTag::mTop = NULL;
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
GetMemAllocStats
|
||
|
|
||
|
Gets some memory allocation stats based on a particular tag.
|
||
|
==================
|
||
|
*/
|
||
|
const char *GetMemAllocStats(int tag, int &num, int &size, int &peak)
|
||
|
{
|
||
|
assert( tag < MA_MAX );
|
||
|
|
||
|
currentHeapArena->GetTagStats( tag, num, size, peak );
|
||
|
return TagNames[tag];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvSetSysHeap
|
||
|
|
||
|
sets the system heap that is associated with the given heap ID value
|
||
|
==================
|
||
|
*/
|
||
|
void rvSetSysHeap(Rv_Sys_Heap_ID_t sysHeapID, rvHeap *heapPtr)
|
||
|
{
|
||
|
assert( (uint) sysHeapID < (uint) rv_heap_ID_max_count );
|
||
|
if ( NULL == heapPtr )
|
||
|
{
|
||
|
// set the system heap back to the default heap
|
||
|
systemHeapArray[ (uint) sysHeapID ] = &defaultHeap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
systemHeapArray[ (uint) sysHeapID ] = heapPtr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvPushSysHeap
|
||
|
|
||
|
pushes the system heap associated with the given identifier to the top of the arena stack, making it current.
|
||
|
==================
|
||
|
*/
|
||
|
void rvPushSysHeap(Rv_Sys_Heap_ID_t sysHeapID)
|
||
|
{
|
||
|
assert( (uint) sysHeapID < (uint) rv_heap_ID_max_count );
|
||
|
assert( systemHeapArray[ (uint) sysHeapID ] != NULL );
|
||
|
systemHeapArray[ (uint) sysHeapID ]->PushCurrent( );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvEnterArenaCriticalSection
|
||
|
|
||
|
enters the heap arena critical section.
|
||
|
==================
|
||
|
*/
|
||
|
void rvEnterArenaCriticalSection( )
|
||
|
{
|
||
|
// the following is necessary for memory allocations that take place
|
||
|
// in static/global object constructors
|
||
|
if ( NULL == currentHeapArena )
|
||
|
{
|
||
|
Mem_Init();
|
||
|
}
|
||
|
assert( currentHeapArena != NULL );
|
||
|
currentHeapArena->EnterArenaCriticalSection( );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvExitArenaCriticalSection
|
||
|
|
||
|
exits the heap arena critical section
|
||
|
==================
|
||
|
*/
|
||
|
void rvExitArenaCriticalSection( )
|
||
|
{
|
||
|
assert( currentHeapArena != NULL );
|
||
|
currentHeapArena->ExitArenaCriticalSection( );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Init
|
||
|
|
||
|
initializes the memory system for use
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Init( void )
|
||
|
{
|
||
|
if ( NULL == currentHeapArena )
|
||
|
{
|
||
|
|
||
|
#if defined(_WIN32) && !defined(_XENON)
|
||
|
hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, // use paging file
|
||
|
NULL, // default security attributes
|
||
|
PAGE_READWRITE, // read/write access
|
||
|
0, // size: high 32-bits
|
||
|
sizeof(systemHeapArray)+sizeof(currentHeapArena), // size: low 32-bits
|
||
|
"quake4share"); // name of map object
|
||
|
if ( hMapObject == NULL )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// The first process to attach initializes memory.
|
||
|
bool firstInit = (GetLastError() != ERROR_ALREADY_EXISTS);
|
||
|
|
||
|
// Get a pointer to the file-mapped shared memory.
|
||
|
sharedMem = MapViewOfFile(hMapObject, // object to map view of
|
||
|
FILE_MAP_WRITE, // read/write access
|
||
|
0, // high offset: map from
|
||
|
0, // low offset: beginning
|
||
|
0); // default: map entire file
|
||
|
if ( sharedMem == NULL )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !firstInit )
|
||
|
{
|
||
|
memcpy( ¤tHeapArena, sharedMem, sizeof(currentHeapArena) );
|
||
|
memcpy( &systemHeapArray, (byte*) sharedMem + sizeof(currentHeapArena), sizeof(systemHeapArray) );
|
||
|
|
||
|
UnmapViewOfFile( sharedMem );
|
||
|
CloseHandle( hMapObject );
|
||
|
|
||
|
sharedMem = NULL;
|
||
|
hMapObject = NULL;
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
currentHeapArena = &mainHeapArena;
|
||
|
currentHeapArena->Init();
|
||
|
defaultHeap.Init( *currentHeapArena, 256*1024*1024 );
|
||
|
defaultHeap.SetDebugID(0);
|
||
|
defaultHeap.SetName("DEFAULT");
|
||
|
rvSetSysHeap( RV_HEAP_ID_DEFAULT, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_PERMANENT, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_LEVEL, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_MULTIPLE_FRAME, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_SINGLE_FRAME, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_TEMPORARY, &defaultHeap );
|
||
|
rvSetSysHeap( RV_HEAP_ID_IO_TEMP, &defaultHeap );
|
||
|
RV_PUSH_SYS_HEAP_ID( RV_HEAP_ID_DEFAULT );
|
||
|
|
||
|
#if defined(_WIN32) && !defined(_XENON)
|
||
|
memcpy( sharedMem, ¤tHeapArena, sizeof(currentHeapArena) );
|
||
|
memcpy( (byte*) sharedMem + sizeof(currentHeapArena), &systemHeapArray, sizeof(systemHeapArray) );
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Shutdown
|
||
|
|
||
|
Shuts down the memory system from all further use
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Shutdown( void )
|
||
|
{
|
||
|
#if defined(_WIN32) && !defined(_XENON)
|
||
|
if ( sharedMem != NULL )
|
||
|
{
|
||
|
UnmapViewOfFile( sharedMem );
|
||
|
CloseHandle( hMapObject );
|
||
|
sharedMem = NULL;
|
||
|
hMapObject = NULL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( currentHeapArena == &mainHeapArena )
|
||
|
{
|
||
|
memset(systemHeapArray, 0, sizeof(systemHeapArray));
|
||
|
defaultHeap.Shutdown();
|
||
|
currentHeapArena->Shutdown();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Mem_EnableLeakTest( const char *name )
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_UpdateStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_UpdateStats( memoryStats_t &stats, int size )
|
||
|
{
|
||
|
stats.num++;
|
||
|
if ( size < stats.minSize ) {
|
||
|
stats.minSize = size;
|
||
|
}
|
||
|
if ( size > stats.maxSize ) {
|
||
|
stats.maxSize = size;
|
||
|
}
|
||
|
stats.totalSize += size;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_UpdateAllocStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_UpdateAllocStats( int size )
|
||
|
{
|
||
|
Mem_UpdateStats( mem_frame_allocs, size );
|
||
|
Mem_UpdateStats( mem_total_allocs, size );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_UpdateFreeStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_UpdateFreeStats( int size ) {
|
||
|
Mem_UpdateStats( mem_frame_frees, size );
|
||
|
mem_total_allocs.num--;
|
||
|
mem_total_allocs.totalSize -= size;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_ClearFrameStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_ClearFrameStats( void )
|
||
|
{
|
||
|
mem_frame_allocs.num = mem_frame_frees.num = 0;
|
||
|
mem_frame_allocs.minSize = mem_frame_frees.minSize = 0x0fffffff;
|
||
|
mem_frame_allocs.maxSize = mem_frame_frees.maxSize = -1;
|
||
|
mem_frame_allocs.totalSize = mem_frame_frees.totalSize = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_GetFrameStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_GetFrameStats( memoryStats_t &allocs, memoryStats_t &frees )
|
||
|
{
|
||
|
allocs = mem_frame_allocs;
|
||
|
frees = mem_frame_frees;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_GetStats
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_GetStats( memoryStats_t &stats )
|
||
|
{
|
||
|
stats = mem_total_allocs;
|
||
|
}
|
||
|
|
||
|
void Mem_AllocDefragBlock( void )
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_ShowMemAlloc_f
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_ShowMemAlloc_f( const idCmdArgs &args )
|
||
|
{
|
||
|
const char *tagName;
|
||
|
int tag, num, size, peak;
|
||
|
DWORD totalOutstanding = 0;
|
||
|
|
||
|
for ( tag = 1; tag < MA_DO_NOT_USE; tag++ )
|
||
|
{
|
||
|
tagName = GetMemAllocStats( tag, num, size, peak );
|
||
|
|
||
|
if ( size || peak )
|
||
|
{
|
||
|
idLib::common->Printf("%-25s peak %9d curr %9d count %9d\n", tagName, peak, size, num );
|
||
|
totalOutstanding += size;
|
||
|
}
|
||
|
}
|
||
|
idLib::common->Printf("Mem_Alloc Outstanding: %d\n", totalOutstanding);
|
||
|
}
|
||
|
|
||
|
#ifdef ID_DEBUG_MEMORY
|
||
|
|
||
|
#undef Mem_Alloc
|
||
|
#undef Mem_ClearedAlloc
|
||
|
#undef Com_ClearedReAlloc
|
||
|
#undef Mem_Free
|
||
|
#undef Mem_CopyString
|
||
|
#undef Mem_Alloc16
|
||
|
#undef Mem_Free16
|
||
|
|
||
|
#define MAX_CALLSTACK_DEPTH 10
|
||
|
|
||
|
// size of this struct must be a multiple of 16 bytes
|
||
|
typedef struct debugMemory_s {
|
||
|
const char * fileName;
|
||
|
short lineNumber;
|
||
|
byte heapId;
|
||
|
byte memTag;
|
||
|
int frameNumber;
|
||
|
int size;
|
||
|
address_t callStack[MAX_CALLSTACK_DEPTH];
|
||
|
struct debugMemory_s * prev;
|
||
|
struct debugMemory_s * next;
|
||
|
} debugMemory_t;
|
||
|
|
||
|
static debugMemory_t * mem_debugMemory = NULL;
|
||
|
static char mem_leakName[256] = "";
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_CleanupFileName
|
||
|
==================
|
||
|
*/
|
||
|
const char *Mem_CleanupFileName( const char *fileName )
|
||
|
{
|
||
|
int i1, i2;
|
||
|
idStr newFileName;
|
||
|
static char newFileNames[4][MAX_STRING_CHARS];
|
||
|
static int index;
|
||
|
|
||
|
newFileName = fileName;
|
||
|
newFileName.BackSlashesToSlashes();
|
||
|
i1 = newFileName.Find( "neo", false );
|
||
|
if ( i1 >= 0 ) {
|
||
|
i1 = newFileName.Find( "/", false, i1 );
|
||
|
newFileName = newFileName.Right( newFileName.Length() - ( i1 + 1 ) );
|
||
|
}
|
||
|
while( 1 ) {
|
||
|
i1 = newFileName.Find( "/../" );
|
||
|
if ( i1 <= 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
i2 = i1 - 1;
|
||
|
while( i2 > 1 && newFileName[i2-1] != '/' ) {
|
||
|
i2--;
|
||
|
}
|
||
|
newFileName = newFileName.Left( i2 - 1 ) + newFileName.Right( newFileName.Length() - ( i1 + 4 ) );
|
||
|
}
|
||
|
index = ( index + 1 ) & 3;
|
||
|
strncpy( newFileNames[index], newFileName.c_str(), sizeof( newFileNames[index] ) );
|
||
|
return newFileNames[index];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Dump
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Dump( const char *fileName )
|
||
|
{
|
||
|
int i, numBlocks, totalSize;
|
||
|
char dump[32], *ptr;
|
||
|
debugMemory_t *b;
|
||
|
idStr module, funcName;
|
||
|
FILE *f;
|
||
|
|
||
|
f = fopen( fileName, "wb" );
|
||
|
if ( !f ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
totalSize = 0;
|
||
|
for ( numBlocks = 0, b = mem_debugMemory; b; b = b->next, numBlocks++ ) {
|
||
|
ptr = ((char *) b) + sizeof(debugMemory_t);
|
||
|
totalSize += b->size;
|
||
|
for ( i = 0; i < (sizeof(dump)-1) && i < b->size; i++) {
|
||
|
if ( ptr[i] >= 32 && ptr[i] < 127 ) {
|
||
|
dump[i] = ptr[i];
|
||
|
} else {
|
||
|
dump[i] = '_';
|
||
|
}
|
||
|
}
|
||
|
dump[i] = '\0';
|
||
|
if ( ( b->size >> 10 ) != 0 ) {
|
||
|
fprintf( f, "size: %6d KB: %s, line: %d [%s], call stack: %s\r\n", ( b->size >> 10 ), Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
|
||
|
}
|
||
|
else {
|
||
|
fprintf( f, "size: %7d B: %s, line: %d [%s], call stack: %s\r\n", b->size, Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idLib::sys->ShutdownSymbols();
|
||
|
|
||
|
fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
|
||
|
fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
|
||
|
|
||
|
fclose( f );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Dump_f
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Dump_f( const idCmdArgs &args )
|
||
|
{
|
||
|
const char *fileName;
|
||
|
|
||
|
if ( args.Argc() >= 2 ) {
|
||
|
fileName = args.Argv( 1 );
|
||
|
}
|
||
|
else {
|
||
|
fileName = "memorydump.txt";
|
||
|
}
|
||
|
Mem_Dump( fileName );
|
||
|
}
|
||
|
|
||
|
typedef struct allocInfo_s
|
||
|
{
|
||
|
const char * fileName;
|
||
|
short lineNumber;
|
||
|
byte heapId;
|
||
|
byte memTag;
|
||
|
int size;
|
||
|
int numAllocs;
|
||
|
address_t callStack[MAX_CALLSTACK_DEPTH];
|
||
|
struct allocInfo_s * next;
|
||
|
} allocInfo_t;
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
MEMSORT_SIZE,
|
||
|
MEMSORT_LOCATION,
|
||
|
MEMSORT_NUMALLOCS,
|
||
|
MEMSORT_CALLSTACK,
|
||
|
MEMSORT_HEAPID,
|
||
|
MEMSORT_MEMTAG
|
||
|
} memorySortType_t;
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_DumpCompressed
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_DumpCompressed( const char *fileName, memorySortType_t memSort, int sortCallStack, int numFrames, bool verbose )
|
||
|
{
|
||
|
int numBlocks, totalSize, r, j;
|
||
|
debugMemory_t *b;
|
||
|
allocInfo_t *a, *nexta, *allocInfo = NULL, *sortedAllocInfo = NULL, *prevSorted, *nextSorted;
|
||
|
idStr module, funcName;
|
||
|
|
||
|
// build list with memory allocations
|
||
|
totalSize = 0;
|
||
|
numBlocks = 0;
|
||
|
nextSorted = NULL;
|
||
|
|
||
|
for ( b = mem_debugMemory; b; b = b->next ) {
|
||
|
|
||
|
if ( numFrames && b->frameNumber < idLib::frameNumber - numFrames ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
numBlocks++;
|
||
|
totalSize += b->size;
|
||
|
|
||
|
// search for an allocation from the same source location
|
||
|
for ( a = allocInfo; a; a = a->next )
|
||
|
{
|
||
|
if ( a->lineNumber != b->lineNumber )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
#ifndef _XENON
|
||
|
// removed the call stack info for better consolidation of info and speed of dump
|
||
|
for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
|
||
|
if ( a->callStack[j] != b->callStack[j] ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( j < MAX_CALLSTACK_DEPTH ) {
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
if ( idStr::Cmp( a->fileName, b->fileName ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( a->heapId != b->heapId )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
a->numAllocs++;
|
||
|
a->size += b->size;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// if this is an allocation from a new source location
|
||
|
if ( !a )
|
||
|
{
|
||
|
a = (allocInfo_t *) currentHeapArena->Allocate( sizeof( allocInfo_t ) );
|
||
|
a->fileName = b->fileName;
|
||
|
a->lineNumber = b->lineNumber;
|
||
|
a->heapId = b->heapId;
|
||
|
a->memTag = b->memTag;
|
||
|
a->size = b->size;
|
||
|
a->numAllocs = 1;
|
||
|
for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
|
||
|
a->callStack[j] = b->callStack[j];
|
||
|
}
|
||
|
a->next = allocInfo;
|
||
|
allocInfo = a;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sort list
|
||
|
for ( a = allocInfo; a; a = nexta )
|
||
|
{
|
||
|
nexta = a->next;
|
||
|
|
||
|
prevSorted = NULL;
|
||
|
switch( memSort ) {
|
||
|
// sort on size
|
||
|
case MEMSORT_SIZE: {
|
||
|
for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
|
||
|
if ( a->size > nextSorted->size ) {
|
||
|
break;
|
||
|
}
|
||
|
prevSorted = nextSorted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// sort on file name and line number
|
||
|
case MEMSORT_LOCATION: {
|
||
|
for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
|
||
|
r = idStr::Cmp( Mem_CleanupFileName( a->fileName ), Mem_CleanupFileName( nextSorted->fileName ) );
|
||
|
if ( r < 0 || ( r == 0 && a->lineNumber < nextSorted->lineNumber ) ) {
|
||
|
break;
|
||
|
}
|
||
|
prevSorted = nextSorted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// sort on the number of allocations
|
||
|
case MEMSORT_NUMALLOCS: {
|
||
|
for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
|
||
|
if ( a->numAllocs > nextSorted->numAllocs ) {
|
||
|
break;
|
||
|
}
|
||
|
prevSorted = nextSorted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// sort on call stack
|
||
|
case MEMSORT_CALLSTACK: {
|
||
|
for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
|
||
|
if ( a->callStack[sortCallStack] < nextSorted->callStack[sortCallStack] ) {
|
||
|
break;
|
||
|
}
|
||
|
prevSorted = nextSorted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// sort on heap ID
|
||
|
case MEMSORT_HEAPID: {
|
||
|
for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
|
||
|
if ( a->heapId > nextSorted->heapId ) {
|
||
|
break;
|
||
|
}
|
||
|
prevSorted = nextSorted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( !prevSorted ) {
|
||
|
a->next = sortedAllocInfo;
|
||
|
sortedAllocInfo = a;
|
||
|
}
|
||
|
else {
|
||
|
prevSorted->next = a;
|
||
|
a->next = nextSorted;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RAVEN BEGIN
|
||
|
// dluetscher: changed xenon version to output anything above 1K to the console
|
||
|
// mwhitlock: added verbose option to show < 1K also since I need to see leaks,
|
||
|
// no matter how small.
|
||
|
#ifdef _XENON
|
||
|
|
||
|
unsigned int notShownSize = 0;
|
||
|
unsigned int notShownNumAllocs = 0;
|
||
|
|
||
|
// write list to debug output and console
|
||
|
char buff[256];
|
||
|
for ( a = sortedAllocInfo; a; a = nexta ) {
|
||
|
nexta = a->next;
|
||
|
|
||
|
if((a->size >> 10) > 0)
|
||
|
{
|
||
|
idStr::snPrintf(buff, sizeof(buff), "%6d K",( a->size >> 10));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
idStr::snPrintf(buff, sizeof(buff), "%6d B", a->size);
|
||
|
}
|
||
|
|
||
|
if ( verbose || ((a->size >> 10) > 0 ) )
|
||
|
{
|
||
|
idLib::common->Printf("size: %s, allocs: %5d: %s, line: %d, heap: %d, call stack: %s\r\n",
|
||
|
buff, a->numAllocs, Mem_CleanupFileName(a->fileName),
|
||
|
a->lineNumber, a->heapId, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
notShownSize+=(unsigned int)a->size;
|
||
|
notShownNumAllocs+=(unsigned int)a->numAllocs;
|
||
|
}
|
||
|
|
||
|
currentHeapArena->Free( a );
|
||
|
}
|
||
|
|
||
|
idLib::sys->ShutdownSymbols();
|
||
|
|
||
|
idLib::common->Printf("%8d bytes in %d allocs not shown\r\n", notShownSize, notShownNumAllocs );
|
||
|
idLib::common->Printf("%8d total memory blocks allocated\r\n", numBlocks );
|
||
|
idLib::common->Printf("%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
|
||
|
|
||
|
#else
|
||
|
// RAVEN END
|
||
|
FILE *f;
|
||
|
|
||
|
// in case you want to flip the above ifdef and write to disc on the xenon
|
||
|
f = fopen( fileName, "wb" );
|
||
|
if ( !f ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// write list to file
|
||
|
for ( a = sortedAllocInfo; a; a = nexta ) {
|
||
|
nexta = a->next;
|
||
|
fprintf( f, "size: %6d KB, allocs: %5d: %s, line: %d, call stack: %s\r\n",
|
||
|
(a->size >> 10), a->numAllocs, Mem_CleanupFileName(a->fileName),
|
||
|
a->lineNumber, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
|
||
|
// RAVEN BEGIN
|
||
|
// jnewquist: send all allocations through one place on the Xenon
|
||
|
currentHeapArena->Free( a );
|
||
|
// RAVEN END
|
||
|
}
|
||
|
|
||
|
idLib::sys->ShutdownSymbols();
|
||
|
|
||
|
fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
|
||
|
fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
|
||
|
|
||
|
fclose( f );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_DumpCompressed_f
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_DumpCompressed_f( const idCmdArgs &args )
|
||
|
{
|
||
|
int argNum;
|
||
|
const char *arg, *fileName;
|
||
|
memorySortType_t memSort = MEMSORT_LOCATION;
|
||
|
int sortCallStack = 0, numFrames = 0;
|
||
|
bool verbose = false;
|
||
|
|
||
|
// get cmd-line options
|
||
|
argNum = 1;
|
||
|
arg = args.Argv( argNum );
|
||
|
while( arg[0] == '-' ) {
|
||
|
arg = args.Argv( ++argNum );
|
||
|
if ( idStr::Icmp( arg, "s" ) == 0 ) {
|
||
|
memSort = MEMSORT_SIZE;
|
||
|
} else if ( idStr::Icmp( arg, "l" ) == 0 ) {
|
||
|
memSort = MEMSORT_LOCATION;
|
||
|
} else if ( idStr::Icmp( arg, "a" ) == 0 ) {
|
||
|
memSort = MEMSORT_NUMALLOCS;
|
||
|
} else if ( idStr::Icmp( arg, "cs1" ) == 0 ) {
|
||
|
memSort = MEMSORT_CALLSTACK;
|
||
|
sortCallStack = 2;
|
||
|
} else if ( idStr::Icmp( arg, "cs2" ) == 0 ) {
|
||
|
memSort = MEMSORT_CALLSTACK;
|
||
|
sortCallStack = 1;
|
||
|
} else if ( idStr::Icmp( arg, "cs3" ) == 0 ) {
|
||
|
memSort = MEMSORT_CALLSTACK;
|
||
|
sortCallStack = 0;
|
||
|
} else if ( idStr::Icmp( arg, "h" ) == 0 ) {
|
||
|
memSort = MEMSORT_HEAPID;
|
||
|
} else if ( idStr::Icmp( arg, "v" ) == 0 ) {
|
||
|
verbose = true;
|
||
|
} else if ( arg[0] == 'f' ) {
|
||
|
numFrames = atoi( arg + 1 );
|
||
|
} else {
|
||
|
idLib::common->Printf( "memoryDumpCompressed [options] [filename]\n"
|
||
|
"options:\n"
|
||
|
" -s sort on size\n"
|
||
|
" -l sort on location\n"
|
||
|
" -a sort on the number of allocations\n"
|
||
|
" -cs1 sort on first function on call stack\n"
|
||
|
" -cs2 sort on second function on call stack\n"
|
||
|
" -cs3 sort on third function on call stack\n"
|
||
|
" -h sort on heap ID\n"
|
||
|
" -f<X> only report allocations the last X frames\n"
|
||
|
" -v verbose (list all, even those totalling less than 1K\n"
|
||
|
"By default the memory allocations are sorted on location.\n"
|
||
|
"By default a 'memorydump.txt' is written if no file name is specified.\n" );
|
||
|
return;
|
||
|
}
|
||
|
arg = args.Argv( ++argNum );
|
||
|
}
|
||
|
if ( argNum >= args.Argc() ) {
|
||
|
fileName = "memorydump.txt";
|
||
|
} else {
|
||
|
fileName = arg;
|
||
|
}
|
||
|
Mem_DumpCompressed( fileName, memSort, sortCallStack, numFrames, verbose );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_AllocDebugMemory
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_AllocDebugMemory( const int size, const char *fileName, const int lineNumber, const bool align16, byte tag )
|
||
|
{
|
||
|
void *p;
|
||
|
debugMemory_t *m;
|
||
|
|
||
|
// the following is necessary for memory allocations that take place
|
||
|
// in static/global object constructors
|
||
|
if ( NULL == currentHeapArena )
|
||
|
{
|
||
|
Mem_Init();
|
||
|
}
|
||
|
|
||
|
if ( align16 )
|
||
|
{
|
||
|
p = currentHeapArena->Allocate16( size + sizeof( debugMemory_t ), tag );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p = currentHeapArena->Allocate( size + sizeof( debugMemory_t ), tag );
|
||
|
}
|
||
|
|
||
|
if ( NULL == p )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Mem_UpdateAllocStats( currentHeapArena->Msize( p ) );
|
||
|
|
||
|
m = (debugMemory_t *) p;
|
||
|
m->fileName = fileName;
|
||
|
m->lineNumber = lineNumber;
|
||
|
m->heapId = currentHeapArena->GetHeap(p)->DebugID();
|
||
|
m->memTag = tag;
|
||
|
m->frameNumber = idLib::frameNumber;
|
||
|
m->size = size;
|
||
|
m->next = mem_debugMemory;
|
||
|
m->prev = NULL;
|
||
|
if ( mem_debugMemory ) {
|
||
|
mem_debugMemory->prev = m;
|
||
|
}
|
||
|
mem_debugMemory = m;
|
||
|
|
||
|
if ( idLib::sys != NULL )
|
||
|
{
|
||
|
idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memset( m->callStack, 0, sizeof(m->callStack) );
|
||
|
}
|
||
|
|
||
|
return ( ( (byte *) p ) + sizeof( debugMemory_t ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_FreeDebugMemory
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_FreeDebugMemory( void *p, const char *fileName, const int lineNumber, const bool align16 )
|
||
|
{
|
||
|
debugMemory_t *m;
|
||
|
|
||
|
if ( !p )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m = (debugMemory_t *) ( ( (byte *) p ) - sizeof( debugMemory_t ) );
|
||
|
|
||
|
if ( m->size < 0 )
|
||
|
{
|
||
|
idLib::common->FatalError( "memory freed twice, first from %s, now from %s", idLib::sys->GetCallStackStr( m->callStack, MAX_CALLSTACK_DEPTH ), idLib::sys->GetCallStackCurStr( MAX_CALLSTACK_DEPTH ) );
|
||
|
}
|
||
|
|
||
|
Mem_UpdateFreeStats( currentHeapArena->Msize( m ) );
|
||
|
|
||
|
if ( m->next )
|
||
|
{
|
||
|
m->next->prev = m->prev;
|
||
|
}
|
||
|
|
||
|
if ( m->prev )
|
||
|
{
|
||
|
m->prev->next = m->next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mem_debugMemory = m->next;
|
||
|
}
|
||
|
|
||
|
m->fileName = fileName;
|
||
|
m->lineNumber = lineNumber;
|
||
|
m->frameNumber = idLib::frameNumber;
|
||
|
m->size = -m->size;
|
||
|
idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
|
||
|
|
||
|
assert( currentHeapArena != NULL );
|
||
|
currentHeapArena->Free( m );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Alloc
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_Alloc( const int size, const char *fileName, const int lineNumber, byte tag )
|
||
|
{
|
||
|
return Mem_AllocDebugMemory( size, fileName, lineNumber, false, tag );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Free
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Free( void *ptr, const char *fileName, const int lineNumber )
|
||
|
{
|
||
|
if ( !ptr )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
Mem_FreeDebugMemory( ptr, fileName, lineNumber, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Alloc16
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_Alloc16( const int size, const char *fileName, const int lineNumber, byte tag )
|
||
|
{
|
||
|
void *mem = Mem_AllocDebugMemory( size, fileName, lineNumber, true, tag );
|
||
|
// make sure the memory is 16 byte aligned
|
||
|
assert( ( ((int)mem) & 15) == 0 );
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Free16
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Free16( void *ptr, const char *fileName, const int lineNumber )
|
||
|
{
|
||
|
if ( !ptr ) {
|
||
|
return;
|
||
|
}
|
||
|
// make sure the memory is 16 byte aligned
|
||
|
assert( ( ((int)ptr) & 15) == 0 );
|
||
|
Mem_FreeDebugMemory( ptr, fileName, lineNumber, true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_ClearedAlloc
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_ClearedAlloc( const int size, const char *fileName, const int lineNumber, byte tag )
|
||
|
{
|
||
|
void *mem = Mem_Alloc( size, fileName, lineNumber, tag );
|
||
|
|
||
|
if ( mem != NULL )
|
||
|
{
|
||
|
SIMDProcessor->Memset( mem, 0, size );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
idLib::common->FatalError( "Ran out of memory during a cleared allocation" );
|
||
|
}
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_CopyString
|
||
|
==================
|
||
|
*/
|
||
|
char *Mem_CopyString( const char *in, const char *fileName, const int lineNumber )
|
||
|
{
|
||
|
char *out;
|
||
|
|
||
|
out = (char *)Mem_Alloc( strlen(in) + 1, fileName, lineNumber );
|
||
|
strcpy( out, in );
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Size
|
||
|
==================
|
||
|
*/
|
||
|
int Mem_Size( void *ptr )
|
||
|
{
|
||
|
if ( !ptr )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
assert( currentHeapArena != NULL );
|
||
|
return currentHeapArena->Msize( (byte*)ptr - sizeof( debugMemory_t ) );
|
||
|
}
|
||
|
|
||
|
#else // #ifdef ID_DEBUG_MEMORY
|
||
|
|
||
|
void Mem_Dump_f( const class idCmdArgs &args )
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
void Mem_DumpCompressed_f( const class idCmdArgs &args )
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Alloc
|
||
|
|
||
|
Allocate memory from the heap at the top of the current arena stack
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_Alloc( const int size, byte tag )
|
||
|
{
|
||
|
void *p;
|
||
|
|
||
|
// the following is necessary for memory allocations that take place
|
||
|
// in static/global object constructors
|
||
|
if ( NULL == currentHeapArena )
|
||
|
{
|
||
|
Mem_Init();
|
||
|
}
|
||
|
|
||
|
p = currentHeapArena->Allocate( size, tag );
|
||
|
if ( NULL == p )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Mem_UpdateAllocStats( currentHeapArena->Msize( p ) );
|
||
|
|
||
|
#if defined( _XENON ) && !defined( _FINAL )
|
||
|
MemTracker::OnAlloc(p, size);
|
||
|
#endif
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
// Mem_ClearedAlloc
|
||
|
//
|
||
|
// Allocate memory from the heap at the top of the current arena stack,
|
||
|
// clear that memory to zero before returning it.
|
||
|
void *Mem_ClearedAlloc( const int size, byte tag )
|
||
|
{
|
||
|
byte *allocation = (byte *) Mem_Alloc( size, tag );
|
||
|
|
||
|
#if defined( _XENON ) && !defined( _FINAL )
|
||
|
MemTracker::OnAlloc(allocation, size);
|
||
|
#endif
|
||
|
|
||
|
if ( allocation != NULL )
|
||
|
{
|
||
|
SIMDProcessor->Memset( allocation, 0, size );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
idLib::common->FatalError( "Ran out of memory during a cleared allocation" );
|
||
|
}
|
||
|
return allocation;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Free
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Free( void *ptr )
|
||
|
{
|
||
|
if ( !ptr )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
assert( currentHeapArena != NULL );
|
||
|
|
||
|
#if defined( _XENON ) && !defined( _FINAL )
|
||
|
MemTracker::OnDelete(ptr);
|
||
|
#endif
|
||
|
|
||
|
Mem_UpdateFreeStats( currentHeapArena->Msize( ptr ) );
|
||
|
|
||
|
currentHeapArena->Free( ptr );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_CopyString
|
||
|
|
||
|
Allocates memory for a copy of the given string, and copies
|
||
|
that string into the new allocation.
|
||
|
==================
|
||
|
*/
|
||
|
char *Mem_CopyString( const char *in )
|
||
|
{
|
||
|
char *out;
|
||
|
|
||
|
out = (char *)Mem_Alloc( strlen(in) + 1, MA_STRING );
|
||
|
if ( out != NULL )
|
||
|
{
|
||
|
strcpy( out, in );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
idLib::common->FatalError( "Ran out of memory during string copy allocation" );
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Alloc16
|
||
|
|
||
|
Allocate memory from the heap at the top of the current arena
|
||
|
stack that is aligned on a 16-byte boundary.
|
||
|
==================
|
||
|
*/
|
||
|
void *Mem_Alloc16( const int size, byte tag )
|
||
|
{
|
||
|
// the following is necessary for memory allocations that take place
|
||
|
// in static/global object constructors
|
||
|
if ( NULL == currentHeapArena )
|
||
|
{
|
||
|
Mem_Init();
|
||
|
}
|
||
|
void *mem = currentHeapArena->Allocate16( size, tag );
|
||
|
|
||
|
#if defined( _XENON ) && !defined( _FINAL )
|
||
|
MemTracker::OnAlloc(mem, size);
|
||
|
#endif
|
||
|
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Free
|
||
|
==================
|
||
|
*/
|
||
|
void Mem_Free16( void *ptr )
|
||
|
{
|
||
|
if ( !ptr )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
assert( currentHeapArena != NULL );
|
||
|
currentHeapArena->Free( ptr );
|
||
|
|
||
|
#if defined( _XENON ) && !defined( _FINAL )
|
||
|
MemTracker::OnDelete(ptr);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Mem_Size
|
||
|
==================
|
||
|
*/
|
||
|
int Mem_Size( void *ptr )
|
||
|
{
|
||
|
if ( !ptr )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
assert( currentHeapArena != NULL );
|
||
|
return currentHeapArena->Msize( ptr );
|
||
|
}
|
||
|
|
||
|
#endif // #else #ifdef ID_DEBUG_MEMORY
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvGetAllSysHeaps
|
||
|
|
||
|
retrieves the specified system heap
|
||
|
==================
|
||
|
*/
|
||
|
|
||
|
rvHeap* rvGetSysHeap( Rv_Sys_Heap_ID_t sysHeapID )
|
||
|
{
|
||
|
assert( (uint) sysHeapID < (uint) rv_heap_ID_max_count );
|
||
|
return systemHeapArray[ (uint) sysHeapID ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvGetAllSysHeaps
|
||
|
|
||
|
retrieves all the MAX_SYSTEM_HEAPS heap pointers into the given array
|
||
|
==================
|
||
|
*/
|
||
|
void rvGetAllSysHeaps( rvHeap *destSystemHeapArray[MAX_SYSTEM_HEAPS] )
|
||
|
{
|
||
|
memcpy(destSystemHeapArray, systemHeapArray, sizeof(systemHeapArray));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvSetAllSysHeaps
|
||
|
|
||
|
associates all the MAX_SYSTEM_HEAPS heap pointers from the given array with their corresponding id value
|
||
|
==================
|
||
|
*/
|
||
|
void rvSetAllSysHeaps( rvHeap *srcSystemHeapArray[MAX_SYSTEM_HEAPS] )
|
||
|
{
|
||
|
memcpy(systemHeapArray, srcSystemHeapArray, sizeof(systemHeapArray));
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
==================
|
||
|
rvGetHeapContainingMemory
|
||
|
|
||
|
Returns pointer to the heap containg the memory specified. If the memory specified
|
||
|
is not in a heap, returns 0.
|
||
|
==================
|
||
|
*/
|
||
|
rvHeap* rvGetHeapContainingMemory( const void* mem )
|
||
|
{
|
||
|
return (currentHeapArena!=0)?currentHeapArena->GetHeap(const_cast<void*>(mem)):0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
rvPushHeapContainingMemory
|
||
|
|
||
|
Pushes the heap containg the memory specified. If the memory specified
|
||
|
is not in a heap, does nothing and returns false, otherwise returns
|
||
|
true on success.
|
||
|
==================
|
||
|
*/
|
||
|
bool rvPushHeapContainingMemory( const void* mem )
|
||
|
{
|
||
|
if(currentHeapArena)
|
||
|
{
|
||
|
rvHeap* heap=currentHeapArena->GetHeap(const_cast<void*>(mem));
|
||
|
if(heap)
|
||
|
{
|
||
|
heap->PushCurrent();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#endif // #ifdef _RV_MEM_SYS_SUPPORT
|