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.

682 lines
24 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Memory allocation!
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "pch_tier0.h"
  8. #include "tier0/dbg.h"
  9. #include "tier0/memalloc.h"
  10. #include "memstd.h"
  11. #if !defined(NO_MALLOC_OVERRIDE)
  12. #if defined( _WIN32 )
  13. #define OVERRIDE override
  14. // warning C4481: nonstandard extension used: override specifier 'override'
  15. #pragma warning( disable : 4481 )
  16. #ifdef _WIN64
  17. // Set the new-style define that indicates a a 64-bit Windows PC
  18. #define PLATFORM_WINDOWS_PC64 1
  19. LONGLONG
  20. FORCEINLINE
  21. InterlockedExchangeAdd64(
  22. __inout LONGLONG volatile *Addend,
  23. __in LONGLONG Value
  24. );
  25. #else
  26. // Set the new-style define that indicates a a 32-bit Windows PC
  27. #define PLATFORM_WINDOWS_PC32 1
  28. #endif
  29. // Support for CHeapMemAlloc for easy switching to using the process heap.
  30. // Track this to decide how to handle out-of-memory.
  31. static bool s_bPageHeapEnabled = false;
  32. //-----------------------------------------------------------------------------
  33. // IMemAlloc must guarantee 16-byte alignment for 16n-byte allocations, so we just
  34. // force 16-byte alignment under win32 (the win64 system heap already 16-byte aligns).
  35. // TODO: this padding negates some of the buffer-overrun protection provided by pageheap, so...
  36. // we should fill padding bytes with a known pattern which is checked in realloc/free
  37. #ifdef PLATFORM_WINDOWS_PC32
  38. #define FORCED_ALIGNMENT 16
  39. #else
  40. #define FORCED_ALIGNMENT 0
  41. #endif
  42. // Round a size up to a multiple of 4 KB to aid in calculating how much
  43. // memory is required if full pageheap is enabled.
  44. static size_t RoundUpToPage( size_t nSize )
  45. {
  46. nSize += 0xFFF;
  47. nSize &= ~0xFFF;
  48. return nSize;
  49. }
  50. static void InterlockedAddSizeT( size_t volatile *Addend, size_t Value )
  51. {
  52. #ifdef PLATFORM_WINDOWS_PC32
  53. // Convenience function to deal with the necessary type-casting
  54. InterlockedExchangeAdd( ( LONG* )Addend, LONG( Value ) );
  55. #else
  56. InterlockedExchangeAdd64( ( LONGLONG* )Addend, LONGLONG( Value ) );
  57. #endif
  58. }
  59. // CHeapDefault supplies default implementations for as many functions as
  60. // possible so that a heap implementation can be as simple as possible.
  61. class CHeapDefault : public IMemAlloc
  62. {
  63. // Since we define the debug versions of Alloc/Realloc/Free in this class but
  64. // not the release versions we implicitly hide the release implementations, which
  65. // makes it impossible for us to call them in order to implement the debug
  66. // versions. These using directives pull these three names into this namespace
  67. // so that we can call them.
  68. using IMemAlloc::Alloc;
  69. using IMemAlloc::Realloc;
  70. using IMemAlloc::Free;
  71. // Release versions
  72. // Alloc, Realloc, and Free must be implemented
  73. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) OVERRIDE { return 0; }
  74. // Debug versions
  75. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Alloc( nSize ); }
  76. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Realloc(pMem, nSize); }
  77. virtual void Free( void *pMem, const char *pFileName, int nLine ) OVERRIDE { Free( pMem ); }
  78. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
  79. // GetSize must be implemented
  80. // Force file + line information for an allocation
  81. virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) OVERRIDE {}
  82. virtual void PopAllocDbgInfo() OVERRIDE {}
  83. // FIXME: Remove when we have our own allocator
  84. // these methods of the Crt debug code is used in our codebase currently
  85. virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc ) OVERRIDE { return 0; }
  86. virtual int CrtSetReportMode( int nReportType, int nReportMode ) OVERRIDE { return 0; }
  87. virtual int CrtIsValidHeapPointer( const void *pMem ) OVERRIDE { return 0; }
  88. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) OVERRIDE { return 0; }
  89. virtual int CrtCheckMemory( void ) OVERRIDE { return 0; }
  90. virtual int CrtSetDbgFlag( int nNewFlag ) OVERRIDE { return 0; }
  91. virtual void CrtMemCheckpoint( _CrtMemState *pState ) OVERRIDE {}
  92. // FIXME: Make a better stats interface
  93. virtual void DumpStats() OVERRIDE {}
  94. virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE { DumpStats(); }
  95. virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) OVERRIDE { return 0; }
  96. // FIXME: Remove when we have our own allocator
  97. virtual void* CrtSetReportFile( int nRptType, void* hFile ) OVERRIDE { return 0; }
  98. virtual void* CrtSetReportHook( void* pfnNewHook ) OVERRIDE { return 0; }
  99. virtual int CrtDbgReport( int nRptType, const char * szFile,
  100. int nLine, const char * szModule, const char * pMsg ) OVERRIDE { return 0; }
  101. virtual int heapchk() OVERRIDE { return _HEAPOK; }
  102. virtual bool IsDebugHeap() OVERRIDE { return 0; }
  103. virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) OVERRIDE { pFileName = ""; nLine = 0; }
  104. virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {}
  105. virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {}
  106. virtual int GetVersion() OVERRIDE { return 0; }
  107. virtual void CompactHeap() OVERRIDE {}
  108. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) OVERRIDE { return 0; }
  109. virtual void DumpBlockStats( void * ) OVERRIDE {}
  110. virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) OVERRIDE {}
  111. // Returns 0 if no failure, otherwise the size_t of the last requested chunk
  112. virtual size_t MemoryAllocFailed() OVERRIDE { return 0; }
  113. virtual void CompactIncremental() OVERRIDE {}
  114. virtual void OutOfMemory( size_t nBytesAttempted = 0 ) OVERRIDE {}
  115. // Region-based allocations
  116. virtual void *RegionAlloc( int region, size_t nSize ) OVERRIDE { return 0; }
  117. virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
  118. // Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system
  119. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) OVERRIDE {}
  120. // Obtain virtual memory manager interface
  121. virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes ) OVERRIDE { return 0; }
  122. // Request 'generic' memory stats (returns a list of N named values; caller should assume this list will change over time)
  123. virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats ) { return 0; }
  124. // handles storing allocation info for coroutines
  125. virtual uint32 GetDebugInfoSize() { return 0; }
  126. virtual void SaveDebugInfo( void *pvDebugInfo ) {}
  127. virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
  128. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
  129. };
  130. class CHeapMemAlloc : public CHeapDefault
  131. {
  132. public:
  133. CHeapMemAlloc()
  134. {
  135. // Do all allocations with the shared process heap so that we can still
  136. // allocate from one DLL and free in another.
  137. m_heap = GetProcessHeap();
  138. }
  139. // minimal IMemAlloc implementation
  140. // Release API
  141. public:
  142. virtual void *Alloc( size_t nSize ) OVERRIDE;
  143. virtual void *Realloc( void *pMem, size_t nSize ) OVERRIDE;
  144. virtual void Free( void *pMem ) OVERRIDE;
  145. // Returns size of a particular allocation
  146. // BUGBUG: this function should be 'const'
  147. virtual size_t GetSize( void *pMem ) OVERRIDE;
  148. // Return 1 to indicate a healthy heap.
  149. // BUGBUG: this function should be 'const'
  150. virtual int CrtCheckMemory( void ) OVERRIDE;
  151. // BUGBUG: this function should be 'const'
  152. virtual void DumpStats() OVERRIDE;
  153. void Init(bool bZeroMemory);
  154. private:
  155. void OutOfMemory( size_t nBytesAttempted = 0 );
  156. // Internal allocation calls used to support alignment
  157. void * Alloc_Unaligned( size_t nSize );
  158. void * Realloc_Unaligned( void *pMem, size_t nSize );
  159. void Free_Unaligned( void *pMem );
  160. size_t GetSize_Unaligned( void *pMem ) const;
  161. // Handle to the process heap.
  162. HANDLE m_heap;
  163. uint32 m_HeapFlags;
  164. // Total outstanding bytes allocated.
  165. volatile size_t m_nOutstandingBytes;
  166. // Total outstanding committed bytes assuming that all allocations are
  167. // put on individual 4-KB pages (true when using full PageHeap from
  168. // App Verifier).
  169. volatile size_t m_nOutstandingPageHeapBytes;
  170. // Total outstanding allocations. With PageHeap enabled each allocation
  171. // requires an extra 4-KB page of address space.
  172. volatile LONG m_nOutstandingAllocations;
  173. LONG m_nOldOutstandingAllocations;
  174. // Total allocations without subtracting freed memory.
  175. volatile LONG m_nLifetimeAllocations;
  176. LONG m_nOldLifetimeAllocations;
  177. };
  178. void CHeapMemAlloc::Init( bool bZeroMemory )
  179. {
  180. m_HeapFlags = bZeroMemory ? HEAP_ZERO_MEMORY : 0;
  181. // Can't use Msg here because it isn't necessarily initialized yet.
  182. if ( s_bPageHeapEnabled )
  183. {
  184. OutputDebugStringA("PageHeap is on. Memory use will be larger than normal.\n" );
  185. }
  186. else
  187. {
  188. OutputDebugStringA("PageHeap is off. Memory use will be normal.\n" );
  189. }
  190. if( bZeroMemory )
  191. {
  192. OutputDebugStringA( " HEAP_ZERO_MEMORY is specified.\n" );
  193. }
  194. }
  195. inline size_t CHeapMemAlloc::GetSize_Unaligned( void *pMem ) const
  196. {
  197. return HeapSize( m_heap, 0, pMem ) - FORCED_ALIGNMENT;
  198. }
  199. inline void *CHeapMemAlloc::Alloc_Unaligned( size_t nSize )
  200. {
  201. // Ensure that the constructor has run already. Poorly defined
  202. // order of construction can result in the allocator being used
  203. // before it is constructed. Which could be bad.
  204. if ( !m_heap )
  205. __debugbreak();
  206. size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
  207. void* pMem = HeapAlloc( m_heap, m_HeapFlags, nAdjustedSize );
  208. if ( !pMem )
  209. {
  210. OutOfMemory( nSize );
  211. }
  212. InterlockedAddSizeT( &m_nOutstandingBytes, nSize );
  213. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) );
  214. InterlockedIncrement( &m_nOutstandingAllocations );
  215. InterlockedIncrement( &m_nLifetimeAllocations );
  216. return pMem;
  217. }
  218. inline void *CHeapMemAlloc::Realloc_Unaligned( void *pMem, size_t nSize )
  219. {
  220. size_t nOldSize = GetSize_Unaligned( pMem );
  221. size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT;
  222. size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
  223. void* pNewMem = HeapReAlloc( m_heap, m_HeapFlags, pMem, nAdjustedSize );
  224. if ( !pNewMem )
  225. {
  226. OutOfMemory( nSize );
  227. }
  228. InterlockedAddSizeT( &m_nOutstandingBytes, nSize - nOldSize );
  229. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) );
  230. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) );
  231. // Outstanding allocation count isn't affected by Realloc, but lifetime allocation count is
  232. InterlockedIncrement( &m_nLifetimeAllocations );
  233. return pNewMem;
  234. }
  235. inline void CHeapMemAlloc::Free_Unaligned( void *pMem )
  236. {
  237. size_t nOldSize = GetSize_Unaligned( pMem );
  238. size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT;
  239. InterlockedAddSizeT( &m_nOutstandingBytes, 0 - nOldSize );
  240. InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) );
  241. InterlockedDecrement( &m_nOutstandingAllocations );
  242. HeapFree( m_heap, 0, pMem );
  243. }
  244. inline void CHeapMemAlloc::OutOfMemory( size_t nBytesAttempted )
  245. {
  246. // It is crucial to stop here, before calling DumpStats, because if an OOM failure happens
  247. // in the logging system then DumpStats will trigger it again. This is made more complicated
  248. // because CUtlBuffer will have updated its size but not its pointer, leading to a buffer
  249. // that thinks it has more room than it actually does.
  250. DebuggerBreakIfDebugging();
  251. // Having PageHeap enabled leads to lots of allocation failures. These
  252. // these crashes we either need to halt immediately on allocation failures,
  253. // or print a message and exit. Printing a message and exiting is better
  254. // for stress testing purposes.
  255. DumpStats();
  256. char buffer[256];
  257. _snprintf( buffer, sizeof( buffer ), FILE_LINE_STRING "***** OUT OF MEMORY! attempted allocation size: %I64d ****\n", (uint64)nBytesAttempted );
  258. buffer[ ARRAYSIZE(buffer) - 1 ] = 0;
  259. // Can't use Msg() in a situation like this.
  260. Plat_DebugString( buffer );
  261. // If page heap is enabled then exit cleanly to simplify stress testing.
  262. if ( !s_bPageHeapEnabled )
  263. {
  264. DebuggerBreakIfDebugging();
  265. }
  266. Plat_ExitProcess( EXIT_FAILURE );
  267. }
  268. inline void CHeapMemAlloc::DumpStats()
  269. {
  270. const size_t MB = 1024 * 1024;
  271. Msg( "Sorry -- no stats saved to file memstats.txt when the heap allocator is enabled.\n" );
  272. // Print requested memory.
  273. Msg( "%u MB allocated.\n", ( unsigned )( m_nOutstandingBytes / MB ) );
  274. // Print memory after rounding up to pages.
  275. Msg( "%u MB memory used assuming maximum PageHeap overhead.\n", ( unsigned )( m_nOutstandingPageHeapBytes / MB ));
  276. // Print memory after adding in reserved page after every allocation. Do 64-bit calculations
  277. // because the pageHeap required memory can easily go over 4 GB.
  278. __int64 pageHeapBytes = m_nOutstandingPageHeapBytes + m_nOutstandingAllocations * 4096LL;
  279. Msg( "%u MB address space used assuming maximum PageHeap overhead.\n", ( unsigned )( pageHeapBytes / MB ));
  280. Msg( "%u outstanding allocations (%d delta).\n", ( unsigned )m_nOutstandingAllocations, ( int )( m_nOutstandingAllocations - m_nOldOutstandingAllocations ) );
  281. Msg( "%u lifetime allocations (%u delta).\n", ( unsigned )m_nLifetimeAllocations, ( unsigned )( m_nLifetimeAllocations - m_nOldLifetimeAllocations ) );
  282. // Update the numbers on outstanding and lifetime allocation counts so
  283. // that we can print out deltas.
  284. m_nOldOutstandingAllocations = m_nOutstandingAllocations;
  285. m_nOldLifetimeAllocations = m_nLifetimeAllocations;
  286. }
  287. int CHeapMemAlloc::CrtCheckMemory( void )
  288. {
  289. #ifdef _WIN32
  290. // HeapValidate is supposed to check the entire heap for validity. However testing with
  291. // intentional heap corruption suggests that it does not. If a block is corrupted and
  292. // then HeapValidate is called on that block then this is detected, but the same corruption
  293. // is not detected when passing NULL as the pointer. But, better to have this functionality
  294. // supported than not.
  295. BOOL result = HeapValidate( m_heap, 0, NULL );
  296. return (result != 0) ? 1 : 0;
  297. #else
  298. // HeapValidate does not exist on the Xbox 360.
  299. return 1;
  300. #endif
  301. }
  302. // Alignment-enforcing wrappers
  303. #if ( FORCED_ALIGNMENT > 0 )
  304. ASSERT_INVARIANT( FORCED_ALIGNMENT < 256 ); // Alignment offset has to fit in 1 byte
  305. inline void *AlignPointer( void *pUnaligned )
  306. {
  307. // Offset the pointer to align it and store the offset in the previous byte
  308. byte nOffset = FORCED_ALIGNMENT - ( ((uintptr_t)pUnaligned) & ( FORCED_ALIGNMENT - 1 ) );
  309. byte *pAligned = ((byte*)pUnaligned) + nOffset;
  310. pAligned[ -1 ] = nOffset;
  311. return pAligned;
  312. }
  313. inline void *UnalignPointer( void *pAligned )
  314. {
  315. // Get the original unaligned pointer, using the offset stored by AlignPointer()
  316. byte *pUnaligned = (byte *)pAligned;
  317. byte nOffset = pUnaligned[ -1 ];
  318. pUnaligned -= nOffset;
  319. // Detect corruption of the offset byte (valid offsets range from 1 to FORCED_ALIGNMENT):
  320. if ( ((uintptr_t)pAligned) % FORCED_ALIGNMENT ) DebuggerBreakIfDebugging();
  321. if ( ( nOffset < 1 ) || ( nOffset > FORCED_ALIGNMENT ) ) DebuggerBreakIfDebugging();
  322. return pUnaligned;
  323. }
  324. #else // FORCED_ALIGNMENT
  325. inline void *AlignPointer( void *pUnaligned ) { return pUnaligned; }
  326. inline void *UnalignPointer( void *pAligned ) { return pAligned; }
  327. #endif // FORCED_ALIGNMENT
  328. inline void *CHeapMemAlloc::Alloc( size_t nSize )
  329. {
  330. // NOTE: see IMemAlloc 'API Rules'
  331. //if ( !nSize )
  332. // return NULL;
  333. if ( !nSize )
  334. nSize = 1;
  335. return AlignPointer( Alloc_Unaligned( nSize ) );
  336. }
  337. inline void CHeapMemAlloc::Free( void *pMem )
  338. {
  339. // NOTE: see IMemAlloc 'API Rules'
  340. if ( !pMem )
  341. return;
  342. return Free_Unaligned( UnalignPointer( pMem ) );
  343. }
  344. inline void *CHeapMemAlloc::Realloc( void *pMem, size_t nSize )
  345. {
  346. // NOTE: see IMemAlloc 'API Rules'
  347. if ( !pMem )
  348. {
  349. return Alloc( nSize );
  350. }
  351. if ( !nSize )
  352. {
  353. Free( pMem );
  354. return NULL;
  355. }
  356. #if ( FORCED_ALIGNMENT == 0 )
  357. return Realloc_Unaligned( pMem, nSize );
  358. #else // FORCED_ALIGNMENT
  359. // Can't use ReAlloc_Unaligned because the leading padding varies, so it will memcpy incorrectly.
  360. void * pUnaligned = UnalignPointer( pMem );
  361. size_t nOldSize = GetSize_Unaligned( pUnaligned );
  362. void * pAligned = AlignPointer( Alloc_Unaligned( nSize ) );
  363. memcpy( pAligned, pMem, MIN( nSize, nOldSize ) );
  364. Free_Unaligned( pUnaligned );
  365. return pAligned;
  366. #endif // FORCED_ALIGNMENT
  367. }
  368. inline size_t CHeapMemAlloc::GetSize( void *pMem )
  369. {
  370. // NOTE: see IMemAlloc 'API Rules'
  371. if ( !pMem )
  372. return 0;
  373. return GetSize_Unaligned( UnalignPointer( pMem ) );
  374. }
  375. void EnableHeapMemAlloc( bool bZeroMemory )
  376. {
  377. // Place this here to guarantee it is constructed
  378. // before we call Init.
  379. static CHeapMemAlloc s_HeapMemAlloc;
  380. static bool s_initCalled = false;
  381. if ( !s_initCalled )
  382. {
  383. s_HeapMemAlloc.Init( bZeroMemory );
  384. SetAllocatorObject( &s_HeapMemAlloc );
  385. s_initCalled = true;
  386. }
  387. }
  388. void ReserveBottomMemory()
  389. {
  390. // If we are running a 64-bit build then reserve all addresses below the
  391. // 4 GB line to push as many pointers as possible above the line.
  392. #ifdef PLATFORM_WINDOWS_PC64
  393. // Avoid the cost of calling this multiple times.
  394. static bool s_initialized = false;
  395. if ( s_initialized )
  396. return;
  397. s_initialized = true;
  398. // Start by reserving large blocks of memory. When those reservations
  399. // have exhausted the bottom 4 GB then halve the size and try again.
  400. // The granularity for reserving address space is 64 KB so if we wanted
  401. // to reserve every single page we would need to continue down to 64 KB.
  402. // However stopping at 1 MB is sufficient because it prevents the Windows
  403. // heap (and dlmalloc and the small block heap) from grabbing address space
  404. // from the bottom 4 GB, while still allowing Steam to allocate a few pages
  405. // for setting up detours.
  406. const size_t LOW_MEM_LINE = 0x100000000LL;
  407. size_t totalReservation = 0;
  408. size_t numVAllocs = 0;
  409. size_t numHeapAllocs = 0;
  410. for ( size_t blockSize = 256 * 1024 * 1024; blockSize >= 1024 * 1024; blockSize /= 2 )
  411. {
  412. for (;;)
  413. {
  414. void* p = VirtualAlloc( 0, blockSize, MEM_RESERVE, PAGE_NOACCESS );
  415. if ( !p )
  416. break;
  417. if ( (size_t)p >= LOW_MEM_LINE )
  418. {
  419. // We don't need this memory, so release it completely.
  420. VirtualFree( p, 0, MEM_RELEASE );
  421. break;
  422. }
  423. totalReservation += blockSize;
  424. ++numVAllocs;
  425. }
  426. }
  427. // Now repeat the same process but making heap allocations, to use up the
  428. // already committed heap blocks that are below the 4 GB line. Now we start
  429. // with 64-KB allocations and proceed down to 16-byte allocations.
  430. HANDLE heap = GetProcessHeap();
  431. for ( size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2 )
  432. {
  433. for (;;)
  434. {
  435. void* p = HeapAlloc( heap, 0, blockSize );
  436. if ( !p )
  437. break;
  438. if ( (size_t)p >= LOW_MEM_LINE )
  439. {
  440. // We don't need this memory, so release it completely.
  441. HeapFree( heap, 0, p );
  442. break;
  443. }
  444. totalReservation += blockSize;
  445. ++numHeapAllocs;
  446. }
  447. }
  448. // Print diagnostics showing how many allocations we had to make in order to
  449. // reserve all of low memory. In one test run it took 55 virtual allocs and
  450. // 85 heap allocs. Note that since the process may have multiple heaps (each
  451. // CRT seems to have its own) there is likely to be a few MB of address space
  452. // that was previously reserved and is available to be handed out by some allocators.
  453. //char buffer[1000];
  454. //sprintf_s( buffer, "Reserved %1.3f MB (%d vallocs, %d heap allocs) to keep allocations out of low-memory.\n",
  455. // totalReservation / (1024 * 1024.0), (int)numVAllocs, (int)numHeapAllocs );
  456. // Can't use Msg here because it isn't necessarily initialized yet.
  457. //OutputDebugString( buffer );
  458. #endif
  459. }
  460. // Check whether PageHeap (part of App Verifier) has been enabled for this process.
  461. // It specifically checks whether it was enabled by the EnableAppVerifier.bat
  462. // batch file. This can be used to automatically enable -processheap when
  463. // App Verifier is in use.
  464. static bool IsPageHeapEnabled( bool& bETWHeapEnabled )
  465. {
  466. // Assume false.
  467. bool result = false;
  468. bETWHeapEnabled = false;
  469. // First we get the application's name so we can look in the registry
  470. // for App Verifier settings.
  471. HMODULE exeHandle = GetModuleHandle( 0 );
  472. if ( exeHandle )
  473. {
  474. char appName[ MAX_PATH ];
  475. if ( GetModuleFileNameA( exeHandle, appName, ARRAYSIZE( appName ) ) )
  476. {
  477. // Guarantee null-termination -- not guaranteed on Windows XP!
  478. appName[ ARRAYSIZE( appName ) - 1 ] = 0;
  479. // Find the file part of the name.
  480. const char* pFilePart = strrchr( appName, '\\' );
  481. if ( pFilePart )
  482. {
  483. ++pFilePart;
  484. size_t len = strlen( pFilePart );
  485. if ( len > 0 && pFilePart[ len - 1 ] == ' ' )
  486. {
  487. OutputDebugStringA( "Trailing space on executable name! This will cause Application Verifier and ETW Heap tracing to fail!\n" );
  488. DebuggerBreakIfDebugging();
  489. }
  490. // Generate the key name for App Verifier settings for this process.
  491. char regPathName[ MAX_PATH ];
  492. _snprintf( regPathName, ARRAYSIZE( regPathName ),
  493. "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s",
  494. pFilePart );
  495. regPathName[ ARRAYSIZE( regPathName ) - 1 ] = 0;
  496. HKEY key;
  497. LONG regResult = RegOpenKeyA( HKEY_LOCAL_MACHINE,
  498. regPathName,
  499. &key );
  500. if ( regResult == ERROR_SUCCESS )
  501. {
  502. // If PageHeapFlags exists then that means that App Verifier is enabled
  503. // for this application. The StackTraceDatabaseSizeInMB is only
  504. // set by Valve's enabling batch file so this indicates that
  505. // a developer at Valve is using App Verifier.
  506. if ( RegQueryValueExA( key, "StackTraceDatabaseSizeInMB", 0, NULL, NULL, NULL ) == ERROR_SUCCESS &&
  507. RegQueryValueExA( key, "PageHeapFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
  508. {
  509. result = true;
  510. }
  511. if ( RegQueryValueExA( key, "TracingFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
  512. bETWHeapEnabled = true;
  513. RegCloseKey( key );
  514. }
  515. }
  516. }
  517. }
  518. return result;
  519. }
  520. // Check for various allocator overrides such as -processheap and -reservelowmem.
  521. // Returns true if -processheap is enabled, by a command line switch or other method.
  522. bool CheckWindowsAllocSettings( const char* upperCommandLine )
  523. {
  524. // Are we doing ETW heap profiling?
  525. bool bETWHeapEnabled = false;
  526. s_bPageHeapEnabled = IsPageHeapEnabled( bETWHeapEnabled );
  527. // Should we reserve the bottom 4 GB of RAM in order to flush out pointer
  528. // truncation bugs? This helps ensure 64-bit compatibility.
  529. // However this needs to be off by default to avoid causing compatibility problems,
  530. // with Steam detours and other systems. It should also be disabled when PageHeap
  531. // is on because for some reason the combination turns into 4 GB of working set, which
  532. // can easily cause problems.
  533. if ( strstr( upperCommandLine, "-RESERVELOWMEM" ) && !s_bPageHeapEnabled )
  534. ReserveBottomMemory();
  535. // Uninitialized data, including pointers, is often set to 0xFFEEFFEE.
  536. // If we reserve that block of memory then we can turn these pointer
  537. // dereferences into crashes a little bit earlier and more reliably.
  538. // We don't really care whether this allocation succeeds, but it's
  539. // worth trying. Note that we do this in all cases -- whether we are using
  540. // -processheap or not.
  541. VirtualAlloc( (void*)0xFFEEFFEE, 1, MEM_RESERVE, PAGE_NOACCESS );
  542. // Enable application termination (breakpoint) on heap corruption. This is
  543. // better than trying to patch it up and continue, both from a security and
  544. // a bug-finding point of view. Do this always on Windows since the heap is
  545. // used by video drivers and other in-proc components.
  546. //HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
  547. // The HeapEnableTerminationOnCorruption requires a recent platform SDK,
  548. // so fake it up.
  549. #if defined(PLATFORM_WINDOWS_PC)
  550. HeapSetInformation( NULL, (HEAP_INFORMATION_CLASS)1, NULL, 0 );
  551. #endif
  552. bool bZeroMemory = false;
  553. bool bProcessHeap = false;
  554. // Should we force using the process heap? This is handy for gathering memory
  555. // statistics with ETW/xperf. When using App Verifier -processheap is automatically
  556. // turned on.
  557. if ( strstr( upperCommandLine, "-PROCESSHEAP" ) )
  558. {
  559. bProcessHeap = true;
  560. bZeroMemory = !!strstr( upperCommandLine, "-PROCESSHEAPZEROMEM" );
  561. }
  562. // Unless specifically disabled, turn on -processheap if pageheap or ETWHeap tracing
  563. // are enabled.
  564. if ( !strstr( upperCommandLine, "-NOPROCESSHEAP" ) && ( s_bPageHeapEnabled || bETWHeapEnabled ) )
  565. bProcessHeap = true;
  566. if ( bProcessHeap )
  567. {
  568. // Now all allocations will go through the system heap.
  569. EnableHeapMemAlloc( bZeroMemory );
  570. }
  571. return bProcessHeap;
  572. }
  573. #endif // _WIN32
  574. #endif // !NO_MALLOC_OVERRIDE