Team Fortress 2 Source Code as on 22/4/2020
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.

1900 lines
48 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Memory allocation!
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "pch_tier0.h"
  8. #if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
  9. #if defined( _WIN32 ) && !defined( _X360 )
  10. #define WIN_32_LEAN_AND_MEAN
  11. #include <windows.h>
  12. #define VA_COMMIT_FLAGS MEM_COMMIT
  13. #define VA_RESERVE_FLAGS MEM_RESERVE
  14. #elif defined( _X360 )
  15. #undef Verify
  16. #define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES)
  17. #define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES)
  18. #endif
  19. #include <malloc.h>
  20. #include "tier0/valve_minmax_off.h" // GCC 4.2.2 headers screw up our min/max defs.
  21. #include <algorithm>
  22. #include "tier0/valve_minmax_on.h" // GCC 4.2.2 headers screw up our min/max defs.
  23. #include "tier0/dbg.h"
  24. #include "tier0/memalloc.h"
  25. #include "tier0/threadtools.h"
  26. #include "mem_helpers.h"
  27. #include "memstd.h"
  28. #ifdef _X360
  29. #include "xbox/xbox_console.h"
  30. #endif
  31. // Force on redirecting all allocations to the process heap on Win64,
  32. // which currently means the GC. This is to make AppVerifier more effective
  33. // at catching memory stomps.
  34. #if defined( _WIN64 )
  35. #define FORCE_PROCESS_HEAP
  36. #elif defined( _WIN32 )
  37. // Define this to force using the OS Heap* functions for allocations. This is useful
  38. // in conjunction with AppVerifier/PageHeap in order to find memory problems, and
  39. // also allows ETW/xperf tracing to be used to record allocations.
  40. // Normally the command-line option -processheap can be used instead.
  41. //#define FORCE_PROCESS_HEAP
  42. #define ALLOW_PROCESS_HEAP
  43. #endif
  44. // Track this to decide how to handle out-of-memory.
  45. static bool s_bPageHeapEnabled = false;
  46. #ifdef TIME_ALLOC
  47. CAverageCycleCounter g_MallocCounter;
  48. CAverageCycleCounter g_ReallocCounter;
  49. CAverageCycleCounter g_FreeCounter;
  50. #define PrintOne( name ) \
  51. Msg("%-48s: %6.4f avg (%8.1f total, %7.3f peak, %5d iters)\n", \
  52. #name, \
  53. g_##name##Counter.GetAverageMilliseconds(), \
  54. g_##name##Counter.GetTotalMilliseconds(), \
  55. g_##name##Counter.GetPeakMilliseconds(), \
  56. g_##name##Counter.GetIters() ); \
  57. memset( &g_##name##Counter, 0, sizeof(g_##name##Counter) )
  58. void PrintAllocTimes()
  59. {
  60. PrintOne( Malloc );
  61. PrintOne( Realloc );
  62. PrintOne( Free );
  63. }
  64. #define PROFILE_ALLOC(name) CAverageTimeMarker name##_ATM( &g_##name##Counter )
  65. #else
  66. #define PROFILE_ALLOC( name ) ((void)0)
  67. #define PrintAllocTimes() ((void)0)
  68. #endif
  69. #if _MSC_VER < 1400 && defined( MSVC ) && !defined(_STATIC_LINKED) && (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  70. void *operator new( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
  71. {
  72. return ::operator new( nSize );
  73. }
  74. void *operator new[] ( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
  75. {
  76. return ::operator new[]( nSize );
  77. }
  78. #endif
  79. #if (!defined(_DEBUG) && !defined(USE_MEM_DEBUG))
  80. // Support for CHeapMemAlloc for easy switching to using the process heap.
  81. #ifdef ALLOW_PROCESS_HEAP
  82. // Round a size up to a multiple of 4 KB to aid in calculating how much
  83. // memory is required if full pageheap is enabled.
  84. static size_t RoundUpToPage( size_t nSize )
  85. {
  86. nSize += 0xFFF;
  87. nSize &= ~0xFFF;
  88. return nSize;
  89. }
  90. // Convenience function to deal with the necessary type-casting
  91. static void InterlockedAddSizeT( size_t volatile *Addend, size_t Value )
  92. {
  93. #ifdef PLATFORM_WINDOWS_PC32
  94. COMPILE_TIME_ASSERT( sizeof( size_t ) == sizeof( int32 ) );
  95. InterlockedExchangeAdd( ( LONG* )Addend, LONG( Value ) );
  96. #else
  97. InterlockedExchangeAdd64( ( LONGLONG* )Addend, LONGLONG( Value ) );
  98. #endif
  99. }
  100. class CHeapMemAlloc : public IMemAlloc
  101. {
  102. public:
  103. CHeapMemAlloc()
  104. {
  105. // Make sure that we return 64-bit addresses in 64-bit builds.
  106. ReserveBottomMemory();
  107. // Do all allocations with the shared process heap so that we can still
  108. // allocate from one DLL and free in another.
  109. m_heap = GetProcessHeap();
  110. }
  111. void Init( bool bZeroMemory )
  112. {
  113. m_HeapFlags = bZeroMemory ? HEAP_ZERO_MEMORY : 0;
  114. // Can't use Msg here because it isn't necessarily initialized yet.
  115. if ( s_bPageHeapEnabled )
  116. {
  117. OutputDebugStringA("PageHeap is on. Memory use will be larger than normal.\n" );
  118. }
  119. else
  120. {
  121. OutputDebugStringA("PageHeap is off. Memory use will be normal.\n" );
  122. }
  123. if( bZeroMemory )
  124. {
  125. OutputDebugStringA( " HEAP_ZERO_MEMORY is specified.\n" );
  126. }
  127. }
  128. // Release versions
  129. virtual void *Alloc( size_t nSize )
  130. {
  131. // Ensure that the constructor has run already. Poorly defined
  132. // order of construction can result in the allocator being used
  133. // before it is constructed. Which could be bad.
  134. if ( !m_heap )
  135. __debugbreak();
  136. void* pMem = HeapAlloc( m_heap, m_HeapFlags, nSize );
  137. if ( pMem )
  138. {
  139. InterlockedAddSizeT( &m_nOutstandingBytes, nSize );
  140. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nSize ) );
  141. InterlockedIncrement( &m_nOutstandingAllocations );
  142. InterlockedIncrement( &m_nLifetimeAllocations );
  143. }
  144. else if ( nSize )
  145. {
  146. // Having PageHeap enabled leads to lots of allocation failures. These
  147. // then lead to crashes. In order to avoid confusion about the cause of
  148. // these crashes, halt immediately on allocation failures.
  149. __debugbreak();
  150. InterlockedIncrement( &m_nAllocFailures );
  151. }
  152. return pMem;
  153. }
  154. virtual void *Realloc( void *pMem, size_t nSize )
  155. {
  156. // If you pass zero to HeapReAlloc then it fails (with GetLastError() saying S_OK!)
  157. // so only call HeapReAlloc if pMem is non-zero.
  158. if ( pMem )
  159. {
  160. if ( !nSize )
  161. {
  162. // Call the regular free function.
  163. Free( pMem );
  164. return 0;
  165. }
  166. size_t nOldSize = HeapSize( m_heap, 0, pMem );
  167. void* pNewMem = HeapReAlloc( m_heap, m_HeapFlags, pMem, nSize );
  168. // If we successfully allocated the requested memory (zero counts as
  169. // success if we requested zero bytes) then update the counters for the
  170. // change.
  171. if ( pNewMem )
  172. {
  173. InterlockedAddSizeT( &m_nOutstandingBytes, nSize - nOldSize );
  174. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nSize ) );
  175. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldSize ) );
  176. // Outstanding allocation count isn't affected by Realloc, but
  177. // lifetime allocation count is.
  178. InterlockedIncrement( &m_nLifetimeAllocations );
  179. }
  180. else
  181. {
  182. // Having PageHeap enabled leads to lots of allocation failures. These
  183. // then lead to crashes. In order to avoid confusion about the cause of
  184. // these crashes, halt immediately on allocation failures.
  185. __debugbreak();
  186. InterlockedIncrement( &m_nAllocFailures );
  187. }
  188. return pNewMem;
  189. }
  190. // Call the regular alloc function.
  191. return Alloc( nSize );
  192. }
  193. virtual void Free( void *pMem )
  194. {
  195. if ( pMem )
  196. {
  197. size_t nOldSize = HeapSize( m_heap, 0, pMem );
  198. InterlockedAddSizeT( &m_nOutstandingBytes, 0 - nOldSize );
  199. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldSize ) );
  200. InterlockedDecrement( &m_nOutstandingAllocations );
  201. HeapFree( m_heap, 0, pMem );
  202. }
  203. }
  204. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) { return 0; }
  205. // Debug versions
  206. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize ); }
  207. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) { return Realloc(pMem, nSize); }
  208. virtual void Free( void *pMem, const char *pFileName, int nLine ) { Free( pMem ); }
  209. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) { return 0; }
  210. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  211. // Not currently implemented
  212. #error
  213. #endif
  214. virtual void *RegionAlloc( int region, size_t nSize ) { __debugbreak(); return 0; }
  215. virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) { __debugbreak(); return 0; }
  216. // Returns size of a particular allocation
  217. // If zero is returned then return the total size of allocated memory.
  218. virtual size_t GetSize( void *pMem )
  219. {
  220. if ( !pMem )
  221. {
  222. return m_nOutstandingBytes;
  223. }
  224. return HeapSize( m_heap, 0, pMem );
  225. }
  226. // Force file + line information for an allocation
  227. virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) {}
  228. virtual void PopAllocDbgInfo() {}
  229. virtual long CrtSetBreakAlloc( long lNewBreakAlloc ) { return 0; }
  230. virtual int CrtSetReportMode( int nReportType, int nReportMode ) { return 0; }
  231. virtual int CrtIsValidHeapPointer( const void *pMem ) { return 0; }
  232. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) { return 0; }
  233. virtual int CrtCheckMemory( void ) { return 0; }
  234. virtual int CrtSetDbgFlag( int nNewFlag ) { return 0; }
  235. virtual void CrtMemCheckpoint( _CrtMemState *pState ) {}
  236. virtual void* CrtSetReportFile( int nRptType, void* hFile ) { return 0; }
  237. virtual void* CrtSetReportHook( void* pfnNewHook ) { return 0; }
  238. virtual int CrtDbgReport( int nRptType, const char * szFile,
  239. int nLine, const char * szModule, const char * pMsg ) { return 0; }
  240. virtual int heapchk() { return -2/*_HEAPOK*/; }
  241. virtual void DumpStats()
  242. {
  243. const size_t MB = 1024 * 1024;
  244. Msg( "Sorry -- no stats saved to file memstats.txt when the heap allocator is enabled.\n" );
  245. // Print requested memory.
  246. Msg( "%u MB allocated.\n", ( unsigned )( m_nOutstandingBytes / MB ) );
  247. // Print memory after rounding up to pages.
  248. Msg( "%u MB assuming maximum PageHeap overhead.\n", ( unsigned )( m_nOutstandingPageHeapBytes / MB ));
  249. // Print memory after adding in reserved page after every allocation. Do 64-bit calculations
  250. // because the pageHeap required memory can easily go over 4 GB.
  251. __int64 pageHeapBytes = m_nOutstandingPageHeapBytes + m_nOutstandingAllocations * 4096LL;
  252. Msg( "%u MB address space used assuming maximum PageHeap overhead.\n", ( unsigned )( pageHeapBytes / MB ));
  253. Msg( "%u outstanding allocations (%d delta).\n", ( unsigned )m_nOutstandingAllocations, ( int )( m_nOutstandingAllocations - m_nOldOutstandingAllocations ) );
  254. Msg( "%u lifetime allocations (%u delta).\n", ( unsigned )m_nLifetimeAllocations, ( unsigned )( m_nLifetimeAllocations - m_nOldLifetimeAllocations ) );
  255. Msg( "%u allocation failures.\n", ( unsigned )m_nAllocFailures );
  256. // Update the numbers on outstanding and lifetime allocation counts so
  257. // that we can print out deltas.
  258. m_nOldOutstandingAllocations = m_nOutstandingAllocations;
  259. m_nOldLifetimeAllocations = m_nLifetimeAllocations;
  260. }
  261. virtual void DumpStatsFileBase( char const *pchFileBase ) {}
  262. virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) { return 0; }
  263. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) {}
  264. virtual bool IsDebugHeap() { return false; }
  265. virtual uint32 GetDebugInfoSize() { return 0; }
  266. virtual void SaveDebugInfo( void *pvDebugInfo ) { }
  267. virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
  268. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
  269. virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {}
  270. virtual void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) {}
  271. virtual void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) {}
  272. virtual int GetVersion() { return MEMALLOC_VERSION; }
  273. virtual void OutOfMemory( size_t nBytesAttempted = 0 ) {}
  274. virtual void CompactHeap() {}
  275. virtual void CompactIncremental() {}
  276. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) { return 0; }
  277. void DumpBlockStats( void *p ) {}
  278. #if defined( _MEMTEST )
  279. // Not currently implemented
  280. #error
  281. #endif
  282. virtual size_t MemoryAllocFailed() { return 0; }
  283. private:
  284. // Handle to the process heap.
  285. HANDLE m_heap;
  286. uint32 m_HeapFlags;
  287. // Total outstanding bytes allocated.
  288. volatile size_t m_nOutstandingBytes;
  289. // Total outstanding committed bytes assuming that all allocations are
  290. // put on individual 4-KB pages (true when using full PageHeap from
  291. // App Verifier).
  292. volatile size_t m_nOutstandingPageHeapBytes;
  293. // Total outstanding allocations. With PageHeap enabled each allocation
  294. // requires an extra 4-KB page of address space.
  295. volatile LONG m_nOutstandingAllocations;
  296. LONG m_nOldOutstandingAllocations;
  297. // Total allocations without subtracting freed memory.
  298. volatile LONG m_nLifetimeAllocations;
  299. LONG m_nOldLifetimeAllocations;
  300. // Total number of allocation failures.
  301. volatile LONG m_nAllocFailures;
  302. };
  303. #endif //ALLOW_PROCESS_HEAP
  304. //-----------------------------------------------------------------------------
  305. // Singletons...
  306. //-----------------------------------------------------------------------------
  307. #pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
  308. #pragma init_seg( compiler )
  309. static CStdMemAlloc s_StdMemAlloc CONSTRUCT_EARLY;
  310. #ifndef TIER0_VALIDATE_HEAP
  311. IMemAlloc *g_pMemAlloc = &s_StdMemAlloc;
  312. #else
  313. IMemAlloc *g_pActualAlloc = &s_StdMemAlloc;
  314. #endif
  315. #if defined(ALLOW_PROCESS_HEAP) && !defined(TIER0_VALIDATE_HEAP)
  316. void EnableHeapMemAlloc( bool bZeroMemory )
  317. {
  318. // Place this here to guarantee it is constructed
  319. // before we call Init.
  320. static CHeapMemAlloc s_HeapMemAlloc;
  321. static bool s_initCalled = false;
  322. if ( !s_initCalled )
  323. {
  324. s_HeapMemAlloc.Init( bZeroMemory );
  325. g_pMemAlloc = &s_HeapMemAlloc;
  326. s_initCalled = true;
  327. }
  328. }
  329. // Check whether PageHeap (part of App Verifier) has been enabled for this process.
  330. // It specifically checks whether it was enabled by the EnableAppVerifier.bat
  331. // batch file. This can be used to automatically enable -processheap when
  332. // App Verifier is in use.
  333. static bool IsPageHeapEnabled( bool& bETWHeapEnabled )
  334. {
  335. // Assume false.
  336. bool result = false;
  337. bETWHeapEnabled = false;
  338. // First we get the application's name so we can look in the registry
  339. // for App Verifier settings.
  340. HMODULE exeHandle = GetModuleHandle( 0 );
  341. if ( exeHandle )
  342. {
  343. char appName[ MAX_PATH ];
  344. if ( GetModuleFileNameA( exeHandle, appName, ARRAYSIZE( appName ) ) )
  345. {
  346. // Guarantee null-termination -- not guaranteed on Windows XP!
  347. appName[ ARRAYSIZE( appName ) - 1 ] = 0;
  348. // Find the file part of the name.
  349. const char* pFilePart = strrchr( appName, '\\' );
  350. if ( pFilePart )
  351. {
  352. ++pFilePart;
  353. size_t len = strlen( pFilePart );
  354. if ( len > 0 && pFilePart[ len - 1 ] == ' ' )
  355. {
  356. OutputDebugStringA( "Trailing space on executable name! This will cause Application Verifier and ETW Heap tracing to fail!\n" );
  357. DebuggerBreakIfDebugging();
  358. }
  359. // Generate the key name for App Verifier settings for this process.
  360. char regPathName[ MAX_PATH ];
  361. _snprintf( regPathName, ARRAYSIZE( regPathName ),
  362. "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s",
  363. pFilePart );
  364. regPathName[ ARRAYSIZE( regPathName ) - 1 ] = 0;
  365. HKEY key;
  366. LONG regResult = RegOpenKeyA( HKEY_LOCAL_MACHINE,
  367. regPathName,
  368. &key );
  369. if ( regResult == ERROR_SUCCESS )
  370. {
  371. // If PageHeapFlags exists then that means that App Verifier is enabled
  372. // for this application. The StackTraceDatabaseSizeInMB is only
  373. // set by Valve's enabling batch file so this indicates that
  374. // a developer at Valve is using App Verifier.
  375. if ( RegQueryValueExA( key, "StackTraceDatabaseSizeInMB", 0, NULL, NULL, NULL ) == ERROR_SUCCESS &&
  376. RegQueryValueExA( key, "PageHeapFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
  377. {
  378. result = true;
  379. }
  380. if ( RegQueryValueExA( key, "TracingFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
  381. bETWHeapEnabled = true;
  382. RegCloseKey( key );
  383. }
  384. }
  385. }
  386. }
  387. return result;
  388. }
  389. // Check for various allocator overrides such as -processheap and -reservelowmem.
  390. // Returns true if -processheap is enabled, by a command line switch or other method.
  391. bool CheckWindowsAllocSettings( const char* upperCommandLine )
  392. {
  393. // Are we doing ETW heap profiling?
  394. bool bETWHeapEnabled = false;
  395. s_bPageHeapEnabled = IsPageHeapEnabled( bETWHeapEnabled );
  396. // Should we reserve the bottom 4 GB of RAM in order to flush out pointer
  397. // truncation bugs? This helps ensure 64-bit compatibility.
  398. // However this needs to be off by default to avoid causing compatibility problems,
  399. // with Steam detours and other systems. It should also be disabled when PageHeap
  400. // is on because for some reason the combination turns into 4 GB of working set, which
  401. // can easily cause problems.
  402. if ( strstr( upperCommandLine, "-RESERVELOWMEM" ) && !s_bPageHeapEnabled )
  403. ReserveBottomMemory();
  404. // Uninitialized data, including pointers, is often set to 0xFFEEFFEE.
  405. // If we reserve that block of memory then we can turn these pointer
  406. // dereferences into crashes a little bit earlier and more reliably.
  407. // We don't really care whether this allocation succeeds, but it's
  408. // worth trying. Note that we do this in all cases -- whether we are using
  409. // -processheap or not.
  410. VirtualAlloc( (void*)0xFFEEFFEE, 1, MEM_RESERVE, PAGE_NOACCESS );
  411. // Enable application termination (breakpoint) on heap corruption. This is
  412. // better than trying to patch it up and continue, both from a security and
  413. // a bug-finding point of view. Do this always on Windows since the heap is
  414. // used by video drivers and other in-proc components.
  415. //HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
  416. // The HeapEnableTerminationOnCorruption requires a recent platform SDK,
  417. // so fake it up.
  418. #if defined(PLATFORM_WINDOWS_PC)
  419. HeapSetInformation( NULL, (HEAP_INFORMATION_CLASS)1, NULL, 0 );
  420. #endif
  421. bool bZeroMemory = false;
  422. bool bProcessHeap = false;
  423. // Should we force using the process heap? This is handy for gathering memory
  424. // statistics with ETW/xperf. When using App Verifier -processheap is automatically
  425. // turned on.
  426. if ( strstr( upperCommandLine, "-PROCESSHEAP" ) )
  427. {
  428. bProcessHeap = true;
  429. bZeroMemory = !!strstr( upperCommandLine, "-PROCESSHEAPZEROMEM" );
  430. }
  431. // Unless specifically disabled, turn on -processheap if pageheap or ETWHeap tracing
  432. // are enabled.
  433. if ( !strstr( upperCommandLine, "-NOPROCESSHEAP" ) && ( s_bPageHeapEnabled || bETWHeapEnabled ) )
  434. bProcessHeap = true;
  435. if ( bProcessHeap )
  436. {
  437. // Now all allocations will go through the system heap.
  438. EnableHeapMemAlloc( bZeroMemory );
  439. }
  440. return bProcessHeap;
  441. }
  442. class CInitGlobalMemAllocPtr
  443. {
  444. public:
  445. CInitGlobalMemAllocPtr()
  446. {
  447. char *pStr = (char*)Plat_GetCommandLineA();
  448. if ( pStr )
  449. {
  450. char tempStr[512];
  451. strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
  452. tempStr[ sizeof( tempStr ) - 1 ] = 0;
  453. _strupr( tempStr );
  454. CheckWindowsAllocSettings( tempStr );
  455. }
  456. #if defined(FORCE_PROCESS_HEAP)
  457. // This may cause EnableHeapMemAlloc to be called twice, but that's okay.
  458. EnableHeapMemAlloc( false );
  459. #endif
  460. }
  461. };
  462. CInitGlobalMemAllocPtr sg_InitGlobalMemAllocPtr;
  463. #endif
  464. #ifdef _WIN32
  465. //-----------------------------------------------------------------------------
  466. // Small block heap (multi-pool)
  467. //-----------------------------------------------------------------------------
  468. #ifndef NO_SBH
  469. #ifdef ALLOW_NOSBH
  470. static bool g_UsingSBH = true;
  471. #define UsingSBH() g_UsingSBH
  472. #else
  473. #define UsingSBH() true
  474. #endif
  475. #else
  476. #define UsingSBH() false
  477. #endif
  478. //-----------------------------------------------------------------------------
  479. //
  480. //-----------------------------------------------------------------------------
  481. template <typename T>
  482. inline T MemAlign( T val, size_t alignment )
  483. {
  484. return (T)( ( (size_t)val + alignment - 1 ) & ~( alignment - 1 ) );
  485. }
  486. //-----------------------------------------------------------------------------
  487. //
  488. //-----------------------------------------------------------------------------
  489. void CSmallBlockPool::Init( unsigned nBlockSize, byte *pBase, unsigned initialCommit )
  490. {
  491. if ( !( nBlockSize % MIN_SBH_ALIGN == 0 && nBlockSize >= MIN_SBH_BLOCK && nBlockSize >= sizeof(TSLNodeBase_t) ) )
  492. DebuggerBreak();
  493. m_nBlockSize = nBlockSize;
  494. m_pCommitLimit = m_pNextAlloc = m_pBase = pBase;
  495. m_pAllocLimit = m_pBase + MAX_POOL_REGION;
  496. if ( initialCommit )
  497. {
  498. initialCommit = MemAlign( initialCommit, SBH_PAGE_SIZE );
  499. if ( !VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE ) )
  500. {
  501. Assert( 0 );
  502. return;
  503. }
  504. m_pCommitLimit += initialCommit;
  505. }
  506. }
  507. size_t CSmallBlockPool::GetBlockSize()
  508. {
  509. return m_nBlockSize;
  510. }
  511. bool CSmallBlockPool::IsOwner( void *p )
  512. {
  513. return ( p >= m_pBase && p < m_pAllocLimit );
  514. }
  515. void *CSmallBlockPool::Alloc()
  516. {
  517. void *pResult = m_FreeList.Pop();
  518. if ( !pResult )
  519. {
  520. int nBlockSize = m_nBlockSize;
  521. byte *pCommitLimit;
  522. byte *pNextAlloc;
  523. for (;;)
  524. {
  525. pCommitLimit = m_pCommitLimit;
  526. pNextAlloc = m_pNextAlloc;
  527. if ( pNextAlloc + nBlockSize <= pCommitLimit )
  528. {
  529. if ( m_pNextAlloc.AssignIf( pNextAlloc, pNextAlloc + m_nBlockSize ) )
  530. {
  531. pResult = pNextAlloc;
  532. break;
  533. }
  534. }
  535. else
  536. {
  537. AUTO_LOCK( m_CommitMutex );
  538. if ( pCommitLimit == m_pCommitLimit )
  539. {
  540. if ( pCommitLimit + COMMIT_SIZE <= m_pAllocLimit )
  541. {
  542. if ( !VirtualAlloc( pCommitLimit, COMMIT_SIZE, VA_COMMIT_FLAGS, PAGE_READWRITE ) )
  543. {
  544. Assert( 0 );
  545. return NULL;
  546. }
  547. m_pCommitLimit = pCommitLimit + COMMIT_SIZE;
  548. }
  549. else
  550. {
  551. return NULL;
  552. }
  553. }
  554. }
  555. }
  556. }
  557. return pResult;
  558. }
  559. void CSmallBlockPool::Free( void *p )
  560. {
  561. Assert( IsOwner( p ) );
  562. m_FreeList.Push( p );
  563. }
  564. // Count the free blocks.
  565. int CSmallBlockPool::CountFreeBlocks()
  566. {
  567. return m_FreeList.Count();
  568. }
  569. // Size of committed memory managed by this heap:
  570. int CSmallBlockPool::GetCommittedSize()
  571. {
  572. unsigned totalSize = (unsigned)m_pCommitLimit - (unsigned)m_pBase;
  573. Assert( 0 != m_nBlockSize );
  574. return totalSize;
  575. }
  576. // Return the total blocks memory is committed for in the heap
  577. int CSmallBlockPool::CountCommittedBlocks()
  578. {
  579. return GetCommittedSize() / GetBlockSize();
  580. }
  581. // Count the number of allocated blocks in the heap:
  582. int CSmallBlockPool::CountAllocatedBlocks()
  583. {
  584. return CountCommittedBlocks( ) - ( CountFreeBlocks( ) + ( m_pCommitLimit - (byte *)m_pNextAlloc ) / GetBlockSize() );
  585. }
  586. int CSmallBlockPool::Compact()
  587. {
  588. int nBytesFreed = 0;
  589. if ( m_FreeList.Count() )
  590. {
  591. int i;
  592. int nFree = CountFreeBlocks();
  593. FreeBlock_t **pSortArray = (FreeBlock_t **)malloc( nFree * sizeof(FreeBlock_t *) ); // can't use new because will reenter
  594. if ( !pSortArray )
  595. {
  596. return 0;
  597. }
  598. i = 0;
  599. while ( i < nFree )
  600. {
  601. pSortArray[i++] = m_FreeList.Pop();
  602. }
  603. std::sort( pSortArray, pSortArray + nFree );
  604. byte *pOldNextAlloc = m_pNextAlloc;
  605. for ( i = nFree - 1; i >= 0; i-- )
  606. {
  607. if ( (byte *)pSortArray[i] == m_pNextAlloc - m_nBlockSize )
  608. {
  609. pSortArray[i] = NULL;
  610. m_pNextAlloc -= m_nBlockSize;
  611. }
  612. else
  613. {
  614. break;
  615. }
  616. }
  617. if ( pOldNextAlloc != m_pNextAlloc )
  618. {
  619. byte *pNewCommitLimit = MemAlign( (byte *)m_pNextAlloc, SBH_PAGE_SIZE );
  620. if ( pNewCommitLimit < m_pCommitLimit )
  621. {
  622. nBytesFreed = m_pCommitLimit - pNewCommitLimit;
  623. VirtualFree( pNewCommitLimit, nBytesFreed, MEM_DECOMMIT );
  624. m_pCommitLimit = pNewCommitLimit;
  625. }
  626. }
  627. if ( pSortArray[0] )
  628. {
  629. for ( i = 0; i < nFree ; i++ )
  630. {
  631. if ( !pSortArray[i] )
  632. {
  633. break;
  634. }
  635. m_FreeList.Push( pSortArray[i] );
  636. }
  637. }
  638. free( pSortArray );
  639. }
  640. return nBytesFreed;
  641. }
  642. //-----------------------------------------------------------------------------
  643. //
  644. //-----------------------------------------------------------------------------
  645. #define GetInitialCommitForPool( i ) 0
  646. CSmallBlockHeap::CSmallBlockHeap()
  647. {
  648. // Make sure that we return 64-bit addresses in 64-bit builds.
  649. ReserveBottomMemory();
  650. if ( !UsingSBH() )
  651. {
  652. return;
  653. }
  654. m_pBase = (byte *)VirtualAlloc( NULL, NUM_POOLS * MAX_POOL_REGION, VA_RESERVE_FLAGS, PAGE_NOACCESS );
  655. m_pLimit = m_pBase + NUM_POOLS * MAX_POOL_REGION;
  656. // Build a lookup table used to find the correct pool based on size
  657. const int MAX_TABLE = MAX_SBH_BLOCK >> 2;
  658. int i = 0;
  659. int nBytesElement = 0;
  660. byte *pCurBase = m_pBase;
  661. CSmallBlockPool *pCurPool = NULL;
  662. int iCurPool = 0;
  663. #if _M_X64
  664. // Blocks sized 0 - 256 are in pools in increments of 16
  665. for ( ; i < 64 && i < MAX_TABLE; i++ )
  666. {
  667. if ( (i + 1) % 4 == 1)
  668. {
  669. nBytesElement += 16;
  670. pCurPool = &m_Pools[iCurPool];
  671. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  672. iCurPool++;
  673. m_PoolLookup[i] = pCurPool;
  674. pCurBase += MAX_POOL_REGION;
  675. }
  676. else
  677. {
  678. m_PoolLookup[i] = pCurPool;
  679. }
  680. }
  681. #else
  682. // Blocks sized 0 - 128 are in pools in increments of 8
  683. for ( ; i < 32; i++ )
  684. {
  685. if ( (i + 1) % 2 == 1)
  686. {
  687. nBytesElement += 8;
  688. pCurPool = &m_Pools[iCurPool];
  689. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  690. iCurPool++;
  691. m_PoolLookup[i] = pCurPool;
  692. pCurBase += MAX_POOL_REGION;
  693. }
  694. else
  695. {
  696. m_PoolLookup[i] = pCurPool;
  697. }
  698. }
  699. // Blocks sized 129 - 256 are in pools in increments of 16
  700. for ( ; i < 64; i++ )
  701. {
  702. if ( (i + 1) % 4 == 1)
  703. {
  704. nBytesElement += 16;
  705. pCurPool = &m_Pools[iCurPool];
  706. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  707. iCurPool++;
  708. m_PoolLookup[i] = pCurPool;
  709. pCurBase += MAX_POOL_REGION;
  710. }
  711. else
  712. {
  713. m_PoolLookup[i] = pCurPool;
  714. }
  715. }
  716. #endif
  717. // Blocks sized 257 - 512 are in pools in increments of 32
  718. for ( ; i < 128; i++ )
  719. {
  720. if ( (i + 1) % 8 == 1)
  721. {
  722. nBytesElement += 32;
  723. pCurPool = &m_Pools[iCurPool];
  724. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  725. iCurPool++;
  726. m_PoolLookup[i] = pCurPool;
  727. pCurBase += MAX_POOL_REGION;
  728. }
  729. else
  730. {
  731. m_PoolLookup[i] = pCurPool;
  732. }
  733. }
  734. // Blocks sized 513 - 768 are in pools in increments of 64
  735. for ( ; i < 192; i++ )
  736. {
  737. if ( (i + 1) % 16 == 1)
  738. {
  739. nBytesElement += 64;
  740. pCurPool = &m_Pools[iCurPool];
  741. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  742. iCurPool++;
  743. m_PoolLookup[i] = pCurPool;
  744. pCurBase += MAX_POOL_REGION;
  745. }
  746. else
  747. {
  748. m_PoolLookup[i] = pCurPool;
  749. }
  750. }
  751. // Blocks sized 769 - 1024 are in pools in increments of 128
  752. for ( ; i < 256; i++ )
  753. {
  754. if ( (i + 1) % 32 == 1)
  755. {
  756. nBytesElement += 128;
  757. pCurPool = &m_Pools[iCurPool];
  758. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  759. iCurPool++;
  760. m_PoolLookup[i] = pCurPool;
  761. pCurBase += MAX_POOL_REGION;
  762. }
  763. else
  764. {
  765. m_PoolLookup[i] = pCurPool;
  766. }
  767. }
  768. // Blocks sized 1025 - 2048 are in pools in increments of 256
  769. for ( ; i < MAX_TABLE; i++ )
  770. {
  771. if ( (i + 1) % 64 == 1)
  772. {
  773. nBytesElement += 256;
  774. pCurPool = &m_Pools[iCurPool];
  775. pCurPool->Init( nBytesElement, pCurBase, GetInitialCommitForPool(iCurPool) );
  776. iCurPool++;
  777. m_PoolLookup[i] = pCurPool;
  778. pCurBase += MAX_POOL_REGION;
  779. }
  780. else
  781. {
  782. m_PoolLookup[i] = pCurPool;
  783. }
  784. }
  785. Assert( iCurPool == NUM_POOLS );
  786. }
  787. bool CSmallBlockHeap::ShouldUse( size_t nBytes )
  788. {
  789. return ( UsingSBH() && nBytes <= MAX_SBH_BLOCK );
  790. }
  791. bool CSmallBlockHeap::IsOwner( void * p )
  792. {
  793. return ( UsingSBH() && p >= m_pBase && p < m_pLimit );
  794. }
  795. void *CSmallBlockHeap::Alloc( size_t nBytes )
  796. {
  797. if ( nBytes == 0)
  798. {
  799. nBytes = 1;
  800. }
  801. Assert( ShouldUse( nBytes ) );
  802. CSmallBlockPool *pPool = FindPool( nBytes );
  803. void *p = pPool->Alloc();
  804. if ( p )
  805. {
  806. return p;
  807. }
  808. if ( s_StdMemAlloc.CallAllocFailHandler( nBytes ) >= nBytes )
  809. {
  810. p = pPool->Alloc();
  811. if ( p )
  812. {
  813. return p;
  814. }
  815. }
  816. void *pRet = malloc( nBytes );
  817. if ( !pRet )
  818. {
  819. s_StdMemAlloc.SetCRTAllocFailed( nBytes );
  820. }
  821. return pRet;
  822. }
  823. void *CSmallBlockHeap::Realloc( void *p, size_t nBytes )
  824. {
  825. if ( nBytes == 0)
  826. {
  827. nBytes = 1;
  828. }
  829. CSmallBlockPool *pOldPool = FindPool( p );
  830. CSmallBlockPool *pNewPool = ( ShouldUse( nBytes ) ) ? FindPool( nBytes ) : NULL;
  831. if ( pOldPool == pNewPool )
  832. {
  833. return p;
  834. }
  835. void *pNewBlock = NULL;
  836. if ( pNewPool )
  837. {
  838. pNewBlock = pNewPool->Alloc();
  839. if ( !pNewBlock )
  840. {
  841. if ( s_StdMemAlloc.CallAllocFailHandler( nBytes ) >= nBytes )
  842. {
  843. pNewBlock = pNewPool->Alloc();
  844. }
  845. }
  846. }
  847. if ( !pNewBlock )
  848. {
  849. pNewBlock = malloc( nBytes );
  850. if ( !pNewBlock )
  851. {
  852. s_StdMemAlloc.SetCRTAllocFailed( nBytes );
  853. }
  854. }
  855. if ( pNewBlock )
  856. {
  857. int nBytesCopy = min( nBytes, pOldPool->GetBlockSize() );
  858. memcpy( pNewBlock, p, nBytesCopy );
  859. }
  860. pOldPool->Free( p );
  861. return pNewBlock;
  862. }
  863. void CSmallBlockHeap::Free( void *p )
  864. {
  865. CSmallBlockPool *pPool = FindPool( p );
  866. pPool->Free( p );
  867. }
  868. size_t CSmallBlockHeap::GetSize( void *p )
  869. {
  870. CSmallBlockPool *pPool = FindPool( p );
  871. return pPool->GetBlockSize();
  872. }
  873. void CSmallBlockHeap::DumpStats( FILE *pFile )
  874. {
  875. bool bSpew = true;
  876. if ( pFile )
  877. {
  878. for ( int i = 0; i < NUM_POOLS; i++ )
  879. {
  880. // output for vxconsole parsing
  881. fprintf( pFile, "Pool %i: Size: %llu Allocated: %i Free: %i Committed: %i CommittedSize: %i\n",
  882. i,
  883. (uint64)m_Pools[i].GetBlockSize(),
  884. m_Pools[i].CountAllocatedBlocks(),
  885. m_Pools[i].CountFreeBlocks(),
  886. m_Pools[i].CountCommittedBlocks(),
  887. m_Pools[i].GetCommittedSize() );
  888. }
  889. bSpew = false;
  890. }
  891. if ( bSpew )
  892. {
  893. unsigned bytesCommitted = 0;
  894. unsigned bytesAllocated = 0;
  895. for ( int i = 0; i < NUM_POOLS; i++ )
  896. {
  897. Msg( "Pool %i: (size: %llu) blocks: allocated:%i free:%i committed:%i (committed size:%u kb)\n",i, (uint64)m_Pools[i].GetBlockSize(),m_Pools[i].CountAllocatedBlocks(), m_Pools[i].CountFreeBlocks(),m_Pools[i].CountCommittedBlocks(), m_Pools[i].GetCommittedSize() / 1024);
  898. bytesCommitted += m_Pools[i].GetCommittedSize();
  899. bytesAllocated += ( m_Pools[i].CountAllocatedBlocks() * m_Pools[i].GetBlockSize() );
  900. }
  901. Msg( "Totals: Committed:%u kb Allocated:%u kb\n", bytesCommitted / 1024, bytesAllocated / 1024 );
  902. }
  903. }
  904. int CSmallBlockHeap::Compact()
  905. {
  906. int nBytesFreed = 0;
  907. for( int i = 0; i < NUM_POOLS; i++ )
  908. {
  909. nBytesFreed += m_Pools[i].Compact();
  910. }
  911. return nBytesFreed;
  912. }
  913. CSmallBlockPool *CSmallBlockHeap::FindPool( size_t nBytes )
  914. {
  915. return m_PoolLookup[(nBytes - 1) >> 2];
  916. }
  917. CSmallBlockPool *CSmallBlockHeap::FindPool( void *p )
  918. {
  919. size_t i = ((byte *)p - m_pBase) / MAX_POOL_REGION;
  920. return &m_Pools[i];
  921. }
  922. #endif
  923. #if USE_PHYSICAL_SMALL_BLOCK_HEAP
  924. CX360SmallBlockPool *CX360SmallBlockPool::gm_AddressToPool[BYTES_X360_SBH/PAGESIZE_X360_SBH];
  925. byte *CX360SmallBlockPool::gm_pPhysicalBlock;
  926. byte *CX360SmallBlockPool::gm_pPhysicalBase;
  927. byte *CX360SmallBlockPool::gm_pPhysicalLimit;
  928. void CX360SmallBlockPool::Init( unsigned nBlockSize )
  929. {
  930. if ( !gm_pPhysicalBlock )
  931. {
  932. gm_pPhysicalBase = (byte *)XPhysicalAlloc( BYTES_X360_SBH, MAXULONG_PTR, 4096, PAGE_READWRITE | MEM_16MB_PAGES );
  933. gm_pPhysicalLimit = gm_pPhysicalBase + BYTES_X360_SBH;
  934. gm_pPhysicalBlock = gm_pPhysicalBase;
  935. }
  936. if ( !( nBlockSize % MIN_SBH_ALIGN == 0 && nBlockSize >= MIN_SBH_BLOCK && nBlockSize >= sizeof(TSLNodeBase_t) ) )
  937. DebuggerBreak();
  938. m_nBlockSize = nBlockSize;
  939. m_pCurBlockEnd = m_pNextAlloc = NULL;
  940. m_CommittedSize = 0;
  941. }
  942. size_t CX360SmallBlockPool::GetBlockSize()
  943. {
  944. return m_nBlockSize;
  945. }
  946. bool CX360SmallBlockPool::IsOwner( void *p )
  947. {
  948. return ( FindPool( p ) == this );
  949. }
  950. void *CX360SmallBlockPool::Alloc()
  951. {
  952. void *pResult = m_FreeList.Pop();
  953. if ( !pResult )
  954. {
  955. if ( !m_pNextAlloc && gm_pPhysicalBlock >= gm_pPhysicalLimit )
  956. {
  957. return NULL;
  958. }
  959. int nBlockSize = m_nBlockSize;
  960. byte *pCurBlockEnd;
  961. byte *pNextAlloc;
  962. for (;;)
  963. {
  964. pCurBlockEnd = m_pCurBlockEnd;
  965. pNextAlloc = m_pNextAlloc;
  966. if ( pNextAlloc + nBlockSize <= pCurBlockEnd )
  967. {
  968. if ( m_pNextAlloc.AssignIf( pNextAlloc, pNextAlloc + m_nBlockSize ) )
  969. {
  970. pResult = pNextAlloc;
  971. break;
  972. }
  973. }
  974. else
  975. {
  976. AUTO_LOCK( m_CommitMutex );
  977. if ( pCurBlockEnd == m_pCurBlockEnd )
  978. {
  979. for (;;)
  980. {
  981. if ( gm_pPhysicalBlock >= gm_pPhysicalLimit )
  982. {
  983. m_pCurBlockEnd = m_pNextAlloc = NULL;
  984. return NULL;
  985. }
  986. byte *pPhysicalBlock = gm_pPhysicalBlock;
  987. if ( ThreadInterlockedAssignPointerIf( (void **)&gm_pPhysicalBlock, (void *)(pPhysicalBlock + PAGESIZE_X360_SBH), (void *)pPhysicalBlock ) )
  988. {
  989. int index = (size_t)((byte *)pPhysicalBlock - gm_pPhysicalBase) / PAGESIZE_X360_SBH;
  990. gm_AddressToPool[index] = this;
  991. m_pNextAlloc = pPhysicalBlock;
  992. m_CommittedSize += PAGESIZE_X360_SBH;
  993. __sync();
  994. m_pCurBlockEnd = pPhysicalBlock + PAGESIZE_X360_SBH;
  995. break;
  996. }
  997. }
  998. }
  999. }
  1000. }
  1001. }
  1002. return pResult;
  1003. }
  1004. void CX360SmallBlockPool::Free( void *p )
  1005. {
  1006. Assert( IsOwner( p ) );
  1007. m_FreeList.Push( p );
  1008. }
  1009. // Count the free blocks.
  1010. int CX360SmallBlockPool::CountFreeBlocks()
  1011. {
  1012. return m_FreeList.Count();
  1013. }
  1014. // Size of committed memory managed by this heap:
  1015. int CX360SmallBlockPool::GetCommittedSize()
  1016. {
  1017. return m_CommittedSize;
  1018. }
  1019. // Return the total blocks memory is committed for in the heap
  1020. int CX360SmallBlockPool::CountCommittedBlocks()
  1021. {
  1022. return GetCommittedSize() / GetBlockSize();
  1023. }
  1024. // Count the number of allocated blocks in the heap:
  1025. int CX360SmallBlockPool::CountAllocatedBlocks()
  1026. {
  1027. int nBytesPossible = ( m_pNextAlloc ) ? ( m_pCurBlockEnd - (byte *)m_pNextAlloc ) : 0;
  1028. return CountCommittedBlocks( ) - ( CountFreeBlocks( ) + nBytesPossible / GetBlockSize() );
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. //
  1032. //-----------------------------------------------------------------------------
  1033. #define GetInitialCommitForPool( i ) 0
  1034. CX360SmallBlockHeap::CX360SmallBlockHeap()
  1035. {
  1036. if ( !UsingSBH() )
  1037. {
  1038. return;
  1039. }
  1040. // Build a lookup table used to find the correct pool based on size
  1041. const int MAX_TABLE = MAX_SBH_BLOCK >> 2;
  1042. int i = 0;
  1043. int nBytesElement = 0;
  1044. CX360SmallBlockPool *pCurPool = NULL;
  1045. int iCurPool = 0;
  1046. // Blocks sized 0 - 128 are in pools in increments of 8
  1047. for ( ; i < 32; i++ )
  1048. {
  1049. if ( (i + 1) % 2 == 1)
  1050. {
  1051. nBytesElement += 8;
  1052. pCurPool = &m_Pools[iCurPool];
  1053. pCurPool->Init( nBytesElement );
  1054. iCurPool++;
  1055. m_PoolLookup[i] = pCurPool;
  1056. }
  1057. else
  1058. {
  1059. m_PoolLookup[i] = pCurPool;
  1060. }
  1061. }
  1062. // Blocks sized 129 - 256 are in pools in increments of 16
  1063. for ( ; i < 64; i++ )
  1064. {
  1065. if ( (i + 1) % 4 == 1)
  1066. {
  1067. nBytesElement += 16;
  1068. pCurPool = &m_Pools[iCurPool];
  1069. pCurPool->Init( nBytesElement );
  1070. iCurPool++;
  1071. m_PoolLookup[i] = pCurPool;
  1072. }
  1073. else
  1074. {
  1075. m_PoolLookup[i] = pCurPool;
  1076. }
  1077. }
  1078. // Blocks sized 257 - 512 are in pools in increments of 32
  1079. for ( ; i < 128; i++ )
  1080. {
  1081. if ( (i + 1) % 8 == 1)
  1082. {
  1083. nBytesElement += 32;
  1084. pCurPool = &m_Pools[iCurPool];
  1085. pCurPool->Init( nBytesElement );
  1086. iCurPool++;
  1087. m_PoolLookup[i] = pCurPool;
  1088. }
  1089. else
  1090. {
  1091. m_PoolLookup[i] = pCurPool;
  1092. }
  1093. }
  1094. // Blocks sized 513 - 768 are in pools in increments of 64
  1095. for ( ; i < 192; i++ )
  1096. {
  1097. if ( (i + 1) % 16 == 1)
  1098. {
  1099. nBytesElement += 64;
  1100. pCurPool = &m_Pools[iCurPool];
  1101. pCurPool->Init( nBytesElement );
  1102. iCurPool++;
  1103. m_PoolLookup[i] = pCurPool;
  1104. }
  1105. else
  1106. {
  1107. m_PoolLookup[i] = pCurPool;
  1108. }
  1109. }
  1110. // Blocks sized 769 - 1024 are in pools in increments of 128
  1111. for ( ; i < 256; i++ )
  1112. {
  1113. if ( (i + 1) % 32 == 1)
  1114. {
  1115. nBytesElement += 128;
  1116. pCurPool = &m_Pools[iCurPool];
  1117. pCurPool->Init( nBytesElement );
  1118. iCurPool++;
  1119. m_PoolLookup[i] = pCurPool;
  1120. }
  1121. else
  1122. {
  1123. m_PoolLookup[i] = pCurPool;
  1124. }
  1125. }
  1126. // Blocks sized 1025 - 2048 are in pools in increments of 256
  1127. for ( ; i < MAX_TABLE; i++ )
  1128. {
  1129. if ( (i + 1) % 64 == 1)
  1130. {
  1131. nBytesElement += 256;
  1132. pCurPool = &m_Pools[iCurPool];
  1133. pCurPool->Init( nBytesElement );
  1134. iCurPool++;
  1135. m_PoolLookup[i] = pCurPool;
  1136. }
  1137. else
  1138. {
  1139. m_PoolLookup[i] = pCurPool;
  1140. }
  1141. }
  1142. Assert( iCurPool == NUM_POOLS );
  1143. }
  1144. bool CX360SmallBlockHeap::ShouldUse( size_t nBytes )
  1145. {
  1146. return ( UsingSBH() && nBytes <= MAX_SBH_BLOCK );
  1147. }
  1148. bool CX360SmallBlockHeap::IsOwner( void * p )
  1149. {
  1150. int index = (size_t)((byte *)p - CX360SmallBlockPool::gm_pPhysicalBase) / PAGESIZE_X360_SBH;
  1151. return ( UsingSBH() && ( index >= 0 && index < ARRAYSIZE(CX360SmallBlockPool::gm_AddressToPool) ) );
  1152. }
  1153. void *CX360SmallBlockHeap::Alloc( size_t nBytes )
  1154. {
  1155. if ( nBytes == 0)
  1156. {
  1157. nBytes = 1;
  1158. }
  1159. Assert( ShouldUse( nBytes ) );
  1160. CX360SmallBlockPool *pPool = FindPool( nBytes );
  1161. void *p = pPool->Alloc();
  1162. if ( p )
  1163. {
  1164. return p;
  1165. }
  1166. return GetStandardSBH()->Alloc( nBytes );
  1167. }
  1168. void *CX360SmallBlockHeap::Realloc( void *p, size_t nBytes )
  1169. {
  1170. if ( nBytes == 0)
  1171. {
  1172. nBytes = 1;
  1173. }
  1174. CX360SmallBlockPool *pOldPool = FindPool( p );
  1175. CX360SmallBlockPool *pNewPool = ( ShouldUse( nBytes ) ) ? FindPool( nBytes ) : NULL;
  1176. if ( pOldPool == pNewPool )
  1177. {
  1178. return p;
  1179. }
  1180. void *pNewBlock = NULL;
  1181. if ( pNewPool )
  1182. {
  1183. pNewBlock = pNewPool->Alloc();
  1184. if ( !pNewBlock )
  1185. {
  1186. pNewBlock = GetStandardSBH()->Alloc( nBytes );
  1187. }
  1188. }
  1189. if ( !pNewBlock )
  1190. {
  1191. pNewBlock = malloc( nBytes );
  1192. }
  1193. if ( pNewBlock )
  1194. {
  1195. int nBytesCopy = min( nBytes, pOldPool->GetBlockSize() );
  1196. memcpy( pNewBlock, p, nBytesCopy );
  1197. }
  1198. pOldPool->Free( p );
  1199. return pNewBlock;
  1200. }
  1201. void CX360SmallBlockHeap::Free( void *p )
  1202. {
  1203. CX360SmallBlockPool *pPool = FindPool( p );
  1204. pPool->Free( p );
  1205. }
  1206. size_t CX360SmallBlockHeap::GetSize( void *p )
  1207. {
  1208. CX360SmallBlockPool *pPool = FindPool( p );
  1209. return pPool->GetBlockSize();
  1210. }
  1211. void CX360SmallBlockHeap::DumpStats( FILE *pFile )
  1212. {
  1213. bool bSpew = true;
  1214. if ( pFile )
  1215. {
  1216. for( int i = 0; i < NUM_POOLS; i++ )
  1217. {
  1218. // output for vxconsole parsing
  1219. fprintf( pFile, "Pool %i: Size: %u Allocated: %i Free: %i Committed: %i CommittedSize: %i\n",
  1220. i,
  1221. m_Pools[i].GetBlockSize(),
  1222. m_Pools[i].CountAllocatedBlocks(),
  1223. m_Pools[i].CountFreeBlocks(),
  1224. m_Pools[i].CountCommittedBlocks(),
  1225. m_Pools[i].GetCommittedSize() );
  1226. }
  1227. bSpew = false;
  1228. }
  1229. if ( bSpew )
  1230. {
  1231. unsigned bytesCommitted = 0;
  1232. unsigned bytesAllocated = 0;
  1233. for( int i = 0; i < NUM_POOLS; i++ )
  1234. {
  1235. bytesCommitted += m_Pools[i].GetCommittedSize();
  1236. bytesAllocated += ( m_Pools[i].CountAllocatedBlocks() * m_Pools[i].GetBlockSize() );
  1237. }
  1238. Msg( "Totals: Committed:%u kb Allocated:%u kb\n", bytesCommitted / 1024, bytesAllocated / 1024 );
  1239. }
  1240. }
  1241. CSmallBlockHeap *CX360SmallBlockHeap::GetStandardSBH()
  1242. {
  1243. return &(GET_OUTER( CStdMemAlloc, m_LargePageSmallBlockHeap )->m_SmallBlockHeap);
  1244. }
  1245. CX360SmallBlockPool *CX360SmallBlockHeap::FindPool( size_t nBytes )
  1246. {
  1247. return m_PoolLookup[(nBytes - 1) >> 2];
  1248. }
  1249. CX360SmallBlockPool *CX360SmallBlockHeap::FindPool( void *p )
  1250. {
  1251. return CX360SmallBlockPool::FindPool( p );
  1252. }
  1253. #endif
  1254. //-----------------------------------------------------------------------------
  1255. // Release versions
  1256. //-----------------------------------------------------------------------------
  1257. void *CStdMemAlloc::Alloc( size_t nSize )
  1258. {
  1259. PROFILE_ALLOC(Malloc);
  1260. void *pMem;
  1261. #ifdef _WIN32
  1262. #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
  1263. if ( m_LargePageSmallBlockHeap.ShouldUse( nSize ) )
  1264. {
  1265. pMem = m_LargePageSmallBlockHeap.Alloc( nSize );
  1266. ApplyMemoryInitializations( pMem, nSize );
  1267. return pMem;
  1268. }
  1269. #endif
  1270. if ( m_SmallBlockHeap.ShouldUse( nSize ) )
  1271. {
  1272. pMem = m_SmallBlockHeap.Alloc( nSize );
  1273. ApplyMemoryInitializations( pMem, nSize );
  1274. return pMem;
  1275. }
  1276. #endif
  1277. pMem = malloc( nSize );
  1278. ApplyMemoryInitializations( pMem, nSize );
  1279. if ( !pMem )
  1280. {
  1281. SetCRTAllocFailed( nSize );
  1282. }
  1283. return pMem;
  1284. }
  1285. void *CStdMemAlloc::Realloc( void *pMem, size_t nSize )
  1286. {
  1287. if ( !pMem )
  1288. {
  1289. return Alloc( nSize );
  1290. }
  1291. PROFILE_ALLOC(Realloc);
  1292. #ifdef MEM_SBH_ENABLED
  1293. #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
  1294. if ( m_LargePageSmallBlockHeap.IsOwner( pMem ) )
  1295. {
  1296. return m_LargePageSmallBlockHeap.Realloc( pMem, nSize );
  1297. }
  1298. #endif
  1299. if ( m_SmallBlockHeap.IsOwner( pMem ) )
  1300. {
  1301. return m_SmallBlockHeap.Realloc( pMem, nSize );
  1302. }
  1303. #endif
  1304. void *pRet = realloc( pMem, nSize );
  1305. if ( !pRet )
  1306. {
  1307. SetCRTAllocFailed( nSize );
  1308. }
  1309. return pRet;
  1310. }
  1311. void CStdMemAlloc::Free( void *pMem )
  1312. {
  1313. if ( !pMem )
  1314. {
  1315. return;
  1316. }
  1317. PROFILE_ALLOC(Free);
  1318. #ifdef MEM_SBH_ENABLED
  1319. #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
  1320. if ( m_LargePageSmallBlockHeap.IsOwner( pMem ) )
  1321. {
  1322. m_LargePageSmallBlockHeap.Free( pMem );
  1323. return;
  1324. }
  1325. #endif
  1326. if ( m_SmallBlockHeap.IsOwner( pMem ) )
  1327. {
  1328. m_SmallBlockHeap.Free( pMem );
  1329. return;
  1330. }
  1331. #endif
  1332. free( pMem );
  1333. }
  1334. void *CStdMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
  1335. {
  1336. return NULL;
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Debug versions
  1340. //-----------------------------------------------------------------------------
  1341. void *CStdMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
  1342. {
  1343. return CStdMemAlloc::Alloc( nSize );
  1344. }
  1345. void *CStdMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
  1346. {
  1347. return CStdMemAlloc::Realloc( pMem, nSize );
  1348. }
  1349. void CStdMemAlloc::Free( void *pMem, const char *pFileName, int nLine )
  1350. {
  1351. CStdMemAlloc::Free( pMem );
  1352. }
  1353. void *CStdMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
  1354. {
  1355. return NULL;
  1356. }
  1357. #if defined (LINUX)
  1358. #include <malloc.h>
  1359. #elif defined (OSX)
  1360. #define malloc_usable_size( ptr ) malloc_size( ptr )
  1361. extern "C" {
  1362. extern size_t malloc_size( const void *ptr );
  1363. }
  1364. #endif
  1365. //-----------------------------------------------------------------------------
  1366. // Returns size of a particular allocation
  1367. //-----------------------------------------------------------------------------
  1368. size_t CStdMemAlloc::GetSize( void *pMem )
  1369. {
  1370. #ifdef MEM_SBH_ENABLED
  1371. if ( !pMem )
  1372. return CalcHeapUsed();
  1373. else
  1374. {
  1375. #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
  1376. if ( m_LargePageSmallBlockHeap.IsOwner( pMem ) )
  1377. {
  1378. return m_LargePageSmallBlockHeap.GetSize( pMem );
  1379. }
  1380. #endif
  1381. if ( m_SmallBlockHeap.IsOwner( pMem ) )
  1382. {
  1383. return m_SmallBlockHeap.GetSize( pMem );
  1384. }
  1385. return _msize( pMem );
  1386. }
  1387. #else
  1388. return malloc_usable_size( pMem );
  1389. #endif
  1390. }
  1391. //-----------------------------------------------------------------------------
  1392. // Force file + line information for an allocation
  1393. //-----------------------------------------------------------------------------
  1394. void CStdMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
  1395. {
  1396. }
  1397. void CStdMemAlloc::PopAllocDbgInfo()
  1398. {
  1399. }
  1400. //-----------------------------------------------------------------------------
  1401. // FIXME: Remove when we make our own heap! Crt stuff we're currently using
  1402. //-----------------------------------------------------------------------------
  1403. long CStdMemAlloc::CrtSetBreakAlloc( long lNewBreakAlloc )
  1404. {
  1405. return 0;
  1406. }
  1407. int CStdMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
  1408. {
  1409. return 0;
  1410. }
  1411. int CStdMemAlloc::CrtIsValidHeapPointer( const void *pMem )
  1412. {
  1413. return 1;
  1414. }
  1415. int CStdMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
  1416. {
  1417. return 1;
  1418. }
  1419. int CStdMemAlloc::CrtCheckMemory( void )
  1420. {
  1421. return 1;
  1422. }
  1423. int CStdMemAlloc::CrtSetDbgFlag( int nNewFlag )
  1424. {
  1425. return 0;
  1426. }
  1427. void CStdMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
  1428. {
  1429. }
  1430. // FIXME: Remove when we have our own allocator
  1431. void* CStdMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
  1432. {
  1433. return 0;
  1434. }
  1435. void* CStdMemAlloc::CrtSetReportHook( void* pfnNewHook )
  1436. {
  1437. return 0;
  1438. }
  1439. int CStdMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
  1440. int nLine, const char * szModule, const char * pMsg )
  1441. {
  1442. return 0;
  1443. }
  1444. int CStdMemAlloc::heapchk()
  1445. {
  1446. #ifdef _WIN32
  1447. return _HEAPOK;
  1448. #else
  1449. return 1;
  1450. #endif
  1451. }
  1452. void CStdMemAlloc::DumpStats()
  1453. {
  1454. DumpStatsFileBase( "memstats" );
  1455. }
  1456. void CStdMemAlloc::DumpStatsFileBase( char const *pchFileBase )
  1457. {
  1458. #ifdef _WIN32
  1459. char filename[ 512 ];
  1460. _snprintf( filename, sizeof( filename ) - 1, ( IsX360() ) ? "D:\\%s.txt" : "%s.txt", pchFileBase );
  1461. filename[ sizeof( filename ) - 1 ] = 0;
  1462. FILE *pFile = fopen( filename, "wt" );
  1463. #ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
  1464. fprintf( pFile, "X360 Large Page SBH:\n" );
  1465. m_LargePageSmallBlockHeap.DumpStats(pFile);
  1466. #endif
  1467. fprintf( pFile, "\nSBH:\n" );
  1468. m_SmallBlockHeap.DumpStats(pFile); // Dump statistics to small block heap
  1469. #if defined( _X360 ) && !defined( _RETAIL )
  1470. XBX_rMemDump( filename );
  1471. #endif
  1472. fclose( pFile );
  1473. #endif
  1474. }
  1475. void CStdMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
  1476. {
  1477. if ( !pUsedMemory || !pFreeMemory )
  1478. return;
  1479. #if defined ( _X360 )
  1480. // GlobalMemoryStatus tells us how much physical memory is free
  1481. MEMORYSTATUS stat;
  1482. ::GlobalMemoryStatus( &stat );
  1483. *pFreeMemory = stat.dwAvailPhys;
  1484. // NOTE: we do not count free memory inside our small block heaps, as this could be misleading
  1485. // (even with lots of SBH memory free, a single allocation over 2kb can still fail)
  1486. #if defined( USE_DLMALLOC )
  1487. // Account for free memory contained within DLMalloc
  1488. for ( int i = 0; i < ARRAYSIZE( g_AllocRegions ); i++ )
  1489. {
  1490. mallinfo info = mspace_mallinfo( g_AllocRegions[ i ] );
  1491. *pFreeMemory += info.fordblks;
  1492. }
  1493. #endif
  1494. // Used is total minus free (discount the 32MB system reservation)
  1495. *pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
  1496. #else
  1497. // no data
  1498. *pFreeMemory = 0;
  1499. *pUsedMemory = 0;
  1500. #endif
  1501. }
  1502. void CStdMemAlloc::CompactHeap()
  1503. {
  1504. #if !defined( NO_SBH ) && defined( _WIN32 )
  1505. int nBytesRecovered = m_SmallBlockHeap.Compact();
  1506. Msg( "Compact freed %d bytes\n", nBytesRecovered );
  1507. #endif
  1508. }
  1509. MemAllocFailHandler_t CStdMemAlloc::SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler )
  1510. {
  1511. MemAllocFailHandler_t pfnPrevious = m_pfnFailHandler;
  1512. m_pfnFailHandler = pfnMemAllocFailHandler;
  1513. return pfnPrevious;
  1514. }
  1515. size_t CStdMemAlloc::DefaultFailHandler( size_t nBytes )
  1516. {
  1517. if ( IsX360() && !IsRetail() )
  1518. {
  1519. #ifdef _X360
  1520. ExecuteOnce(
  1521. {
  1522. char buffer[256];
  1523. _snprintf( buffer, sizeof( buffer ), "***** Memory pool overflow, attempted allocation size: %u ****\n", nBytes );
  1524. XBX_OutputDebugString( buffer );
  1525. }
  1526. );
  1527. #endif
  1528. }
  1529. return 0;
  1530. }
  1531. #if defined( _MEMTEST )
  1532. void CStdMemAlloc::void SetStatsExtraInfo( const char *pMapName, const char *pComment )
  1533. {
  1534. }
  1535. #endif
  1536. void CStdMemAlloc::SetCRTAllocFailed( size_t nSize )
  1537. {
  1538. m_sMemoryAllocFailed = nSize;
  1539. MemAllocOOMError( nSize );
  1540. }
  1541. size_t CStdMemAlloc::MemoryAllocFailed()
  1542. {
  1543. return m_sMemoryAllocFailed;
  1544. }
  1545. #endif
  1546. void ReserveBottomMemory()
  1547. {
  1548. // If we are running a 64-bit build then reserve all addresses below the
  1549. // 4 GB line to push as many pointers as possible above the line.
  1550. #ifdef PLATFORM_WINDOWS_PC64
  1551. // Avoid the cost of calling this multiple times.
  1552. static bool s_initialized = false;
  1553. if ( s_initialized )
  1554. return;
  1555. s_initialized = true;
  1556. // If AppVerifier is enabled then memory reservations get turned into committed
  1557. // memory in the working set. This means that ReserveBottomMemory() can end
  1558. // up adding almost 4 GB to the working set, which is a significant problem if
  1559. // you run many processes in parallel. Therefore, if vfbasics.dll (part of AppVerifier)
  1560. // is loaded, don't do the reservation.
  1561. HMODULE vfBasicsDLL = GetModuleHandle( "vfbasics.dll" );
  1562. if ( vfBasicsDLL )
  1563. return;
  1564. // Start by reserving large blocks of memory. When those reservations
  1565. // have exhausted the bottom 4 GB then halve the size and try again.
  1566. // The granularity for reserving address space is 64 KB so if we wanted
  1567. // to reserve every single page we would need to continue down to 64 KB.
  1568. // However stopping at 1 MB is sufficient because it prevents the Windows
  1569. // heap (and dlmalloc and the small block heap) from grabbing address space
  1570. // from the bottom 4 GB, while still allowing Steam to allocate a few pages
  1571. // for setting up detours.
  1572. const size_t LOW_MEM_LINE = 0x100000000LL;
  1573. size_t totalReservation = 0;
  1574. size_t numVAllocs = 0;
  1575. size_t numHeapAllocs = 0;
  1576. for ( size_t blockSize = 256 * 1024 * 1024; blockSize >= 1024 * 1024; blockSize /= 2 )
  1577. {
  1578. for (;;)
  1579. {
  1580. void* p = VirtualAlloc( 0, blockSize, MEM_RESERVE, PAGE_NOACCESS );
  1581. if ( !p )
  1582. break;
  1583. if ( (size_t)p >= LOW_MEM_LINE )
  1584. {
  1585. // We don't need this memory, so release it completely.
  1586. VirtualFree( p, 0, MEM_RELEASE );
  1587. break;
  1588. }
  1589. totalReservation += blockSize;
  1590. ++numVAllocs;
  1591. }
  1592. }
  1593. // Now repeat the same process but making heap allocations, to use up the
  1594. // already committed heap blocks that are below the 4 GB line. Now we start
  1595. // with 64-KB allocations and proceed down to 16-byte allocations.
  1596. HANDLE heap = GetProcessHeap();
  1597. for ( size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2 )
  1598. {
  1599. for (;;)
  1600. {
  1601. void* p = HeapAlloc( heap, 0, blockSize );
  1602. if ( !p )
  1603. break;
  1604. if ( (size_t)p >= LOW_MEM_LINE )
  1605. {
  1606. // We don't need this memory, so release it completely.
  1607. HeapFree( heap, 0, p );
  1608. break;
  1609. }
  1610. totalReservation += blockSize;
  1611. ++numHeapAllocs;
  1612. }
  1613. }
  1614. // Print diagnostics showing how many allocations we had to make in order to
  1615. // reserve all of low memory. In one test run it took 55 virtual allocs and
  1616. // 85 heap allocs. Note that since the process may have multiple heaps (each
  1617. // CRT seems to have its own) there is likely to be a few MB of address space
  1618. // that was previously reserved and is available to be handed out by some allocators.
  1619. //char buffer[1000];
  1620. //sprintf_s( buffer, "Reserved %1.3f MB (%d vallocs, %d heap allocs) to keep allocations out of low-memory.\n",
  1621. // totalReservation / (1024 * 1024.0), (int)numVAllocs, (int)numHeapAllocs );
  1622. // Can't use Msg here because it isn't necessarily initialized yet.
  1623. //OutputDebugString( buffer );
  1624. #endif
  1625. }
  1626. #endif // STEAM