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.
413 lines
11 KiB
413 lines
11 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: MemoryTracking.cpp
|
|
* Content: Debug memory tracking for detecting leaks, overruns, etc.
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 11/14/2001 masonb Created
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "dncmni.h"
|
|
|
|
|
|
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
BOOL g_fAllocationsAllowed = TRUE;
|
|
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|
|
#ifdef DBG
|
|
|
|
#ifdef _WIN64
|
|
#define GUARD_SIGNATURE 0xABABABABABABABAB
|
|
#else // !_WIN64
|
|
#define GUARD_SIGNATURE 0xABABABAB
|
|
#endif // _WIN64
|
|
|
|
// Structure prepended to memory allocations to check for leaks.
|
|
struct MEMORY_HEADER
|
|
{
|
|
CBilink blLinkage; // size = two pointers
|
|
DWORD_PTR dwpSize; // size = pointer
|
|
CCallStack AllocCallStack; // size = 12 pointers
|
|
DWORD_PTR dwpPreGuard; // size = pointer
|
|
// We want what follows to always be 16-byte aligned and #pragma pack doesn't seem to ensure that
|
|
};
|
|
|
|
CRITICAL_SECTION g_AllocatedMemoryLock;
|
|
CBilink g_blAllocatedMemory;
|
|
DWORD_PTR g_dwpCurrentNumMemAllocations = 0;
|
|
DWORD_PTR g_dwpCurrentMemAllocated = 0;
|
|
DWORD_PTR g_dwpTotalNumMemAllocations = 0;
|
|
DWORD_PTR g_dwpTotalMemAllocated = 0;
|
|
DWORD_PTR g_dwpPeakNumMemAllocations = 0;
|
|
DWORD_PTR g_dwpPeakMemAllocated = 0;
|
|
|
|
#endif // DBG
|
|
|
|
|
|
|
|
#if ((defined(DBG)) || (defined(DPNBUILD_FIXEDMEMORYMODEL)))
|
|
|
|
HANDLE g_hMemoryHeap = NULL;
|
|
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackInitialize"
|
|
BOOL DNMemoryTrackInitialize(DWORD_PTR dwpMaxMemUsage)
|
|
{
|
|
// Ensure that we stay heap aligned for SLISTs
|
|
#ifdef _WIN64
|
|
DBG_CASSERT(sizeof(MEMORY_HEADER) % 16 == 0);
|
|
#else // !_WIN64
|
|
DBG_CASSERT(sizeof(MEMORY_HEADER) % 8 == 0);
|
|
#endif // _WIN64
|
|
|
|
// Check for double init
|
|
DNASSERT(g_hMemoryHeap == NULL);
|
|
#ifndef DPNBUILD_FIXEDMEMORYMODEL
|
|
DNASSERT(dwpMaxMemUsage == 0);
|
|
#endif // ! DPNBUILD_FIXEDMEMORYMODEL
|
|
|
|
DPFX(DPFPREP, 5, "Initializing Memory Tracking");
|
|
|
|
// In debug we always maintain a separate heap and track allocations. In retail,
|
|
// we don't track allocations, and will use the process heap except for
|
|
// DPNBUILD_FIXEDMEMORYMODEL builds, where we use a separate heap so we
|
|
// can cap the total allocation size.
|
|
#ifdef DPNBUILD_ONLYONETHREAD
|
|
g_hMemoryHeap = HeapCreate(HEAP_NO_SERIALIZE, // flags
|
|
#else // ! DPNBUILD_ONLYONETHREAD
|
|
g_hMemoryHeap = HeapCreate(0, // flags
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
dwpMaxMemUsage, // initial size
|
|
dwpMaxMemUsage // maximum heap size (if 0, it can grow)
|
|
);
|
|
|
|
if (g_hMemoryHeap == NULL)
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to create memory heap!");
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DBG
|
|
#pragma TODO(masonb, "Handle possibility of failure")
|
|
InitializeCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
g_blAllocatedMemory.Initialize();
|
|
|
|
g_dwpCurrentNumMemAllocations = 0;
|
|
g_dwpCurrentMemAllocated = 0;
|
|
g_dwpTotalNumMemAllocations = 0;
|
|
g_dwpTotalMemAllocated = 0;
|
|
g_dwpPeakNumMemAllocations = 0;
|
|
g_dwpPeakMemAllocated = 0;
|
|
#endif // DBG
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackDeinitialize"
|
|
void DNMemoryTrackDeinitialize()
|
|
{
|
|
// Validate the heap if we're on NT and a debug build, and then destroy the heap.
|
|
if (g_hMemoryHeap != NULL)
|
|
{
|
|
BOOL fResult;
|
|
#ifdef DBG
|
|
DWORD dwError;
|
|
|
|
|
|
DPFX(DPFPREP, 5, "Deinitializing Memory Tracking");
|
|
DPFX(DPFPREP, 5, "Total num mem allocations = %u", g_dwpTotalNumMemAllocations);
|
|
DPFX(DPFPREP, 5, "Total mem allocated = %u", g_dwpTotalMemAllocated);
|
|
DPFX(DPFPREP, 5, "Peak num mem allocations = %u", g_dwpPeakNumMemAllocations);
|
|
DPFX(DPFPREP, 5, "Peak mem allocated = %u", g_dwpPeakMemAllocated);
|
|
|
|
DeleteCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
#ifdef WINNT
|
|
// Validate heap contents before shutdown. This code only works on NT.
|
|
fResult = HeapValidate(g_hMemoryHeap, 0, NULL);
|
|
if (! fResult)
|
|
{
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem validating heap on destroy %d!", dwError );
|
|
DNASSERT(! "Problem validating heap on destroy!");
|
|
}
|
|
#endif // WINNT
|
|
#endif // DBG
|
|
|
|
fResult = HeapDestroy(g_hMemoryHeap);
|
|
if (! fResult)
|
|
{
|
|
#ifdef DBG
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem destroying heap %d!", dwError );
|
|
DNASSERT(! "Problem destroying heap!");
|
|
#endif // DBG
|
|
}
|
|
|
|
g_hMemoryHeap = NULL;
|
|
}
|
|
}
|
|
|
|
#endif // DBG or DPNBUILD_FIXEDMEMORYMODEL
|
|
|
|
|
|
|
|
#ifdef DBG
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackHeapAlloc"
|
|
void* DNMemoryTrackHeapAlloc(DWORD_PTR dwpSize)
|
|
{
|
|
MEMORY_HEADER* pMemory;
|
|
void* pReturn;
|
|
|
|
DNASSERT(g_hMemoryHeap != NULL);
|
|
|
|
// Voice and lobby currently try allocating 0 byte buffers, can't enable this check yet.
|
|
//DNASSERT( Size > 0 );
|
|
|
|
DNMemoryTrackValidateMemory();
|
|
|
|
if (DNMemoryTrackAreAllocationsAllowed())
|
|
{
|
|
// We need enough room for our header plus what the user wants plus the guard signature at the end
|
|
pMemory = (MEMORY_HEADER*)HeapAlloc(g_hMemoryHeap, 0, sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR));
|
|
#if ((! defined(WINCE)) && (! defined(WIN95)))
|
|
if (pMemory == NULL)
|
|
{
|
|
DWORD_PTR dwpLargestFreeBlock;
|
|
|
|
|
|
// Compact the heap to see how much size is available, and possibly try allocating again.
|
|
dwpLargestFreeBlock = HeapCompact(g_hMemoryHeap, 0);
|
|
if (dwpLargestFreeBlock >= (sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR)))
|
|
{
|
|
DPFX(DPFPREP, 1, "Largest free block after compacting is %u bytes, allocating %u bytes again.", dwpLargestFreeBlock, dwpSize);
|
|
pMemory = (MEMORY_HEADER*)HeapAlloc(g_hMemoryHeap, 0, sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR));
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 1, "Largest free block after compacting is %u bytes, cannot allocate %u bytes.", dwpLargestFreeBlock, dwpSize);
|
|
}
|
|
}
|
|
#endif // ! WINCE and ! WIN95
|
|
if (pMemory != NULL)
|
|
{
|
|
pMemory->blLinkage.Initialize();
|
|
pMemory->dwpSize = dwpSize;
|
|
pMemory->AllocCallStack.NoteCurrentCallStack();
|
|
pMemory->dwpPreGuard = GUARD_SIGNATURE;
|
|
*(DWORD_PTR UNALIGNED *)((BYTE*)(pMemory + 1) + dwpSize) = GUARD_SIGNATURE;
|
|
|
|
EnterCriticalSection(&g_AllocatedMemoryLock);
|
|
pMemory->blLinkage.InsertAfter(&g_blAllocatedMemory);
|
|
g_dwpCurrentNumMemAllocations++;
|
|
g_dwpCurrentMemAllocated += dwpSize;
|
|
g_dwpTotalNumMemAllocations++;
|
|
g_dwpTotalMemAllocated += dwpSize;
|
|
if (g_dwpCurrentNumMemAllocations > g_dwpPeakNumMemAllocations)
|
|
{
|
|
g_dwpPeakNumMemAllocations = g_dwpCurrentNumMemAllocations;
|
|
}
|
|
if (g_dwpCurrentMemAllocated > g_dwpPeakMemAllocated)
|
|
{
|
|
g_dwpPeakMemAllocated = g_dwpCurrentMemAllocated;
|
|
}
|
|
LeaveCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
pReturn = pMemory + 1;
|
|
|
|
// We require that the pointers we pass back are heap aligned
|
|
DNASSERT(((DWORD_PTR)pReturn & 0xF) == 0 || // IA64
|
|
(((DWORD_PTR)pReturn & 0x7) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x8) || // NT32
|
|
(((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x4) || // WIN9X
|
|
(((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0xC) // WIN9X
|
|
);
|
|
|
|
DPFX(DPFPREP, 5, "Memory Allocated, pData[%p], Size[%d]", pReturn, dwpSize);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed allocating %u bytes of memory.", dwpSize);
|
|
pReturn = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Memory allocations are not currently allowed!");
|
|
DNASSERT(! "Memory allocations are not currently allowed!");
|
|
pReturn = NULL;
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackHeapFree"
|
|
void DNMemoryTrackHeapFree(void* pvData)
|
|
{
|
|
CBilink* pbl;
|
|
MEMORY_HEADER* pMemory;
|
|
|
|
DNASSERT(g_hMemoryHeap != NULL);
|
|
|
|
DNMemoryTrackValidateMemory();
|
|
|
|
if (pvData == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnterCriticalSection( &g_AllocatedMemoryLock );
|
|
|
|
// Verify that we know of this pointer
|
|
pbl = g_blAllocatedMemory.GetNext();
|
|
while (pbl != &g_blAllocatedMemory)
|
|
{
|
|
pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
|
|
if ((pMemory + 1) == pvData)
|
|
{
|
|
break;
|
|
}
|
|
pbl = pbl->GetNext();
|
|
}
|
|
DNASSERT(pbl != &g_blAllocatedMemory);
|
|
|
|
pMemory->blLinkage.RemoveFromList();
|
|
g_dwpCurrentNumMemAllocations--;
|
|
g_dwpCurrentMemAllocated -= pMemory->dwpSize;
|
|
|
|
LeaveCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
DPFX(DPFPREP, 5, "Memory Freed, pData[%p], Size[%d]", pMemory + 1, pMemory->dwpSize);
|
|
|
|
// Zero it in case someone is still trying to use it
|
|
memset(pMemory, 0, sizeof(MEMORY_HEADER) + pMemory->dwpSize + sizeof(DWORD_PTR));
|
|
|
|
HeapFree(g_hMemoryHeap, 0, pMemory);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackValidateMemory"
|
|
void DNMemoryTrackValidateMemory()
|
|
{
|
|
CBilink* pbl;
|
|
MEMORY_HEADER* pMemory;
|
|
LPCTSTR pszCause;
|
|
DWORD_PTR dwpNumAllocations = 0;
|
|
DWORD_PTR dwpTotalAllocated = 0;
|
|
TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
|
|
|
|
DNASSERT(g_hMemoryHeap != NULL);
|
|
|
|
// validate all of the allocated memory
|
|
EnterCriticalSection( &g_AllocatedMemoryLock );
|
|
|
|
pbl = g_blAllocatedMemory.GetNext();
|
|
while (pbl != &g_blAllocatedMemory)
|
|
{
|
|
pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
|
|
|
|
if (pMemory->dwpPreGuard != GUARD_SIGNATURE)
|
|
{
|
|
pszCause = _T("UNDERRUN DETECTED");
|
|
}
|
|
else if (*(DWORD_PTR UNALIGNED *)((BYTE*)(pMemory + 1) + pMemory->dwpSize) != GUARD_SIGNATURE)
|
|
{
|
|
pszCause = _T("OVERRUN DETECTED");
|
|
}
|
|
else
|
|
{
|
|
pszCause = NULL;
|
|
dwpNumAllocations++;
|
|
dwpTotalAllocated += pMemory->dwpSize;
|
|
}
|
|
|
|
if (pszCause)
|
|
{
|
|
pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
|
|
|
|
DPFX(DPFPREP, 0, "Memory corruption[%s], pData[%p], Size[%d]\n%s", pszCause, pMemory + 1, pMemory->dwpSize, CallStackBuffer);
|
|
|
|
DNASSERT(FALSE);
|
|
}
|
|
|
|
pbl = pbl->GetNext();
|
|
}
|
|
|
|
DNASSERT(dwpNumAllocations == g_dwpCurrentNumMemAllocations);
|
|
DNASSERT(dwpTotalAllocated == g_dwpCurrentMemAllocated);
|
|
|
|
LeaveCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
#ifdef WINNT
|
|
// Ask the OS to validate the heap
|
|
if (HeapValidate(g_hMemoryHeap, 0, NULL) == FALSE)
|
|
{
|
|
DNASSERT(FALSE);
|
|
}
|
|
#endif // WINNT
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackDumpLeaks"
|
|
BOOL DNMemoryTrackDumpLeaks()
|
|
{
|
|
MEMORY_HEADER* pMemory;
|
|
TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
|
|
BOOL fLeaked = FALSE;
|
|
|
|
DNASSERT(g_hMemoryHeap != NULL);
|
|
|
|
EnterCriticalSection( &g_AllocatedMemoryLock );
|
|
|
|
while (!g_blAllocatedMemory.IsEmpty())
|
|
{
|
|
pMemory = CONTAINING_RECORD(g_blAllocatedMemory.GetNext(), MEMORY_HEADER, blLinkage);
|
|
|
|
pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
|
|
|
|
DPFX(DPFPREP, 0, "Memory leaked, pData[%p], Size[%d]\n%s", pMemory + 1, pMemory->dwpSize, CallStackBuffer);
|
|
|
|
pMemory->blLinkage.RemoveFromList();
|
|
|
|
HeapFree(g_hMemoryHeap, 0, pMemory);
|
|
|
|
fLeaked = TRUE;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_AllocatedMemoryLock);
|
|
|
|
return fLeaked;
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNMemoryTrackAllowAllocations"
|
|
void DNMemoryTrackAllowAllocations(BOOL fAllow)
|
|
{
|
|
DPFX(DPFPREP, 1, "Memory allocations allowed = %i.", fAllow);
|
|
DNInterlockedExchange((LONG*) (&g_fAllocationsAllowed), fAllow);
|
|
}
|
|
|
|
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|