456 lines
9.7 KiB
C++
456 lines
9.7 KiB
C++
//
|
||
// File: memory.cpp
|
||
//
|
||
// Debug memory tracking per-module
|
||
|
||
#include "precomp.h"
|
||
|
||
|
||
static BOOL s_fZeroInit = FALSE;
|
||
|
||
|
||
|
||
|
||
#if defined(DEBUG)
|
||
|
||
#define DBG_NAME_LENGTH 16
|
||
typedef struct tagMemTag
|
||
{
|
||
DWORD dwSignature;
|
||
BOOL fActive;
|
||
LPVOID callerAddress;
|
||
CHAR szFileName[DBG_NAME_LENGTH];
|
||
UINT nLineNumber;
|
||
UINT cbSize;
|
||
struct tagMemTag *next;
|
||
}
|
||
MEM_TAG;
|
||
|
||
static MEM_TAG *s_pDbgActiveMemPool = NULL;
|
||
#define CLEAN_BYTE ((BYTE) 0xCD)
|
||
|
||
static UINT s_cDbgActiveMemAlloc = 0;
|
||
static UINT s_cbDbgActiveMem = 0;
|
||
const DWORD MEM_TAG_SIGNATURE = 0x12345678UL;
|
||
|
||
static CRITICAL_SECTION s_DbgCritSect;
|
||
static char s_szDbgModuleName[DBG_NAME_LENGTH] = { 0 };
|
||
static void _GetFileName(LPSTR pszTarget, LPSTR pszSrc);
|
||
static void _DbgGetFileLine(LPSTR *, UINT *);
|
||
|
||
#define DBG_MEM_TRACK_DUMP_ALL ((UINT) -1)
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// DbgMemTrackReverseList()
|
||
//
|
||
void WINAPI DbgMemTrackReverseList(void)
|
||
{
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
if (NULL != s_pDbgActiveMemPool && NULL != s_pDbgActiveMemPool->next)
|
||
{
|
||
MEM_TAG *p, *q, *r;;
|
||
|
||
for (q = (p = s_pDbgActiveMemPool)->next, r = q; // make sure r is not null in the beginning
|
||
NULL != r;
|
||
p = q, q = r)
|
||
{
|
||
r = q->next;
|
||
q->next = p;
|
||
}
|
||
|
||
s_pDbgActiveMemPool->next = NULL;
|
||
s_pDbgActiveMemPool = p;
|
||
}
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
}
|
||
|
||
|
||
//
|
||
// DbgMemTrackDumpCurrent()
|
||
//
|
||
void WINAPI DbgMemTrackDumpCurrent(void)
|
||
{
|
||
MEM_TAG *p;
|
||
int i;
|
||
char szBuf[128];
|
||
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
for (p = s_pDbgActiveMemPool, i = 0; p; p = p->next, i++)
|
||
{
|
||
if (p->callerAddress)
|
||
{
|
||
// No file/line, just caller
|
||
wsprintfA(szBuf, "%s: mem leak [%u]: caller address=0x%p, size=%u, ptr=0x%p\r\n",
|
||
s_szDbgModuleName, i,
|
||
p->callerAddress, p->cbSize, (p+1));
|
||
}
|
||
else
|
||
{
|
||
// File & line number
|
||
wsprintfA(szBuf, "%s: mem leak [%u]: file=%s, line=%u, size=%u, ptr=0x%p\r\n",
|
||
s_szDbgModuleName, i,
|
||
p->szFileName, p->nLineNumber, p->cbSize, (p+1));
|
||
}
|
||
OutputDebugStringA(szBuf);
|
||
}
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
}
|
||
|
||
|
||
//
|
||
// DbgMemTrackFinalCheck()
|
||
//
|
||
// Dumps any left-around (leaked) memory blocks. Call this on
|
||
// DLL_PROCESS_DETACH from your .DLL or at the end of WinMain of your .EXE
|
||
//
|
||
void WINAPI DbgMemTrackFinalCheck(void)
|
||
{
|
||
DbgMemTrackReverseList();
|
||
DbgMemTrackDumpCurrent();
|
||
if (NULL != s_pDbgActiveMemPool ||
|
||
NULL != s_cDbgActiveMemAlloc ||
|
||
NULL != s_cbDbgActiveMem)
|
||
{
|
||
DebugBreak();
|
||
}
|
||
|
||
DeleteCriticalSection(&s_DbgCritSect);
|
||
}
|
||
|
||
|
||
//
|
||
// _GetFileName()
|
||
//
|
||
static void _GetFileName(LPSTR pszTarget, LPSTR pszSrc)
|
||
{
|
||
LPSTR psz = pszSrc;
|
||
while (*psz != '\0')
|
||
{
|
||
if (*psz++ == '\\')
|
||
{
|
||
pszSrc = psz;
|
||
}
|
||
}
|
||
lstrcpynA(pszTarget, pszSrc, DBG_NAME_LENGTH);
|
||
}
|
||
|
||
|
||
//
|
||
// DbgMemAlloc()
|
||
//
|
||
// Debug memory allocation
|
||
//
|
||
LPVOID WINAPI DbgMemAlloc
|
||
(
|
||
UINT cbSize,
|
||
LPVOID callerAddress,
|
||
LPSTR pszFileName,
|
||
UINT nLineNumber
|
||
)
|
||
{
|
||
MEM_TAG *p;
|
||
UINT cbToAlloc;
|
||
|
||
cbToAlloc = sizeof(MEM_TAG) + cbSize;
|
||
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
|
||
p = (MEM_TAG *) LocalAlloc(LPTR, cbToAlloc);
|
||
if (p != NULL)
|
||
{
|
||
p->dwSignature = MEM_TAG_SIGNATURE;
|
||
p->fActive = TRUE;
|
||
p->callerAddress = callerAddress;
|
||
|
||
if (pszFileName)
|
||
{
|
||
_GetFileName(p->szFileName, pszFileName);
|
||
p->nLineNumber = nLineNumber;
|
||
}
|
||
|
||
p->cbSize = cbSize;
|
||
p->next = s_pDbgActiveMemPool;
|
||
s_pDbgActiveMemPool = p;
|
||
s_cDbgActiveMemAlloc++;
|
||
s_cbDbgActiveMem += p->cbSize;
|
||
p++;
|
||
|
||
//
|
||
// If no zero-init, fill with clean byte
|
||
//
|
||
if (!s_fZeroInit)
|
||
{
|
||
FillMemory(p, cbSize, CLEAN_BYTE);
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
|
||
return (LPVOID) p;
|
||
}
|
||
|
||
|
||
//
|
||
// DbgMemFree()
|
||
//
|
||
// Debug memory free
|
||
//
|
||
void WINAPI DbgMemFree(LPVOID ptr)
|
||
{
|
||
if (ptr != NULL)
|
||
{
|
||
MEM_TAG *p = (MEM_TAG *) ptr;
|
||
p--;
|
||
if (! IsBadWritePtr(p, sizeof(MEM_TAG)) &&
|
||
(p->dwSignature == MEM_TAG_SIGNATURE))
|
||
{
|
||
if (! p->fActive)
|
||
{
|
||
//
|
||
// This memory has been freed already.
|
||
//
|
||
ERROR_OUT(("DbgMemFree called with invalid pointer 0x%08x", p));
|
||
return;
|
||
}
|
||
|
||
MEM_TAG *q, *q0;
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
for (q = s_pDbgActiveMemPool; q != NULL; q = (q0 = q)->next)
|
||
{
|
||
if (q == p)
|
||
{
|
||
if (q == s_pDbgActiveMemPool)
|
||
{
|
||
s_pDbgActiveMemPool = p->next;
|
||
}
|
||
else
|
||
{
|
||
q0->next = p->next;
|
||
}
|
||
s_cDbgActiveMemAlloc--;
|
||
s_cbDbgActiveMem -= p->cbSize;
|
||
p->fActive = FALSE;
|
||
|
||
//
|
||
// Fill app pointer data with CLEAN_BYTE, to see if
|
||
// anybody tries later to access it after it's been
|
||
// freed.
|
||
//
|
||
FillMemory(p+1, p->cbSize, CLEAN_BYTE);
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("DbgMemFree called with invalid pointer 0x%08x", p));
|
||
return;
|
||
}
|
||
|
||
LocalFree(p);
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// DbgMemReAlloc()
|
||
//
|
||
// Debug memory reallocate
|
||
//
|
||
LPVOID WINAPI DbgMemReAlloc(LPVOID ptr, UINT cbSize, UINT uFlags, LPSTR pszFileName, UINT nLineNumber)
|
||
{
|
||
MEM_TAG *p;
|
||
void *q;
|
||
|
||
if (ptr == NULL)
|
||
return DbgMemAlloc(cbSize, 0, pszFileName, nLineNumber);
|
||
|
||
p = (MEM_TAG *) ptr;
|
||
p--;
|
||
|
||
if (IsBadWritePtr(p, sizeof(MEM_TAG)) ||
|
||
p->dwSignature != MEM_TAG_SIGNATURE)
|
||
{
|
||
DebugBreak();
|
||
return LocalReAlloc(ptr, cbSize, uFlags);
|
||
}
|
||
|
||
q = DbgMemAlloc(cbSize, 0, pszFileName, nLineNumber);
|
||
if (q != NULL)
|
||
{
|
||
CopyMemory(q, ptr, p->cbSize);
|
||
DbgMemFree(ptr);
|
||
}
|
||
|
||
return q;
|
||
}
|
||
|
||
|
||
typedef struct
|
||
{
|
||
DWORD dwThreadID;
|
||
LPSTR pszFileName;
|
||
UINT nLineNumber;
|
||
}
|
||
DBG_THREAD_FILE_LINE;
|
||
|
||
#define DBG_MAX_THREADS 32
|
||
static DBG_THREAD_FILE_LINE s_aThreadFileLine[DBG_MAX_THREADS] = { 0 };
|
||
|
||
void WINAPI DbgSaveFileLine(LPSTR pszFileName, UINT nLineNumber)
|
||
{
|
||
DWORD dwThreadID = GetCurrentThreadId();
|
||
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
UINT c = DBG_MAX_THREADS;
|
||
DBG_THREAD_FILE_LINE *p;
|
||
for (p = s_aThreadFileLine; c--; p++)
|
||
{
|
||
if (p->dwThreadID == 0)
|
||
{
|
||
p->dwThreadID = dwThreadID;
|
||
p->pszFileName = pszFileName;
|
||
p->nLineNumber = nLineNumber;
|
||
break;
|
||
}
|
||
else
|
||
if (p->dwThreadID == dwThreadID)
|
||
{
|
||
p->pszFileName = pszFileName;
|
||
p->nLineNumber = nLineNumber;
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
}
|
||
|
||
void WINAPI DbgGetFileLine(LPSTR *ppszFileName, UINT *pnLineNumber)
|
||
{
|
||
*ppszFileName = NULL;
|
||
*pnLineNumber = 0;
|
||
|
||
DWORD dwThreadID = GetCurrentThreadId();
|
||
|
||
EnterCriticalSection(&s_DbgCritSect);
|
||
UINT c = DBG_MAX_THREADS;
|
||
DBG_THREAD_FILE_LINE *p;
|
||
for (p = s_aThreadFileLine; c--; p++)
|
||
{
|
||
if (p->dwThreadID == 0)
|
||
{
|
||
break;
|
||
}
|
||
else if (p->dwThreadID == dwThreadID)
|
||
{
|
||
*ppszFileName = p->pszFileName;
|
||
*pnLineNumber = p->nLineNumber;
|
||
p->pszFileName = NULL;
|
||
p->nLineNumber = 0;
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection(&s_DbgCritSect);
|
||
}
|
||
|
||
|
||
|
||
LPVOID __cdecl ::operator new(size_t uObjSize)
|
||
{
|
||
LPVOID callerAddress;
|
||
LPSTR pszFileName;
|
||
UINT nLineNumber;
|
||
|
||
DbgGetFileLine(&pszFileName, &nLineNumber);
|
||
|
||
if (pszFileName)
|
||
{
|
||
callerAddress = NULL;
|
||
}
|
||
else
|
||
{
|
||
#ifdef _X86_
|
||
LPVOID * lpParams;
|
||
|
||
//
|
||
// LAURABU HACK: This doesn't work for alpha. But it's not bad
|
||
// for normal debugging. We're going to grab the return address
|
||
// of whomever called new()
|
||
//
|
||
lpParams = (LPVOID *)&uObjSize;
|
||
callerAddress = *(lpParams - 1);
|
||
#else
|
||
callerAddress = NULL;
|
||
#endif // _X86_
|
||
}
|
||
|
||
return(DbgMemAlloc(uObjSize, callerAddress, pszFileName, nLineNumber));
|
||
}
|
||
|
||
#else // RETAIL
|
||
|
||
|
||
LPVOID __cdecl ::operator new(size_t uObjSize)
|
||
{
|
||
if (s_fZeroInit)
|
||
{
|
||
return(LocalAlloc(LPTR, uObjSize));
|
||
}
|
||
else
|
||
{
|
||
return(LocalAlloc(LMEM_FIXED, uObjSize));
|
||
}
|
||
}
|
||
|
||
#endif // defined(DEBUG)
|
||
|
||
|
||
|
||
//
|
||
// delete() is the same for both debug and retail
|
||
//
|
||
void __cdecl ::operator delete(LPVOID pObj)
|
||
{
|
||
MemFree(pObj);
|
||
}
|
||
|
||
|
||
//
|
||
// DbgInitMemTrack()
|
||
//
|
||
// Initialize debug memory tracking. Call this on DLL_PROCESS_ATTACH in
|
||
// your .DLL or at beginning of WinMain of your .EXE
|
||
//
|
||
void WINAPI DbgInitMemTrack(HINSTANCE hDllInst, BOOL fZeroInit)
|
||
{
|
||
s_fZeroInit = fZeroInit;
|
||
|
||
#if defined(DEBUG)
|
||
InitializeCriticalSection(&s_DbgCritSect);
|
||
|
||
char szPath[MAX_PATH];
|
||
if (0 != GetModuleFileNameA(hDllInst, szPath, MAX_PATH))
|
||
{
|
||
_GetFileName(s_szDbgModuleName, szPath);
|
||
LPSTR psz = s_szDbgModuleName;
|
||
while (*psz != '\0')
|
||
{
|
||
if (*psz == '.')
|
||
{
|
||
*psz = '\0';
|
||
break;
|
||
}
|
||
psz++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
lstrcpyA(s_szDbgModuleName, "unknown");
|
||
}
|
||
#endif // DEBUG
|
||
}
|
||
|