mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
* All libraries have been isolated from each other, and build into separate artifacts. * Project has been restructured to support isolating libraries. * CCrashHandler now calls a callback on crash (setup from core/dllmain.cpp, this can be setup in any way for any project. This callback is getting called when the apllication crashes. Useful for flushing buffers before closing handles to logging files for example). * Tier0 'CoreMsgV' function now calls a callback sink, which could be set by the user (currently setup to the SDK's internal logger in core/dllmain.cpp). TODO: * Add a batch file to autogenerate all projects. * Add support for dedicated server. * Add support for client dll. Bugs: * Game crashes on the title screen after the UI script compiler has finished (root cause unknown). * Curl error messages are getting logged twice for the dedicated server due to the removal of all "DEDICATED" preprocessor directives to support isolating projects. This has to be fixed properly!
492 lines
12 KiB
C++
492 lines
12 KiB
C++
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======//
|
||
//
|
||
// Purpose:
|
||
//
|
||
//=============================================================================//
|
||
|
||
#include "tier0_pch.h"
|
||
#include "tier0/dbg.h"
|
||
#include "tier0/memstd.h"
|
||
#include "tier1/memstack.h"
|
||
#include "tier1/utlmap.h"
|
||
#include "tier0/memdbgon.h"
|
||
|
||
#ifdef _WIN32
|
||
#pragma warning(disable:4073)
|
||
#pragma init_seg(lib)
|
||
#endif
|
||
|
||
#define VA_COMMIT_FLAGS MEM_COMMIT
|
||
#define VA_RESERVE_FLAGS MEM_RESERVE
|
||
|
||
static volatile bool bSpewAllocations = false; // TODO: Register CMemoryStacks with g_pMemAlloc, so it can spew a summary
|
||
|
||
//-----------------------------------------------------------------------------
|
||
|
||
MEMALLOC_DEFINE_EXTERNAL_TRACKING(CMemoryStack);
|
||
|
||
//-----------------------------------------------------------------------------
|
||
|
||
void PrintStatus( void* p )
|
||
{
|
||
CMemoryStack* pMemoryStack = (CMemoryStack*)p;
|
||
|
||
pMemoryStack->PrintContents();
|
||
}
|
||
|
||
CMemoryStack::CMemoryStack()
|
||
: m_pNextAlloc( nullptr )
|
||
, m_pCommitLimit( nullptr )
|
||
, m_pAllocLimit( nullptr )
|
||
, m_pHighestAllocLimit( nullptr )
|
||
, m_pBase( nullptr )
|
||
, m_pUnkPtr( nullptr )
|
||
, m_bRegisteredAllocation( false )
|
||
, m_unkSize( 0 )
|
||
, m_maxSize( 0 )
|
||
, m_alignment( 16 )
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
, m_commitIncrement( 0 )
|
||
, m_minCommit( 0 )
|
||
#ifdef _PS3
|
||
, m_pVirtualMemorySection( NULL )
|
||
#endif
|
||
#endif
|
||
{
|
||
//AddMemoryInfoCallback( this );
|
||
m_pszAllocOwner = strdup( "CMemoryStack unattributed" );
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
CMemoryStack::~CMemoryStack()
|
||
{
|
||
if ( m_pBase )
|
||
Term();
|
||
|
||
//RemoveMemoryInfoCallback( this );
|
||
free( m_pszAllocOwner );
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
bool CMemoryStack::Init( const char *pszAllocOwner, uint64 maxSize, uint64 commitIncrement, uint64 initialCommit, uint64 alignment )
|
||
{
|
||
Assert( !m_pBase );
|
||
|
||
m_bPhysical = false;
|
||
|
||
m_maxSize = maxSize;
|
||
m_alignment = AlignValue( alignment, 4 );
|
||
|
||
Assert( m_alignment == alignment );
|
||
Assert( m_maxSize > 0 );
|
||
|
||
SetAllocOwner( pszAllocOwner );
|
||
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
|
||
#ifdef _PS3
|
||
// Memory can only be committed in page-size increments on PS3
|
||
static const unsigned PS3_PAGE_SIZE = 64*1024;
|
||
if ( commitSize < PS3_PAGE_SIZE )
|
||
commitSize = PS3_PAGE_SIZE;
|
||
#endif
|
||
|
||
if ( commitIncrement != 0 )
|
||
{
|
||
m_commitIncrement = commitIncrement;
|
||
}
|
||
|
||
uint64 pageSize;
|
||
|
||
#ifdef _PS3
|
||
pageSize = PS3_PAGE_SIZE;
|
||
#elif defined( _X360 )
|
||
pageSize = 64 * 1024;
|
||
#else
|
||
SYSTEM_INFO sysInfo;
|
||
GetSystemInfo( &sysInfo );
|
||
Assert( !( sysInfo.dwPageSize & (sysInfo.dwPageSize-1)) );
|
||
pageSize = sysInfo.dwPageSize;
|
||
#endif
|
||
|
||
if ( m_commitIncrement == 0 )
|
||
{
|
||
m_commitIncrement = pageSize;
|
||
}
|
||
else
|
||
{
|
||
m_commitIncrement = AlignValue( m_commitIncrement, pageSize );
|
||
}
|
||
|
||
m_maxSize = AlignValue( m_maxSize, m_commitIncrement );
|
||
|
||
Assert( m_maxSize % pageSize == 0 && m_commitIncrement % pageSize == 0 && m_commitIncrement <= m_maxSize );
|
||
|
||
#ifdef _WIN32
|
||
m_pBase = (unsigned char *)VirtualAlloc( NULL, m_maxSize, VA_RESERVE_FLAGS, PAGE_NOACCESS );
|
||
#else
|
||
m_pVirtualMemorySection = g_pMemAlloc->AllocateVirtualMemorySection( m_maxSize );
|
||
if ( !m_pVirtualMemorySection )
|
||
{
|
||
Warning( "AllocateVirtualMemorySection failed( size=%d )\n", m_maxSize );
|
||
Assert( 0 );
|
||
m_pBase = NULL;
|
||
}
|
||
else
|
||
{
|
||
m_pBase = ( byte* ) m_pVirtualMemorySection->GetBaseAddress();
|
||
}
|
||
#endif
|
||
if ( !m_pBase )
|
||
{
|
||
#if !defined( NO_MALLOC_OVERRIDE )
|
||
g_pMemAlloc->OutOfMemory();
|
||
#endif
|
||
return false;
|
||
}
|
||
m_pCommitLimit = m_pNextAlloc = m_pBase;
|
||
|
||
if ( initialCommit )
|
||
{
|
||
initialCommit = AlignValue( initialCommit, m_commitIncrement );
|
||
Assert( initialCommit <= m_maxSize );
|
||
bool bInitialCommitSucceeded = false;
|
||
#ifdef _WIN32
|
||
bInitialCommitSucceeded = !!VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE );
|
||
#else
|
||
m_pVirtualMemorySection->CommitPages( m_pCommitLimit, initialCommit );
|
||
bInitialCommitSucceeded = true;
|
||
#endif
|
||
if ( !bInitialCommitSucceeded )
|
||
{
|
||
#if !defined( NO_MALLOC_OVERRIDE )
|
||
g_pMemAlloc->OutOfMemory( initialCommit );
|
||
#endif
|
||
return false;
|
||
}
|
||
m_minCommit = initialCommit;
|
||
m_pCommitLimit += initialCommit;
|
||
RegisterAllocation();
|
||
}
|
||
|
||
#else
|
||
m_pBase = (byte*)MemAlloc_AllocAligned( m_maxSize, alignment ? alignment : 1 );
|
||
m_pNextAlloc = m_pBase;
|
||
m_pCommitLimit = m_pBase + m_maxSize;
|
||
#endif
|
||
|
||
m_pHighestAllocLimit = m_pNextAlloc;
|
||
|
||
m_pAllocLimit = m_pBase + m_maxSize;
|
||
|
||
return ( m_pBase != nullptr );
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
#ifdef _GAMECONSOLE
|
||
bool CMemoryStack::InitPhysical( const char *pszAllocOwner, uint size, uint nBaseAddrAlignment, uint alignment, uint32 nFlags )
|
||
{
|
||
m_bPhysical = true;
|
||
|
||
m_maxSize = m_commitIncrement = size;
|
||
m_alignment = AlignValue( alignment, 4 );
|
||
|
||
SetAllocOwner( pszAllocOwner );
|
||
|
||
#ifdef _X360
|
||
int flags = PAGE_READWRITE | nFlags;
|
||
if ( size >= 16*1024*1024 )
|
||
{
|
||
flags |= MEM_16MB_PAGES;
|
||
}
|
||
else
|
||
{
|
||
flags |= MEM_LARGE_PAGES;
|
||
}
|
||
m_pBase = (unsigned char *)XPhysicalAlloc( m_maxSize, MAXULONG_PTR, nBaseAddrAlignment, flags );
|
||
#elif defined (_PS3)
|
||
m_pBase = (byte*)nFlags;
|
||
m_pBase = (byte*)AlignValue( (uintp)m_pBase, m_alignment );
|
||
#else
|
||
#pragma error
|
||
#endif
|
||
|
||
Assert( m_pBase );
|
||
m_pNextAlloc = m_pBase;
|
||
m_pCommitLimit = m_pBase + m_maxSize;
|
||
m_pAllocLimit = m_pBase + m_maxSize;
|
||
m_pHighestAllocLimit = m_pNextAlloc;
|
||
|
||
RegisterAllocation();
|
||
return ( m_pBase != NULL );
|
||
}
|
||
#endif
|
||
|
||
//-------------------------------------
|
||
|
||
void CMemoryStack::Term()
|
||
{
|
||
FreeAll();
|
||
if ( m_pBase )
|
||
{
|
||
#ifdef _GAMECONSOLE
|
||
if ( m_bPhysical )
|
||
{
|
||
#if defined( _X360 )
|
||
XPhysicalFree( m_pBase );
|
||
#elif defined( _PS3 )
|
||
#else
|
||
#pragma error
|
||
#endif
|
||
m_pCommitLimit = m_pBase = NULL;
|
||
m_maxSize = 0;
|
||
RegisterDeallocation(true);
|
||
m_bPhysical = false;
|
||
return;
|
||
}
|
||
#endif // _GAMECONSOLE
|
||
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
#if defined(_WIN32)
|
||
VirtualFree( m_pBase, 0, MEM_RELEASE );
|
||
#else
|
||
m_pVirtualMemorySection->Release();
|
||
m_pVirtualMemorySection = NULL;
|
||
#endif
|
||
#else
|
||
MemAlloc_FreeAligned( m_pBase );
|
||
#endif
|
||
m_pBase = nullptr;
|
||
// Zero these variables to avoid getting misleading mem_dump
|
||
// results when m_pBase is NULL.
|
||
m_pNextAlloc = nullptr;
|
||
m_pCommitLimit = nullptr;
|
||
m_pHighestAllocLimit = nullptr;
|
||
m_pUnkPtr = nullptr;
|
||
m_maxSize = 0;
|
||
RegisterDeallocation(true);
|
||
}
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
uint64 CMemoryStack::GetSize() const
|
||
{
|
||
if ( m_bPhysical )
|
||
return m_maxSize;
|
||
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
return m_pCommitLimit - m_pBase;
|
||
#else
|
||
return m_maxSize;
|
||
#endif
|
||
}
|
||
|
||
|
||
//-------------------------------------
|
||
|
||
bool CMemoryStack::CommitTo( byte *pNextAlloc ) RESTRICT
|
||
{
|
||
if ( m_bPhysical )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
unsigned char * pNewCommitLimit = AlignValue( pNextAlloc, m_commitIncrement );
|
||
ptrdiff_t commitIncrement = pNewCommitLimit - m_pCommitLimit;
|
||
|
||
if( m_pCommitLimit + commitIncrement > m_pAllocLimit )
|
||
{
|
||
#if !defined( NO_MALLOC_OVERRIDE )
|
||
g_pMemAlloc->OutOfMemory( commitIncrement );
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
if ( pNewCommitLimit > m_pCommitLimit )
|
||
{
|
||
RegisterDeallocation(false);
|
||
bool bAllocationSucceeded = false;
|
||
#ifdef _WIN32
|
||
bAllocationSucceeded = !!VirtualAlloc( m_pCommitLimit, commitIncrement, VA_COMMIT_FLAGS, PAGE_READWRITE );
|
||
#else
|
||
bAllocationSucceeded = m_pVirtualMemorySection->CommitPages( m_pCommitLimit, commitIncrement );
|
||
#endif
|
||
if ( !bAllocationSucceeded )
|
||
{
|
||
#if !defined( NO_MALLOC_OVERRIDE )
|
||
g_pMemAlloc->OutOfMemory( commitIncrement );
|
||
#endif
|
||
return false;
|
||
}
|
||
m_pCommitLimit = pNewCommitLimit;
|
||
|
||
RegisterAllocation();
|
||
}
|
||
else if ( pNewCommitLimit < m_pCommitLimit )
|
||
{
|
||
if ( m_pNextAlloc > pNewCommitLimit )
|
||
{
|
||
Warning( eDLL_T::COMMON, "ATTEMPTED TO DECOMMIT OWNED MEMORY STACK SPACE\n" );
|
||
pNewCommitLimit = AlignValue( m_pNextAlloc, m_commitIncrement );
|
||
}
|
||
|
||
if ( pNewCommitLimit < m_pCommitLimit )
|
||
{
|
||
RegisterDeallocation(false);
|
||
ptrdiff_t decommitIncrement = m_pCommitLimit - pNewCommitLimit;
|
||
#ifdef _WIN32
|
||
VirtualFree( pNewCommitLimit, decommitIncrement, MEM_DECOMMIT );
|
||
#else
|
||
m_pVirtualMemorySection->DecommitPages( pNewCommitLimit, decommitIncrement );
|
||
#endif
|
||
m_pCommitLimit = pNewCommitLimit;
|
||
RegisterAllocation();
|
||
}
|
||
}
|
||
|
||
return true;
|
||
#else
|
||
return false;
|
||
#endif
|
||
}
|
||
|
||
// Identify the owner of this memory stack's memory
|
||
void CMemoryStack::SetAllocOwner( const char *pszAllocOwner )
|
||
{
|
||
if ( !pszAllocOwner || !Q_strcmp( m_pszAllocOwner, pszAllocOwner ) )
|
||
return;
|
||
free( m_pszAllocOwner );
|
||
m_pszAllocOwner = strdup( pszAllocOwner );
|
||
}
|
||
|
||
void CMemoryStack::RegisterAllocation()
|
||
{
|
||
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here:
|
||
if ( IsPS3() && m_bPhysical )
|
||
return;
|
||
|
||
if ( GetSize() )
|
||
{
|
||
if ( m_bRegisteredAllocation )
|
||
Warning( eDLL_T::COMMON, "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" );
|
||
|
||
// NOTE: we deliberately don't use MemAlloc_RegisterExternalAllocation. CMemoryStack needs to bypass 'GetActualDbgInfo'
|
||
// due to the way it allocates memory: there's just one representative memory address (m_pBase), it grows at unpredictable
|
||
// times (in CommitTo, not every Alloc call) and it is freed en-masse (instead of freeing each individual allocation).
|
||
MemAlloc_RegisterAllocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 );
|
||
}
|
||
m_bRegisteredAllocation = true;
|
||
|
||
// Temp memorystack spew: very useful when we crash out of memory
|
||
if ( IsGameConsole() && bSpewAllocations ) DevMsg( eDLL_T::COMMON, "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner );
|
||
}
|
||
|
||
void CMemoryStack::RegisterDeallocation( bool bShouldSpewSize )
|
||
{
|
||
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here:
|
||
if ( IsPS3() && m_bPhysical )
|
||
return;
|
||
|
||
if ( GetSize() )
|
||
{
|
||
if ( !m_bRegisteredAllocation )
|
||
Warning( eDLL_T::COMMON, "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" );
|
||
MemAlloc_RegisterDeallocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 );
|
||
}
|
||
m_bRegisteredAllocation = false;
|
||
|
||
// Temp memorystack spew: very useful when we crash out of memory
|
||
if ( bShouldSpewSize && IsGameConsole() && bSpewAllocations ) DevMsg( eDLL_T::COMMON, "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner );
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
void CMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit )
|
||
{
|
||
mark = AlignValue( mark, m_alignment );
|
||
byte *pAllocPoint = m_pBase + mark;
|
||
|
||
Assert( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc );
|
||
if ( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc )
|
||
{
|
||
m_pNextAlloc = pAllocPoint;
|
||
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
|
||
if ( bDecommit && !m_bPhysical )
|
||
{
|
||
CommitTo( MAX( m_pNextAlloc, (m_pBase + m_minCommit) ) );
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
void CMemoryStack::FreeAll( bool bDecommit )
|
||
{
|
||
if ( m_pBase && ( m_pBase < m_pCommitLimit ) )
|
||
{
|
||
FreeToAllocPoint( 0, bDecommit );
|
||
}
|
||
}
|
||
|
||
//-------------------------------------
|
||
|
||
void CMemoryStack::Access( void **ppRegion, uint64 *pBytes )
|
||
{
|
||
*ppRegion = m_pBase;
|
||
*pBytes = ( m_pNextAlloc - m_pBase);
|
||
}
|
||
|
||
//const char* CMemoryStack::GetMemoryName() const
|
||
//{
|
||
// return m_pszAllocOwner;
|
||
//}
|
||
//
|
||
//size_t CMemoryStack::GetAllocatedBytes() const
|
||
//{
|
||
// return GetUsed();
|
||
//}
|
||
//
|
||
//size_t CMemoryStack::GetCommittedBytes() const
|
||
//{
|
||
// return GetSize();
|
||
//}
|
||
//
|
||
//size_t CMemoryStack::GetReservedBytes() const
|
||
//{
|
||
// return GetMaxSize();
|
||
//}
|
||
|
||
//size_t CMemoryStack::GetHighestBytes() const
|
||
//{
|
||
// size_t highest = m_pHighestAllocLimit - m_pBase;
|
||
// return highest;
|
||
//}
|
||
|
||
//-------------------------------------
|
||
|
||
void CMemoryStack::PrintContents() const
|
||
{
|
||
size_t highest = m_pHighestAllocLimit - m_pBase;
|
||
MEMORY_BASIC_INFORMATION info;
|
||
char moduleName[260];
|
||
strcpy( moduleName, "unknown module" );
|
||
// Because this code is statically linked into each DLL, this function and the PrintStatus
|
||
// function will be in the DLL that constructed the CMemoryStack object. We can then
|
||
// retrieve the DLL name to give slightly more verbose memory dumps.
|
||
if ( VirtualQuery( &PrintStatus, &info, sizeof( info ) ) == sizeof( info ) )
|
||
{
|
||
GetModuleFileNameA( (HMODULE) info.AllocationBase, moduleName, _countof( moduleName ) );
|
||
moduleName[ _countof( moduleName )-1 ] = 0;
|
||
}
|
||
DevMsg( eDLL_T::COMMON, "CMemoryStack %s in %s\n", m_pszAllocOwner, moduleName );
|
||
DevMsg( eDLL_T::COMMON, " Total used memory: %zu KB\n", GetUsed() / 1024 );
|
||
DevMsg( eDLL_T::COMMON, " Total committed memory: %zu KB\n", GetSize() / 1024 );
|
||
DevMsg( eDLL_T::COMMON, " Max committed memory: %zu KB out of %zu KB\n", highest / 1024, GetMaxSize() / 1024 );
|
||
}
|