|
|
/*==========================================================================
* * 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
|