//--------------------------------------------------------------------------- // // 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 #pragma hdrstop #include #include #include #include // 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 #include #include #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