sqwarmed/sdk_src/public/tier0/memalloc.h

576 lines
18 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: This header should never be used directly from leaf code!!!
// Instead, just add the file memoverride.cpp into your project and all this
// will automagically be used
//
// $NoKeywords: $
//=============================================================================//
#ifndef TIER0_MEMALLOC_H
#define TIER0_MEMALLOC_H
#ifdef _WIN32
#pragma once
#endif
// These memory debugging switches aren't relevant under Linux builds since memoverride.cpp
// isn't built into Linux projects
#ifndef POSIX
// Define this in release to get memory tracking even in release builds
//#define USE_MEM_DEBUG 1
// Define this in release to get light memory debugging
//#define USE_LIGHT_MEM_DEBUG
// Define this to require -uselmd to turn light memory debugging on
//#define LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH
#endif
#if defined( _MEMTEST )
#ifdef _WIN32
#define USE_MEM_DEBUG 1
#endif
#endif
// Undefine this if using a compiler lacking threadsafe RTTI (like vc6)
#define MEM_DEBUG_CLASSNAME 1
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
#include <stddef.h>
#ifdef LINUX
#undef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
#include "tier0/mem.h"
struct _CrtMemState;
#define MEMALLOC_VERSION 1
typedef size_t (*MemAllocFailHandler_t)( size_t );
//-----------------------------------------------------------------------------
// NOTE! This should never be called directly from leaf code
// Just use new,delete,malloc,free etc. They will call into this eventually
//-----------------------------------------------------------------------------
abstract_class IMemAlloc
{
private:
// Release versions
virtual void *Alloc( size_t nSize ) = 0;
public:
virtual void *Realloc( void *pMem, size_t nSize ) = 0;
virtual void Free( void *pMem ) = 0;
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) = 0;
private:
// Debug versions
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) = 0;
public:
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0;
virtual void Free( void *pMem, const char *pFileName, int nLine ) = 0;
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0;
inline void *IndirectAlloc( size_t nSize ) { return Alloc( nSize ); }
inline void *IndirectAlloc( size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize, pFileName, nLine ); }
// Returns size of a particular allocation
virtual size_t GetSize( void *pMem ) = 0;
// Force file + line information for an allocation
virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) = 0;
virtual void PopAllocDbgInfo() = 0;
// FIXME: Remove when we have our own allocator
// these methods of the Crt debug code is used in our codebase currently
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc ) = 0;
virtual int CrtSetReportMode( int nReportType, int nReportMode ) = 0;
virtual int CrtIsValidHeapPointer( const void *pMem ) = 0;
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) = 0;
virtual int CrtCheckMemory( void ) = 0;
virtual int CrtSetDbgFlag( int nNewFlag ) = 0;
virtual void CrtMemCheckpoint( _CrtMemState *pState ) = 0;
// FIXME: Make a better stats interface
virtual void DumpStats() = 0;
virtual void DumpStatsFileBase( char const *pchFileBase ) = 0;
virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) = 0;
// FIXME: Remove when we have our own allocator
virtual void* CrtSetReportFile( int nRptType, void* hFile ) = 0;
virtual void* CrtSetReportHook( void* pfnNewHook ) = 0;
virtual int CrtDbgReport( int nRptType, const char * szFile,
int nLine, const char * szModule, const char * pMsg ) = 0;
virtual int heapchk() = 0;
virtual bool IsDebugHeap() = 0;
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) = 0;
virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) = 0;
virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) = 0;
virtual int GetVersion() = 0;
virtual void CompactHeap() = 0;
// Function called when malloc fails or memory limits hit to attempt to free up memory (can come in any thread)
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) = 0;
virtual void DumpBlockStats( void * ) = 0;
#if defined( _MEMTEST )
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) = 0;
#endif
// Returns 0 if no failure, otherwise the size_t of the last requested chunk
virtual size_t MemoryAllocFailed() = 0;
virtual void CompactIncremental() = 0;
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) = 0;
// Region-based allocations
virtual void *RegionAlloc( int region, size_t nSize ) = 0;
virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) = 0;
// Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) = 0;
};
//-----------------------------------------------------------------------------
// Singleton interface
//-----------------------------------------------------------------------------
MEM_INTERFACE IMemAlloc *g_pMemAlloc;
//-----------------------------------------------------------------------------
#ifdef MEMALLOC_REGIONS
#ifndef MEMALLOC_REGION
#define MEMALLOC_REGION 0
#endif
inline void *MemAlloc_Alloc( size_t nSize )
{
return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize );
}
inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine )
{
return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize, pFileName, nLine );
}
#else
#undef MEMALLOC_REGION
inline void *MemAlloc_Alloc( size_t nSize )
{
return g_pMemAlloc->IndirectAlloc( nSize );
}
inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine )
{
return g_pMemAlloc->IndirectAlloc( nSize, pFileName, nLine );
}
#endif
//-----------------------------------------------------------------------------
#ifdef MEMALLOC_REGIONS
#else
#endif
inline bool ValueIsPowerOfTwo( size_t value ) // don't clash with mathlib definition
{
return (value & ( value - 1 )) == 0;
}
inline void *MemAlloc_AllocAlignedUnattributed( size_t size, size_t align )
{
unsigned char *pAlloc, *pResult;
if (!ValueIsPowerOfTwo(align))
return NULL;
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size ) ) == (unsigned char*)NULL)
return NULL;
pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align );
((unsigned char**)(pResult))[-1] = pAlloc;
return (void *)pResult;
}
inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile, int nLine )
{
unsigned char *pAlloc, *pResult;
if (!ValueIsPowerOfTwo(align))
return NULL;
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size, pszFile, nLine ) ) == (unsigned char*)NULL)
return NULL;
pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align );
((unsigned char**)(pResult))[-1] = pAlloc;
return (void *)pResult;
}
#ifdef USE_MEM_DEBUG
#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedFileLine( s, a, __FILE__, __LINE__ )
#elif defined(USE_LIGHT_MEM_DEBUG)
extern const char *g_pszModule;
#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedFileLine( s, a, g_pszModule, 0 )
#else
#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedUnattributed( s, a )
#endif
inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align )
{
if ( !ValueIsPowerOfTwo( align ) )
return NULL;
// Don't change alignment between allocation + reallocation.
if ( ( (size_t)ptr & ( align - 1 ) ) != 0 )
return NULL;
if ( !ptr )
return MemAlloc_AllocAligned( size, align );
void *pAlloc, *pResult;
// Figure out the actual allocation point
pAlloc = ptr;
pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *));
pAlloc = *( (void **)pAlloc );
// See if we have enough space
size_t nOffset = (size_t)ptr - (size_t)pAlloc;
size_t nOldSize = g_pMemAlloc->GetSize( pAlloc );
if ( nOldSize >= size + nOffset )
return ptr;
pResult = MemAlloc_AllocAligned( size, align );
memcpy( pResult, ptr, nOldSize - nOffset );
g_pMemAlloc->Free( pAlloc );
return pResult;
}
inline void MemAlloc_FreeAligned( void *pMemBlock )
{
void *pAlloc;
if ( pMemBlock == NULL )
return;
pAlloc = pMemBlock;
// pAlloc points to the pointer to starting of the memory block
pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *));
// pAlloc is the pointer to the start of memory block
pAlloc = *( (void **)pAlloc );
g_pMemAlloc->Free( pAlloc );
}
inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile, int nLine )
{
void *pAlloc;
if ( pMemBlock == NULL )
return;
pAlloc = pMemBlock;
// pAlloc points to the pointer to starting of the memory block
pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *));
// pAlloc is the pointer to the start of memory block
pAlloc = *( (void **)pAlloc );
g_pMemAlloc->Free( pAlloc, pszFile, nLine );
}
inline size_t MemAlloc_GetSizeAligned( void *pMemBlock )
{
void *pAlloc;
if ( pMemBlock == NULL )
return 0;
pAlloc = pMemBlock;
// pAlloc points to the pointer to starting of the memory block
pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *));
// pAlloc is the pointer to the start of memory block
pAlloc = *((void **)pAlloc );
return g_pMemAlloc->GetSize( pAlloc ) - ( (byte *)pMemBlock - (byte *)pAlloc );
}
struct aligned_tmp_t
{
// empty base class
};
// template here to allow adding alignment at levels of hierarchy that aren't the base
template< int bytesAlignment = 16, class T = aligned_tmp_t >
class CAlignedNewDelete : public T
{
public:
void *operator new( size_t nSize )
{
return MemAlloc_AllocAligned( nSize, bytesAlignment );
}
void* operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
{
return MemAlloc_AllocAlignedFileLine( nSize, bytesAlignment, pFileName, nLine );
}
void operator delete(void *pData)
{
if ( pData )
{
MemAlloc_FreeAligned( pData );
}
}
void operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
{
if ( pData )
{
MemAlloc_FreeAligned( pData, pFileName, nLine );
}
}
};
//-----------------------------------------------------------------------------
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
#define MEM_ALLOC_CREDIT_(tag) CMemAllocAttributeAlloction memAllocAttributeAlloction( tag, __LINE__ )
#define MemAlloc_PushAllocDbgInfo( pszFile, line ) g_pMemAlloc->PushAllocDbgInfo( pszFile, line )
#define MemAlloc_PopAllocDbgInfo() g_pMemAlloc->PopAllocDbgInfo()
#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime )
#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime )
#else
#define MEM_ALLOC_CREDIT_(tag) ((void)0)
#define MemAlloc_PushAllocDbgInfo( pszFile, line ) ((void)0)
#define MemAlloc_PopAllocDbgInfo() ((void)0)
#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0)
#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0)
#endif
//-----------------------------------------------------------------------------
class CMemAllocAttributeAlloction
{
public:
CMemAllocAttributeAlloction( const char *pszFile, int line )
{
MemAlloc_PushAllocDbgInfo( pszFile, line );
}
~CMemAllocAttributeAlloction()
{
MemAlloc_PopAllocDbgInfo();
}
};
#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__)
//-----------------------------------------------------------------------------
#if defined(MSVC) && ( defined(_DEBUG) || defined(USE_MEM_DEBUG) )
#pragma warning(disable:4290)
#pragma warning(push)
#include <typeinfo.h>
// MEM_DEBUG_CLASSNAME is opt-in.
// Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads
// simultaneously, it'll need a mutex.
#if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME)
template <typename T> const char *MemAllocClassName( T *p )
{
static const char *pszName = typeid(*p).name(); // @TODO: support having debug heap ignore certain allocations, and ignore memory allocated here [5/7/2009 tom]
return pszName;
}
#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( MemAllocClassName( this ) )
#define MEM_ALLOC_CLASSNAME(type) (typeid((type*)(0)).name())
#else
#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( __FILE__ )
#define MEM_ALLOC_CLASSNAME(type) (__FILE__)
#endif
// MEM_ALLOC_CREDIT_FUNCTION is used when no this pointer is available ( inside 'new' overloads, for example )
#ifdef _MSC_VER
#define MEM_ALLOC_CREDIT_FUNCTION() MEM_ALLOC_CREDIT_( __FUNCTION__ )
#else
#define MEM_ALLOC_CREDIT_FUNCTION() (__FILE__)
#endif
#pragma warning(pop)
#else
#define MEM_ALLOC_CREDIT_CLASS()
#define MEM_ALLOC_CLASSNAME(type) NULL
#define MEM_ALLOC_CREDIT_FUNCTION()
#endif
//-----------------------------------------------------------------------------
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
struct MemAllocFileLine_t
{
const char *pszFile;
int line;
};
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) \
static CUtlMap<void *, MemAllocFileLine_t, int> s_##tag##Allocs( DefLessFunc( void *) ); \
CUtlMap<void *, MemAllocFileLine_t, int> * g_p##tag##Allocs = &s_##tag##Allocs; \
const char * g_psz##tag##Alloc = strcpy( (char *)MemAlloc_Alloc( strlen( #tag "Alloc" ) + 1, "intentional leak", 0 ), #tag "Alloc" );
#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag ) \
extern CUtlMap<void *, MemAllocFileLine_t, int> * g_p##tag##Allocs; \
extern const char * g_psz##tag##Alloc;
#define MemAlloc_RegisterExternalAllocation( tag, p, size ) \
if ( !p ) \
; \
else \
{ \
MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \
g_pMemAlloc->GetActualDbgInfo( fileLine.pszFile, fileLine.line ); \
if ( fileLine.pszFile != g_psz##tag##Alloc ) \
{ \
g_p##tag##Allocs->Insert( p, fileLine ); \
} \
\
MemAlloc_RegisterAllocation( fileLine.pszFile, fileLine.line, size, size, 0); \
}
#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) \
if ( !p ) \
; \
else \
{ \
MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \
CUtlMap<void *, MemAllocFileLine_t, int>::IndexType_t iRecordedFileLine = g_p##tag##Allocs->Find( p ); \
if ( iRecordedFileLine != g_p##tag##Allocs->InvalidIndex() ) \
{ \
fileLine = (*g_p##tag##Allocs)[iRecordedFileLine]; \
g_p##tag##Allocs->RemoveAt( iRecordedFileLine ); \
} \
\
MemAlloc_RegisterDeallocation( fileLine.pszFile, fileLine.line, size, size, 0); \
}
#else
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag )
#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag )
#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0)
#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0)
#endif
//-----------------------------------------------------------------------------
#endif // !STEAM && !NO_MALLOC_OVERRIDE
//-----------------------------------------------------------------------------
#if !defined(STEAM) && defined(NO_MALLOC_OVERRIDE)
#include <malloc.h>
#define MEM_ALLOC_CREDIT_(tag) ((void)0)
#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__)
#define MEM_ALLOC_CREDIT_CLASS()
#define MEM_ALLOC_CLASSNAME(type) NULL
#define MemAlloc_PushAllocDbgInfo( pszFile, line )
#define MemAlloc_PopAllocDbgInfo()
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag )
#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0)
#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0)
inline void *MemAlloc_AllocAligned( size_t size, size_t align )
{
return (void *)_aligned_malloc( size, align );
}
inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFile, int nLine )
{
pszFile = pszFile;
nLine = nLine;
return (void *)_aligned_malloc( size, align );
}
inline void MemAlloc_FreeAligned( void *pMemBlock )
{
_aligned_free( pMemBlock );
}
inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile, int nLine )
{
pszFile = pszFile;
nLine = nLine;
_aligned_free( pMemBlock );
}
#endif // !STEAM && NO_MALLOC_OVERRIDE
//-----------------------------------------------------------------------------
// linux memory tracking via hooks.
#ifdef _POSIX
PLATFORM_INTERFACE void MemoryLogMessage( char const *s ); // throw a message into the memory log
PLATFORM_INTERFACE void EnableMemoryLogging( bool bOnOff );
PLATFORM_INTERFACE void DumpMemoryLog( int nThresh );
PLATFORM_INTERFACE void DumpMemorySummary( void );
PLATFORM_INTERFACE void SetMemoryMark( void );
PLATFORM_INTERFACE void DumpChangedMemory( int nThresh );
// ApproximateProcessMemoryUsage returns the approximate memory footprint of this process.
PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage( void );
#else
inline void MemoryLogMessage( char const * )
{
}
inline void EnableMemoryLogging( bool )
{
}
inline void DumpMemoryLog( int )
{
}
inline void DumpMemorySummary( void )
{
}
inline void SetMemoryMark( void )
{
}
inline void DumpChangedMemory( int )
{
}
inline size_t ApproximateProcessMemoryUsage( void )
{
return 0;
}
#endif
#endif /* TIER0_MEMALLOC_H */