|
|
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Memory allocation!
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/dbg.h"
#include "tier0/memalloc.h"
#include "memstd.h"
#if !defined(NO_MALLOC_OVERRIDE)
#if defined( _WIN32 )
#define OVERRIDE override
// warning C4481: nonstandard extension used: override specifier 'override'
#pragma warning( disable : 4481 )
#ifdef _WIN64
// Set the new-style define that indicates a a 64-bit Windows PC
#define PLATFORM_WINDOWS_PC64 1
LONGLONG FORCEINLINE InterlockedExchangeAdd64( __inout LONGLONG volatile *Addend, __in LONGLONG Value ); #else
// Set the new-style define that indicates a a 32-bit Windows PC
#define PLATFORM_WINDOWS_PC32 1
#endif
// Support for CHeapMemAlloc for easy switching to using the process heap.
// Track this to decide how to handle out-of-memory.
static bool s_bPageHeapEnabled = false;
//-----------------------------------------------------------------------------
// IMemAlloc must guarantee 16-byte alignment for 16n-byte allocations, so we just
// force 16-byte alignment under win32 (the win64 system heap already 16-byte aligns).
// TODO: this padding negates some of the buffer-overrun protection provided by pageheap, so...
// we should fill padding bytes with a known pattern which is checked in realloc/free
#ifdef PLATFORM_WINDOWS_PC32
#define FORCED_ALIGNMENT 16
#else
#define FORCED_ALIGNMENT 0
#endif
// Round a size up to a multiple of 4 KB to aid in calculating how much
// memory is required if full pageheap is enabled.
static size_t RoundUpToPage( size_t nSize ) { nSize += 0xFFF; nSize &= ~0xFFF; return nSize; }
static void InterlockedAddSizeT( size_t volatile *Addend, size_t Value ) { #ifdef PLATFORM_WINDOWS_PC32
// Convenience function to deal with the necessary type-casting
InterlockedExchangeAdd( ( LONG* )Addend, LONG( Value ) ); #else
InterlockedExchangeAdd64( ( LONGLONG* )Addend, LONGLONG( Value ) ); #endif
}
// CHeapDefault supplies default implementations for as many functions as
// possible so that a heap implementation can be as simple as possible.
class CHeapDefault : public IMemAlloc { // Since we define the debug versions of Alloc/Realloc/Free in this class but
// not the release versions we implicitly hide the release implementations, which
// makes it impossible for us to call them in order to implement the debug
// versions. These using directives pull these three names into this namespace
// so that we can call them.
using IMemAlloc::Alloc; using IMemAlloc::Realloc; using IMemAlloc::Free;
// Release versions
// Alloc, Realloc, and Free must be implemented
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) OVERRIDE { return 0; }
// Debug versions
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Alloc( nSize ); } virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Realloc(pMem, nSize); } virtual void Free( void *pMem, const char *pFileName, int nLine ) OVERRIDE { Free( pMem ); } virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
// GetSize must be implemented
// Force file + line information for an allocation
virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) OVERRIDE {} virtual void PopAllocDbgInfo() OVERRIDE {}
// FIXME: Remove when we have our own allocator
// these methods of the Crt debug code is used in our codebase currently
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc ) OVERRIDE { return 0; } virtual int CrtSetReportMode( int nReportType, int nReportMode ) OVERRIDE { return 0; } virtual int CrtIsValidHeapPointer( const void *pMem ) OVERRIDE { return 0; } virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) OVERRIDE { return 0; } virtual int CrtCheckMemory( void ) OVERRIDE { return 0; } virtual int CrtSetDbgFlag( int nNewFlag ) OVERRIDE { return 0; } virtual void CrtMemCheckpoint( _CrtMemState *pState ) OVERRIDE {}
// FIXME: Make a better stats interface
virtual void DumpStats() OVERRIDE {} virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE { DumpStats(); } virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) OVERRIDE { return 0; }
// FIXME: Remove when we have our own allocator
virtual void* CrtSetReportFile( int nRptType, void* hFile ) OVERRIDE { return 0; } virtual void* CrtSetReportHook( void* pfnNewHook ) OVERRIDE { return 0; } virtual int CrtDbgReport( int nRptType, const char * szFile, int nLine, const char * szModule, const char * pMsg ) OVERRIDE { return 0; }
virtual int heapchk() OVERRIDE { return _HEAPOK; } virtual bool IsDebugHeap() OVERRIDE { return 0; }
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) OVERRIDE { pFileName = ""; nLine = 0; } virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {} virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {}
virtual int GetVersion() OVERRIDE { return 0; }
virtual void CompactHeap() OVERRIDE {}
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) OVERRIDE { return 0; }
virtual void DumpBlockStats( void * ) OVERRIDE {}
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) OVERRIDE {}
// Returns 0 if no failure, otherwise the size_t of the last requested chunk
virtual size_t MemoryAllocFailed() OVERRIDE { return 0; }
virtual void CompactIncremental() OVERRIDE {}
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) OVERRIDE {}
// Region-based allocations
virtual void *RegionAlloc( int region, size_t nSize ) OVERRIDE { return 0; } virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
// Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) OVERRIDE {}
// Obtain virtual memory manager interface
virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes ) OVERRIDE { return 0; }
// Request 'generic' memory stats (returns a list of N named values; caller should assume this list will change over time)
virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats ) { return 0; }
// handles storing allocation info for coroutines
virtual uint32 GetDebugInfoSize() { return 0; } virtual void SaveDebugInfo( void *pvDebugInfo ) {} virtual void RestoreDebugInfo( const void *pvDebugInfo ) {} virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {} };
class CHeapMemAlloc : public CHeapDefault { public: CHeapMemAlloc() { // Do all allocations with the shared process heap so that we can still
// allocate from one DLL and free in another.
m_heap = GetProcessHeap(); }
// minimal IMemAlloc implementation
// Release API
public: virtual void *Alloc( size_t nSize ) OVERRIDE; virtual void *Realloc( void *pMem, size_t nSize ) OVERRIDE; virtual void Free( void *pMem ) OVERRIDE;
// Returns size of a particular allocation
// BUGBUG: this function should be 'const'
virtual size_t GetSize( void *pMem ) OVERRIDE;
// Return 1 to indicate a healthy heap.
// BUGBUG: this function should be 'const'
virtual int CrtCheckMemory( void ) OVERRIDE;
// BUGBUG: this function should be 'const'
virtual void DumpStats() OVERRIDE;
void Init(bool bZeroMemory);
private:
void OutOfMemory( size_t nBytesAttempted = 0 );
// Internal allocation calls used to support alignment
void * Alloc_Unaligned( size_t nSize ); void * Realloc_Unaligned( void *pMem, size_t nSize ); void Free_Unaligned( void *pMem ); size_t GetSize_Unaligned( void *pMem ) const;
// Handle to the process heap.
HANDLE m_heap; uint32 m_HeapFlags;
// Total outstanding bytes allocated.
volatile size_t m_nOutstandingBytes;
// Total outstanding committed bytes assuming that all allocations are
// put on individual 4-KB pages (true when using full PageHeap from
// App Verifier).
volatile size_t m_nOutstandingPageHeapBytes;
// Total outstanding allocations. With PageHeap enabled each allocation
// requires an extra 4-KB page of address space.
volatile LONG m_nOutstandingAllocations; LONG m_nOldOutstandingAllocations;
// Total allocations without subtracting freed memory.
volatile LONG m_nLifetimeAllocations; LONG m_nOldLifetimeAllocations; };
void CHeapMemAlloc::Init( bool bZeroMemory ) { m_HeapFlags = bZeroMemory ? HEAP_ZERO_MEMORY : 0;
// Can't use Msg here because it isn't necessarily initialized yet.
if ( s_bPageHeapEnabled ) { OutputDebugStringA("PageHeap is on. Memory use will be larger than normal.\n" ); } else { OutputDebugStringA("PageHeap is off. Memory use will be normal.\n" ); } if( bZeroMemory ) { OutputDebugStringA( " HEAP_ZERO_MEMORY is specified.\n" ); } }
inline size_t CHeapMemAlloc::GetSize_Unaligned( void *pMem ) const { return HeapSize( m_heap, 0, pMem ) - FORCED_ALIGNMENT; }
inline void *CHeapMemAlloc::Alloc_Unaligned( size_t nSize ) { // Ensure that the constructor has run already. Poorly defined
// order of construction can result in the allocator being used
// before it is constructed. Which could be bad.
if ( !m_heap ) __debugbreak();
size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
void* pMem = HeapAlloc( m_heap, m_HeapFlags, nAdjustedSize ); if ( !pMem ) { OutOfMemory( nSize ); }
InterlockedAddSizeT( &m_nOutstandingBytes, nSize ); InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) ); InterlockedIncrement( &m_nOutstandingAllocations ); InterlockedIncrement( &m_nLifetimeAllocations ); return pMem; }
inline void *CHeapMemAlloc::Realloc_Unaligned( void *pMem, size_t nSize ) { size_t nOldSize = GetSize_Unaligned( pMem ); size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT; size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
void* pNewMem = HeapReAlloc( m_heap, m_HeapFlags, pMem, nAdjustedSize ); if ( !pNewMem ) { OutOfMemory( nSize ); }
InterlockedAddSizeT( &m_nOutstandingBytes, nSize - nOldSize ); InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) ); InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) ); // Outstanding allocation count isn't affected by Realloc, but lifetime allocation count is
InterlockedIncrement( &m_nLifetimeAllocations );
return pNewMem; }
inline void CHeapMemAlloc::Free_Unaligned( void *pMem ) { size_t nOldSize = GetSize_Unaligned( pMem ); size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT; InterlockedAddSizeT( &m_nOutstandingBytes, 0 - nOldSize ); InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) ); InterlockedDecrement( &m_nOutstandingAllocations );
HeapFree( m_heap, 0, pMem ); }
inline void CHeapMemAlloc::OutOfMemory( size_t nBytesAttempted ) { // It is crucial to stop here, before calling DumpStats, because if an OOM failure happens
// in the logging system then DumpStats will trigger it again. This is made more complicated
// because CUtlBuffer will have updated its size but not its pointer, leading to a buffer
// that thinks it has more room than it actually does.
DebuggerBreakIfDebugging(); // Having PageHeap enabled leads to lots of allocation failures. These
// these crashes we either need to halt immediately on allocation failures,
// or print a message and exit. Printing a message and exiting is better
// for stress testing purposes.
DumpStats(); char buffer[256]; _snprintf( buffer, sizeof( buffer ), FILE_LINE_STRING "***** OUT OF MEMORY! attempted allocation size: %I64d ****\n", (uint64)nBytesAttempted ); buffer[ ARRAYSIZE(buffer) - 1 ] = 0; // Can't use Msg() in a situation like this.
Plat_DebugString( buffer );
// If page heap is enabled then exit cleanly to simplify stress testing.
if ( !s_bPageHeapEnabled ) { DebuggerBreakIfDebugging(); }
Plat_ExitProcess( EXIT_FAILURE ); }
inline void CHeapMemAlloc::DumpStats() { const size_t MB = 1024 * 1024; Msg( "Sorry -- no stats saved to file memstats.txt when the heap allocator is enabled.\n" ); // Print requested memory.
Msg( "%u MB allocated.\n", ( unsigned )( m_nOutstandingBytes / MB ) ); // Print memory after rounding up to pages.
Msg( "%u MB memory used assuming maximum PageHeap overhead.\n", ( unsigned )( m_nOutstandingPageHeapBytes / MB )); // Print memory after adding in reserved page after every allocation. Do 64-bit calculations
// because the pageHeap required memory can easily go over 4 GB.
__int64 pageHeapBytes = m_nOutstandingPageHeapBytes + m_nOutstandingAllocations * 4096LL; Msg( "%u MB address space used assuming maximum PageHeap overhead.\n", ( unsigned )( pageHeapBytes / MB )); Msg( "%u outstanding allocations (%d delta).\n", ( unsigned )m_nOutstandingAllocations, ( int )( m_nOutstandingAllocations - m_nOldOutstandingAllocations ) ); Msg( "%u lifetime allocations (%u delta).\n", ( unsigned )m_nLifetimeAllocations, ( unsigned )( m_nLifetimeAllocations - m_nOldLifetimeAllocations ) );
// Update the numbers on outstanding and lifetime allocation counts so
// that we can print out deltas.
m_nOldOutstandingAllocations = m_nOutstandingAllocations; m_nOldLifetimeAllocations = m_nLifetimeAllocations; }
int CHeapMemAlloc::CrtCheckMemory( void ) { #ifdef _WIN32
// HeapValidate is supposed to check the entire heap for validity. However testing with
// intentional heap corruption suggests that it does not. If a block is corrupted and
// then HeapValidate is called on that block then this is detected, but the same corruption
// is not detected when passing NULL as the pointer. But, better to have this functionality
// supported than not.
BOOL result = HeapValidate( m_heap, 0, NULL );
return (result != 0) ? 1 : 0; #else
// HeapValidate does not exist on the Xbox 360.
return 1; #endif
}
// Alignment-enforcing wrappers
#if ( FORCED_ALIGNMENT > 0 )
ASSERT_INVARIANT( FORCED_ALIGNMENT < 256 ); // Alignment offset has to fit in 1 byte
inline void *AlignPointer( void *pUnaligned ) { // Offset the pointer to align it and store the offset in the previous byte
byte nOffset = FORCED_ALIGNMENT - ( ((uintptr_t)pUnaligned) & ( FORCED_ALIGNMENT - 1 ) ); byte *pAligned = ((byte*)pUnaligned) + nOffset; pAligned[ -1 ] = nOffset; return pAligned; } inline void *UnalignPointer( void *pAligned ) { // Get the original unaligned pointer, using the offset stored by AlignPointer()
byte *pUnaligned = (byte *)pAligned; byte nOffset = pUnaligned[ -1 ]; pUnaligned -= nOffset; // Detect corruption of the offset byte (valid offsets range from 1 to FORCED_ALIGNMENT):
if ( ((uintptr_t)pAligned) % FORCED_ALIGNMENT ) DebuggerBreakIfDebugging(); if ( ( nOffset < 1 ) || ( nOffset > FORCED_ALIGNMENT ) ) DebuggerBreakIfDebugging(); return pUnaligned; } #else // FORCED_ALIGNMENT
inline void *AlignPointer( void *pUnaligned ) { return pUnaligned; } inline void *UnalignPointer( void *pAligned ) { return pAligned; } #endif // FORCED_ALIGNMENT
inline void *CHeapMemAlloc::Alloc( size_t nSize ) { // NOTE: see IMemAlloc 'API Rules'
//if ( !nSize )
// return NULL;
if ( !nSize ) nSize = 1;
return AlignPointer( Alloc_Unaligned( nSize ) ); }
inline void CHeapMemAlloc::Free( void *pMem ) { // NOTE: see IMemAlloc 'API Rules'
if ( !pMem ) return; return Free_Unaligned( UnalignPointer( pMem ) ); }
inline void *CHeapMemAlloc::Realloc( void *pMem, size_t nSize ) { // NOTE: see IMemAlloc 'API Rules'
if ( !pMem ) { return Alloc( nSize ); } if ( !nSize ) { Free( pMem ); return NULL; }
#if ( FORCED_ALIGNMENT == 0 )
return Realloc_Unaligned( pMem, nSize );
#else // FORCED_ALIGNMENT
// Can't use ReAlloc_Unaligned because the leading padding varies, so it will memcpy incorrectly.
void * pUnaligned = UnalignPointer( pMem ); size_t nOldSize = GetSize_Unaligned( pUnaligned ); void * pAligned = AlignPointer( Alloc_Unaligned( nSize ) ); memcpy( pAligned, pMem, MIN( nSize, nOldSize ) ); Free_Unaligned( pUnaligned ); return pAligned;
#endif // FORCED_ALIGNMENT
}
inline size_t CHeapMemAlloc::GetSize( void *pMem ) { // NOTE: see IMemAlloc 'API Rules'
if ( !pMem ) return 0; return GetSize_Unaligned( UnalignPointer( pMem ) ); }
void EnableHeapMemAlloc( bool bZeroMemory ) { // Place this here to guarantee it is constructed
// before we call Init.
static CHeapMemAlloc s_HeapMemAlloc; static bool s_initCalled = false;
if ( !s_initCalled ) { s_HeapMemAlloc.Init( bZeroMemory ); SetAllocatorObject( &s_HeapMemAlloc ); s_initCalled = true; } }
void ReserveBottomMemory() { // If we are running a 64-bit build then reserve all addresses below the
// 4 GB line to push as many pointers as possible above the line.
#ifdef PLATFORM_WINDOWS_PC64
// Avoid the cost of calling this multiple times.
static bool s_initialized = false; if ( s_initialized ) return; s_initialized = true;
// Start by reserving large blocks of memory. When those reservations
// have exhausted the bottom 4 GB then halve the size and try again.
// The granularity for reserving address space is 64 KB so if we wanted
// to reserve every single page we would need to continue down to 64 KB.
// However stopping at 1 MB is sufficient because it prevents the Windows
// heap (and dlmalloc and the small block heap) from grabbing address space
// from the bottom 4 GB, while still allowing Steam to allocate a few pages
// for setting up detours.
const size_t LOW_MEM_LINE = 0x100000000LL; size_t totalReservation = 0; size_t numVAllocs = 0; size_t numHeapAllocs = 0; for ( size_t blockSize = 256 * 1024 * 1024; blockSize >= 1024 * 1024; blockSize /= 2 ) { for (;;) { void* p = VirtualAlloc( 0, blockSize, MEM_RESERVE, PAGE_NOACCESS ); if ( !p ) break;
if ( (size_t)p >= LOW_MEM_LINE ) { // We don't need this memory, so release it completely.
VirtualFree( p, 0, MEM_RELEASE ); break; }
totalReservation += blockSize; ++numVAllocs; } }
// Now repeat the same process but making heap allocations, to use up the
// already committed heap blocks that are below the 4 GB line. Now we start
// with 64-KB allocations and proceed down to 16-byte allocations.
HANDLE heap = GetProcessHeap(); for ( size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2 ) { for (;;) { void* p = HeapAlloc( heap, 0, blockSize ); if ( !p ) break;
if ( (size_t)p >= LOW_MEM_LINE ) { // We don't need this memory, so release it completely.
HeapFree( heap, 0, p ); break; }
totalReservation += blockSize; ++numHeapAllocs; } }
// Print diagnostics showing how many allocations we had to make in order to
// reserve all of low memory. In one test run it took 55 virtual allocs and
// 85 heap allocs. Note that since the process may have multiple heaps (each
// CRT seems to have its own) there is likely to be a few MB of address space
// that was previously reserved and is available to be handed out by some allocators.
//char buffer[1000];
//sprintf_s( buffer, "Reserved %1.3f MB (%d vallocs, %d heap allocs) to keep allocations out of low-memory.\n",
// totalReservation / (1024 * 1024.0), (int)numVAllocs, (int)numHeapAllocs );
// Can't use Msg here because it isn't necessarily initialized yet.
//OutputDebugString( buffer );
#endif
} // Check whether PageHeap (part of App Verifier) has been enabled for this process.
// It specifically checks whether it was enabled by the EnableAppVerifier.bat
// batch file. This can be used to automatically enable -processheap when
// App Verifier is in use.
static bool IsPageHeapEnabled( bool& bETWHeapEnabled ) { // Assume false.
bool result = false; bETWHeapEnabled = false;
// First we get the application's name so we can look in the registry
// for App Verifier settings.
HMODULE exeHandle = GetModuleHandle( 0 ); if ( exeHandle ) { char appName[ MAX_PATH ]; if ( GetModuleFileNameA( exeHandle, appName, ARRAYSIZE( appName ) ) ) { // Guarantee null-termination -- not guaranteed on Windows XP!
appName[ ARRAYSIZE( appName ) - 1 ] = 0; // Find the file part of the name.
const char* pFilePart = strrchr( appName, '\\' ); if ( pFilePart ) { ++pFilePart; size_t len = strlen( pFilePart ); if ( len > 0 && pFilePart[ len - 1 ] == ' ' ) { OutputDebugStringA( "Trailing space on executable name! This will cause Application Verifier and ETW Heap tracing to fail!\n" ); DebuggerBreakIfDebugging(); }
// Generate the key name for App Verifier settings for this process.
char regPathName[ MAX_PATH ]; _snprintf( regPathName, ARRAYSIZE( regPathName ), "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s", pFilePart ); regPathName[ ARRAYSIZE( regPathName ) - 1 ] = 0;
HKEY key; LONG regResult = RegOpenKeyA( HKEY_LOCAL_MACHINE, regPathName, &key ); if ( regResult == ERROR_SUCCESS ) { // If PageHeapFlags exists then that means that App Verifier is enabled
// for this application. The StackTraceDatabaseSizeInMB is only
// set by Valve's enabling batch file so this indicates that
// a developer at Valve is using App Verifier.
if ( RegQueryValueExA( key, "StackTraceDatabaseSizeInMB", 0, NULL, NULL, NULL ) == ERROR_SUCCESS && RegQueryValueExA( key, "PageHeapFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS ) { result = true; }
if ( RegQueryValueExA( key, "TracingFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS ) bETWHeapEnabled = true;
RegCloseKey( key ); } } } }
return result; }
// Check for various allocator overrides such as -processheap and -reservelowmem.
// Returns true if -processheap is enabled, by a command line switch or other method.
bool CheckWindowsAllocSettings( const char* upperCommandLine ) { // Are we doing ETW heap profiling?
bool bETWHeapEnabled = false; s_bPageHeapEnabled = IsPageHeapEnabled( bETWHeapEnabled );
// Should we reserve the bottom 4 GB of RAM in order to flush out pointer
// truncation bugs? This helps ensure 64-bit compatibility.
// However this needs to be off by default to avoid causing compatibility problems,
// with Steam detours and other systems. It should also be disabled when PageHeap
// is on because for some reason the combination turns into 4 GB of working set, which
// can easily cause problems.
if ( strstr( upperCommandLine, "-RESERVELOWMEM" ) && !s_bPageHeapEnabled ) ReserveBottomMemory();
// Uninitialized data, including pointers, is often set to 0xFFEEFFEE.
// If we reserve that block of memory then we can turn these pointer
// dereferences into crashes a little bit earlier and more reliably.
// We don't really care whether this allocation succeeds, but it's
// worth trying. Note that we do this in all cases -- whether we are using
// -processheap or not.
VirtualAlloc( (void*)0xFFEEFFEE, 1, MEM_RESERVE, PAGE_NOACCESS );
// Enable application termination (breakpoint) on heap corruption. This is
// better than trying to patch it up and continue, both from a security and
// a bug-finding point of view. Do this always on Windows since the heap is
// used by video drivers and other in-proc components.
//HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
// The HeapEnableTerminationOnCorruption requires a recent platform SDK,
// so fake it up.
#if defined(PLATFORM_WINDOWS_PC)
HeapSetInformation( NULL, (HEAP_INFORMATION_CLASS)1, NULL, 0 ); #endif
bool bZeroMemory = false; bool bProcessHeap = false; // Should we force using the process heap? This is handy for gathering memory
// statistics with ETW/xperf. When using App Verifier -processheap is automatically
// turned on.
if ( strstr( upperCommandLine, "-PROCESSHEAP" ) ) { bProcessHeap = true; bZeroMemory = !!strstr( upperCommandLine, "-PROCESSHEAPZEROMEM" ); }
// Unless specifically disabled, turn on -processheap if pageheap or ETWHeap tracing
// are enabled.
if ( !strstr( upperCommandLine, "-NOPROCESSHEAP" ) && ( s_bPageHeapEnabled || bETWHeapEnabled ) ) bProcessHeap = true;
if ( bProcessHeap ) { // Now all allocations will go through the system heap.
EnableHeapMemAlloc( bZeroMemory ); }
return bProcessHeap; }
#endif // _WIN32
#endif // !NO_MALLOC_OVERRIDE
|