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.
623 lines
17 KiB
623 lines
17 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 2000.
|
|
//
|
|
// File: MEMORY.CXX
|
|
//
|
|
// Contents: Memory allocators
|
|
//
|
|
// History: 9-27-93 IsaacHe Heavy modifications throughout:
|
|
// Elimination of Buddy Heap
|
|
// Rework of stack backtrace & leak checks
|
|
// Use VirtualAlloc on large chunks
|
|
// Elimination of KERNEL and FLAT stuff
|
|
// Cosmetics
|
|
// 10-12-93 IsaacHe Made a separate info level for heap
|
|
// 12-3-93 IsaacHe Removed the VirtualAlloc stuff. Moved
|
|
// debug support code to another file.
|
|
// 7/27/94 Cruft removal
|
|
// 4/30/96 dlee Cruft removal
|
|
// 9/26/96 dlee Cruft removal
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <falloc.hxx>
|
|
#include <cidllsem.hxx>
|
|
#include <isreg.hxx>
|
|
#include <ciregkey.hxx>
|
|
|
|
// Uncomment this to include OLE heap tracking. This isn't normally done
|
|
// because there are some issues with exiting cleanly.
|
|
//#define USE_IMALLOCSPY
|
|
|
|
typedef void * (* PAllocFun)( UINT cb );
|
|
typedef void (* PFreeFun)( void * pv );
|
|
typedef UINT (* PSizeFun)( const void * pv );
|
|
typedef BOOL (* PValidateFun)( const void * pv );
|
|
typedef void (* PUtilizationFun)( void );
|
|
|
|
PAllocFun realAlloc = 0;
|
|
PFreeFun realFree = 0;
|
|
PSizeFun realSize = 0;
|
|
PValidateFun realValidate = 0;
|
|
PUtilizationFun realUtilization = 0;
|
|
|
|
HANDLE gmem_hHeap = 0;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: UseLowFragmentationHeap
|
|
//
|
|
// Synopsis: Tells the heap manager to use the new low-fragmentation
|
|
// implementation. It's faster on MP machines and is better
|
|
// suited to long-running applications.
|
|
//
|
|
// Arguments: [hHeap] -- The heap to configure
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void UseLowFragmentationHeap( HANDLE hHeap )
|
|
{
|
|
//
|
|
// Ignore failures here. Note that if the process is started in a
|
|
// debugger then the HEAP_DEBUG_FLAGS flag is turned on, so the
|
|
// heap can't be converted to a low-fragmentation heap and this
|
|
// call will fail.
|
|
//
|
|
|
|
#define HEAP_FRONT_LOWFRAGHEAP 2 //see sdnt\base\ntos\rtl\heappriv.h and sdnt\base\ntos\rtl\heapdll.c for details
|
|
ULONG ulHeapMode = HEAP_FRONT_LOWFRAGHEAP;
|
|
|
|
HeapSetInformation( hHeap,
|
|
HeapCompatibilityInformation,
|
|
&ulHeapMode,
|
|
sizeof ulHeapMode );
|
|
} //UseLowFragmentationHeap
|
|
|
|
inline void * heapAlloc( UINT cbAlloc )
|
|
{
|
|
Win4Assert( 0 != gmem_hHeap );
|
|
void * p = (void *) HeapAlloc( gmem_hHeap, 0, cbAlloc );
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
if ( 0 != p )
|
|
RtlFillMemory( p, cbAlloc, 0xda );
|
|
#endif
|
|
|
|
return p;
|
|
} //heapAlloc
|
|
|
|
inline void heapFree( void * p )
|
|
{
|
|
Win4Assert( 0 != gmem_hHeap );
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
UINT cb = (UINT)HeapSize( gmem_hHeap, 0, p );
|
|
if ( ~0 != cb )
|
|
RtlFillMemory( p, cb, 0xdc );
|
|
#endif
|
|
|
|
if ( !HeapFree( gmem_hHeap, 0, p ) )
|
|
Win4Assert(!"Bad ptr for operator delete");
|
|
} //heapFree
|
|
|
|
inline UINT heapSize( void const * p )
|
|
{
|
|
return (UINT) HeapSize( gmem_hHeap, 0, p );
|
|
}
|
|
|
|
inline BOOL heapValidate( const void * p )
|
|
{
|
|
if ( 0 == p )
|
|
return TRUE;
|
|
|
|
Win4Assert( 0 != gmem_hHeap );
|
|
|
|
if ( HeapSize( gmem_hHeap, 0, p ) <= 0 )
|
|
{
|
|
Win4Assert( !"Invalid pointer detected" );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
} //heapValidate
|
|
|
|
inline void heapUtilization()
|
|
{
|
|
// No stats here when you use the system heap. Use !heap or dh.exe
|
|
} //heapUtilization
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
|
|
#include <tracheap.h>
|
|
#include <dbgpoint.hxx>
|
|
#include <propapi.h>
|
|
|
|
#include "spy.hxx"
|
|
|
|
CMallocSpy * g_pMallocSpy = 0;
|
|
|
|
void __cdecl HeapExit()
|
|
{
|
|
#ifdef USE_IMALLOCSPY
|
|
if ( g_pMallocSpy )
|
|
{
|
|
MallocSpyUnRegister( g_pMallocSpy );
|
|
g_pMallocSpy->Release();
|
|
}
|
|
#endif // USE_IMALLOCSPY
|
|
|
|
AllocArenaDump( 0 );
|
|
} //HeapExit
|
|
|
|
//
|
|
// Debugging dialog support...
|
|
//
|
|
|
|
extern ULONG Win4InfoLevel;
|
|
extern ULONG Win4InfoMask;
|
|
|
|
BOOL g_fDumpArena = FALSE;
|
|
DWORD g_FailTestRatio = FALSE;
|
|
|
|
void EnableCiFailTest( unsigned Ratio )
|
|
{
|
|
g_FailTestRatio = Ratio;
|
|
}
|
|
|
|
DECLARE_DEBUG( heap );
|
|
#define heapDebugOut(x) heapInlineDebugOut x
|
|
|
|
CRITICAL_SECTION g_csDbgMemExclusive; // ensures single creator of arena
|
|
|
|
UINT ciAddAllocRecord( UINT cb )
|
|
{
|
|
return cb + sizeof 1 + sizeof AHeader;
|
|
}
|
|
|
|
void * ciRecordAlloc( void * p, UINT size )
|
|
{
|
|
if ( 0 == p )
|
|
return p;
|
|
|
|
static AllocArena * pAllocArena = (AllocArena *) -1;
|
|
|
|
if ( pAllocArena == (AllocArena *) -1 )
|
|
{
|
|
// Note: If the cs is invalid at this point, it's because
|
|
// you're calling operator new too early. Construct
|
|
// your global object later.
|
|
|
|
EnterCriticalSection( &g_csDbgMemExclusive );
|
|
if ( pAllocArena == (AllocArena *) -1 )
|
|
{
|
|
pAllocArena = AllocArenaCreate( MEMCTX_TASK,
|
|
"Operator new");
|
|
#ifdef USE_IMALLOCSPY
|
|
MallocSpyRegister( &g_pMallocSpy );
|
|
#endif // USE_IMALLOCSPY
|
|
|
|
atexit( HeapExit );
|
|
}
|
|
LeaveCriticalSection( &g_csDbgMemExclusive );
|
|
}
|
|
|
|
((AHeader *)p)->size = size;
|
|
((AHeader *)p)->p = AllocArenaRecordAlloc( pAllocArena, size );
|
|
p = (AHeader *)p + 1;
|
|
*((char *)p + size ) = ALLOC_SIGNATURE;
|
|
return p;
|
|
} //ciRecordAlloc
|
|
|
|
UINT ciAllocSize( void * p )
|
|
{
|
|
if ( 0 == p )
|
|
return 0;
|
|
|
|
AHeader *ap = (AHeader *)p - 1;
|
|
return ap->size;
|
|
} //ciAllocSize
|
|
|
|
void * ciRecordFree( void * p )
|
|
{
|
|
if ( 0 == p )
|
|
return 0;
|
|
|
|
AHeader *ap = (AHeader *)p - 1;
|
|
|
|
switch( *((char *)p + ap->size) )
|
|
{
|
|
case ALLOC_SIGNATURE:
|
|
break;
|
|
case FREE_SIGNATURE:
|
|
heapDebugOut(( DEB_WARN, "Invalid freed pointer: 0x%x\n", p ));
|
|
Win4Assert( !"Invalid freed pointer" );
|
|
return 0;
|
|
break;
|
|
default:
|
|
heapDebugOut((DEB_WARN, "Invalid overrun pointer: 0x%x\n", p ));
|
|
Win4Assert( !"Invalid overrun pointer" );
|
|
return 0;
|
|
break;
|
|
}
|
|
*((char *)p + ap->size) = FREE_SIGNATURE;
|
|
if ( 0 != ap->p )
|
|
AllocArenaRecordFree( ap->p, ap->size );
|
|
|
|
return (void *) ap;
|
|
} //CiRecordFree
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ciNewDebug
|
|
//
|
|
// Synopsis: Debugging allocator
|
|
//
|
|
// Arguments: [size] -- Size of the memory to allocate.
|
|
//
|
|
// Returns: A pointer to the allocated memory.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void * ciNewDebug( size_t size )
|
|
{
|
|
// just a convenient way to dump the allocation arena
|
|
|
|
if ( g_fDumpArena )
|
|
{
|
|
// Note: If the cs is invalid at this point, it's because
|
|
// you're calling operator new too early. Construct
|
|
// your global object later.
|
|
|
|
EnterCriticalSection( &g_csDbgMemExclusive );
|
|
|
|
if ( g_fDumpArena )
|
|
{
|
|
AllocArenaDump( 0 );
|
|
realUtilization();
|
|
g_fDumpArena = FALSE;
|
|
heapDebugOut(( DEB_FORCE, "done dumping the heap\n" ));
|
|
DebugBreak();
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csDbgMemExclusive );
|
|
}
|
|
|
|
// fail test?
|
|
|
|
if ( ( 0 != g_FailTestRatio ) &&
|
|
( ( rand() % g_FailTestRatio ) == 1 ) )
|
|
return 0;
|
|
|
|
UINT cb = ciAddAllocRecord( size );
|
|
void *p = realAlloc( cb );
|
|
|
|
if ( 0 != p )
|
|
p = ciRecordAlloc( p, size );
|
|
|
|
return p;
|
|
} //CiNewDebug
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ciNewDebugNoRecord
|
|
//
|
|
// Synopsis: Compatible with ciNewDebug, but doesn't log allocation.
|
|
//
|
|
// Arguments: [size] -- Size of the memory to allocate.
|
|
//
|
|
// Returns: A pointer to the allocated memory.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void * ciNewDebugNoRecord( size_t size )
|
|
{
|
|
void *p = realAlloc( 1 + size + sizeof AHeader );
|
|
|
|
if ( 0 != p )
|
|
{
|
|
((AHeader *)p)->size = size;
|
|
((AHeader *)p)->p = 0;
|
|
p = (AHeader *)p + 1;
|
|
*((char *)p + size ) = ALLOC_SIGNATURE;
|
|
}
|
|
|
|
return p;
|
|
} //CiNewDebugNoRecord
|
|
|
|
#ifdef UseCICoTaskMem
|
|
|
|
#undef CoTaskMemAlloc
|
|
WINOLEAPI_(LPVOID) CoTaskMemAlloc(IN ULONG cb);
|
|
void * CICoTaskMemAlloc( ULONG cb )
|
|
{
|
|
// fail test?
|
|
|
|
if ( ( 0 != g_FailTestRatio ) &&
|
|
( ( rand() % g_FailTestRatio ) == 1 ) )
|
|
return 0;
|
|
|
|
ULONG cbAlloc = ciAddAllocRecord( cb );
|
|
void *p = CoTaskMemAlloc( cbAlloc );
|
|
return ciRecordAlloc( p, cb );
|
|
}
|
|
|
|
#undef CoTaskMemFree
|
|
WINOLEAPI_(void) CoTaskMemFree(IN LPVOID p);
|
|
void CICoTaskMemFree( LPVOID p )
|
|
{
|
|
void *pv = ciRecordFree( p );
|
|
CoTaskMemFree( pv );
|
|
}
|
|
|
|
#endif // UseCICoTaskMem
|
|
|
|
#else // CIDBG == 1 || DBG == 1
|
|
|
|
#define heapDebugOut(x)
|
|
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ExceptDllMain
|
|
//
|
|
// Synopsis: Entry point on DLL initialization for exception-specific stuff.
|
|
//
|
|
// History: 10-12-93 kevinro Created
|
|
// 02-28-96 KyleP Cleanup
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
extern CDLLStaticMutexSem g_mxsAssert;
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
extern CStaticMutexSem g_mtxGetStackTrace;
|
|
extern CMemMutex gmem_mutex;
|
|
|
|
BOOL ExceptDllMain( HANDLE hDll, DWORD dwReason, LPVOID lpReserved )
|
|
{
|
|
switch( dwReason )
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
#if CIDBG == 1 || DBG == 1
|
|
{
|
|
// These two objects are initialized from win.ini
|
|
|
|
static CInfoLevel level( L"Win4InfoLevel",Win4InfoLevel);
|
|
static CInfoLevel mask( L"Win4InfoMask", Win4InfoMask, (ULONG)-1 );
|
|
}
|
|
|
|
InitializeCriticalSection( &g_csDbgMemExclusive );
|
|
g_mxsAssert.Init();
|
|
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
g_mtxGetStackTrace.Init();
|
|
gmem_mutex.Init();
|
|
|
|
gmem_hHeap = HeapCreate( 0, 0, 0 );
|
|
|
|
UseLowFragmentationHeap( gmem_hHeap );
|
|
|
|
//
|
|
// HeapAlloc is faster on MP machines, and falloc is faster and
|
|
// uses less working set on UP machines.
|
|
//
|
|
|
|
HKEY hKey;
|
|
DWORD fUseSystemHeap;
|
|
BOOL fRegKeySet = FALSE;
|
|
DWORD dwError = RegOpenKeyW( HKEY_LOCAL_MACHINE, wcsRegAdminSubKey, &hKey );
|
|
if( ERROR_SUCCESS == dwError )
|
|
{
|
|
DWORD dwType;
|
|
DWORD cb = sizeof( fUseSystemHeap );
|
|
dwError = RegQueryValueExW( hKey,
|
|
L"UseSystemHeap",
|
|
0,
|
|
&dwType,
|
|
( BYTE * ) & fUseSystemHeap,
|
|
&cb );
|
|
RegCloseKey( hKey );
|
|
fRegKeySet = ( ERROR_SUCCESS == dwError && REG_DWORD == dwType );
|
|
}
|
|
|
|
DWORD dwNumberOfProcessors;
|
|
|
|
if( !fRegKeySet )
|
|
{
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo( &si );
|
|
fUseSystemHeap = si.dwNumberOfProcessors > 1;
|
|
}
|
|
|
|
if ( fUseSystemHeap )
|
|
{
|
|
realAlloc = heapAlloc;
|
|
realFree = heapFree;
|
|
realSize = heapSize;
|
|
realValidate = heapValidate;
|
|
realUtilization = heapUtilization;
|
|
}
|
|
else
|
|
{
|
|
realAlloc = memAlloc;
|
|
realFree = memFree;
|
|
realSize = memSize;
|
|
realValidate = memIsValidPointer;
|
|
realUtilization = memUtilization;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
if ( 0 != gmem_hHeap )
|
|
{
|
|
HeapDestroy( gmem_hHeap );
|
|
gmem_hHeap = 0;
|
|
}
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
DeleteCriticalSection( &g_csDbgMemExclusive );
|
|
g_mxsAssert.Delete();
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
break;
|
|
}
|
|
case DLL_THREAD_ATTACH:
|
|
#if 0
|
|
//
|
|
// can't do the exception translation here for several reasons:
|
|
//
|
|
// 1) iis creates threads before we are called, so they don't have
|
|
// their exceptions translated (on dll load, those threads don't
|
|
// send thread_attach's, per the sdk)
|
|
// 2) for some reason, we aren't getting thread attaches anyway.
|
|
// 3) the first thread to call us only is guaranteed to send
|
|
// a process_attach, not a thread_attach (per sdk)
|
|
// 4) we don't want to hose other ISAPI apps by changing
|
|
// their exception handler.
|
|
//
|
|
|
|
//DbgPrint("thread attach!\n");
|
|
#if defined(NATIVE_EH)
|
|
_set_se_translator( SystemExceptionTranslator );
|
|
//DbgPrint("set the handler!\n");
|
|
#endif // NATIVE_EH
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
} //ExceptDllMain
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: function ciNew, public
|
|
//
|
|
// Synopsis: Global operator new which throws exceptions.
|
|
//
|
|
// Effects: Keeps track of the most recent heap allocation in each
|
|
// thread. This information is used to determine when to
|
|
// unlink CUnwindable objects.
|
|
//
|
|
// Arguments: [size] -- Size of the memory to allocate.
|
|
//
|
|
// Returns: A pointer to the allocated memory.
|
|
// Is *NOT* initialized to 0!
|
|
// It is 8-byte aligned.
|
|
//
|
|
// Modifies: _pLastNew in _exceptioncontext.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void * ciNew( size_t size )
|
|
{
|
|
Win4Assert( 0 != realAlloc );
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
void* p = ciNewDebug( size );
|
|
#else // CIDBG == 1 || DBG == 1
|
|
void * p = realAlloc( size );
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
if ( 0 == p )
|
|
THROW( CException( E_OUTOFMEMORY ) );
|
|
|
|
return p;
|
|
} //ciNew
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ciDelete, public
|
|
//
|
|
// Synopsis: Matches the operator new above.
|
|
//
|
|
// Arguments: [p] -- The pointer to delete.
|
|
//
|
|
// Requires: [p] was allocated with ciNew
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void ciDelete( void * p )
|
|
{
|
|
#if CIDBG == 1 || DBG == 1
|
|
|
|
p = ciRecordFree( p );
|
|
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
if ( 0 == p )
|
|
return;
|
|
|
|
realFree( p );
|
|
} //ciDelete
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ciIsValidPointer, public
|
|
//
|
|
// Synopsis: Determines if a pointer is valid, was allocated with ciNew,
|
|
// and can be freed with ciDelete.
|
|
//
|
|
// Arguments: [p] -- The pointer to check
|
|
//
|
|
// Returns: TRUE if the pointer appears valid, FALSE otherwise
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL ciIsValidPointer( const void * p )
|
|
{
|
|
if ( 0 == p )
|
|
return TRUE;
|
|
|
|
Win4Assert( 0 != realValidate );
|
|
|
|
//
|
|
// Allocations are rounded up to at least 8 bytes, so at least that
|
|
// much must be writable.
|
|
//
|
|
|
|
if ( IsBadWritePtr( (void *) p, 8 ) )
|
|
{
|
|
heapDebugOut(( DEB_WARN, "Invalid non-writable pointer: 0x%x\n", p ));
|
|
Win4Assert( !"Invalid non-writable pointer" );
|
|
return FALSE;
|
|
}
|
|
|
|
#if CIDBG == 1 || DBG == 1
|
|
|
|
AHeader *ap = (AHeader *)p - 1;
|
|
|
|
switch( *((char *)p + ap->size) )
|
|
{
|
|
case ALLOC_SIGNATURE:
|
|
break;
|
|
case FREE_SIGNATURE:
|
|
heapDebugOut(( DEB_WARN, "Invalid freed pointer: 0x%x\n", p ));
|
|
Win4Assert( !"Invalid freed pointer" );
|
|
return FALSE;
|
|
break;
|
|
default:
|
|
heapDebugOut((DEB_WARN, "Invalid overrun pointer: 0x%x\n", p ));
|
|
Win4Assert( !"Invalid overrun pointer" );
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
p = (void *) ap;
|
|
|
|
#endif // CIDBG == 1 || DBG == 1
|
|
|
|
return realValidate( p );
|
|
} //ciIsValidPointer
|
|
|