/*========================================================================== * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: OSInd.cpp * Content: OS indirection functions to abstract OS specific items. * * History: * Date By Reason * ==== == ====== * 07/12/99 jtk Created * 09/21/99 rodtoll Fixed for retail builds * 09/22/99 jtk Added callstacks to memory allocations * 08/28/2000 masonb Voice Merge: Allow new and delete with size of 0 * 11/28/2000 rodtoll: WinBug #206257 - Retail DPNET.DLL links to DebugBreak() * 12/22/2000 aarono: ManBug # 190380 use process heap for retail. ***************************************************************************/ #include "dncmni.h" #define PROF_SECT "DirectPlay8" //********************************************************************** // Constant definitions //********************************************************************** // // CRC key for validating memory linkages // Signature for validating memory blocks // #ifdef _WIN64 #define MEMORY_CRC 0X5AA55AA55AA55AA5 #define GUARD_SIGNATURE 0x0F1E2D3C4B5A6978 #else #define MEMORY_CRC 0X5AA55AA5 #define GUARD_SIGNATURE 0x0F1E2D3C #endif // _WIN64 static CRITICAL_SECTION g_AllocatedMemoryLock; // // signature for validating memory blocks // // // enumerated values to indicate how to report memory leaks // #if defined( DN_MEMORY_TRACKING ) || defined( DN_CRITICAL_SECTION_TRACKING ) #define MEMORY_LEAK_REPORT_NONE 0x00000000 #define MEMORY_LEAK_REPORT_DPF 0x00000001 #define MEMORY_LEAK_REPORT_DIALOG 0x00000002 #endif //********************************************************************** // Macro definitions //********************************************************************** // // Macro to compute the offset of an element inside of a larger structure. // Copied from MSDEV's STDLIB.H and modified to return INT_PTR // #define OFFSETOF(s,m) ( ( INT_PTR ) &( ( (s*) 0 )->m ) ) // // macro for length of array // #define LENGTHOF( arg ) ( sizeof( arg ) / sizeof( arg[ 0 ] ) ) // // ASSERT macro // #ifdef _DEBUG #ifdef _X86_ #define ASSERT( arg ) if ( arg == FALSE ) { _asm { int 3 }; } #else #define ASSERT( arg ) if ( arg == FALSE ) { DebugBreak(); } #endif #else // _DEBUG #define ASSERT( arg ) #endif //_DEBUG //********************************************************************** // Structure definitions //********************************************************************** //********************************************************************** // Variable definitions //********************************************************************** // // debug variable to make sure we're initialized before having any functions // called // DEBUG_ONLY( static BOOL g_fOSIndirectionLayerInitialized = FALSE ); // // time variables // static DNCRITICAL_SECTION g_TimeLock; static DWORD g_dwLastTimeCall = 0; #ifdef DN_CRITICAL_SECTION_TRACKING CBilink g_blCritSecs; DNCRITICAL_SECTION g_CSLock; #endif #ifdef DN_MEMORY_TRACKING DWORD g_dwMemLeakDisplayFlags = MEMORY_LEAK_REPORT_DPF; #endif // // OS items // static OSVERSIONINFO g_OSVersionInfo; static HINSTANCE g_hApplicationInstance; // // memory heap // HANDLE g_hMemoryHeap = NULL; PSECURITY_ATTRIBUTES g_psa = NULL; SECURITY_ATTRIBUTES g_sa; BYTE g_pSD[SECURITY_DESCRIPTOR_MIN_LENGTH]; BOOL g_fDaclInited = FALSE; //********************************************************************** // Function prototypes //********************************************************************** #ifdef DN_MEMORY_TRACKING static int DisplayMemoryLeaks( void ); BOOL DNMemoryTrackInitialize( void ); #endif #if defined( DN_MEMORY_TRACKING ) || defined( DN_CRITICAL_SECTION_TRACKING ) static int DisplayCallStack( const char *const pszMsg, const char *const pszTitle, const char *const pCallStack ); #endif //********************************************************************** // Function definitions //********************************************************************** typedef BOOL (WINAPI *PFNINITCRITSECANDSPINCOUNT)(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); PFNINITCRITSECANDSPINCOUNT g_pfnInitializeCriticalSectionAndSpinCount = NULL; //********************************************************************** // ------------------------------ // DNOSIndirectionInit - initialize the OS indirection layer // // Entry: Nothing // // Exit: Boolean indicating success // TRUE = initialization successful // FALSE = initialization unsuccessful // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNOSIndirectionInit" BOOL DNOSIndirectionInit( void ) { BOOL fReturn; DNASSERT( g_fOSIndirectionLayerInitialized == FALSE ); // // initialize // fReturn = TRUE; // // note OS version // memset( &g_OSVersionInfo, 0x00, sizeof( g_OSVersionInfo ) ); g_OSVersionInfo.dwOSVersionInfoSize = sizeof( g_OSVersionInfo ); if ( GetVersionEx( &g_OSVersionInfo ) == FALSE ) { return FALSE; } HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); if (hKernel32 != NULL) { g_pfnInitializeCriticalSectionAndSpinCount = (PFNINITCRITSECANDSPINCOUNT) GetProcAddress(hKernel32, "InitializeCriticalSectionAndSpinCount"); } // // note application instance // g_hApplicationInstance = GetModuleHandle( NULL ); if ( g_hApplicationInstance == NULL ) { DWORD dwError; dwError = GetLastError(); DPFX(DPFPREP, 0, "Failed to GetModuleHandle: 0x%x", dwError ); goto Failure; } #ifdef DN_MEMORY_TRACKING g_dwMemLeakDisplayFlags = GetProfileIntA( PROF_SECT, "MemoryLeakOutput", MEMORY_LEAK_REPORT_DPF ); #endif // // intialize critical section tracking code before anything else! // #ifdef DN_CRITICAL_SECTION_TRACKING g_blCritSecs.Initialize(); if ( DNInitializeCriticalSection(&g_CSLock) == FALSE ) { DPFX(DPFPREP, 0, "Failed to initialize critical section tracking code!" ); DNASSERT( FALSE ); goto Failure; } #endif // DN_CRITICAL_SECTION_TRACKING // // intiailize memory tracking before creating new memory heap // #ifdef DN_MEMORY_TRACKING if ( DNMemoryTrackInitialize() == FALSE ) { DPFX(DPFPREP, 0, "Failed to initialize memory tracking code!" ); DNASSERT( FALSE ); goto Failure; } #endif // DN_MEMORY_TRACKING DNASSERT( g_hMemoryHeap == NULL ); #ifdef _DEBUG g_hMemoryHeap = HeapCreate( 0, // flags (none) 0, // initial size (default) 0 // maximum heap size (allow heap to grow) ); #else g_hMemoryHeap = GetProcessHeap(); #endif if ( g_hMemoryHeap == NULL ) { DPFX(DPFPREP, 0, "Failed to create memory heap!" ); goto Failure; } // // get initial time for timebase // g_dwLastTimeCall = GETTIMESTAMP(); if ( DNInitializeCriticalSection( &g_TimeLock ) == FALSE ) { goto Failure; } goto Exit; Exit: if ( fReturn != FALSE ) { DEBUG_ONLY( g_fOSIndirectionLayerInitialized = TRUE ); } return fReturn; Failure: fReturn = FALSE; DNOSIndirectionDeinit(); goto Exit; } //********************************************************************** //********************************************************************** // ------------------------------ // DNOSIndirectionDeinit - deinitialize OS indirection layer // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNOSIndirectionDeinit" void DNOSIndirectionDeinit( void ) { // // clean up time management resources // DNDeleteCriticalSection( &g_TimeLock ); #ifdef DN_CRITICAL_SECTION_TRACKING // // Display CritSec leaks before displaying memory leaks, because displaying memory leaks // may free the memory for the CritSec and corrupt the CritSec bilink // BOOL fDisplayLeaks = TRUE; DNEnterCriticalSection(&g_CSLock); CBilink* pblCS = g_blCritSecs.GetNext(); while (pblCS != &g_blCritSecs) { UINT_PTR MessageReturn; char CallStackBuffer[ CALLSTACK_BUFFER_SIZE ]; char LeakSizeString[ 50 ]; char DialogTitle[ 1000 ]; DNCRITICAL_SECTION* pCS = CONTAINING_RECORD(pblCS, DNCRITICAL_SECTION, blCritSec); #ifdef _IA64_ wsprintf( LeakSizeString, "Critical Section leaked at address 0x%p!\n", pCS ); #else wsprintf( LeakSizeString, "Critical Section leaked at address 0x%08x!\n", pCS ); #endif strcpy( DialogTitle, "DirectPlay8 critical section leak detected!"); pCS->AllocCallStack.GetCallStackString( CallStackBuffer ); if ( ( g_dwMemLeakDisplayFlags & MEMORY_LEAK_REPORT_DPF ) != 0 ) { DPFX(DPFPREP, 0, "%s%s%s\n", DialogTitle, LeakSizeString, CallStackBuffer ); } if ( ( g_dwMemLeakDisplayFlags & MEMORY_LEAK_REPORT_DIALOG ) != 0 ) { if ( fDisplayLeaks != FALSE ) { MessageReturn = DisplayCallStack( LeakSizeString, DialogTitle, CallStackBuffer ); switch ( MessageReturn ) { // // stop application now // case IDABORT: { fDisplayLeaks = FALSE; break; } // // display next leak // case IDIGNORE: { break; } // // stop in the debugger // case IDRETRY: { DNASSERT( FALSE ); break; } // // unknown // default: { DNASSERT( FALSE ); break; } } } } pblCS = pblCS->GetNext(); } DNLeaveCriticalSection(&g_CSLock); DNDeleteCriticalSection( &g_CSLock ); #endif // DN_CRITICAL_SECTION_TRACKING // // Report memory leaks, validate the heap if we're on NT and then destroy // the heap. // if ( g_hMemoryHeap != NULL ) { // // report memory leaks, if applicable // #ifdef DN_MEMORY_TRACKING DNMemoryTrackDisplayMemoryLeaks(); DeleteCriticalSection( &g_AllocatedMemoryLock ); #endif // DN_MEMORY_TRACKING // // Validate heap contents before shutdown. This code only works on NT. // #ifdef _DEBUG if ( DNGetOSType() == VER_PLATFORM_WIN32_NT) { // // Check heap // if ( HeapValidate( g_hMemoryHeap, 0, NULL ) == FALSE ) { DPFX(DPFPREP, 0, "Problem validating heap on destroy!" ); } } // // destroy heap - debug only, we use the process heap for retail. // if ( HeapDestroy( g_hMemoryHeap ) == FALSE ) { DWORD dwErrorReturn; dwErrorReturn = GetLastError(); DPFX(DPFPREP, 0, "Problem destroying heap in DNOSIndirectionDeinit!" ); DisplayErrorCode( 0, dwErrorReturn ); } #endif _DEBUG g_hMemoryHeap = NULL; } // // clean critical section management resources // DEBUG_ONLY( g_fOSIndirectionLayerInitialized = FALSE ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNGetOSType - get OS type // // Entry: Nothing // // Exit: OS type // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNGetOSType" UINT_PTR DNGetOSType( void ) { DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); return g_OSVersionInfo.dwPlatformId; } //********************************************************************** // ------------------------------ // DNOSIsXPOrGreater - return TRUE if OS is WindowsXP or later or NT flavor // // Entry: Nothing // // Exit: BOOL // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNOSIsXPOrGreater" BOOL DNOSIsXPOrGreater( void ) { DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); return ((g_OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && ((g_OSVersionInfo.dwMajorVersion > 5) || ((g_OSVersionInfo.dwMajorVersion == 5) && (g_OSVersionInfo.dwMinorVersion >= 1))) ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNGetNullDacl - get a SECURITY_ATTRIBUTE structure that specifies a // NULL DACL which is accesible by all users. // // Entry: Nothing // // Exit: PSECURITY_ATTRIBUTES // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNGetNullDacl" PSECURITY_ATTRIBUTES DNGetNullDacl() { // This is done to make this function independent of DNOSIndirectionInit so that the debug // layer can call it before the indirection layer is initialized. if (!g_fDaclInited) { if (!InitializeSecurityDescriptor((SECURITY_DESCRIPTOR*)g_pSD, SECURITY_DESCRIPTOR_REVISION)) { DPFX(DPFPREP, 0, "Failed to initialize security descriptor" ); } else { // Add a NULL DACL to the security descriptor.. if (!SetSecurityDescriptorDacl((SECURITY_DESCRIPTOR*)g_pSD, TRUE, (PACL) NULL, FALSE)) { DPFX(DPFPREP, 0, "Failed to set NULL DACL on security descriptor" ); } else { g_sa.nLength = sizeof(SECURITY_ATTRIBUTES); g_sa.lpSecurityDescriptor = g_pSD; g_sa.bInheritHandle = FALSE; g_psa = &g_sa; } } g_fDaclInited = TRUE; } return g_psa; } //********************************************************************** //********************************************************************** // ------------------------------ // DNGetApplcationInstance - application instance // // Entry: Nothing // // Exit: Application instance // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNGetApplicationInstance" HINSTANCE DNGetApplicationInstance( void ) { DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); return g_hApplicationInstance; } //********************************************************************** //********************************************************************** // ------------------------------ // DNTimeGet - get time in milliseconds // // Entry: Pointer to destination time // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNTimeGet" void DNTimeGet( DN_TIME *const pTimeDestination ) { static DN_TIME Time = { 0 }; DN_TIME DeltaT; DWORD dwCurrentTime; DNASSERT( pTimeDestination != NULL ); DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); DNEnterCriticalSection( &g_TimeLock ); // // we'll assume that we're getting called more than once every 40 days // so time wraps can be easily accounted for // dwCurrentTime = GETTIMESTAMP(); DeltaT.Time32.TimeHigh = 0; DeltaT.Time32.TimeLow = dwCurrentTime - g_dwLastTimeCall; if ( DeltaT.Time32.TimeLow > 0x7FFFFFFF ) { DNASSERT( FALSE ); DeltaT.Time32.TimeLow = -static_cast( DeltaT.Time32.TimeLow ); } g_dwLastTimeCall = dwCurrentTime; DNTimeAdd( &Time, &DeltaT, &Time ); DBG_CASSERT( sizeof( *pTimeDestination ) == sizeof( Time ) ); memcpy( pTimeDestination, &Time, sizeof( *pTimeDestination ) ); DNLeaveCriticalSection( &g_TimeLock ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNTimeCompare - compare two times // // Entry: Pointer to time1 // Pointer to time2 // // Exit: Value indicating relative magnitude // -1 = *pTime1 < *pTime2 // 0 = *pTime1 == *pTime2 // 1 = *pTime1 > *pTime2 // // Notes: This function comes in 32-bit and 64-bit flavors. This function // will result in a compile error if compiled on an unsupported platform. // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNTimeCompare" INT_PTR DNTimeCompare( const DN_TIME *const pTime1, const DN_TIME *const pTime2 ) { UINT_PTR iReturnValue; DNASSERT( pTime1 != NULL ); DNASSERT( pTime2 != NULL ); DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); #pragma TODO( johnkan, "Should this be inlined?" ) //#ifdef _WIN32 if ( pTime1->Time32.TimeHigh < pTime2->Time32.TimeHigh ) { iReturnValue = -1; } else { if ( pTime1->Time32.TimeHigh > pTime2->Time32.TimeHigh ) { iReturnValue = 1; } else { if ( pTime1->Time32.TimeLow < pTime2->Time32.TimeLow ) { iReturnValue = -1; } else { if ( pTime1->Time32.TimeLow == pTime2->Time32.TimeLow ) { iReturnValue = 0; } else { iReturnValue = 1; } } } } //#endif // _WIN32 //#ifdef _WIN64 // // debug me! // DNASSERT( FALSE ); // // if ( pTime1->Time < pTime2->Time ) // { // iReturnValue = -1; // } // else // { // if ( pTime1->Time == pTime2->Time ) // { // iReturnValue = 0; // } // else // { // iReturnValue = 1; // } // } //#endif // _WIN64 return iReturnValue; } //********************************************************************** //********************************************************************** // ------------------------------ // DNTimeAdd - add two times // // Entry: Pointer to time1 // Pointer to time2 // Pointer to time result // // Exit: Nothing // // Note: This function assumes that the time calculation won't wrap! // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNTimeAdd" void DNTimeAdd( const DN_TIME *const pTime1, const DN_TIME *const pTime2, DN_TIME *const pTimeResult ) { DNASSERT( pTime1 != NULL ); DNASSERT( pTime2 != NULL ); DNASSERT( pTimeResult != NULL ); DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); #pragma TODO( johnkan, "Should this be inlined?" ) #ifdef _X86_ _asm { mov ecx, pTime1 mov eax, ( DN_TIME [ ecx ] ).Time32.TimeLow; mov edx, ( DN_TIME [ ecx ] ).Time32.TimeHigh mov ecx, pTime2 add eax, ( DN_TIME [ ecx ] ).Time32.TimeLow adc edx, ( DN_TIME [ ecx ] ).Time32.TimeHigh mov ecx, pTimeResult mov ( DN_TIME [ ecx ] ).Time32.TimeLow, eax mov ( DN_TIME [ ecx ] ).Time32.TimeHigh, edx }; #else // _X86_ /* #ifdef _ALPHA_ // debug me DebugBreak(); __asm{ mov $t0, *pTime1 mov $t1, *pTime2 addq $t0, $t1 mov *pTimeResult, $t0 }; #else // _ALPHA_ */ //#ifdef _WIN32 DWORD dwTempLowTime; dwTempLowTime = pTime1->Time32.TimeLow; pTimeResult->Time32.TimeLow = pTime1->Time32.TimeLow + pTime2->Time32.TimeLow; pTimeResult->Time32.TimeHigh = pTime1->Time32.TimeHigh + pTime2->Time32.TimeHigh; // // check for overflow in low 32-bits and increment high value if applicable // if ( pTimeResult->Time32.TimeLow < dwTempLowTime ) { pTimeResult->Time32.TimeHigh++; } //#endif // _WIN32 //#ifdef _WIN64 // DEBUG_ONLY( UINT_PTR ReferenceTime ); // // // debug me! // DNASSERT( FALSE ); // // DEBUG_ONLY( ReferenceTime = pTime1->Time ); // *pTimeResult = pTime1->Time + pTime2->Time; // DNASSERT( *pTimeResult >= ReferenceTime ); // //#endif // _WIN64 // #endif // _ALPHA_ #endif // _X86_ } //********************************************************************** //********************************************************************** // ------------------------------ // DNTimeSubtract - subtract two times // // Entry: Pointer to time1 // Pointer to time2 // Pointer to time result // // Exit: Nothing // // Notes: This function assumes no underflow! // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNTimeSubtract" void DNTimeSubtract( const DN_TIME *const pTime1, const DN_TIME *const pTime2, DN_TIME *const pTimeResult ) { DNASSERT( pTime1 != NULL ); DNASSERT( pTime2 != NULL ); DNASSERT( pTimeResult != NULL ); DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); #pragma TODO( johnkan, "Should this be inlined?" ) #ifdef _X86_ _asm { mov ecx, pTime1 mov eax, ( DN_TIME [ ecx ] ).Time32.TimeLow mov edx, ( DN_TIME [ ecx ] ).Time32.TimeHigh mov ecx, pTime2 sub eax, ( DN_TIME [ ecx ] ).Time32.TimeLow sbb edx, ( DN_TIME [ ecx ] ).Time32.TimeHigh mov ecx, pTimeResult mov ( DN_TIME [ ecx ] ).Time32.TimeLow, eax mov ( DN_TIME [ ecx ] ).Time32.TimeHigh, edx }; #else // _X86_ /* #ifdef _ALPHA_ // debug me DebugBreak(); mov $t0, *pTime1 mov $t1, *pTime2 addq $t0, $t1 mov *pTimeResult, $t0 #else // _ALPHA_ */ //#ifdef _WIN32 DWORD dwTempLowTime; dwTempLowTime = pTime1->Time32.TimeLow; pTimeResult->Time32.TimeLow = pTime1->Time32.TimeLow - pTime2->Time32.TimeLow; pTimeResult->Time32.TimeHigh = pTime1->Time32.TimeHigh - pTime2->Time32.TimeHigh; // // check for underflow in low 32-bits and decrement high value if applicable // if ( pTimeResult->Time32.TimeLow > dwTempLowTime ) { pTimeResult->Time32.TimeHigh--; } //#endif // _WIN32 //#ifdef _WIN64 // // debug me! // DNASSERT( FALSE ); // // DNASSERT( pTime1->Time > pTime2->Time ); // pTimeResult = pTime1->Time - pTime2->Time; //#endif // _WIN64 // #endif // _ALPHA_ #endif // _X86_ } //********************************************************************** //********************************************************************** // ------------------------------ // DNInitializeCriticalSection - initialize a critical section // // Entry: Pointer to critical section // // Exit: Boolean indicating success // TRUE = success // FALSE = failue // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNInitializeCriticalSection" BOOL DNInitializeCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { BOOL fReturn; DNASSERT( pCriticalSection != NULL ); fReturn = TRUE; memset( pCriticalSection, 0x00, sizeof( *pCriticalSection ) ); #ifdef DN_CRITICAL_SECTION_TRACKING pCriticalSection->OwningThreadID = DN_INVALID_THREAD_ID; pCriticalSection->MaxLockCount = -1; #endif // DN_CRITICAL_SECTION_TRACKING // // attempt to enter the critical section once // _try { // Do this for Win95 support only if (g_pfnInitializeCriticalSectionAndSpinCount) { // Pre-allocate the critsec event by setting the high bit of the spin count and set spin to 1000 // Win98 and up map calls to this function to InitializeCriticalSection which makes sense since // 9x only uses one processor. NT also converts the spin to 0 for single proc machines. fReturn = g_pfnInitializeCriticalSectionAndSpinCount( &pCriticalSection->CriticalSection , 0x80000000 | 1000); // Believe it or not Win9x defines this function as returning VOID so its return value is garbage if (g_OSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) { fReturn = TRUE; } } else { InitializeCriticalSection( &pCriticalSection->CriticalSection ); } } _except( EXCEPTION_EXECUTE_HANDLER ) { fReturn = FALSE; } _try { if (fReturn) { EnterCriticalSection( &pCriticalSection->CriticalSection ); } } _except( EXCEPTION_EXECUTE_HANDLER ) { DeleteCriticalSection(&pCriticalSection->CriticalSection); fReturn = FALSE; } // // if we didn't fail on entering the critical section, make sure // we release it // if ( fReturn != FALSE ) { LeaveCriticalSection( &pCriticalSection->CriticalSection ); #ifdef DN_CRITICAL_SECTION_TRACKING pCriticalSection->AllocCallStack.NoteCurrentCallStack(); // NOTE: We will not add g_CSLock to our list if (pCriticalSection != &g_CSLock) { DNEnterCriticalSection(&g_CSLock); pCriticalSection->blCritSec.InsertBefore(&g_blCritSecs); DNLeaveCriticalSection(&g_CSLock); } #endif } return fReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // DNDeleteCriticalSection - delete a critical section // // Entry: Pointer to critical section // // Exit: Nothing // // Notes: This function wrapping is overkill, but we're closing down so // the overhead is negligible. // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNDeleteCriticalSection" void DNDeleteCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { DNASSERT( pCriticalSection != NULL ); DNASSERT( g_fOSIndirectionLayerInitialized != FALSE ); #ifdef DN_CRITICAL_SECTION_TRACKING DNASSERT( pCriticalSection->LockCount == 0 ); // NOTE: We will not remove g_CSLock from our list since we never added it if (pCriticalSection != &g_CSLock) { DNEnterCriticalSection(&g_CSLock); pCriticalSection->blCritSec.RemoveFromList(); DNLeaveCriticalSection(&g_CSLock); } #endif DeleteCriticalSection( &pCriticalSection->CriticalSection ); memset( &pCriticalSection->CriticalSection, 0x00, sizeof( pCriticalSection->CriticalSection ) ); } //********************************************************************** #ifdef DN_MEMORY_TRACKING //********************************************************************** //** //** THIS IS THE MEMORY TRACKING SECTION. ONLY ADD FUNCTIONS HERE THAT ARE //** FOR MEMORY TRACKING!! //** //********************************************************************** // // Structure prepended to memory allocations to check for leaks. // Disable warning 4200 (zero sized structure elements). // #pragma warning ( disable : 4200 ) typedef struct _MEMORY_LINK { public: void Initialize( void ) { m_pNext = this; m_pPrev = this; } BOOL IsValid( void ) const { return ( m_Checksum == ( reinterpret_cast( m_pPrev ) ^ reinterpret_cast( m_pNext ) ^ m_Size ^ MEMORY_CRC ) ); } void UpdateCRC( void ) { m_Checksum = reinterpret_cast( m_pPrev ) ^ reinterpret_cast( m_pNext ) ^ m_Size ^ MEMORY_CRC; } BOOL IsEmpty( void ) { return ( ( m_pNext == this ) && ( m_pPrev == this ) ); } _MEMORY_LINK *GetNext( void ) const { return m_pNext; } _MEMORY_LINK *GetPrev( void ) const { return m_pPrev; } void *GetDataPointer( void ) { return &m_Data[ sizeof( DWORD_PTR ) ]; } static _MEMORY_LINK *PointerFromData( void *const pData ) { return reinterpret_cast<_MEMORY_LINK*>( &( reinterpret_cast( pData )[ - ( OFFSETOF( MEMORY_LINK, m_Data ) + static_cast( sizeof( DWORD_PTR ) ) ) ] ) ); } void SetSize( const UINT_PTR Size ){ m_Size = Size; } UINT_PTR GetSize( void ) const { return m_Size; } #undef DPF_MODNAME #define DPF_MODNAME "_MEMORY_LINK::LinkAfterOther" void LinkAfterOther( _MEMORY_LINK &OtherLink ) { ASSERT( OtherLink.IsValid() != FALSE ); if ( OtherLink.m_pNext != NULL ) { ASSERT( OtherLink.m_pNext->IsValid() != FALSE ); OtherLink.m_pNext->m_pPrev = this; OtherLink.m_pNext->UpdateCRC(); } m_pNext = OtherLink.m_pNext; m_pPrev = &OtherLink; UpdateCRC(); OtherLink.m_pNext = this; OtherLink.UpdateCRC(); } #undef DPF_MODNAME #define DPF_MODNAME "_MEMORY_LINK::RemoveFromList" void RemoveFromList( void ) { ASSERT( IsValid() != FALSE ); ASSERT( m_pPrev != NULL ); if ( m_pNext != NULL ) { ASSERT( m_pNext->IsValid() != FALSE ); m_pNext->m_pPrev = m_pPrev; m_pNext->UpdateCRC(); } ASSERT( m_pPrev->IsValid() != FALSE ); m_pPrev->m_pNext = m_pNext; m_pPrev->UpdateCRC(); m_pNext = NULL; m_pPrev = NULL; } void NoteCurrentCallStack( void ) { m_CallStack.NoteCurrentCallStack(); } void GetCallStack( char *const pBuffer ) const { m_CallStack.GetCallStackString( pBuffer ); } void SetOverrunSignatures( void ) { *reinterpret_cast( m_Data ) = GUARD_SIGNATURE; // 6/30/2000(RichGr) - IA64: Specifying UNALIGNED generates the correct IA64 code for non-8-byte alignment. *reinterpret_cast( &m_Data[ m_Size + sizeof( DWORD_PTR ) ] ) = GUARD_SIGNATURE; } BOOL UnderrunDetected( void ) const { return ( *reinterpret_cast( m_Data ) != GUARD_SIGNATURE ); } // 6/30/2000(RichGr) - IA64: Specifying UNALIGNED generates the correct IA64 code for non-8-byte alignment. BOOL OverrunDetected( void ) const { return ( *reinterpret_cast( &m_Data[ m_Size + sizeof( DWORD_PTR ) ] ) != GUARD_SIGNATURE ); } BOOL IsCorrupted( void ) const { return ( !IsValid() || UnderrunDetected() || OverrunDetected() ); } protected: private: UINT_PTR m_Checksum; UINT_PTR m_Size; _MEMORY_LINK *m_pPrev; _MEMORY_LINK *m_pNext; CCallStack m_CallStack; BYTE m_Data[]; } MEMORY_LINK; #pragma warning ( default : 4200 ) // // forward structure references // typedef struct _MEMORY_LINK MEMORY_LINK; // // memory tracking variables // static UINT_PTR g_uAllocatedMemoryCount; static MEMORY_LINK g_AllocatedMemory; //********************************************************************** // ------------------------------ // DNMemoryTrackInitialize - initialize memory tracking // // Entry: Nothing // // Exit: Error code // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackInitialize" static BOOL DNMemoryTrackInitialize( void ) { BOOL fReturn; fReturn = TRUE; memset( &g_AllocatedMemory, 0x00, sizeof ( g_AllocatedMemory ) ); g_uAllocatedMemoryCount = 0; g_AllocatedMemory.Initialize(); g_AllocatedMemory.UpdateCRC(); InitializeCriticalSection( &g_AllocatedMemoryLock ); return fReturn; } //********************************************************************** //********************************************************************** //********************************************************************** // ------------------------------ // DNMemoryTrackHeapAlloc - allocate from heap and track allocations // // Entry: Size of memory to allocate // // Exit: Pointer to allocated memory // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackHeapAlloc" void *DNMemoryTrackHeapAlloc( const UINT_PTR Size ) { UINT_PTR RequiredSize; MEMORY_LINK *pMemoryLink; void *pReturn; // // Voice and lobby currently try allocating 0 byte buffers, can't enable this check yet. // //ASSERT( Size > 0 ); RequiredSize = Size + sizeof( MEMORY_LINK ) + ( sizeof( DWORD_PTR ) * 2 ); DNMemoryTrackingValidateMemory(); pMemoryLink = static_cast( HeapAlloc( g_hMemoryHeap, 0, RequiredSize ) ); if ( pMemoryLink != NULL ) { pMemoryLink->Initialize(); EnterCriticalSection( &g_AllocatedMemoryLock ); g_uAllocatedMemoryCount++; pMemoryLink->SetSize( Size ); pMemoryLink->LinkAfterOther( g_AllocatedMemory ); pMemoryLink->NoteCurrentCallStack(); pMemoryLink->SetOverrunSignatures(); LeaveCriticalSection( &g_AllocatedMemoryLock ); pReturn = pMemoryLink->GetDataPointer(); } else { pReturn = NULL; } return pReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // DNMemoryTrackHeapReAlloc - reallocate from heap and track allocations // // Entry: Pointer to old memory // Size of memory to allocate // // Exit: Pointer to allocated memory // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackHeapReAlloc" void *DNMemoryTrackHeapReAlloc( void *const pMemory, const UINT_PTR MemorySize ) { UINT_PTR RequiredSize; MEMORY_LINK *pMemoryLink; void *pReturn; ASSERT( pMemory != NULL ); // // Voice and lobby currently try allocating 0 byte buffers, can't enable this check yet. // //ASSERT( MemorySize > 0 ); pMemoryLink = MEMORY_LINK::PointerFromData( pMemory ); DNMemoryTrackingValidateMemory(); EnterCriticalSection( &g_AllocatedMemoryLock ); g_uAllocatedMemoryCount--; pMemoryLink->RemoveFromList(); pMemoryLink->SetSize( 0 ); LeaveCriticalSection( &g_AllocatedMemoryLock ); RequiredSize = MemorySize + sizeof( MEMORY_LINK ) + ( sizeof( DWORD_PTR ) * 2 ); pMemoryLink = static_cast( HeapReAlloc( g_hMemoryHeap, 0, pMemoryLink, RequiredSize ) ); if ( pMemoryLink != NULL ) { EnterCriticalSection( &g_AllocatedMemoryLock ); g_uAllocatedMemoryCount++; pMemoryLink->SetSize( MemorySize ); pMemoryLink->LinkAfterOther( g_AllocatedMemory ); pMemoryLink->SetOverrunSignatures(); LeaveCriticalSection( & g_AllocatedMemoryLock ); pReturn = pMemoryLink->GetDataPointer(); } else { pReturn = NULL; } return pReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // DNMemoryTrackHeapFree - free from heap and track memory // // Entry: Pointer to old memory // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackHeapFree" void DNMemoryTrackHeapFree( void *const pMemory ) { MEMORY_LINK *pMemoryLink; ASSERT( pMemory != NULL ); DNMemoryTrackingValidateMemory(); pMemoryLink = MEMORY_LINK::PointerFromData( pMemory ); EnterCriticalSection( &g_AllocatedMemoryLock ); g_uAllocatedMemoryCount--; pMemoryLink->RemoveFromList(); pMemoryLink->SetSize( 0 ); LeaveCriticalSection( &g_AllocatedMemoryLock ); HeapFree( g_hMemoryHeap, 0, pMemoryLink ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNMemoryTrackValidateMemory - validate allocated memory // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackingValidateMemory" void DNMemoryTrackingValidateMemory( void ) { MEMORY_LINK *pMemoryLink; char CallStackBuffer[ CALLSTACK_BUFFER_SIZE ]; // // validate all of the allocated memory // EnterCriticalSection( &g_AllocatedMemoryLock ); pMemoryLink = g_AllocatedMemory.GetNext(); while ( pMemoryLink != &g_AllocatedMemory ) { if ( pMemoryLink->IsCorrupted() != FALSE ) { UINT_PTR MessageReturn; char MessageString[ 1000 ]; wsprintf( MessageString, // 7/28/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers and handles. #ifdef _IA64_ // 8/05/2000(RichGr) - IA64: GetSize() returns UINT_PTR (64-bit), so we may as well handle in hex as %d expects a DWORD. "Memory block: 0x%p\tAllocated size: 0x%p bytes\nCorruption Type: ", #else "Memory block: 0x%08x\tAllocated size: %d bytes\nCorruption Type: ", #endif pMemoryLink->GetDataPointer(), pMemoryLink->GetSize() ); if ( !pMemoryLink->IsValid() ) { strcat( MessageString, " ,INVALID LINK"); } if ( pMemoryLink->UnderrunDetected() ) { strcat( MessageString, " ,UNDERRUN DETECTED"); } if ( pMemoryLink->OverrunDetected() ) { strcat( MessageString, " ,OVERRUN DETECTED"); } pMemoryLink->GetCallStack( CallStackBuffer ); MessageReturn = DisplayCallStack( MessageString, "Memory Corruption!", CallStackBuffer ); switch ( MessageReturn ) { case IDABORT: { DNASSERT( FALSE ); break; } case IDIGNORE: { // // You're probably going to get stopped in the heap // manager!!! // break; } case IDRETRY: { DNASSERT( FALSE ); break; } } } pMemoryLink = pMemoryLink->GetNext(); } LeaveCriticalSection( &g_AllocatedMemoryLock ); // // ask the OS to validate the heap // if ( HeapValidate( g_hMemoryHeap, 0, NULL ) == FALSE ) { DNASSERT( FALSE ); } } //********************************************************************** //********************************************************************** // ------------------------------ // DNMemoryTrackDisplayMemoryLeaks - display memory leaks // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNMemoryTrackDisplayMemoryLeaks" static void DNMemoryTrackDisplayMemoryLeaks( void ) { BOOL fDisplayLeaks; UINT_PTR uMaxMemoryLeakCount; UINT_PTR uMemoryLeakIndex; // // initialize // fDisplayLeaks = TRUE; uMaxMemoryLeakCount = g_uAllocatedMemoryCount; uMemoryLeakIndex = 0; // // Check for outstanding memory allocations. // while ( g_AllocatedMemory.IsEmpty() == FALSE ) { MEMORY_LINK *pTemp; UINT_PTR MessageReturn; char CallStackBuffer[ CALLSTACK_BUFFER_SIZE ]; char LeakSizeString[ 50 ]; char DialogTitle[ 1000 ]; pTemp = g_AllocatedMemory.GetNext(); uMemoryLeakIndex++; // 7/28/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers and handles. #ifdef _IA64_ // 8/05/2000(RichGr) - IA64: GetSize() returns UINT_PTR (64-bit), so we may as well handle in hex as %d expects a DWORD. wsprintf( LeakSizeString, "0x%p bytes leaked at address 0x%p!\n", pTemp->GetSize(), pTemp->GetDataPointer() ); #else wsprintf( LeakSizeString, "%d bytes leaked at address 0x%08x!\n", pTemp->GetSize(), pTemp->GetDataPointer() ); #endif wsprintf( DialogTitle, "DirectPlay8 memory leak detected! ( #%d of %d )", uMemoryLeakIndex, uMaxMemoryLeakCount ); pTemp->GetCallStack( CallStackBuffer ); if ( ( g_dwMemLeakDisplayFlags & MEMORY_LEAK_REPORT_DPF ) != 0 ) { DPFX(DPFPREP, 0, "%s%s%s\n", DialogTitle, LeakSizeString, CallStackBuffer ); } if ( ( g_dwMemLeakDisplayFlags & MEMORY_LEAK_REPORT_DIALOG ) != 0 ) { if ( fDisplayLeaks != FALSE ) { MessageReturn = DisplayCallStack( LeakSizeString, DialogTitle, CallStackBuffer ); switch ( MessageReturn ) { // // stop application now // case IDABORT: { fDisplayLeaks = FALSE; break; } // // display next leak // case IDIGNORE: { break; } // // stop in the debugger // case IDRETRY: { DNASSERT( FALSE ); break; } // // unknown // default: { DNASSERT( FALSE ); break; } } } } DNFree( pTemp->GetDataPointer() ); } } //********************************************************************** //********************************************************************** //** //** THIS IS THE END OF THE MEMORY LOGGING SECTION. //** //********************************************************************** #endif // DN_MEMORY_TRACKING #if defined( DN_MEMORY_TRACKING ) || defined( DN_CRITICAL_SECTION_TRACKING ) //********************************************************************** // ------------------------------ // DisplayCallStack - display a call stack message box // // Entry: Pointer to information string // Pointer to title string // Pointer to call stack string // // Exit: Dialog return code // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DisplayCallStack" static int DisplayCallStack( const char *const pszMsg, const char *const pszTitle, const char *const pCallStackString ) { MSGBOXPARAMS MessageBoxParams; char szStackTraceMsg[ CALLSTACK_BUFFER_SIZE ]; _snprintf(szStackTraceMsg, CALLSTACK_BUFFER_SIZE-1, "%s%s", pszMsg, pCallStackString); // // display message box // memset( &MessageBoxParams, 0x00, sizeof( MessageBoxParams ) ); MessageBoxParams.cbSize = sizeof( MessageBoxParams ); MessageBoxParams.lpszText = szStackTraceMsg; MessageBoxParams.lpszCaption = pszTitle; MessageBoxParams.dwStyle = MB_ABORTRETRYIGNORE | MB_SETFOREGROUND | MB_TOPMOST | MB_DEFBUTTON2; MessageBoxParams.hInstance = NULL; return MessageBoxIndirect( &MessageBoxParams ); } //********************************************************************** //********************************************************************** //** //** END OF CALL STACK TRACKING SECTION. //** //********************************************************************** #endif // defined( DN_MEMORY_TRACKING ) || defined( DN_CRITICAL_SECTION_TRACKING ) #ifdef DN_CRITICAL_SECTION_TRACKING //********************************************************************** //** //** THIS IS THE CRITICAL-SECTION TRACKING SECTION. DON'T ADD FUNCTIONS HERE //** THAT ARE NOT FOR CRITICAL SECTIONS!! //** //********************************************************************** //********************************************************************** // ------------------------------ // DNCSTrackInitialize - initialize critical section tracking code // // Entry: Nothing // // Exit: Boolean indicating success // TRUE = initialization succeeded // FALSE = initialization failed // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNCSTrackInitialize" static BOOL DNCSTrackInitialize( void ) { g_blCritSecs.Initialize(); return DNInitializeCriticalSection(&g_CSLock); } //********************************************************************** //********************************************************************** // ------------------------------ // DNCSTrackSetCriticalSectionRecursionCount - set the maximum recursion depth // allowed by a DNCRITICAL_SECTION. // // Entry: Pointer to critical section // Recursion count // // Exit: Nothing // // Note: The recursion count is the number of times we allow reentry so // we need to bais by one to allow the user to take the lock at // least once. // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNCSTrackSetCriticalRecursionCount" void DNCSTrackSetCriticalSectionRecursionCount( DNCRITICAL_SECTION *const pCriticalSection, const UINT_PTR RecursionCount ) { UINT_PTR LocalRecursionCount; DNASSERT( pCriticalSection != NULL ); // // make sure we don't overflow // LocalRecursionCount = RecursionCount; if ( LocalRecursionCount == -1 ) { LocalRecursionCount--; } pCriticalSection->MaxLockCount = LocalRecursionCount + 1; DNASSERT( pCriticalSection->MaxLockCount != 0 ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNCSTrackEnterCriticalSection - enter a debug critical section // // Entry: Pointer to critical section // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNCSTrackEnterCriticalSection" void DNCSTrackEnterCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { UINT_PTR ThisThreadID; static BOOL fDisplayCallStacks = TRUE; DNASSERT( pCriticalSection != NULL ); EnterCriticalSection( &pCriticalSection->CriticalSection ); ThisThreadID = GetCurrentThreadId(); if ( pCriticalSection->OwningThreadID != ThisThreadID ) { DNASSERT( pCriticalSection->OwningThreadID == DN_INVALID_THREAD_ID ); pCriticalSection->OwningThreadID = ThisThreadID; DNASSERT( pCriticalSection->LockCount == 0 ); } else { DNASSERT( pCriticalSection->LockCount != 0 ); } if ( pCriticalSection->LockCount == 0 ) { pCriticalSection->CallStack.NoteCurrentCallStack(); } pCriticalSection->LockCount++; if ( pCriticalSection->LockCount > pCriticalSection->MaxLockCount ) { if ( pCriticalSection->MaxLockCount == 1 ) { if ( fDisplayCallStacks != FALSE ) { char CallStackBuffer[ CALLSTACK_BUFFER_SIZE ]; // // Exceeded recursion depth of 1, display stack of call orignally // holding the lock. // pCriticalSection->CallStack.GetCallStackString( CallStackBuffer ); switch ( DisplayCallStack( "Stack trace of function that originally held the lock:", "DNCritical section has been reentered!", CallStackBuffer ) ) { // // don't display any more critical section warnings! // case IDABORT: { fDisplayCallStacks = FALSE; break; } // // acknowledged // case IDIGNORE: { break; } // // stop in debugger // case IDRETRY: { DNASSERT( FALSE ); break; } } } } else { // // exceeded recursion depth, check your code!! // DNASSERT( FALSE ); } } } //********************************************************************** //********************************************************************** // ------------------------------ // DNCSTrackLeaveCriticalSection - leave a debug critical section // // Entry: Pointer to critical section // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNCSTrackLeaveCriticalSection" void DNCSTrackLeaveCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { DNASSERT( pCriticalSection != NULL ); DNASSERT( pCriticalSection->OwningThreadID == GetCurrentThreadId() ); DNASSERT( pCriticalSection->LockCount <= pCriticalSection->MaxLockCount ); DNASSERT( pCriticalSection->LockCount != 0 ); pCriticalSection->LockCount--; if ( pCriticalSection->LockCount == 0 ) { memset( &pCriticalSection->CallStack, 0x00, sizeof( pCriticalSection->CallStack ) ); pCriticalSection->OwningThreadID = DN_INVALID_THREAD_ID; } LeaveCriticalSection( &pCriticalSection->CriticalSection ); } //********************************************************************** //********************************************************************** // ------------------------------ // DNCSTrackCriticalSectionIsTakenByThisThread - determine if this thread has taken a // specific critical section // // Entry: Pointer to critical section // Boolean condition to test for // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNCSTrackCriticalSectionIsTakenByThisThread" void DNCSTrackCriticalSectionIsTakenByThisThread( const DNCRITICAL_SECTION *const pCriticalSection, const BOOL fFlag ) { ASSERT( fFlag == ( pCriticalSection->OwningThreadID == GetCurrentThreadId() ) ) } //********************************************************************** //********************************************************************** //** //** THIS IS THE END OF THE CRITICAL-SECTION TRACKING CODE. //** //********************************************************************** #endif // DN_CRITICAL_SECTION_TRACKING