Counter Strike : Global Offensive Source Code
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.

518 lines
15 KiB

  1. //-----------------------------------------------------------------------------
  2. // NOTE! This should never be called directly from leaf code
  3. // Just use new,delete,malloc,free etc. They will call into this eventually
  4. // Note: we might want to move to Large Page memory for servers (GC). See https://msdn.microsoft.com/en-us/library/windows/desktop/aa366720(v=vs.85).aspx
  5. //-----------------------------------------------------------------------------
  6. #include "pch_tier0.h"
  7. #if IS_WINDOWS_PC
  8. #define WIN_32_LEAN_AND_MEAN
  9. #include <windows.h>
  10. #define VA_COMMIT_FLAGS MEM_COMMIT
  11. #define VA_RESERVE_FLAGS MEM_RESERVE
  12. #elif defined( _X360 )
  13. #undef Verify
  14. #define _XBOX
  15. #include <xtl.h>
  16. #undef _XBOX
  17. #include "xbox/xbox_win32stubs.h"
  18. #define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES)
  19. #define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES)
  20. #elif defined( _PS3 )
  21. #include "sys/memory.h"
  22. #include "sys/mempool.h"
  23. #include "sys/process.h"
  24. #include <sys/vm.h>
  25. #endif
  26. //#include <malloc.h>
  27. #include <algorithm>
  28. #include "tier0/dbg.h"
  29. #include "tier0/memalloc.h"
  30. #include "tier0/threadtools.h"
  31. #include "tier0/tslist.h"
  32. #include "mem_helpers.h"
  33. #ifndef _PS3
  34. #pragma pack(4)
  35. #endif
  36. #define MIN_SBH_BLOCK 8
  37. #define MIN_SBH_ALIGN 8
  38. #ifdef PLATFORM_WINDOWS_PC64
  39. #define MAX_SBH_BLOCK (16*1024*1024)
  40. #define SBH_BLOCK_LOOKUP_GRANULARITY 4
  41. #else
  42. #define MAX_SBH_BLOCK 2048
  43. #define SBH_BLOCK_LOOKUP_GRANULARITY 2
  44. #endif
  45. //#define MAX_POOL_REGION (4*1024*1024)
  46. #if defined( PLATFORM_WINDOWS_PC64 )
  47. #define SBH_LARGE_MEM 1
  48. #endif
  49. #ifdef PLATFORM_WINDOWS_PC64
  50. #define NUM_POOLS 47 // block sizes are more granular on 64-bit
  51. #else
  52. #define NUM_POOLS 42
  53. #endif
  54. #if defined( _WIN32 ) || defined( _PS3 )
  55. // Small block heap on win64 is expecting SLIST_HEADER to look different than it does on win64. It was disabled for a long time because of this.
  56. #define MEM_SBH_ENABLED 1
  57. #endif
  58. #if !defined(_CERT) && ( defined(_X360) || defined(_PS3) || defined( PLATFORM_WINDOWS_PC64 ) || DEVELOPMENT_ONLY )
  59. #define TRACK_SBH_COUNTS
  60. #endif
  61. #if defined(_X360)
  62. // 360 uses a 48MB primary (physical) SBH and 10MB secondary (virtual) SBH, with no fallback
  63. #define MBYTES_PRIMARY_SBH 32
  64. #define MEMALLOC_USE_SECONDARY_SBH
  65. #define MBYTES_SECONDARY_SBH 10
  66. #define MEMALLOC_NO_FALLBACK
  67. #elif defined(_PS3)
  68. // PS3 uses just a 32MB SBH - this was enough to avoid overflow when Portal 2 shipped.
  69. // NOTE: when Steam uses the game's tier0 allocator (see memalloc.h), we increase the size
  70. // of the SBH and MBH (see memstd.cpp) to accommodate those extra allocations.
  71. #define MBYTES_PRIMARY_SBH ( 34 + MBYTES_STEAM_SBH_USAGE )
  72. #define MEMALLOC_NO_FALLBACK
  73. #else // _X360 | _PS3
  74. #ifdef PLATFORM_WINDOWS_PC64
  75. // in 64-bit server , with huge pages and many pools, we need a lot to start with, otherwise we'll start falling into fallback SBH right away
  76. #define MBYTES_PRIMARY_SBH 256
  77. #else
  78. // Other platforms use a 48MB primary SBH and a (32MB) fallback SBH
  79. // CSGO was hitting falling out of the small block heap (many expensive calls
  80. // to CompactOnFail) with it set to 48 MB, so let's try higher.
  81. #define MBYTES_PRIMARY_SBH 64
  82. #endif
  83. #endif // _X360 | _PS3
  84. #define MEMSTD_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;}
  85. //-----------------------------------------------------------------------------
  86. // Small block pool
  87. //-----------------------------------------------------------------------------
  88. class CFreeList : public CTSListBase
  89. {
  90. public:
  91. void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
  92. byte *Pop() { return (byte *)CTSListBase::Pop(); }
  93. };
  94. template <typename CAllocator>
  95. class CSmallBlockHeap;
  96. template <typename CAllocator>
  97. class CSmallBlockPool
  98. {
  99. public:
  100. CSmallBlockPool()
  101. {
  102. m_nBlockSize = 0;
  103. m_nCommittedPages = 0;
  104. m_pFirstPage = NULL;
  105. }
  106. void Init( unsigned nBlockSize );
  107. size_t GetBlockSize();
  108. void *Alloc();
  109. void Free( void *p );
  110. int CountFreeBlocks();
  111. size_t GetCommittedSize();
  112. int CountCommittedBlocks();
  113. int CountAllocatedBlocks();
  114. size_t Compact( bool bIncremental );
  115. bool Validate();
  116. enum
  117. {
  118. BYTES_PAGE = CAllocator::BYTES_PAGE,
  119. NOT_COMMITTED = -1
  120. };
  121. private:
  122. typedef CSmallBlockHeap<CAllocator> CHeap;
  123. friend class CSmallBlockHeap<CAllocator>;
  124. struct PageStatus_t : public TSLNodeBase_t
  125. {
  126. PageStatus_t()
  127. {
  128. m_pPool = NULL;
  129. m_nAllocated = NOT_COMMITTED;
  130. m_pNextPageInPool = NULL;
  131. }
  132. CSmallBlockPool<CAllocator> * m_pPool;
  133. PageStatus_t * m_pNextPageInPool;
  134. CInterlockedInt m_nAllocated;
  135. CTSListBase m_SortList;
  136. };
  137. struct SharedData_t
  138. {
  139. CAllocator m_Allocator;
  140. CTSListBase m_FreePages;
  141. CThreadSpinRWLock m_Lock;
  142. size_t m_numPages;
  143. byte * m_pNextBlock;
  144. byte * m_pBase;
  145. byte * m_pLimit;
  146. PageStatus_t m_PageStatus[ 114688 ]; // This is sufficient to hold pages for SBH sized up to 1,792 MB (almost 2GB) in 32-bit SBH with 16 Kb pages; or up to 56Gb worth of data in 64-bit GC with fixed-size allocator with 512Kb pages
  147. };
  148. static int PageSort( const void *p1, const void *p2 ) ;
  149. bool RemovePagesFromFreeList( byte **pPages, int nPages, bool bSortList );
  150. void ValidateFreelist( SharedData_t *pSharedData );
  151. CFreeList m_FreeList;
  152. CInterlockedPtr<byte> m_pNextAlloc;
  153. PageStatus_t * m_pFirstPage;
  154. unsigned m_nBlockSize;
  155. unsigned m_nCommittedPages;
  156. CThreadFastMutex m_CommitMutex;
  157. CInterlockedInt m_nIsCompact;
  158. #ifdef TRACK_SBH_COUNTS
  159. CInterlockedInt m_nFreeBlocks;
  160. #endif
  161. static SharedData_t *GetSharedData()
  162. {
  163. return &gm_SharedData;
  164. }
  165. static SharedData_t gm_SharedData;
  166. };
  167. //-----------------------------------------------------------------------------
  168. // Small block heap (multi-pool)
  169. //-----------------------------------------------------------------------------
  170. template <typename CAllocator>
  171. class CSmallBlockHeap
  172. {
  173. public:
  174. CSmallBlockHeap();
  175. bool ShouldUse( size_t nBytes );
  176. bool IsOwner( void * p );
  177. void *Alloc( size_t nBytes );
  178. void *Realloc( void *p, size_t nBytes );
  179. void Free( void *p );
  180. size_t GetSize( void *p );
  181. void DumpStats( const char *pszTag, FILE *pFile = NULL, IMemAlloc::DumpStatsFormat_t nFormat = IMemAlloc::FORMAT_TEXT );
  182. void Usage( size_t &bytesCommitted, size_t &bytesAllocated );
  183. size_t Compact( bool bIncremental );
  184. bool Validate();
  185. void InitPools( const uint *pSizes );
  186. enum
  187. {
  188. BYTES_PAGE = CAllocator::BYTES_PAGE
  189. };
  190. private:
  191. typedef CSmallBlockPool<CAllocator> CPool;
  192. typedef struct CSmallBlockPool<CAllocator>::SharedData_t SharedData_t;
  193. CPool *FindPool( size_t nBytes );
  194. CPool *FindPool( void *p );
  195. // Map size to a pool address to a pool
  196. CPool *m_PoolLookup[ MAX_SBH_BLOCK >> SBH_BLOCK_LOOKUP_GRANULARITY ];
  197. CPool m_Pools[NUM_POOLS];
  198. SharedData_t *m_pSharedData;
  199. };
  200. //-----------------------------------------------------------------------------
  201. //
  202. //-----------------------------------------------------------------------------
  203. class CStdMemAlloc : public IMemAlloc
  204. {
  205. public:
  206. CStdMemAlloc();
  207. // Internal versions
  208. void *InternalAlloc( int region, size_t nSize );
  209. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  210. void *InternalAllocAligned( int region, size_t nSize, size_t align );
  211. #endif
  212. void *InternalAllocFromPools( size_t nSize );
  213. void *InternalRealloc( void *pMem, size_t nSize );
  214. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  215. void *InternalReallocAligned( void *pMem, size_t nSize, size_t align );
  216. #endif
  217. void InternalFree( void *pMem );
  218. size_t InternalCompact( bool bSmallBlockOnly );
  219. void CompactOnFail();
  220. // Release versions
  221. virtual void *Alloc( size_t nSize );
  222. virtual void *Realloc( void *pMem, size_t nSize );
  223. virtual void Free( void *pMem );
  224. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
  225. // Debug versions
  226. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
  227. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
  228. virtual void Free( void *pMem, const char *pFileName, int nLine );
  229. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
  230. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  231. virtual void *AllocAlign( size_t nSize, size_t align );
  232. virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
  233. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
  234. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
  235. #endif
  236. virtual void *RegionAlloc( int region, size_t nSize );
  237. virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine );
  238. // Returns size of a particular allocation
  239. virtual size_t GetSize( void *pMem );
  240. // Force file + line information for an allocation
  241. virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
  242. virtual void PopAllocDbgInfo();
  243. virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
  244. virtual int CrtSetReportMode( int nReportType, int nReportMode );
  245. virtual int CrtIsValidHeapPointer( const void *pMem );
  246. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
  247. virtual int CrtCheckMemory( void );
  248. virtual int CrtSetDbgFlag( int nNewFlag );
  249. virtual void CrtMemCheckpoint( _CrtMemState *pState );
  250. void* CrtSetReportFile( int nRptType, void* hFile );
  251. void* CrtSetReportHook( void* pfnNewHook );
  252. int CrtDbgReport( int nRptType, const char * szFile,
  253. int nLine, const char * szModule, const char * pMsg );
  254. virtual int heapchk();
  255. virtual void DumpStats();
  256. virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE;
  257. virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
  258. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
  259. virtual bool IsDebugHeap() { return false; }
  260. virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {}
  261. virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
  262. virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
  263. virtual int GetVersion() { return MEMALLOC_VERSION; }
  264. virtual void OutOfMemory( size_t nBytesAttempted = 0 ) { SetCRTAllocFailed( nBytesAttempted ); }
  265. virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
  266. virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats );
  267. virtual void CompactHeap();
  268. virtual void CompactIncremental();
  269. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
  270. size_t CallAllocFailHandler( size_t nBytes ) { return (*m_pfnFailHandler)( nBytes); }
  271. virtual uint32 GetDebugInfoSize() { return 0; }
  272. virtual void SaveDebugInfo( void *pvDebugInfo ) { }
  273. virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
  274. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
  275. static size_t DefaultFailHandler( size_t );
  276. void DumpBlockStats( void *p ) {}
  277. #if MEM_SBH_ENABLED
  278. class CVirtualAllocator
  279. {
  280. public:
  281. enum
  282. {
  283. #if SBH_LARGE_MEM
  284. BYTES_PAGE = ( 16 * 1024 * 1024 ),
  285. TOTAL_BYTES = ( 1ull * 1024 * 1024 * 1024 ),
  286. #else
  287. BYTES_PAGE = (64*1024),
  288. TOTAL_BYTES = (32*1024*1024),
  289. #endif
  290. MIN_RESERVE_PAGES = 4,
  291. };
  292. byte *AllocatePoolMemory()
  293. {
  294. #ifdef _WIN32
  295. return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_RESERVE_FLAGS, PAGE_NOACCESS );
  296. #elif defined( _PS3 )
  297. Error( "" );
  298. return NULL;
  299. #else
  300. #error
  301. #endif
  302. }
  303. inline size_t GetTotalBytes() const { return TOTAL_BYTES; }
  304. inline size_t GetNumPages() const { return TOTAL_BYTES/BYTES_PAGE; }
  305. inline size_t GetMinReservePages() const { return MIN_RESERVE_PAGES; }
  306. static bool IsVirtual()
  307. {
  308. return true;
  309. }
  310. bool Decommit( void *pPage )
  311. {
  312. #ifdef _WIN32
  313. return ( VirtualFree( pPage, BYTES_PAGE, MEM_DECOMMIT ) != 0 );
  314. #elif defined( _PS3 )
  315. return false;
  316. #else
  317. #error
  318. #endif
  319. }
  320. bool Commit( void *pPage )
  321. {
  322. #ifdef _WIN32
  323. return ( VirtualAlloc( pPage, BYTES_PAGE, VA_COMMIT_FLAGS, PAGE_READWRITE ) != NULL );
  324. #elif defined( _PS3 )
  325. return false;
  326. #else
  327. #error
  328. #endif
  329. }
  330. };
  331. typedef CSmallBlockHeap<CVirtualAllocator> CVirtualSmallBlockHeap;
  332. template <size_t SIZE_MB, bool bPhysical>
  333. class CFixedAllocator
  334. {
  335. public:
  336. enum
  337. {
  338. #if SBH_LARGE_MEM
  339. BYTES_PAGE = ( 16 * 1024 * 1024 ), // use really coarse pages on 64-bit GC
  340. #else
  341. BYTES_PAGE = (16*1024),
  342. #endif
  343. };
  344. private:
  345. // Private info:
  346. enum
  347. {
  348. TOTAL_BYTES = (SIZE_MB*1024*1024),
  349. };
  350. public:
  351. byte *AllocatePoolMemory()
  352. {
  353. #ifdef _WIN32
  354. #ifdef _X360
  355. if ( bPhysical )
  356. return (byte *)XPhysicalAlloc( TOTAL_BYTES, MAXULONG_PTR, 4096, PAGE_READWRITE | MEM_16MB_PAGES );
  357. #endif
  358. size_t numBytesToAllocate = TOTAL_BYTES;
  359. if ( bPhysical )
  360. {
  361. // Allow command line override
  362. extern size_t g_nSBHOverride;
  363. numBytesToAllocate = g_nSBHOverride;
  364. }
  365. return (byte *)VirtualAlloc( NULL, numBytesToAllocate, VA_COMMIT_FLAGS, PAGE_READWRITE );
  366. #elif defined( _PS3 )
  367. // TODO: release this section on shutdown (use GetMemorySectionForAddress)
  368. extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
  369. IVirtualMemorySection *pSection = VirtualMemoryManager_AllocateVirtualMemorySection( TOTAL_BYTES );
  370. if ( !pSection )
  371. Error( "CFixedAllocator::AllocatePoolMemory() failed in VirtualMemoryManager_AllocateVirtualMemorySection\n" );
  372. if ( !pSection->CommitPages( pSection->GetBaseAddress(), TOTAL_BYTES ) )
  373. Error( "CFixedAllocator::AllocatePoolMemory() failed in IVirtualMemorySection::CommitPages\n" );
  374. return reinterpret_cast<byte *>( pSection->GetBaseAddress() );
  375. #else
  376. #error
  377. #endif
  378. }
  379. inline size_t GetTotalBytes() const
  380. {
  381. if ( bPhysical )
  382. {
  383. // Allow command line override
  384. extern size_t g_nSBHOverride;
  385. return g_nSBHOverride;
  386. }
  387. else
  388. return TOTAL_BYTES;
  389. }
  390. inline size_t GetNumPages() const { return GetTotalBytes()/BYTES_PAGE; }
  391. inline size_t GetMinReservePages() const { return GetNumPages(); }
  392. static bool IsVirtual()
  393. {
  394. return false;
  395. }
  396. bool Decommit( void *pPage )
  397. {
  398. return false;
  399. }
  400. bool Commit( void *pPage )
  401. {
  402. return false;
  403. }
  404. };
  405. typedef CSmallBlockHeap<CFixedAllocator< MBYTES_PRIMARY_SBH, true> > CFixedSmallBlockHeap;
  406. #ifdef MEMALLOC_USE_SECONDARY_SBH
  407. typedef CSmallBlockHeap<CFixedAllocator< MBYTES_SECONDARY_SBH, false> > CFixedVirtualSmallBlockHeap; // @TODO: move back into above heap if number stays at 16 [7/15/2009 tom]
  408. #endif
  409. CFixedSmallBlockHeap m_PrimarySBH;
  410. #ifdef MEMALLOC_USE_SECONDARY_SBH
  411. CFixedVirtualSmallBlockHeap m_SecondarySBH;
  412. #endif
  413. #ifndef MEMALLOC_NO_FALLBACK
  414. CVirtualSmallBlockHeap m_FallbackSBH;
  415. #endif
  416. #endif // MEM_SBH_ENABLED
  417. virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment );
  418. virtual size_t MemoryAllocFailed();
  419. void SetCRTAllocFailed( size_t nMemSize );
  420. MemAllocFailHandler_t m_pfnFailHandler;
  421. size_t m_sMemoryAllocFailed;
  422. CThreadFastMutex m_CompactMutex;
  423. bool m_bInCompact;
  424. };
  425. #ifndef _PS3
  426. #pragma pack()
  427. #endif