You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
14 KiB
485 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Memory allocation! |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "pch_tier0.h" |
|
|
|
#ifndef STEAM |
|
|
|
#ifdef TIER0_VALIDATE_HEAP |
|
|
|
#include <malloc.h> |
|
#include "tier0/dbg.h" |
|
#include "tier0/memalloc.h" |
|
#include "mem_helpers.h" |
|
|
|
extern IMemAlloc *g_pActualAlloc; |
|
|
|
//----------------------------------------------------------------------------- |
|
// NOTE! This should never be called directly from leaf code |
|
// Just use new,delete,malloc,free etc. They will call into this eventually |
|
//----------------------------------------------------------------------------- |
|
class CValidateAlloc : public IMemAlloc |
|
{ |
|
public: |
|
enum |
|
{ |
|
HEAP_PREFIX_BUFFER_SIZE = 12, |
|
HEAP_SUFFIX_BUFFER_SIZE = 8, |
|
}; |
|
|
|
CValidateAlloc(); |
|
|
|
// Release versions |
|
virtual void *Alloc( size_t nSize ); |
|
virtual void *Realloc( void *pMem, size_t nSize ); |
|
virtual void Free( void *pMem ); |
|
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ); |
|
|
|
// Debug versions |
|
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ); |
|
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ); |
|
virtual void Free( void *pMem, const char *pFileName, int nLine ); |
|
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ); |
|
|
|
// Returns size of a particular allocation |
|
virtual size_t GetSize( void *pMem ); |
|
|
|
// Force file + line information for an allocation |
|
virtual void PushAllocDbgInfo( const char *pFileName, int nLine ); |
|
virtual void PopAllocDbgInfo(); |
|
|
|
virtual long CrtSetBreakAlloc( long lNewBreakAlloc ); |
|
virtual int CrtSetReportMode( int nReportType, int nReportMode ); |
|
virtual int CrtIsValidHeapPointer( const void *pMem ); |
|
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ); |
|
virtual int CrtCheckMemory( void ); |
|
virtual int CrtSetDbgFlag( int nNewFlag ); |
|
virtual void CrtMemCheckpoint( _CrtMemState *pState ); |
|
void* CrtSetReportFile( int nRptType, void* hFile ); |
|
void* CrtSetReportHook( void* pfnNewHook ); |
|
int CrtDbgReport( int nRptType, const char * szFile, |
|
int nLine, const char * szModule, const char * pMsg ); |
|
virtual int heapchk(); |
|
|
|
virtual void DumpStats() {} |
|
virtual void DumpStatsFileBase( char const *pchFileBase ) {} |
|
|
|
virtual bool IsDebugHeap() |
|
{ |
|
return true; |
|
} |
|
|
|
virtual int GetVersion() { return MEMALLOC_VERSION; } |
|
|
|
virtual void CompactHeap(); |
|
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ); |
|
|
|
virtual uint32 GetDebugInfoSize() { return 0; } |
|
virtual void SaveDebugInfo( void *pvDebugInfo ) { } |
|
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {} |
|
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {} |
|
|
|
private: |
|
struct HeapPrefix_t |
|
{ |
|
HeapPrefix_t *m_pPrev; |
|
HeapPrefix_t *m_pNext; |
|
int m_nSize; |
|
unsigned char m_Prefix[HEAP_PREFIX_BUFFER_SIZE]; |
|
}; |
|
|
|
struct HeapSuffix_t |
|
{ |
|
unsigned char m_Suffix[HEAP_SUFFIX_BUFFER_SIZE]; |
|
}; |
|
|
|
private: |
|
// Returns the actual debug info |
|
void GetActualDbgInfo( const char *&pFileName, int &nLine ); |
|
|
|
// Updates stats |
|
void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ); |
|
void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ); |
|
|
|
HeapSuffix_t *Suffix( HeapPrefix_t *pPrefix ); |
|
void *AllocationStart( HeapPrefix_t *pBase ); |
|
HeapPrefix_t *PrefixFromAllocation( void *pAlloc ); |
|
const HeapPrefix_t *PrefixFromAllocation( const void *pAlloc ); |
|
|
|
// Add to the list! |
|
void AddToList( HeapPrefix_t *pHeap, int nSize ); |
|
|
|
// Remove from the list! |
|
void RemoveFromList( HeapPrefix_t *pHeap ); |
|
|
|
// Validate the allocation |
|
bool ValidateAllocation( HeapPrefix_t *pHeap ); |
|
|
|
private: |
|
HeapPrefix_t *m_pFirstAllocation; |
|
char m_pPrefixImage[HEAP_PREFIX_BUFFER_SIZE]; |
|
char m_pSuffixImage[HEAP_SUFFIX_BUFFER_SIZE]; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton... |
|
//----------------------------------------------------------------------------- |
|
static CValidateAlloc s_ValidateAlloc; |
|
IMemAlloc *g_pMemAlloc = &s_ValidateAlloc; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor. |
|
//----------------------------------------------------------------------------- |
|
CValidateAlloc::CValidateAlloc() |
|
{ |
|
m_pFirstAllocation = 0; |
|
memset( m_pPrefixImage, 0xBE, HEAP_PREFIX_BUFFER_SIZE ); |
|
memset( m_pSuffixImage, 0xAF, HEAP_SUFFIX_BUFFER_SIZE ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Accessors... |
|
//----------------------------------------------------------------------------- |
|
inline CValidateAlloc::HeapSuffix_t *CValidateAlloc::Suffix( HeapPrefix_t *pPrefix ) |
|
{ |
|
return reinterpret_cast<HeapSuffix_t *>( (unsigned char*)( pPrefix + 1 ) + pPrefix->m_nSize ); |
|
} |
|
|
|
inline void *CValidateAlloc::AllocationStart( HeapPrefix_t *pBase ) |
|
{ |
|
return static_cast<void *>( pBase + 1 ); |
|
} |
|
|
|
inline CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( void *pAlloc ) |
|
{ |
|
if ( !pAlloc ) |
|
return NULL; |
|
|
|
return ((HeapPrefix_t*)pAlloc) - 1; |
|
} |
|
|
|
inline const CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( const void *pAlloc ) |
|
{ |
|
return ((const HeapPrefix_t*)pAlloc) - 1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Add to the list! |
|
//----------------------------------------------------------------------------- |
|
void CValidateAlloc::AddToList( HeapPrefix_t *pHeap, int nSize ) |
|
{ |
|
pHeap->m_pPrev = NULL; |
|
pHeap->m_pNext = m_pFirstAllocation; |
|
if ( m_pFirstAllocation ) |
|
{ |
|
m_pFirstAllocation->m_pPrev = pHeap; |
|
} |
|
pHeap->m_nSize = nSize; |
|
|
|
m_pFirstAllocation = pHeap; |
|
|
|
HeapSuffix_t *pSuffix = Suffix( pHeap ); |
|
memcpy( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE ); |
|
memcpy( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Remove from the list! |
|
//----------------------------------------------------------------------------- |
|
void CValidateAlloc::RemoveFromList( HeapPrefix_t *pHeap ) |
|
{ |
|
if ( !pHeap ) |
|
return; |
|
|
|
ValidateAllocation( pHeap ); |
|
if ( pHeap->m_pPrev ) |
|
{ |
|
pHeap->m_pPrev->m_pNext = pHeap->m_pNext; |
|
} |
|
else |
|
{ |
|
m_pFirstAllocation = pHeap->m_pNext; |
|
} |
|
|
|
if ( pHeap->m_pNext ) |
|
{ |
|
pHeap->m_pNext->m_pPrev = pHeap->m_pPrev; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Validate the allocation |
|
//----------------------------------------------------------------------------- |
|
bool CValidateAlloc::ValidateAllocation( HeapPrefix_t *pHeap ) |
|
{ |
|
HeapSuffix_t *pSuffix = Suffix( pHeap ); |
|
|
|
bool bOk = true; |
|
if ( memcmp( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE ) ) |
|
{ |
|
bOk = false; |
|
} |
|
|
|
if ( memcmp( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE ) ) |
|
{ |
|
bOk = false; |
|
} |
|
|
|
if ( !bOk ) |
|
{ |
|
Warning("Memory trash detected in allocation %X!\n", (void*)(pHeap+1) ); |
|
Assert( 0 ); |
|
} |
|
|
|
return bOk; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Release versions |
|
//----------------------------------------------------------------------------- |
|
void *CValidateAlloc::Alloc( size_t nSize ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize ); |
|
AddToList( pHeap, nSize ); |
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
void *CValidateAlloc::Realloc( void *pMem, size_t nSize ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize ); |
|
AddToList( pHeap, nSize ); |
|
|
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
void CValidateAlloc::Free( void *pMem ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
g_pActualAlloc->Free( pHeap ); |
|
} |
|
|
|
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize ); |
|
AddToList( pHeap, nSize ); |
|
|
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Debug versions |
|
//----------------------------------------------------------------------------- |
|
void *CValidateAlloc::Alloc( size_t nSize, const char *pFileName, int nLine ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize, pFileName, nLine ); |
|
AddToList( pHeap, nSize ); |
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
void *CValidateAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize, pFileName, nLine ); |
|
AddToList( pHeap, nSize ); |
|
|
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
void CValidateAlloc::Free( void *pMem, const char *pFileName, int nLine ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
g_pActualAlloc->Free( pHeap, pFileName, nLine ); |
|
} |
|
|
|
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) |
|
{ |
|
Assert( heapchk() == _HEAPOK ); |
|
Assert( CrtCheckMemory() ); |
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
RemoveFromList( pHeap ); |
|
|
|
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t); |
|
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize, pFileName, nLine ); |
|
AddToList( pHeap, nSize ); |
|
|
|
return AllocationStart( pHeap ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns size of a particular allocation |
|
//----------------------------------------------------------------------------- |
|
size_t CValidateAlloc::GetSize( void *pMem ) |
|
{ |
|
if ( !pMem ) |
|
return CalcHeapUsed(); |
|
|
|
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
return pHeap->m_nSize; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Force file + line information for an allocation |
|
//----------------------------------------------------------------------------- |
|
void CValidateAlloc::PushAllocDbgInfo( const char *pFileName, int nLine ) |
|
{ |
|
g_pActualAlloc->PushAllocDbgInfo( pFileName, nLine ); |
|
} |
|
|
|
void CValidateAlloc::PopAllocDbgInfo() |
|
{ |
|
g_pActualAlloc->PopAllocDbgInfo( ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// FIXME: Remove when we make our own heap! Crt stuff we're currently using |
|
//----------------------------------------------------------------------------- |
|
long CValidateAlloc::CrtSetBreakAlloc( long lNewBreakAlloc ) |
|
{ |
|
return g_pActualAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); |
|
} |
|
|
|
int CValidateAlloc::CrtSetReportMode( int nReportType, int nReportMode ) |
|
{ |
|
return g_pActualAlloc->CrtSetReportMode( nReportType, nReportMode ); |
|
} |
|
|
|
int CValidateAlloc::CrtIsValidHeapPointer( const void *pMem ) |
|
{ |
|
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
return g_pActualAlloc->CrtIsValidHeapPointer( pHeap ); |
|
} |
|
|
|
int CValidateAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access ) |
|
{ |
|
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem ); |
|
return g_pActualAlloc->CrtIsValidPointer( pHeap, size, access ); |
|
} |
|
|
|
int CValidateAlloc::CrtCheckMemory( void ) |
|
{ |
|
return g_pActualAlloc->CrtCheckMemory( ); |
|
} |
|
|
|
int CValidateAlloc::CrtSetDbgFlag( int nNewFlag ) |
|
{ |
|
return g_pActualAlloc->CrtSetDbgFlag( nNewFlag ); |
|
} |
|
|
|
void CValidateAlloc::CrtMemCheckpoint( _CrtMemState *pState ) |
|
{ |
|
g_pActualAlloc->CrtMemCheckpoint( pState ); |
|
} |
|
|
|
void* CValidateAlloc::CrtSetReportFile( int nRptType, void* hFile ) |
|
{ |
|
return g_pActualAlloc->CrtSetReportFile( nRptType, hFile ); |
|
} |
|
|
|
void* CValidateAlloc::CrtSetReportHook( void* pfnNewHook ) |
|
{ |
|
return g_pActualAlloc->CrtSetReportHook( pfnNewHook ); |
|
} |
|
|
|
int CValidateAlloc::CrtDbgReport( int nRptType, const char * szFile, |
|
int nLine, const char * szModule, const char * pMsg ) |
|
{ |
|
return g_pActualAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg ); |
|
} |
|
|
|
int CValidateAlloc::heapchk() |
|
{ |
|
bool bOk = true; |
|
|
|
// Validate the heap |
|
HeapPrefix_t *pHeap = m_pFirstAllocation; |
|
for( pHeap = m_pFirstAllocation; pHeap; pHeap = pHeap->m_pNext ) |
|
{ |
|
if ( !ValidateAllocation( pHeap ) ) |
|
{ |
|
bOk = false; |
|
} |
|
} |
|
|
|
#ifdef _WIN32 |
|
return bOk ? _HEAPOK : 0; |
|
#elif POSIX |
|
return bOk; |
|
#else |
|
#error |
|
#endif |
|
} |
|
|
|
// Returns the actual debug info |
|
void CValidateAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine ) |
|
{ |
|
g_pActualAlloc->GetActualDbgInfo( pFileName, nLine ); |
|
} |
|
|
|
// Updates stats |
|
void CValidateAlloc::RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) |
|
{ |
|
g_pActualAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ); |
|
} |
|
|
|
void CValidateAlloc::RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) |
|
{ |
|
g_pActualAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ); |
|
} |
|
|
|
void CValidateAlloc::CompactHeap() |
|
{ |
|
g_pActualAlloc->CompactHeap(); |
|
} |
|
|
|
MemAllocFailHandler_t CValidateAlloc::SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) |
|
{ |
|
return g_pActualAlloc->SetAllocFailHandler( pfnMemAllocFailHandler ); |
|
} |
|
|
|
#endif // TIER0_VALIDATE_HEAP |
|
|
|
#endif // STEAM
|
|
|