//========= Copyright Valve Corporation, All rights reserved. ============// //----------------------------------------------------------------------------- // NOTE! This should never be called directly from leaf code // Just use new,delete,malloc,free etc. They will call into this eventually //----------------------------------------------------------------------------- #include "pch_tier0.h" #if defined(_WIN32) #if !defined(_X360) #define WIN_32_LEAN_AND_MEAN #include #else #undef Verify #define _XBOX #include #undef _XBOX #include "xbox/xbox_win32stubs.h" #endif #endif #include #include #include "tier0/dbg.h" #include "tier0/memalloc.h" #include "tier0/threadtools.h" #include "tier0/tslist.h" #include "mem_helpers.h" #pragma pack(4) #ifdef _X360 #define USE_PHYSICAL_SMALL_BLOCK_HEAP 1 #endif // #define NO_SBH 1 #define MIN_SBH_BLOCK 8 #define MIN_SBH_ALIGN 8 #define MAX_SBH_BLOCK 2048 #define MAX_POOL_REGION (4*1024*1024) #if !defined(_X360) #define SBH_PAGE_SIZE (4*1024) #define COMMIT_SIZE (16*SBH_PAGE_SIZE) #else #define SBH_PAGE_SIZE (64*1024) #define COMMIT_SIZE (SBH_PAGE_SIZE) #endif #if _M_X64 #define NUM_POOLS 34 #else #define NUM_POOLS 42 #endif // SBH not enabled for LINUX right now. Unlike on Windows, we can't globally hook malloc. Well, // we can and did in override_init_hook(), but that unfortunately causes all malloc functions // to get hooked - including the nVidia driver, etc. And these hooks appear to happen after // nVidia has alloc'd some memory and it crashes when they try to free that. // So we need things to work without this global hook - which means we rely on memdbgon.h / memdbgoff.h. // Unfortunately, that stuff always comes in source files after the headers are included, and // that means any alloc calls in the header files call the real libc functions. It's a mess. // I believe I've cleaned most of it up, and it appears to be working. However right now we are totally // gated on other performance issues, and the SBH doesn't give us any win, so I've disabled it for now. // Once those perf issues are worked out, it might make sense to do perf tests with SBH, libc, and tcmalloc. // //$ #if defined( _WIN32 ) || defined( _PS3 ) || defined( LINUX ) #if defined( _WIN32 ) || defined( _PS3 ) #define MEM_SBH_ENABLED 1 #endif class ALIGN16 CSmallBlockPool { public: void Init( unsigned nBlockSize, byte *pBase, unsigned initialCommit = 0 ); size_t GetBlockSize(); bool IsOwner( void *p ); void *Alloc(); void Free( void *p ); int CountFreeBlocks(); int GetCommittedSize(); int CountCommittedBlocks(); int CountAllocatedBlocks(); int Compact(); private: typedef TSLNodeBase_t FreeBlock_t; class CFreeList : public CTSListBase { public: void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); } }; CFreeList m_FreeList; unsigned m_nBlockSize; CInterlockedPtr m_pNextAlloc; byte * m_pCommitLimit; byte * m_pAllocLimit; byte * m_pBase; CThreadFastMutex m_CommitMutex; } ALIGN16_POST; class ALIGN16 CSmallBlockHeap { public: CSmallBlockHeap(); bool ShouldUse( size_t nBytes ); bool IsOwner( void * p ); void *Alloc( size_t nBytes ); void *Realloc( void *p, size_t nBytes ); void Free( void *p ); size_t GetSize( void *p ); void DumpStats( FILE *pFile = NULL ); int Compact(); private: CSmallBlockPool *FindPool( size_t nBytes ); CSmallBlockPool *FindPool( void *p ); CSmallBlockPool *m_PoolLookup[MAX_SBH_BLOCK >> 2]; CSmallBlockPool m_Pools[NUM_POOLS]; byte *m_pBase; byte *m_pLimit; } ALIGN16_POST; #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP #define BYTES_X360_SBH (32*1024*1024) #define PAGESIZE_X360_SBH (64*1024) class CX360SmallBlockPool { public: void Init( unsigned nBlockSize ); size_t GetBlockSize(); bool IsOwner( void *p ); void *Alloc(); void Free( void *p ); int CountFreeBlocks(); int GetCommittedSize(); int CountCommittedBlocks(); int CountAllocatedBlocks(); static CX360SmallBlockPool *FindPool( void *p ) { int index = (size_t)((byte *)p - gm_pPhysicalBase) / PAGESIZE_X360_SBH; if ( index < 0 || index >= ARRAYSIZE(gm_AddressToPool) ) return NULL; return gm_AddressToPool[ index ]; } private: friend class CX360SmallBlockHeap; typedef TSLNodeBase_t FreeBlock_t; class CFreeList : public CTSListBase { public: void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); } }; CFreeList m_FreeList; unsigned m_nBlockSize; unsigned m_CommittedSize; CInterlockedPtr m_pNextAlloc; byte * m_pCurBlockEnd; CThreadFastMutex m_CommitMutex; static CX360SmallBlockPool *gm_AddressToPool[BYTES_X360_SBH/PAGESIZE_X360_SBH]; static byte *gm_pPhysicalBlock; static byte *gm_pPhysicalBase; static byte *gm_pPhysicalLimit; }; class CX360SmallBlockHeap { public: CX360SmallBlockHeap(); bool ShouldUse( size_t nBytes ); bool IsOwner( void * p ); void *Alloc( size_t nBytes ); void *Realloc( void *p, size_t nBytes ); void Free( void *p ); size_t GetSize( void *p ); void DumpStats( FILE *pFile = NULL ); CSmallBlockHeap *GetStandardSBH(); private: CX360SmallBlockPool *FindPool( size_t nBytes ); CX360SmallBlockPool *FindPool( void *p ); CX360SmallBlockPool *m_PoolLookup[MAX_SBH_BLOCK >> 2]; CX360SmallBlockPool m_Pools[NUM_POOLS]; }; #endif class ALIGN16 CStdMemAlloc : public IMemAlloc { public: CStdMemAlloc() : m_pfnFailHandler( DefaultFailHandler ), m_sMemoryAllocFailed( (size_t)0 ) { // Make sure that we return 64-bit addresses in 64-bit builds. ReserveBottomMemory(); } // 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 void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ); virtual bool IsDebugHeap() { return false; } virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {} virtual void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) {} virtual void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) {} virtual int GetVersion() { return MEMALLOC_VERSION; } virtual void CompactHeap(); virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ); size_t CallAllocFailHandler( size_t nBytes ) { return (*m_pfnFailHandler)( nBytes); } 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 ) {} static size_t DefaultFailHandler( size_t ); void DumpBlockStats( void *p ) {} #ifdef MEM_SBH_ENABLED CSmallBlockHeap m_SmallBlockHeap; #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP CX360SmallBlockHeap m_LargePageSmallBlockHeap; #endif #endif #if defined( _MEMTEST ) virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ); #endif virtual size_t MemoryAllocFailed(); void SetCRTAllocFailed( size_t nMemSize ); MemAllocFailHandler_t m_pfnFailHandler; size_t m_sMemoryAllocFailed; } ALIGN16_POST;