|
|
/*==========================================================================
* * Copyright (C) 2002 Microsoft Corporation. All Rights Reserved. * * File: MemoryTracking.cpp * Content: Debug memory tracking for detecting leaks, overruns, etc. * * History: * Date By Reason * ==== == ====== * 1/10/2002 masonb Created * ***************************************************************************/
#include "dncmni.h"
#ifndef DPNBUILD_ONLYONETHREAD
#ifdef DBG
//
// Uncomment this line to turn critical section internal structure validation on.
//
//#define DNCS_VALIDATE
#define DN_INVALID_THREAD_ID -1
CBilink g_blAllCritSecs; CBilink g_blGlobalCritSecsHeldGroup; CRITICAL_SECTION g_CSLock; DWORD g_dwNumCritSecsAllocated = 0; DWORD g_dwNumCritSecsEntered = 0;
#ifdef DNCS_VALIDATE
void DNCSTrackInternalValidate(); #else // ! DNCS_VALIDATE
#define DNCSTrackInternalValidate()
#endif // DNCS_VALIDATE
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackInitialize"
BOOL DNCSTrackInitialize() { g_blAllCritSecs.Initialize(); g_blGlobalCritSecsHeldGroup.Initialize();
if ( DNOSInitializeCriticalSection(&g_CSLock) == FALSE ) { DPFX(DPFPREP, 0, "Failed to initialize critical section tracking code!" ); DNASSERT( FALSE ); return FALSE; }
return TRUE; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackDeinitialize"
void DNCSTrackDeinitialize() { DeleteCriticalSection(&g_CSLock); }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackDumpLeaks"
BOOL DNCSTrackDumpLeaks() { DNCRITICAL_SECTION* pCS; TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE]; BOOL fLeaked = FALSE;
EnterCriticalSection(&g_CSLock); while (!g_blAllCritSecs.IsEmpty()) { pCS = CONTAINING_OBJECT(g_blAllCritSecs.GetNext(), DNCRITICAL_SECTION, blAllCritSecs); pCS->AllocCallStack.GetCallStackString(CallStackBuffer);
DPFX(DPFPREP, 0, "Critical Section leaked at address 0x%p\n%s", pCS, CallStackBuffer );
pCS->blAllCritSecs.RemoveFromList();
DeleteCriticalSection(&pCS->CriticalSection);
fLeaked = TRUE; } LeaveCriticalSection(&g_CSLock);
return fLeaked; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackInitializeCriticalSection"
BOOL DNCSTrackInitializeCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { BOOL fReturn;
DNASSERT( pCriticalSection != NULL ); memset( pCriticalSection, 0x00, sizeof( *pCriticalSection ) );
pCriticalSection->OwningThreadID = DN_INVALID_THREAD_ID; pCriticalSection->MaxLockCount = -1; pCriticalSection->blCritSecsHeld.Initialize(); pCriticalSection->blAllCritSecs.Initialize(); pCriticalSection->pblCritSecsHeldGroup = &g_blGlobalCritSecsHeldGroup;
fReturn = DNOSInitializeCriticalSection(&pCriticalSection->CriticalSection); if ( fReturn != FALSE ) { pCriticalSection->AllocCallStack.NoteCurrentCallStack();
EnterCriticalSection(&g_CSLock); pCriticalSection->blAllCritSecs.InsertBefore(&g_blAllCritSecs); g_dwNumCritSecsAllocated++; DNCSTrackInternalValidate(); LeaveCriticalSection(&g_CSLock); }
return fReturn; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackDeleteCriticalSection"
void DNCSTrackDeleteCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { DNASSERT( pCriticalSection != NULL ); DNASSERT( pCriticalSection->LockCount == 0 );
EnterCriticalSection(&g_CSLock);
pCriticalSection->blAllCritSecs.RemoveFromList(); g_dwNumCritSecsAllocated--;
// NOTE: If they delete the CS without leaving it, still remove it from the held list.
// If asserts are on, this will have asserted above at LockCount == 0.
// Calling this is safe whether it is on the list or not.
pCriticalSection->blCritSecsHeld.RemoveFromList();
DNCSTrackInternalValidate(); LeaveCriticalSection(&g_CSLock);
DeleteCriticalSection( &pCriticalSection->CriticalSection ); memset( &pCriticalSection->CriticalSection, 0x00, sizeof( pCriticalSection->CriticalSection ) ); }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackSetCriticalRecursionCount"
void DNCSTrackSetCriticalSectionRecursionCount( DNCRITICAL_SECTION *const pCriticalSection, const UINT_PTR RecursionCount ) { DNASSERT( pCriticalSection != NULL );
pCriticalSection->MaxLockCount = RecursionCount + 1;
DNASSERT( pCriticalSection->MaxLockCount != 0 ); }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackSetCriticalSectionGroup"
void DNCSTrackSetCriticalSectionGroup( DNCRITICAL_SECTION *const pCriticalSection, CBilink * const pblGroup ) { DNASSERT( pCriticalSection != NULL ); DNASSERT( pblGroup != NULL );
pCriticalSection->pblCritSecsHeldGroup = pblGroup; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackSetCriticalSectionLockOrder"
void DNCSTrackSetCriticalSectionLockOrder( DNCRITICAL_SECTION *const pCriticalSection, const DWORD dwLockOrder ) { DNASSERT( pCriticalSection != NULL ); DNASSERT( dwLockOrder > 0 );
pCriticalSection->dwLockOrder = dwLockOrder; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackEnterCriticalSection"
void DNCSTrackEnterCriticalSection( DNCRITICAL_SECTION *const pCriticalSection ) { UINT_PTR ThisThreadID;
DNASSERT( pCriticalSection != NULL );
EnterCriticalSection( &pCriticalSection->CriticalSection );
ThisThreadID = GetCurrentThreadId(); if ( pCriticalSection->OwningThreadID != ThisThreadID ) { DNASSERT( pCriticalSection->OwningThreadID == DN_INVALID_THREAD_ID ); DNASSERT( pCriticalSection->LockCount == 0 ); pCriticalSection->OwningThreadID = ThisThreadID; } else { DNASSERT( pCriticalSection->LockCount != 0 ); }
if ( pCriticalSection->LockCount == 0 ) { pCriticalSection->CallStack.NoteCurrentCallStack();
// Track this critical section that was just entered for the first time.
EnterCriticalSection(&g_CSLock);
pCriticalSection->LockCount++;
//
// If this critical section has a lock order, assert that we're not
// violating it.
//
if (pCriticalSection->dwLockOrder != 0) { CBilink * pBilink; DNCRITICAL_SECTION * pCS;
pBilink = pCriticalSection->pblCritSecsHeldGroup->GetNext(); while (pBilink != pCriticalSection->pblCritSecsHeldGroup) { pCS = CONTAINING_OBJECT(pBilink, DNCRITICAL_SECTION, blCritSecsHeld); if (pCS->dwLockOrder != 0) { DNASSERT( pCS->dwLockOrder <= pCriticalSection->dwLockOrder ); } pBilink = pBilink->GetNext(); } } pCriticalSection->blCritSecsHeld.InsertBefore(pCriticalSection->pblCritSecsHeldGroup); DNASSERT(g_dwNumCritSecsEntered < g_dwNumCritSecsAllocated); g_dwNumCritSecsEntered++; DNCSTrackInternalValidate(); LeaveCriticalSection(&g_CSLock); } else { pCriticalSection->LockCount++; }
if ( pCriticalSection->LockCount > pCriticalSection->MaxLockCount ) { if ( pCriticalSection->MaxLockCount == 1 ) { TCHAR CallStackBuffer[ CALLSTACK_BUFFER_SIZE ];
//
// Exceeded recursion depth of 1, display stack of call originally
// holding the lock.
//
pCriticalSection->CallStack.GetCallStackString( CallStackBuffer );
DPFX(DPFPREP, 0, "Critical section 0x%p has been reentered!\nOriginal Holder's Stack:\n%s", pCriticalSection, CallStackBuffer);
DNASSERT(FALSE); } else { //
// exceeded recursion depth, check your code!!
//
DNASSERT(FALSE); } } }
#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 );
if ( pCriticalSection->LockCount == 1 ) { memset( &pCriticalSection->CallStack, 0x00, sizeof( pCriticalSection->CallStack ) ); pCriticalSection->OwningThreadID = DN_INVALID_THREAD_ID;
// Track this critical section being left for the last time.
EnterCriticalSection(&g_CSLock); pCriticalSection->LockCount--; pCriticalSection->blCritSecsHeld.RemoveFromList(); DNASSERT(g_dwNumCritSecsEntered > 0); g_dwNumCritSecsEntered--; DNCSTrackInternalValidate(); LeaveCriticalSection(&g_CSLock); } else { pCriticalSection->LockCount--; }
LeaveCriticalSection( &pCriticalSection->CriticalSection ); }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackCriticalSectionIsTakenByThisThread"
void DNCSTrackCriticalSectionIsTakenByThisThread( const DNCRITICAL_SECTION *const pCriticalSection, const BOOL fFlag ) { DNASSERT( fFlag == ( pCriticalSection->OwningThreadID == GetCurrentThreadId() ) ); }
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackNoCriticalSectionsTakenByThisThread"
void DNCSTrackNoCriticalSectionsTakenByThisThread( CBilink * pblGroup ) { CBilink* pBilink; DNCRITICAL_SECTION* pCS;
if (pblGroup == NULL) { pblGroup = &g_blGlobalCritSecsHeldGroup; }
EnterCriticalSection(&g_CSLock);
pBilink = pblGroup->GetNext(); while (pBilink != pblGroup) { pCS = CONTAINING_OBJECT(pBilink, DNCRITICAL_SECTION, blCritSecsHeld); DNASSERT( pCS->OwningThreadID != GetCurrentThreadId() ); pBilink = pBilink->GetNext(); }
DNCSTrackInternalValidate();
LeaveCriticalSection(&g_CSLock); }
#ifdef DNCS_VALIDATE
#undef DPF_MODNAME
#define DPF_MODNAME "DNCSTrackInternalValidate"
void DNCSTrackInternalValidate() { CBilink* pBilink; DNCRITICAL_SECTION* pCS; DWORD dwNumAllocated = 0; DWORD dwNumEntered = 0;
//
// The global critical section lock must be held!
//
DNASSERT(g_dwNumCritSecsEntered <= g_dwNumCritSecsAllocated); pBilink = g_blAllCritSecs.GetNext(); while (pBilink != &g_blAllCritSecs) { DNASSERT(pBilink->GetNext() != pBilink); DNASSERT(pBilink->GetPrev() != pBilink); DNASSERT(pBilink->IsListMember(&g_blAllCritSecs)); pCS = CONTAINING_OBJECT(pBilink, DNCRITICAL_SECTION, blAllCritSecs);
dwNumAllocated++; if (pCS->blCritSecsHeld.IsEmpty()) { DNASSERT(pCS->LockCount == 0); } else { DNASSERT(pCS->LockCount > 0); dwNumEntered++; }
pBilink = pBilink->GetNext(); } DNASSERT(dwNumAllocated == g_dwNumCritSecsAllocated); DNASSERT(dwNumEntered == g_dwNumCritSecsEntered); }
#endif // DNCS_VALIDATE
#endif // DBG
#endif // !DPNBUILD_ONLYONETHREAD
|