397 lines
9.9 KiB
C++
397 lines
9.9 KiB
C++
/*==========================================================================
|
|
*
|
|
* 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 );
|
|
|
|
pCriticalSection->OwningThreadID = ThisThreadID;
|
|
|
|
DNASSERT( pCriticalSection->LockCount == 0 );
|
|
}
|
|
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
|