/***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: System Library ** ** ** ** Module: Memory (Mem) ** ** ** ** File name: memman.cpp ** ** ** ** Created by: 03/20/00 - mjb ** ** ** ** Description: Memory manager ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #include "memman.h" #include #include // for memory profiling, to see if we ahve the extra memory #include "heap.h" #include "alloc.h" #include #ifdef __PLAT_XBOX__ #include #endif #ifdef __PLAT_NGC__ #include #endif #include #ifdef __PLAT_NGPS__ static bool s_use_semaphore = false; void WaitSemaMaybe(int sema) { if (s_use_semaphore) { WaitSema(sema); } } void SignalSemaMaybe(int sema) { if (s_use_semaphore) { SignalSema(sema); } } #endif /***************************************************************************** ** DBG Information ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ #if defined ( __PLAT_NGPS__ ) extern char _code_start[] __attribute__((section(".text"))); extern char _code_end[] __attribute__((section(".text"))); extern char _data_end[] __attribute__((section(".text"))); extern char _mem_start[] __attribute__((section(".mem_block"))); extern char _mem_end[] __attribute__((section(".mem_block"))); extern char _std_mem_end[] __attribute__((section(".mem_block"))); extern char _debug_heap_start[] __attribute__((section(".mem_block"))); extern char _script_heap_start[] __attribute__((section(".mem_block"))); #else char* _code_start; char* _code_end; char* _data_end; char* _mem_start; char* _mem_end; char* _std_mem_end; char * _debug_heap_start; #endif /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mem { /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ char Manager::s_manager_buffer[sizeof(Manager)]; char Manager::s_region_buffer[sizeof(Region)]; char Manager::s_script_region_buffer[sizeof(Region)]; char Manager::s_top_heap_buffer[sizeof(Heap)]; char Manager::s_bot_heap_buffer[sizeof(Heap)]; char Manager::s_debug_heap_buffer[sizeof(Heap)]; Manager* Manager::sp_instance = NULL; #ifdef __PLAT_NGPS__ static int s_context_semaphore; #endif char Manager::s_debug_region_buffer[sizeof(Region)]; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ Manager::Manager( void ) { m_current_id = 0; mp_process_man = NULL; # if defined ( __PLAT_XBOX__ ) // Just grab 33mb of main memory. _mem_start = (char*)XPhysicalAlloc( 33 * 1024 * 1024, // size of region MAXULONG_PTR, // base physical address 0, // memory alignment PAGE_READWRITE ); // memory protection and type _mem_end = _mem_start + ( 33 * 1024 * 1024 ); _std_mem_end = _mem_end; # elif defined ( __PLAT_WN32__ ) // Nasty hack for WN32 for now - just grab 38mb of main memory via a malloc. _mem_start = (char *)malloc( 38 * 1024 * 1024 ); _mem_end = _mem_start + ( 38 * 1024 * 1024 ); _std_mem_end = _mem_end; # endif # if defined ( __PLAT_NGC__ ) // Fill in what we know. _code_start = (char *)0xfadebabe; // Junk addresses. _code_end = (char *)0xfacced00; _data_end = (char *)0xcaccfacc; _mem_start = &((char *)OSGetArenaLo())[8192]; // Real, actual useful addresses. _mem_end = (char *)OSGetArenaHi(); _std_mem_end = (char *)OSGetArenaHi(); # endif // create root memory region mp_region = new ((void*)s_region_buffer) Region( nAlignUp( _mem_start ), nAlignDown( _std_mem_end ) ); // create default heaps mp_top_heap = new ((void*)s_top_heap_buffer) Heap( mp_region, Heap::vTOP_DOWN, "Top Down" ); mp_bot_heap = new ((void*)s_bot_heap_buffer) Heap( mp_region, Heap::vBOTTOM_UP, "BottomUp" ); m_heap_list[0] = mp_top_heap; m_heap_list[1] = mp_bot_heap; m_num_heaps = 2; # if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) ) uint codesize = ((uint)(_code_end) - (uint)(_code_start))/1024; uint datasize = ((uint)(_data_end) - (uint)(_code_end))/1024; printf ( "code [%p - %p] (%dK) + data [%p - %p] (%dK) = %dK \n", _code_start, _code_end, codesize, _code_end, _data_end, datasize, codesize + datasize ); #endif m_pushed_context_count = 0; mp_internet_region = NULL; mp_net_misc_region = NULL; mp_cutscene_region = NULL; mp_cutscene_bottom_heap = NULL; mp_cutscene_top_heap = NULL; #ifdef __PLAT_NGPS__ // Create a semaphore to prevent threads from re-entering push/pop memory contexts struct SemaParam params; params.initCount = 1; params.maxCount = 10; s_context_semaphore = CreateSema( ¶ms ); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ Manager::~Manager( void ) { mp_top_heap->~Heap(); mp_bot_heap->~Heap(); mp_region->~Region(); #ifdef __PLAT_NGPS__ DeleteSema( s_context_semaphore ); #endif } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void* Manager::New( size_t size, bool assert_on_fail, Allocator* pAlloc ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ if ( !pAlloc ) // set to 'default' allocator { pAlloc = mp_context->mp_alloc; } void* p_ret = pAlloc->allocate( size, assert_on_fail ); if ( p_ret ) // if allocation was successful { Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle } #if 0 if ( mp_process_man ) { mp_process_man->AllocEv( p_ret, size, pAlloc ); } #endif #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ return p_ret; } // Returns the amount of memory avaialbe in the current context // currently this is only valid for Heaps int Manager::Available() { return mp_context->mp_alloc->available(); } // Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used // by a pre file that has just been loaded in, so that the pre can be decompressed. void* Manager::ReallocateDown( size_t newSize, void *pOld, Allocator* pAlloc ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ if ( !pAlloc ) // set to 'default' allocator { pAlloc = mp_context->mp_alloc; } void* p_ret = pAlloc->reallocate_down( newSize, pOld ); if ( p_ret ) // if allocation was successful { Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle } #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ return p_ret; } // Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used // by a pre file that has just been loaded in, so that the pre can be decompressed. // This is for when the bottoms-up heap is being used. void *Manager::ReallocateUp( size_t newSize, void *pOld, Allocator* pAlloc ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ if ( !pAlloc ) // set to 'default' allocator { pAlloc = mp_context->mp_alloc; } void* p_ret = pAlloc->reallocate_up( newSize, pOld ); if ( p_ret ) // if allocation was successful { Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle } #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ return p_ret; } void *Manager::ReallocateShrink( size_t newSize, void *pOld, Allocator* pAlloc ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ if ( !pAlloc ) // set to 'default' allocator { pAlloc = mp_context->mp_alloc; } void* p_ret = pAlloc->reallocate_shrink( newSize, pOld ); if ( p_ret ) // if allocation was successful { Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle } #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::PushMemoryMarker( uint32 uiID ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::PopMemoryMarker( uint32 uiID ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::Delete( void* pAddr ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ if( pAddr != NULL ) { Allocator::s_free( pAddr ); #if 0 // 000810 JAB: Modified s_free to return the allocator. if (mp_process_man) { mp_process_man->FreeEv( pAddr, p_alloc ); } #endif } #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Manager::Valid( void* pAddr ) { if( pAddr != NULL ) { return Allocator::s_valid( pAddr ); } else { return false; } } /******************************************************************/ /* */ /* */ /******************************************************************/ size_t Manager::GetAllocSize( void* pAddr ) { if( pAddr != NULL ) { return Allocator::s_get_alloc_size( pAddr ); } else { Dbg_MsgAssert( 0, ( "Trying to get the size of an invalid block" ) ); return 0; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::PushContext( Allocator* alloc ) { Dbg_AssertType ( alloc, Allocator ); #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ // printf ("Pushed context %d to %s\n",m_pushed_context_count, alloc->GetName()); // DumpUnwindStack(20,0); Dbg_MsgAssert(m_pushed_context_count < vMAX_CONTEXT-1,("Pushed too many contexts")); mp_context = &m_contexts[m_pushed_context_count]; mp_context->mp_alloc = alloc; m_pushed_context_count++; #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::PopContext( void ) { #ifdef __PLAT_NGPS__ WaitSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ Dbg_MsgAssert( m_pushed_context_count,( "Heap stack underflow" )); m_pushed_context_count--; m_contexts[m_pushed_context_count].mp_alloc = (Mem::Allocator*)-1; if (m_pushed_context_count) { mp_context = &m_contexts[m_pushed_context_count-1]; } else { mp_context = NULL; // stack has now been emptied } #ifdef __PLAT_NGPS__ SignalSemaMaybe( s_context_semaphore ); #endif // __PLAT_NGPS__ } //GJ: Context number doesn't seem to be used... //Originally, it was used so that you could access the heaps from script, //but now you can access the heaps using names, so the numbers aren't needed //any more //int Manager::GetContextNumber() //{ // return mp_context->mp_alloc->GetNumber(); //} char * Manager::GetContextName() { return mp_context->mp_alloc->GetName(); } Allocator* Manager::GetContextAllocator() { return mp_context->mp_alloc; } // Added by Ken, so that pip.cpp knows whether to try and expand a memory block up or down. Allocator::Direction Manager::GetContextDirection() { return mp_context->mp_alloc->GetDirection(); } /******************************************************************/ /* */ /* */ /******************************************************************/ const char* Manager::GetHeapName( uint32 whichHeap ) { // first look through the named heaps... CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap ); if ( pInfo ) { return pInfo->mp_heap_name; } switch ( whichHeap ) { case 0xc7800b0: // bottomupheap return "BottomUpHeap"; break; case 0x8fdb68af: // topdownheap return "TopDownHeap"; break; case 0x62f3a0f3: // frontendheap return "FrontEndHeap"; break; case 0xe3551d2e: // networkheap return "NetworkHeap"; break; case 0x96d29d93: // netmischeap return "NetMiscHeap"; break; case 0xfcd5166b: // internettopdownheap return "InternetTopDownHeap"; break; case 0x90020867: // internetbottomupheap return "InternetBottomUpHeap"; break; case 0xfa33d9b: // scriptheap return "ScriptHeap"; break; case 0x70cb0238: // debugheap return "DebugHeap"; break; case 0xc3909393: // skaterheap0 return "Skater0"; break; case 0xb497a305: // skaterheap1 return "Skater1"; break; case 0x2d9ef2bf: // skaterheap2 return "Skater2"; break; case 0x5a99c229: // skaterheap3 return "Skater3"; break; case 0x572a9f4c: // skatergeomheap0 return "SkaterGeom0"; break; case 0x202dafda: // skatergeomheap1 return "SkaterGeom1"; break; case 0xb924fe60: // skatergeomheap2 return "SkaterGeom2"; break; case 0xce23cef6: // skatergeomheap3 return "SkaterGeom3"; break; case 0x8682d24: // skaterinfoheap return "SkaterInfo"; break; case 0x17ecb880: // themeheap return "ThemeHeap"; break; case 0x7ed56b49: // CutsceneBottomUpHeap return "CutsceneBottomUp"; break; case 0x25d71469: // CutsceneTopDownHeap return "CutsceneTopDown"; break; default: // unrecognized heap Dbg_Assert ( 0 ); } return "Unrecognized Heap"; } /******************************************************************/ /* */ /* */ /******************************************************************/ Heap* Manager::GetHeap( uint32 whichHeap ) { Heap* pHeap = NULL; // first look through the named heaps... CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap ); if ( pInfo ) { return pInfo->mp_heap; } switch ( whichHeap ) { case 0xc7800b0: // bottomupheap pHeap = BottomUpHeap(); break; case 0x8fdb68af: // topdownheap pHeap = TopDownHeap(); break; case 0x62f3a0f3: // frontendheap pHeap = FrontEndHeap(); break; case 0xe3551d2e: // networkheap pHeap = NetworkHeap(); break; case 0x96d29d93: // netmischeap pHeap = NetMiscHeap(); break; case 0xfcd5166b: // internettopdownheap pHeap = InternetTopDownHeap(); break; case 0x90020867: // internetbottomupheap pHeap = InternetBottomUpHeap(); break; case 0xfa33d9b: // scriptheap pHeap = ScriptHeap(); break; case 0x70cb0238: // debugheap pHeap = DebugHeap(); break; case 0xc3909393: // skaterheap0 pHeap = SkaterHeap(0); break; case 0xb497a305: // skaterheap1 pHeap = SkaterHeap(1); break; case 0x2d9ef2bf: // skaterheap2 pHeap = SkaterHeap(2); break; case 0x5a99c229: // skaterheap3 pHeap = SkaterHeap(3); break; case 0x572a9f4c: // skatergeomheap0 pHeap = SkaterGeomHeap(0); break; case 0x202dafda: // skatergeomheap1 pHeap = SkaterGeomHeap(1); break; case 0xb924fe60: // skatergeomheap2 pHeap = SkaterGeomHeap(2); break; case 0xce23cef6: // skatergeomheap3 pHeap = SkaterGeomHeap(3); break; case 0x8682d24: // skaterinfoheap pHeap = SkaterInfoHeap(); break; case 0x17ecb880: // themeheap pHeap = ThemeHeap(); break; case 0x7ed56b49: // CutsceneBottomUpHeap pHeap = CutsceneBottomUpHeap(); break; case 0x25d71469: // CutsceneTopDownHeap pHeap = CutsceneTopDownHeap(); break; default: // unrecognized heap Dbg_MsgAssert ( 0, ( "Unrecognized heap %08x", whichHeap ) ); } return pHeap; } Mem::Heap *Manager::CreateHeap( Region* region, Mem::Allocator::Direction dir, char* p_name) { Mem::Heap * pHeap = new Mem::Heap(region, dir, p_name); // At this point we can maintain the heap list m_heap_list[m_num_heaps++] = pHeap; return pHeap; } void Manager::RemoveHeap(Mem::Heap *pHeap) { #ifndef __PLAT_WN32__ #ifdef __NOPT_ASSERT__ // Check first to see if there is something on the heap before it is deleted // Deleting a heap with stuff on it might indicate an error if (pHeap->mUsedBlocks.m_count) { printf ("Deleting a heap %s with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count); #ifndef __PLAT_NGC__ printf ("\n\nDumping Heap\n"); MemView_DumpHeap(pHeap); printf ("\n\nAnalyzing Heap\n"); MemView_AnalyzeHeap(pHeap); Dbg_MsgAssert(0, ("Deleting heap <%s> with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count)); #endif // __PLAT_NGC__ } #endif #endif // remove from list of heaps for (int i=0;iPushContext( sp_instance->mp_bot_heap ); #else // but with assertions there is not enough room, so put it on the debug heap if (Config::GotExtraMemory()) { sp_instance->PushContext( sp_instance->mp_debug_heap ); } else { // running on a regular PS2, allow them to try, but will probably fail later with out of memory sp_instance->PushContext( sp_instance->mp_bot_heap ); } #endif mp_net_misc_region = new Mem::AllocRegion( (NETMISC_HEAP_SIZE) ); mp_net_misc_heap = CreateHeap( mp_net_misc_region, Mem::Allocator::vBOTTOM_UP, "NetMisc" ); sp_instance->PopContext(); } } void Manager::DeleteNetMiscHeap() { if( mp_net_misc_region ) { RemoveHeap( mp_net_misc_heap ); delete mp_net_misc_region; mp_net_misc_region = NULL; } } void Manager::InitInternetHeap() { if( mp_internet_region == NULL ) { //#ifndef __NOPT_ASSERT__ #if 1 // always allocate internet heap normally // normally the internet heap just goes on the top down heap sp_instance->PushContext( sp_instance->mp_bot_heap ); #else // but with assertions there is not enough room, so put it on the debug heap if (Config::GotExtraMemory()) { sp_instance->PushContext( sp_instance->mp_debug_heap ); } else { // running on a regular PS2, allow them to try, but will probably fail later with out of memory sp_instance->PushContext( sp_instance->mp_bot_heap ); } #endif mp_internet_region = new Mem::AllocRegion( (INTERNET_HEAP_SIZE) ); mp_internet_top_heap = CreateHeap( mp_internet_region, Mem::Allocator::vTOP_DOWN, "InternetTopDown" ); mp_internet_bottom_heap = CreateHeap( mp_internet_region, Mem::Allocator::vBOTTOM_UP, "InternetBottomUp" ); sp_instance->PopContext(); } } void Manager::DeleteInternetHeap() { if( mp_internet_region ) { RemoveHeap( mp_internet_top_heap ); RemoveHeap( mp_internet_bottom_heap ); delete mp_internet_region; mp_internet_region = NULL; } } void Manager::InitCutsceneHeap( int heap_size ) { // Note that it will create the cutscene heap on the current mem context... if ( mp_cutscene_region == NULL ) { // put it on the top-down heap, because it's used only temporarily // sp_instance->PushContext( sp_instance->mp_top_heap ); mp_cutscene_region = new Mem::AllocRegion( heap_size ); Dbg_MsgAssert( mp_cutscene_bottom_heap == NULL, ( "CutsceneBottomUpHeap already exists" ) ); mp_cutscene_bottom_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vBOTTOM_UP, "CutsceneBottomUp" ); Dbg_MsgAssert( mp_cutscene_top_heap == NULL, ( "CutsceneTopDownHeap already exists" ) ); mp_cutscene_top_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vTOP_DOWN, "CutsceneTopDown" ); // sp_instance->PopContext(); } } void Manager::DeleteCutsceneHeap() { if ( mp_cutscene_region ) { RemoveHeap( mp_cutscene_bottom_heap ); mp_cutscene_bottom_heap = NULL; RemoveHeap( mp_cutscene_top_heap ); mp_cutscene_top_heap = NULL; delete mp_cutscene_region; mp_cutscene_region = NULL; } } void Manager::InitDebugHeap() { #ifdef __PLAT_NGPS__ // The Debug heap is allocated directly from debug memory (>32MB on PS2) // as such, it should only ever be used on the TOOL (T10K) debug stations, or equivalents on other platforms mp_debug_region = new ((void*)s_debug_region_buffer) Region( nAlignUp( _debug_heap_start ), nAlignDown( _debug_heap_start+DEBUG_HEAP_SIZE ) ); mp_debug_heap = CreateHeap( mp_debug_region, Mem::Allocator::vBOTTOM_UP, "debug" ); #endif } void Manager::InitSkaterHeaps(int players) { printf ("Init Skater Heaps\n"); // some game modes specify 8 players, however 4 of those // are observers ??? // anyway, for now, just don't allow them to create more heaps than the max // it will assert later if you try to access a heap that has not been created if (players > NUM_SKATER_HEAPS) { players = NUM_SKATER_HEAPS; } // Initialize the other skater heaps for (int heap = NUM_PERM_SKATER_HEAPS; heap < players; heap++) { Dbg_MsgAssert(!mp_skater_region[heap],( "Skater region %d is still active!!!\n",heap)); Dbg_MsgAssert(!mp_skater_heap[heap],( "Skater heap %d is still active!!!\n",heap)); mp_skater_region[heap] = new Mem::AllocRegion( (SKATER_HEAP_SIZE) ); printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_region[heap]); mp_skater_heap[heap] = CreateHeap( mp_skater_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterX" ); printf ("EXTRA: Setup mp_skater_heap at %p\n",mp_skater_heap[heap]); Dbg_MsgAssert(!mp_skater_geom_region[heap],( "Skater geom region %d is still active!!!\n",heap)); Dbg_MsgAssert(!mp_skater_geom_heap[heap],( "Skater geom heap %d is still active!!!\n",heap)); mp_skater_geom_region[heap] = new Mem::AllocRegion( (SKATER_GEOM_HEAP_SIZE) ); printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_geom_region[heap]); mp_skater_geom_heap[heap] = CreateHeap( mp_skater_geom_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterGeomX" ); printf ("EXTRA: Setup mp_skater_geom_heap at %p\n",mp_skater_geom_heap[heap]); } printf ("END Init Skater Heaps\n"); } // Delete the temporary skater heaps void Manager::DeleteSkaterHeaps() { for (int i=NUM_PERM_SKATER_HEAPS;iPushContext( sp_instance->mp_bot_heap ); // make bottom-up heap default // sp_instance->InitOtherHeaps(); } else { Dbg_Warning( "Already Initialized!" ); } } // K: Separated this out from sSetUp because this needs to be called from main(), after // the config manager has initialised, because it must only be called if extra memory is // available, and we only know that after Config::Init has been called. // sSetUp is called from pre_main, and the config manager cannot be called from there // because it needs the command line args. void Manager::sSetUpDebugHeap( void ) { if ( sp_instance ) { sp_instance->InitDebugHeap(); } else { Dbg_MsgAssert(0,("Called sSetUpDebugHeap when mem manager not initialized!")); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::sCloseDown( void ) { if ( sp_instance ) { #ifndef __PLAT_WN32__ sp_instance->DeleteOtherHeaps(); #endif sp_instance->PopContext(); sp_instance->~Manager(); sp_instance = NULL; } else { Dbg_Warning( "Not Initialized!" ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::RegisterPcsMemMan( Pcs::Manager* pReg ) { Dbg_Assert( mp_process_man == NULL ); // should not initialize twice. mp_process_man = pReg; } /******************************************************************/ /* */ /* */ /******************************************************************/ CNamedHeapInfo* Manager::find_named_heap_info( uint32 name ) { for ( int i = 0; i < NUM_NAMED_HEAPS; i++ ) { if ( m_named_heap_info[i].m_used && m_named_heap_info[i].m_name == name ) { return &m_named_heap_info[i]; } } return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ Heap* Manager::NamedHeap( uint32 name, bool assertOnFail ) { CNamedHeapInfo* pInfo = find_named_heap_info( name ); if ( pInfo ) { return pInfo->mp_heap; } Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x", name ) ); return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Manager::InitNamedHeap( uint32 name, uint32 size, const char* pHeapName ) { CNamedHeapInfo* pNamedHeapInfo = NULL; for ( int i = 0; i < NUM_NAMED_HEAPS; i++ ) { if ( !m_named_heap_info[i].m_used ) { pNamedHeapInfo = &m_named_heap_info[i]; break; } } Dbg_MsgAssert( pNamedHeapInfo, ( "No more free named heaps (Increase NUM_NAMED_HEAPS from %d)!", NUM_NAMED_HEAPS ) ); // set the correct name pNamedHeapInfo->m_name = name; Dbg_MsgAssert( strlen(pHeapName) < CNamedHeapInfo::vMAX_HEAP_NAME_LEN, ( "Heap name %s is too long", pHeapName ) ); strcpy( pNamedHeapInfo->mp_heap_name, pHeapName ); Dbg_MsgAssert(!pNamedHeapInfo->mp_region, ( "Named region is still active" ) ); pNamedHeapInfo->mp_region = new Mem::AllocRegion( size ); Dbg_Message( "EXTRA: Allocated pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_region ); Dbg_MsgAssert(!pNamedHeapInfo->mp_heap, ("Named heap is still active")); pNamedHeapInfo->mp_heap = CreateHeap( pNamedHeapInfo->mp_region, Mem::Allocator::vBOTTOM_UP, pNamedHeapInfo->mp_heap_name ); Dbg_Message( "EXTRA: Setup pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_heap ); pNamedHeapInfo->m_used = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Manager::DeleteNamedHeap( uint32 name, bool assertOnFail ) { CNamedHeapInfo* pInfo = find_named_heap_info( name ); if ( pInfo ) { Dbg_Message( "Deleting named heap %s", pInfo->mp_heap_name ); Dbg_Assert( pInfo->mp_region ); Dbg_Assert( pInfo->mp_heap ); RemoveHeap( pInfo->mp_heap ); delete pInfo->mp_region; pInfo->mp_region = NULL; pInfo->mp_heap = NULL; pInfo->m_name = 0; pInfo->m_used = false; return true; } else { Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x to delete", name ) ); } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Malloc( size_t size ) { void *v = Mem::Manager::sHandle().New( size, true ); return v; } int Available() { return Mem::Manager::sHandle().Available(); } void* ReallocateDown( size_t newSize, void *pOld ) { return Mem::Manager::sHandle().ReallocateDown(newSize,pOld); } void* ReallocateUp( size_t newSize, void *pOld ) { return Mem::Manager::sHandle().ReallocateUp(newSize,pOld); } void* ReallocateShrink( size_t newSize, void *pOld ) { return Mem::Manager::sHandle().ReallocateShrink(newSize,pOld); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Free( void* pAddr ) { Mem::Manager::sHandle().Delete( pAddr ); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Valid( void* pAddr ) { return Mem::Manager::sHandle().Valid( pAddr ); } /******************************************************************/ /* */ /* */ /******************************************************************/ size_t GetAllocSize( void* pAddr ) { return Mem::Manager::sHandle().GetAllocSize( pAddr ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Realloc( void* mem, size_t newSize ) { /* should really add resize functions to do this more efficiently */ void* ptr = NULL; if ( newSize ) { // Mem::Manager::sHandle().PushContext(Manager::sHandle().TopDownHeap()); ptr = Mem::Manager::sHandle().New( newSize, true ); // Mem::Manager::sHandle().PopContext(); } if ( mem ) { if ( ptr ) { memmove ( ptr, mem, newSize ); } Mem::Manager::sHandle().Delete( mem ); } return ptr; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Calloc( size_t numObj, size_t sizeObj ) { return Mem::Manager::sHandle().New(( numObj * sizeObj ), true ); } /******************************************************************/ /* */ /* */ /******************************************************************/ // enable multiple threads to access the memory manager at once void SetThreadSafe(bool safe) { #ifdef __PLAT_NGPS__ s_use_semaphore = safe; #endif } bool IsThreadSafe( void ) { #ifdef __PLAT_NGPS__ return s_use_semaphore; #else return false; #endif } #ifdef __NOPT_ASSERT__ #define MAX_PROFILE_NAME 64 #define MAX_NUM_PROFILES 5000 #define MAX_LEVELS 32 class CMemProfile { public: char m_type[MAX_PROFILE_NAME]; int m_blocks; int m_size; int m_depth; int m_count; // number of pushes of same nam, same depth context int m_link; // link forward to profile that has merged with this profile }; //static CMemProfile s_profile_list[MAX_NUM_PROFILES]; // a flat array of all profiles static CMemProfile * s_profile_list; // a dynamically allocated flat array of all profiles static CMemProfile * s_profile_stack[MAX_LEVELS]; // stack of pushed profiles static int s_profile_count = 0; static CMemProfile* sp_current_profile = NULL; // current entry in profile list (will need to pop back.... static int s_profile_stack_depth = 0; // depth in stack (index into m_profile_stack[]) static int s_next_profile = 0; // index into array //static int s_launched = false; // true after we've laucnhed the level once static bool s_active = true; void PushMemProfile(char *p_type) { if( s_active ) { if (!s_profile_list) { if (Config::GotExtraMemory()) { Mem::Manager::sHandle().PushContext(Manager::sHandle().DebugHeap()); s_profile_list = (CMemProfile *)Mem::Malloc(MAX_NUM_PROFILES *sizeof(CMemProfile)); Mem::Manager::sHandle().PopContext(); } else { s_active = false; // no extra memory, or maybe it's an X-Box.... return; } } if (s_next_profile >= MAX_NUM_PROFILES) { s_active = false; return; } /* if (!strcmp("LaunchLevel",p_type)) { if (s_launched) { s_active = false; return; } s_launched = true; } */ // if we have a current profile, then push it if (sp_current_profile) { Dbg_MsgAssert(s_profile_stack_depth < MAX_LEVELS,("mem profile stack overflow, unmatched push?")); s_profile_stack[s_profile_stack_depth++] = sp_current_profile; } // get a new profile from the list Dbg_MsgAssert(s_next_profile < MAX_NUM_PROFILES,("mem profile heap overflow, too many pushes")); sp_current_profile = &s_profile_list[s_next_profile++]; // just copy over the memory containing the name char *p = &(sp_current_profile->m_type[0]); char *q = (char*) p_type; for (int i=0;im_blocks = 0; sp_current_profile->m_size = 0; sp_current_profile->m_depth = s_profile_stack_depth; sp_current_profile->m_count = 1; sp_current_profile->m_link = 0; // Then, search back through the list to see if there // are any entries att he same level that have this same string // if so, then zero the old instance, and add the size and count to this one // there should only be one, as any other one would already be zeroed int check = s_next_profile-2; while (check>0 && s_profile_list[check].m_depth == s_profile_stack_depth) { if (s_profile_list[check].m_depth == s_profile_stack_depth) { // same depth if (strcmp(s_profile_list[check].m_type,p_type) == 0) { // same string, so bring this one forward to add to this sp_current_profile->m_blocks += s_profile_list[check].m_blocks; sp_current_profile->m_size += s_profile_list[check].m_size; sp_current_profile->m_count += s_profile_list[check].m_count; // and zero out the old one s_profile_list[check].m_blocks = 0; s_profile_list[check].m_size = 0; // Patch in the index of the new profile, // so late deletions can be accounted for s_profile_list[check].m_link = s_profile_stack_depth - 1; break; } } check--; } } } /******************************************************************/ void PopMemProfile() { if( s_active) { Dbg_MsgAssert(sp_current_profile,("Popped one more memory profile than we pushed")); // set time on the current profile // sp_current_profile->m_end_time = Tmr::GetTimeInUSeconds(); if ( ! s_profile_stack_depth) { // nothing left on stack, so set current profile to NULL sp_current_profile = NULL; // update the count of valid profiles s_profile_count = s_next_profile; } else { // get the last profile pushed on the stack sp_current_profile = s_profile_stack[--s_profile_stack_depth]; } } } // given an index in the the mem profile list // then get the total size of all allocations // at this level, or below // up the next entry at the same depth, or the end of the list int total_size(int n) { int size = s_profile_list[n].m_size; int depth = s_profile_list[n].m_depth; n++; while (n < s_next_profile && s_profile_list[n].m_depth > depth) { size += s_profile_list[n].m_size; n++; } return size; } #ifndef __PLAT_WN32__ // dump the memory profile in a tree format, like // // level stuff 100000 // peds 5000 // cars 23000 // other 72000 // skater stuff ...... void DumpMemProfile(int level, char *p_type) { char buf[512]; if( s_active ) { printf ("\nDumping Memory Profile\n"); printf ("There are %d mem profile contexts\n",s_next_profile); for (int i=0;i%10s____",Str::PrintThousands(total_size(i))); dump_printf(buf); if (s_profile_list[i].m_count == 1) { sprintf (buf,"%s\n",s_profile_list[i].m_type); } else { sprintf (buf,"%s (%d) (avg: %s)\n",s_profile_list[i].m_type,s_profile_list[i].m_count,Str::PrintThousands(total_size(i)/s_profile_list[i].m_count)); } dump_printf(buf); if (s_profile_list[i].m_depth < level && s_profile_list[i].m_size && total_size(i) != s_profile_list[i].m_size) { sprintf (buf,"%2d: ",s_profile_list[i].m_depth+1); dump_printf(buf); for (int tab2 = 0;tab2 < s_profile_list[i].m_depth+1;tab2++) { sprintf(buf," "); dump_printf(buf); } sprintf (buf,">%10s____",Str::PrintThousands(s_profile_list[i].m_size)); dump_printf(buf); sprintf (buf,"Untracked %s\n",s_profile_list[i].m_type); dump_printf(buf); } } } } } else { printf ("Mem Profiler not active, probably overflowed, try restarting...\n"); } } #endif// __PLAT_WN32__ void AllocMemProfile(Allocator::BlockHeader* p_block) { if( s_active ) { if (sp_current_profile) { sp_current_profile->m_blocks++; // If it's on the debug heap, then set size to zero to avoid confusion if (Mem::Manager::sHandle().GetContextAllocator() != Mem::Manager::sHandle().DebugHeap()) { sp_current_profile->m_size += p_block->mSize; } p_block->mp_profile = sp_current_profile; } } } void FreeMemProfile(Allocator::BlockHeader* p_block) { if( s_active ) { CMemProfile * p_profile = p_block->mp_profile; if (p_profile) { // Dbg_MsgAssert(p_profile->m_blocks,("mutli-block freed out of context")); // Skip over any that have been combined, until we find the final combined block while (p_profile->m_link != 0) { p_profile = &s_profile_list[p_profile->m_link]; } p_profile->m_blocks--; p_profile->m_size -= p_block->mSize; p_profile = NULL; } } } #else void PushMemProfile(char *p_type) { } void PopMemProfile() { } #ifndef __PLAT_WN32__ void DumpMemProfile(int level, char *p_type) { } #endif// __PLAT_WN32__ void AllocMemProfile(Allocator::BlockHeader* p_block) { } void FreeMemProfile(Allocator::BlockHeader* p_block) { } #endif __NOPT_ASSERT__ } // namespace Mem