From a0e5701b2695111cf3a324d2c511c8c19a4a2d0c Mon Sep 17 00:00:00 2001 From: GAMMACASE <31375974+GAMMACASE@users.noreply.github.com> Date: Thu, 18 Jan 2024 02:58:55 +0300 Subject: [PATCH] Update IMemAlloc --- public/datacache/imdlcache.h | 2 +- public/tier0/commonmacros.h | 2 +- public/tier0/memalloc.h | 307 +++++++++++++++++------------------ public/tier0/memoverride.cpp | 50 +++--- 4 files changed, 182 insertions(+), 179 deletions(-) diff --git a/public/datacache/imdlcache.h b/public/datacache/imdlcache.h index 8ab212cd..f2cb8ec2 100644 --- a/public/datacache/imdlcache.h +++ b/public/datacache/imdlcache.h @@ -43,7 +43,7 @@ typedef unsigned short MDLHandle_t; inline MDLHandle_t VoidPtrToMDLHandle( void *ptr ) { - return (MDLHandle_t)(int)ptr; + return (MDLHandle_t)(int64)ptr; } enum diff --git a/public/tier0/commonmacros.h b/public/tier0/commonmacros.h index ba2416bf..007220a1 100644 --- a/public/tier0/commonmacros.h +++ b/public/tier0/commonmacros.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h index d485b159..8b757ad3 100644 --- a/public/tier0/memalloc.h +++ b/public/tier0/memalloc.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= 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 @@ -54,6 +54,46 @@ #if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) +#define MEMALLOC_REGION_ALLOC_1 '4' +#define MEMALLOC_REGION_ALLOC_2 '6' +#define MEMALLOC_REGION_ALLOC_3 '8' +#define MEMALLOC_REGION_ALLOC_4 ':' +#define MEMALLOC_REGION_ALLOC_5 '<' +#define MEMALLOC_REGION_ALLOC_6 '>' + +#define MEMALLOC_REGION_FREE_1 '5' +#define MEMALLOC_REGION_FREE_2 '7' +#define MEMALLOC_REGION_FREE_3 '9' +#define MEMALLOC_REGION_FREE_4 ';' +#define MEMALLOC_REGION_FREE_5 '=' +#define MEMALLOC_REGION_FREE_6 '?' + +enum MemoryState +{ + MemoryState_UnexpectedlyAllocated = 0, + MemoryState_UnexpectedlyFreed = 1, + MemoryState_UnexpectedlyUnrecognized = 2, + MemoryState_Corrupt = 3, + MemoryState_Invalid = 4, + MemoryState_Operational = 5, + MemoryState_Unknown = 6 +}; + +enum MemoryDebugType : int16 +{ + MemoryDebugType_None = 0, + MemoryDebugType_Light = 1, + MemoryDebugType_Full = 2 +}; + +struct MemoryInfoState +{ + MemoryInfoState( MemoryDebugType type, bool debug = false ) : m_MemType( type ), m_bDebug( debug ) { } + + MemoryDebugType m_MemType; + bool m_bDebug; +}; + struct _CrtMemState; #define MEMALLOC_VERSION 1 @@ -68,6 +108,9 @@ PLATFORM_INTERFACE void CMemAllocSystemInitialize(); //----------------------------------------------------------------------------- abstract_class IMemAlloc { + // AMNOTE: A lot of functions in here might be stubbed out and not do what their description tells + // this heavily depends on the allocator implementation and should be taken into account when using this directly! + private: virtual ~IMemAlloc() = 0; @@ -75,90 +118,134 @@ private: 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; - // ================================================================= - // GAMMACASE: Interface structure beyond this point is incorrect and any usage of the functions below should be discouraged!!! - // ================================================================= - - // GAMMACASE: Seems like the debug versions are gone now, or atleast they aren't here anymore - // leaving them all here for future reference, but these shouldn't be used anymore! - // renamed with a postfix "2" otherwise they would be placed near the first functions in the virtual list private: - // Debug versions - virtual void *Alloc2( size_t nSize, const char *pFileName, int nLine ) = 0; + virtual void *AllocAligned( size_t nSize, size_t align ) = 0; public: - virtual void *Realloc2( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; - virtual void Free2( void *pMem, const char *pFileName, int nLine ) = 0; - // virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; + virtual void *ReallocAligned( void *pMem, size_t nSize, size_t align ) = 0; + virtual void FreeAligned( void *pMem ) = 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 ); } + inline void *IndirectAlloc( size_t nSize ) { return Alloc( nSize ); } + inline void *IndirectAllocAligned( size_t nSize, size_t align ) { return AllocAligned( nSize, align ); } + + // AMNOTE: It's unclear if the functions below are actually a debug variants, as in binaries they are + // absolutely the same to the above functions, but with a different name as they aren't merged together + // in the vtable in the win binaries. So it's mostly a guess, same goes to their params. + // =============================================================== +private: + virtual void *AllocDbg( size_t nSize ) = 0; +public: + virtual void *ReallocDbg( void *pMem, size_t nSize ) = 0; + virtual void FreeDbg( void *pMem ) = 0; + +private: + virtual void *AllocAlignedDbg( size_t nSize, size_t align ) = 0; +public: + virtual void *ReallocAlignedDbg( void *pMem, size_t nSize, size_t align ) = 0; + virtual void FreeAlignedDbg( void *pMem ) = 0; + // =============================================================== + + // Region-based allocations + // Use MEMALLOC_REGION_* defines for the region arg + // AMNOTE: Region name is mostly a guess! + virtual void *RegionAlloc( uint8 region, size_t nSize ) = 0; + virtual void RegionFree( uint8 region, void *pMem ) = 0; + + virtual void *RegionAllocAligned( uint8 region, size_t nSize, size_t align ) = 0; + virtual void RegionFreeAligned( uint8 region, void *pMem ) = 0; // Returns size of a particular allocation virtual size_t GetSize( void *pMem ) = 0; + virtual size_t GetSizeAligned( void *pMem ) = 0; - // Force file + line information for an allocation - virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) = 0; - virtual void PopAllocDbgInfo() = 0; + // If out arg is NULL or "" it would output all the info to console, + // otherwise a file with that name would be created at the game root folder + virtual void DumpStats( const char *out_path ) = 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; + // AMNOTE: Stub, returns -1 + virtual int unk001() = 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; + // AMNOTE: Stub + virtual void unk002() = 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; + // AMNOTE: Stub, returns false and writes -1 to the ret_out + virtual bool unk003( int *ret_out ) = 0; - virtual int heapchk() = 0; + // AMNOTE: Stub, returns false + virtual bool unk004() = 0; - virtual bool IsDebugHeap() = 0; + // AMNOTE: Stub + virtual void unk005() = 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 void CompactOnFail() = 0; - virtual int GetVersion() = 0; + // Logs the out of memory message, breaks in the debugger and exits the process + virtual void ReportFailedAllocation( int nSize ) = 0; - virtual void CompactHeap() = 0; + // memset's at the pMem location with nSize bytes of specified type, where: + // MemoryDebugType_None - would do nothing; + // MemoryDebugType_Light - would memset with 0xDD bytes; + // MemoryDebugType_Full - would memset with 0xD8 bytes; + // the input pMem is returned + // + // NOTE: This would do nothing if the allocator is not in the debug memory mode or if + // state has the m_bDebug set to false + virtual void *MemSetDbg( void *pMem, size_t nSize, MemoryInfoState state ) = 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; + // Returns true if the underlying allocator is using DebugMemoryType different to DebugMemoryType::DebugMemoryType_None + // would also return true if the state argument has m_bDebug set as true + virtual bool IsInDebugMode( MemoryInfoState state ) = 0; - virtual void DumpBlockStats( void * ) = 0; + // If memory is not in the debug mode, returns either MemoryState_Invalid or MemoryState_Operational + // Otherwise a more deep check would be performed + virtual MemoryState GetMemoryState( void *pMem ) = 0; -#if defined( _MEMTEST ) - virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) = 0; -#endif + // Logs a warning and breaks in a debugger if the pMem state doesn't match provided state + virtual void ReportBadMemory( void *pMem, MemoryState state = MemoryState_Operational ) = 0; - // Returns 0 if no failure, otherwise the size_t of the last requested chunk - virtual size_t MemoryAllocFailed() = 0; + // Returns memory debug type of this allocator + virtual MemoryDebugType GetMemoryDebugType() = 0; - virtual void CompactIncremental() = 0; + // Returns previous total allocation size that was used + // Directly limits how much bytes could be allocated with (Re)Alloc* functions + virtual size_t SetTotalAllocationSize( size_t new_total_alloc_size ) = 0; - virtual void OutOfMemory( size_t nBytesAttempted = 0 ) = 0; + // Returns previous allocation limit size that was used + // Directly limits how much bytes could be allocated with (Re)Alloc* functions + virtual size_t SetAllocationLimitSize( size_t new_alloc_limit_size ) = 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; + // Writes detailed info about this allocator and settings used + // Example output of a non debug allocator: + // Heap: standard allocator pass-through to low-level + // Low - level allocator : jemalloc + // + // Example output of a debug allocator with custom settings: + // Heap: standard allocator + mem init + stackstats + light verifier + full mem debug + // Low - level allocator : jemalloc + virtual void GetAllocatorDescription( CBufferString &buf ) = 0; - // Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system - virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) = 0; + // Returns true if stackstats is enabled and -memstackstats_disable_pools launch option was used + virtual bool IsStackStatsPoolsDisabled() = 0; + + // Returns true if stackstats is enabled for this allocator + virtual bool IsStackStatsEnabled() = 0; + + // AMNOTE: Stub, returns 0 + virtual int unk101() = 0; + + // AMNOTE: Stub + virtual void unk102() = 0; + + // AMNOTE: Copies data to an unknown struct of byte size 56 + // Returns true if data was written, false otherwise + virtual bool unk103( void *out_val ) = 0; + + // Calls the lower-level allocator functions directly + virtual void *AllocRaw( size_t nSize ) = 0; + virtual void *ReallocRaw( void *pMem, size_t nSize ) = 0; + virtual void FreeRaw( void *pMem ) = 0; + virtual size_t GetSizeRaw( void *pMem ) = 0; }; //----------------------------------------------------------------------------- @@ -217,38 +304,12 @@ inline bool ValueIsPowerOfTwo( size_t value ) // don't clash with mathlib defi 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; + return g_pMemAlloc->IndirectAllocAligned( size, align ); } 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; + return g_pMemAlloc->IndirectAllocAligned( size, align /*, pszFile, nLine*/ ); } #ifdef USE_MEM_DEBUG @@ -262,84 +323,22 @@ extern const char *g_pszModule; 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; + return g_pMemAlloc->ReallocAligned( ptr, size, align ); } 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 ); + g_pMemAlloc->FreeAligned( pMemBlock ); } 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*/ ); + g_pMemAlloc->FreeAligned( pMemBlock /*, 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 ); + return g_pMemAlloc->GetSizeAligned( pMemBlock ); } struct aligned_tmp_t diff --git a/public/tier0/memoverride.cpp b/public/tier0/memoverride.cpp index 916750e2..bf25059c 100644 --- a/public/tier0/memoverride.cpp +++ b/public/tier0/memoverride.cpp @@ -288,7 +288,11 @@ int __cdecl _set_sbh_threshold( size_t ) int _heapchk() { - return g_pMemAlloc->heapchk(); +#ifdef _WIN32 + return _HEAPOK; +#else + return 1; +#endif } int _heapmin() @@ -422,6 +426,12 @@ void __cdecl operator delete[] ( void *pMem ) #ifndef _STATIC_LINKED #ifdef _WIN32 +#ifdef PREVENT_DEBUG_USAGE + +#define AttribIfCrt() + +#else + // This here just hides the internal file names, etc of allocations // made in the c runtime library #define CRT_INTERNAL_FILE_NAME "C-runtime internal" @@ -429,31 +439,28 @@ void __cdecl operator delete[] ( void *pMem ) class CAttibCRT { public: - CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse) + CAttibCRT( int nBlockUse ) : m_nBlockUse( nBlockUse ) { - if (m_nBlockUse == _CRT_BLOCK) + if(m_nBlockUse == _CRT_BLOCK) { - g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0); + g_pMemAlloc->PushAllocDbgInfo( CRT_INTERNAL_FILE_NAME, 0 ); } } - + ~CAttibCRT() { - if (m_nBlockUse == _CRT_BLOCK) + if(m_nBlockUse == _CRT_BLOCK) { g_pMemAlloc->PopAllocDbgInfo(); } } - + private: int m_nBlockUse; }; - -#ifdef PREVENT_DEBUG_USAGE -#define AttribIfCrt() -#else #define AttribIfCrt() CAttibCRT _attrib(nBlockUse) + #endif #elif defined(POSIX) @@ -640,7 +647,7 @@ _CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient ) int _CrtSetDbgFlag( int nNewFlag ) { - return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); + return 0; } // 64-bit port. @@ -674,23 +681,22 @@ _CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc ) { - return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); + return 0; } int __cdecl _CrtIsValidHeapPointer( const void *pMem ) { - return g_pMemAlloc->CrtIsValidHeapPointer( pMem ); + return 1; } int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access ) { - return g_pMemAlloc->CrtIsValidPointer( pMem, size, access ); + return 1; } int __cdecl _CrtCheckMemory( void ) { - // FIXME: Remove this when we re-implement the heap - return g_pMemAlloc->CrtCheckMemory( ); + return 1; } int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize, @@ -713,8 +719,6 @@ void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState ) void __cdecl _CrtMemCheckpoint( _CrtMemState *pState ) { - // FIXME: Remove this when we re-implement the heap - g_pMemAlloc->CrtMemCheckpoint( pState ); } void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState ) @@ -735,17 +739,17 @@ long _crtAssertBusy = -1; int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) { - return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); + return 0; } _HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) { - return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile ); + return NULL; } _CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) { - return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); + return NULL; } int __cdecl _CrtDbgReport( int nRptType, const char * szFile, @@ -757,7 +761,7 @@ int __cdecl _CrtDbgReport( int nRptType, const char * szFile, _vsnprintf( output, sizeof( output )-1, szFormat, args ); va_end( args ); - return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output ); + return 0; } #if _MSC_VER >= 1400