#ifndef TIER0_MEMALLOC_H #define TIER0_MEMALLOC_H #ifdef _WIN32 #pragma once #endif #ifndef POSIX #endif #if defined( _MEMTEST ) #ifdef _WIN32 #define USE_MEM_DEBUG 1 #endif #endif #define MEM_DEBUG_CLASSNAME 1 #include #if defined( OSX ) #include #endif #include "mem.h" #if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) struct _CrtMemState; #define MEMALLOC_VERSION 1 typedef size_t(*MemAllocFailHandler_t)(size_t); class IMemAlloc { public: virtual void* Alloc(size_t nSize) = 0; 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; virtual void* Alloc(size_t nSize, const char* pFileName, int nLine) = 0; 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; virtual size_t GetSize(void* pMem) = 0; virtual void PushAllocDbgInfo(const char* pFileName, int nLine) = 0; virtual void PopAllocDbgInfo() = 0; virtual long CrtSetBreakAlloc(long 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; virtual void DumpStats() = 0; virtual void DumpStatsFileBase(char const* pchFileBase) = 0; 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, int nLogicalSize, int nActualSize, unsigned nTime) = 0; virtual void RegisterDeallocation(const char* pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime) = 0; virtual int GetVersion() = 0; virtual void CompactHeap() = 0; 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 virtual size_t MemoryAllocFailed() = 0; virtual uint32 GetDebugInfoSize() = 0; virtual void SaveDebugInfo(void* pvDebugInfo) = 0; virtual void RestoreDebugInfo(const void* pvDebugInfo) = 0; virtual void InitDebugInfo(void* pvDebugInfo, const char* pchRootFileName, int nLine) = 0; virtual void GlobalMemoryStatus(size_t* pUsedMemory, size_t* pFreeMemory) = 0; }; 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->Alloc(nSize); } inline void* MemAlloc_Alloc(size_t nSize, const char* pFileName, int nLine) { return g_pMemAlloc->Alloc(nSize, pFileName, nLine); } #endif inline void MemAlloc_Free(void* ptr) { g_pMemAlloc->Free(ptr); } inline void MemAlloc_Free(void* ptr, const char* pFileName, int nLine) { g_pMemAlloc->Free(ptr, pFileName, nLine); } inline bool ValueIsPowerOfTwo(size_t value) { return (value & (value - 1)) == 0; } inline void* MemAlloc_AllocAligned(size_t size, size_t align) { unsigned char* pAlloc, * pResult; if (!IsPowerOfTwo(align)) return NULL; align = (align > sizeof(void*) ? align : sizeof(void*)) - 1; if ((pAlloc = (unsigned char*)g_pMemAlloc->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_AllocAligned(size_t size, size_t align, const char* pszFile, int nLine) { unsigned char* pAlloc, * pResult; if (!IsPowerOfTwo(align)) return NULL; align = (align > sizeof(void*) ? align : sizeof(void*)) - 1; if ((pAlloc = (unsigned char*)g_pMemAlloc->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; } 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; } inline void* MemAlloc_ReallocAligned(void* ptr, size_t size, size_t align) { if (!IsPowerOfTwo(align)) return NULL; if (((size_t)ptr & (align - 1)) != 0) return NULL; if (!ptr) return MemAlloc_AllocAligned(size, align); void* pAlloc, * pResult; pAlloc = ptr; pAlloc = (void*)(((size_t)pAlloc & ~(sizeof(void*) - 1)) - sizeof(void*)); pAlloc = *((void**)pAlloc); 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 = (void*)(((size_t)pAlloc & ~(sizeof(void*) - 1)) - sizeof(void*)); pAlloc = *((void**)pAlloc); g_pMemAlloc->Free(pAlloc); } inline void MemAlloc_FreeAligned(void* pMemBlock, const char* pFileName, int nLine) { void* pAlloc; if (pMemBlock == NULL) return; pAlloc = pMemBlock; pAlloc = (void*)(((size_t)pAlloc & ~(sizeof(void*) - 1)) - sizeof(void*)); pAlloc = *((void**)pAlloc); g_pMemAlloc->Free(pAlloc, pFileName, nLine); } inline size_t MemAlloc_GetSizeAligned(void* pMemBlock) { void* pAlloc; if (pMemBlock == NULL) return 0; pAlloc = pMemBlock; pAlloc = (void*)(((size_t)pAlloc & ~(sizeof(void*) - 1)) - sizeof(void*)); pAlloc = *((void**)pAlloc); return g_pMemAlloc->GetSize(pAlloc) - ((byte*)pMemBlock - (byte*)pAlloc); } #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 #define MemAlloc_DumpStats() g_pMemAlloc->DumpStats() #define MemAlloc_CompactHeap() g_pMemAlloc->CompactHeap() #define MemAlloc_OutOfMemory() g_pMemAlloc->OutOfMemory() #define MemAlloc_CompactIncremental() g_pMemAlloc->CompactIncremental() #define MemAlloc_DumpStatsFileBase( _filename ) g_pMemAlloc->DumpStatsFileBase( _filename ) #define MemAlloc_CrtCheckMemory() g_pMemAlloc->CrtCheckMemory() #define MemAlloc_GlobalMemoryStatus( _usedMemory, _freeMemory ) g_pMemAlloc->GlobalMemoryStatus( _usedMemory, _freeMemory ) #define MemAlloc_MemoryAllocFailed() g_pMemAlloc->MemoryAllocFailed() #define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() #define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) #define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) #define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) #define MemAlloc_GetSize( x ) g_pMemAlloc->GetSize( x ); 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(_WIN32) && ( defined(_DEBUG) || defined(USE_MEM_DEBUG) ) #pragma warning(disable:4290) #pragma warning(push) #include #if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME) #define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( typeid(*this).name() ) #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 #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 g_##tag##Allocs( DefLessFunc( void *) ); \ static const char *g_psz##tag##Alloc = strcpy( (char *)g_pMemAlloc->Alloc( strlen( #tag "Alloc" ) + 1, "intentional leak", 0 ), #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_##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::IndexType_t iRecordedFileLine = g_##tag##Allocs.Find( p ); \ if ( iRecordedFileLine != g_##tag##Allocs.InvalidIndex() ) \ { \ fileLine = g_##tag##Allocs[iRecordedFileLine]; \ g_##tag##Allocs.RemoveAt( iRecordedFileLine ); \ } \ \ MemAlloc_RegisterDeallocation( fileLine.pszFile, fileLine.line, size, size, 0); \ } #else #define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) #define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) #define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) #endif #elif defined( POSIX ) #if defined( OSX ) inline void* memalign(size_t alignment, size_t size) { void* pTmp = NULL; pTmp = malloc(size); return pTmp; } #endif inline void* _aligned_malloc(size_t nSize, size_t align) { return memalign(align, nSize); } inline void _aligned_free(void* ptr) { free(ptr); } inline void* MemAlloc_Alloc(size_t nSize, const char* pFileName = NULL, int nLine = 0) { return malloc(nSize); } inline void MemAlloc_Free(void* ptr, const char* pFileName = NULL, int nLine = 0) { free(ptr); } inline void* MemAlloc_AllocAligned(size_t size, size_t align, const char* pszFile = NULL, int nLine = 0) { return memalign(align, size); } inline void* MemAlloc_AllocAlignedFileLine(size_t size, size_t align, const char* pszFile = NULL, int nLine = 0) { return memalign(align, size); } inline void MemAlloc_FreeAligned(void* pMemBlock, const char* pszFile = NULL, int nLine = 0) { free(pMemBlock); } #if defined( OSX ) inline size_t _msize(void* ptr) { return malloc_size(ptr); } #else inline size_t _msize(void* ptr) { return malloc_usable_size(ptr); } #endif inline void* MemAlloc_ReallocAligned(void* ptr, size_t size, size_t align) { void* ptr_new_aligned = memalign(align, size); if (ptr_new_aligned) { size_t old_size = _msize(ptr); size_t copy_size = (size < old_size) ? size : old_size; memcpy(ptr_new_aligned, ptr, copy_size); free(ptr); } return ptr_new_aligned; } #else #define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() #define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) #define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) #define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) #endif #if !defined(STEAM) && defined(NO_MALLOC_OVERRIDE) #define MEM_ALLOC_CREDIT_(tag) ((void)0) #define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) #define MEM_ALLOC_CREDIT_FUNCTION() #define MEM_ALLOC_CREDIT_CLASS() #define MEM_ALLOC_CLASSNAME(type) NULL #define MemAlloc_PushAllocDbgInfo( pszFile, line ) #define MemAlloc_PopAllocDbgInfo() #define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) #define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) #define MemAlloc_DumpStats() ((void)0) #define MemAlloc_CompactHeap() ((void)0) #define MemAlloc_OutOfMemory() ((void)0) #define MemAlloc_CompactIncremental() ((void)0) #define MemAlloc_DumpStatsFileBase( _filename ) ((void)0) inline bool MemAlloc_CrtCheckMemory() { return true; } inline void MemAlloc_GlobalMemoryStatus(size_t* pusedMemory, size_t* pfreeMemory) { *pusedMemory = 0; *pfreeMemory = 0; } #define MemAlloc_MemoryAllocFailed() 0 #define MemAlloc_GetDebugInfoSize() 0 #define MemAlloc_SaveDebugInfo( pvDebugInfo ) ((void)0) #define MemAlloc_RestoreDebugInfo( pvDebugInfo ) ((void)0) #define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) ((void)0) #define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) #define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) #define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) #endif #if defined( POSIX ) && !defined( NO_HOOK_MALLOC ) PLATFORM_INTERFACE void MemoryLogMessage(char const* s); 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); #else FORCEINLINE void MemoryLogMessage(char const* s) { } FORCEINLINE void EnableMemoryLogging(bool bOnOff) { } FORCEINLINE void DumpMemoryLog(int nThresh) { } FORCEINLINE void DumpMemorySummary(void) { } FORCEINLINE void SetMemoryMark(void) { } FORCEINLINE void DumpChangedMemory(int nThresh) { } #endif #ifdef POSIX PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage(void); #else FORCEINLINE size_t ApproximateProcessMemoryUsage(void) { return 0; } #endif struct aligned_tmp_t { }; 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); } } }; #endif