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.
524 lines
13 KiB
524 lines
13 KiB
//
|
|
// mem.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "ciccs.h"
|
|
#include "mem.h"
|
|
#ifdef USECRT
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifndef DEBUG
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RETAIL memory functions.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern "C" void *cicMemAlloc(UINT uCount)
|
|
{
|
|
#ifdef USECRT
|
|
return malloc(uCount);
|
|
#else
|
|
return LocalAlloc(LMEM_FIXED, uCount);
|
|
#endif
|
|
}
|
|
|
|
extern "C" void *cicMemAllocClear(UINT uCount)
|
|
{
|
|
#ifdef USECRT
|
|
return calloc(uCount, 1);
|
|
#else
|
|
return LocalAlloc(LPTR, uCount);
|
|
#endif
|
|
}
|
|
|
|
extern "C" void cicMemFree(void *pv)
|
|
{
|
|
if (pv == NULL)
|
|
return;
|
|
|
|
#ifdef USECRT
|
|
free(pv);
|
|
#else
|
|
HLOCAL hLocal;
|
|
|
|
hLocal = LocalFree(pv);
|
|
|
|
Assert(hLocal == NULL);
|
|
#endif
|
|
}
|
|
|
|
extern "C" void *cicMemReAlloc(void *pv, UINT uCount)
|
|
{
|
|
#ifdef USECRT
|
|
return realloc(pv, uCount);
|
|
#else
|
|
return LocalReAlloc((HLOCAL)pv, uCount, LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
#endif
|
|
}
|
|
|
|
extern "C" UINT cicMemSize(void *pv)
|
|
{
|
|
#ifdef USECRT
|
|
return _msize(pv);
|
|
#else
|
|
return (UINT)LocalSize((HLOCAL)pv);
|
|
#endif
|
|
}
|
|
|
|
#else // DEBUG
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DEBUG memory functions.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MEM_SUSPICIOUSLY_LARGE_ALLOC 0x1000000 // 16MB
|
|
|
|
// All the debug state goes here.
|
|
// Be thread safe: make sure you hold s_Dbg_cs before touching/reading anything!
|
|
|
|
DBG_MEMSTATS s_Dbg_MemStats = { 0 };
|
|
|
|
DBG_MEM_COUNTER *s_rgCounters = NULL;
|
|
|
|
static CCicCriticalSectionStatic s_Dbg_cs;
|
|
|
|
static void *s_Dbg_pvBreak = (void *)-1; // set this to something to break on at runtime in MemAlloc/MemAllocClear/MemReAlloc
|
|
|
|
extern "C" TCHAR *Dbg_CopyString(const TCHAR *pszSrc)
|
|
{
|
|
TCHAR *pszCpy;
|
|
int c;
|
|
|
|
c = lstrlen(pszSrc)+1;
|
|
pszCpy = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, c*sizeof(TCHAR));
|
|
|
|
if (pszCpy != NULL)
|
|
{
|
|
memcpy(pszCpy, pszSrc, c*sizeof(TCHAR));
|
|
}
|
|
|
|
return pszCpy;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemInit
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL Dbg_MemInit(const TCHAR *pszName, DBG_MEM_COUNTER *rgCounters)
|
|
{
|
|
if (!s_Dbg_cs.Init())
|
|
return FALSE;
|
|
|
|
s_Dbg_MemStats.pszName = Dbg_CopyString(pszName);
|
|
s_rgCounters = rgCounters;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemUninit
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL Dbg_MemUninit()
|
|
{
|
|
DBG_MEMALLOC *pdma;
|
|
DBG_MEMALLOC *pdmaTmp;
|
|
TCHAR achID[64];
|
|
BOOL bMemLeak = FALSE;
|
|
|
|
// dump stats
|
|
Dbg_MemDumpStats();
|
|
|
|
// everything free?
|
|
pdma = s_Dbg_MemStats.pMemAllocList;
|
|
|
|
if (pdma != NULL ||
|
|
s_Dbg_MemStats.uTotalAlloc != s_Dbg_MemStats.uTotalFree) // second test necessary to catch size 0 objects
|
|
{
|
|
TraceMsg(TF_MEMORY_LEAK, "%s: Memory leak detected! %x total bytes leaked!",
|
|
s_Dbg_MemStats.pszName, s_Dbg_MemStats.uTotalAlloc - s_Dbg_MemStats.uTotalFree);
|
|
bMemLeak = TRUE;
|
|
}
|
|
|
|
while (pdma != NULL)
|
|
{
|
|
if (pdma->dwID == DWORD(-1))
|
|
{
|
|
achID[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
wsprintf(achID, " (ID = 0x%x)", pdma->dwID);
|
|
}
|
|
|
|
TraceMsg(TF_MEMORY_LEAK, " Address: %8.8lx Size: %8.8lx TID: %8.8lx %s%s%s line %i %s",
|
|
pdma->pvAlloc, pdma->uCount, pdma->dwThreadID, pdma->pszName ? pdma->pszName : "", pdma->pszName ? " -- " : "", pdma->pszFile, pdma->iLine, achID);
|
|
|
|
// free the DBG_MEMALLOC
|
|
pdmaTmp = pdma->next;
|
|
LocalFree(pdma->pszName);
|
|
LocalFree(pdma);
|
|
pdma = pdmaTmp;
|
|
}
|
|
|
|
// Assert after tracing.
|
|
if (bMemLeak)
|
|
AssertPrivate(0);
|
|
|
|
s_Dbg_MemStats.pMemAllocList = NULL; // in case someone wants to call Dbg_MemInit again
|
|
|
|
s_Dbg_cs.Delete();
|
|
|
|
LocalFree(s_Dbg_MemStats.pszName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemDumpStats
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" void Dbg_MemDumpStats()
|
|
{
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %s allocated %x bytes, freed %x bytes.",
|
|
s_Dbg_MemStats.pszName, s_Dbg_MemStats.uTotalAlloc, s_Dbg_MemStats.uTotalFree);
|
|
|
|
if (s_Dbg_MemStats.uTotalAlloc != s_Dbg_MemStats.uTotalFree)
|
|
{
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %s %x bytes currently allocated.",
|
|
s_Dbg_MemStats.pszName, s_Dbg_MemStats.uTotalAlloc - s_Dbg_MemStats.uTotalFree);
|
|
}
|
|
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %x MemAlloc", s_Dbg_MemStats.uTotalMemAllocCalls);
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %x MemAllocClear", s_Dbg_MemStats.uTotalMemAllocClearCalls);
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %x MemReAlloc", s_Dbg_MemStats.uTotalMemReAllocCalls);
|
|
TraceMsg(TF_MEMORY_LEAK, "Memory: %x MemFree", s_Dbg_MemStats.uTotalMemFreeCalls);
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemAlloc
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" void *Dbg_MemAlloc(UINT uCount, const TCHAR *pszFile, int iLine)
|
|
{
|
|
void *pv;
|
|
DBG_MEMALLOC *pdma;
|
|
|
|
InterlockedIncrement(&s_Dbg_MemStats.uTotalMemAllocCalls);
|
|
|
|
if (uCount == 0)
|
|
{
|
|
// TraceMsg(TF_MEMORY_LEAK, "Zero size memory allocation! %s line %i", pszFile, iLine);
|
|
//Assert(0);
|
|
}
|
|
if (uCount >= MEM_SUSPICIOUSLY_LARGE_ALLOC)
|
|
{
|
|
TraceMsg(TF_MEMORY_LEAK, "Suspiciously large memory allocation (0x%x bytes)! %s line %i", uCount, pszFile, iLine);
|
|
Assert(0);
|
|
}
|
|
|
|
pv = LocalAlloc(LMEM_FIXED, uCount);
|
|
|
|
if (pv == NULL)
|
|
return NULL;
|
|
|
|
//
|
|
// record this allocation
|
|
//
|
|
|
|
if ((pdma = (DBG_MEMALLOC *)LocalAlloc(LPTR, sizeof(DBG_MEMALLOC))) == NULL)
|
|
{
|
|
// this is a transaction -- fail if we can't allocate the debug info
|
|
LocalFree(pv);
|
|
return NULL;
|
|
}
|
|
|
|
pdma->pvAlloc = pv;
|
|
pdma->uCount = uCount;
|
|
pdma->pszFile = pszFile;
|
|
pdma->iLine = iLine;
|
|
pdma->dwThreadID = GetCurrentThreadId();
|
|
pdma->dwID = (DWORD)-1;
|
|
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
pdma->next = s_Dbg_MemStats.pMemAllocList;
|
|
s_Dbg_MemStats.pMemAllocList = pdma;
|
|
|
|
//
|
|
// update global stats
|
|
//
|
|
|
|
s_Dbg_MemStats.uTotalAlloc += uCount;
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
if (pv == s_Dbg_pvBreak)
|
|
Assert(0);
|
|
|
|
return pv;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemAllocClear
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" void *Dbg_MemAllocClear(UINT uCount, const TCHAR *pszFile, int iLine)
|
|
{
|
|
void *pv;
|
|
|
|
InterlockedIncrement(&s_Dbg_MemStats.uTotalMemAllocClearCalls);
|
|
InterlockedDecrement(&s_Dbg_MemStats.uTotalMemAllocCalls); // compensate for wrapping
|
|
|
|
pv = Dbg_MemAlloc(uCount, pszFile, iLine);
|
|
|
|
if (pv != NULL)
|
|
{
|
|
// clear out the mem
|
|
memset(pv, 0, uCount);
|
|
}
|
|
|
|
return pv;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemFree
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" void Dbg_MemFree(void *pv)
|
|
{
|
|
HLOCAL hLocal;
|
|
DBG_MEMALLOC *pdma;
|
|
DBG_MEMALLOC **ppdma;
|
|
|
|
InterlockedIncrement(&s_Dbg_MemStats.uTotalMemFreeCalls);
|
|
|
|
if (pv != NULL) // MemFree(NULL) is legal
|
|
{
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
// was this guy allocated?
|
|
ppdma = &s_Dbg_MemStats.pMemAllocList;
|
|
|
|
if (ppdma)
|
|
{
|
|
while ((pdma = *ppdma) && pdma->pvAlloc != pv)
|
|
{
|
|
ppdma = &pdma->next;
|
|
}
|
|
|
|
if (pdma != NULL)
|
|
{
|
|
// found it, update and delete
|
|
s_Dbg_MemStats.uTotalFree += pdma->uCount;
|
|
*ppdma = pdma->next;
|
|
LocalFree(pdma->pszName);
|
|
LocalFree(pdma);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_MEMORY_LEAK, "%s: MemFree'ing a bogus pointer %x!", s_Dbg_MemStats.pszName, pv);
|
|
// Assert(0); // freeing bogus pointer
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert(0); // freeing bogus pointer
|
|
}
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
hLocal = LocalFree(pv); // to match retail behavior, we don't call LocalFree for pv == NULL
|
|
Assert(hLocal == NULL);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemReAlloc
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" void *Dbg_MemReAlloc(void *pv, UINT uCount, const TCHAR *pszFile, int iLine)
|
|
{
|
|
DBG_MEMALLOC *pdma;
|
|
|
|
InterlockedIncrement(&s_Dbg_MemStats.uTotalMemReAllocCalls);
|
|
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
// was this guy allocated?
|
|
for (pdma = s_Dbg_MemStats.pMemAllocList; pdma != NULL && pdma->pvAlloc != pv; pdma = pdma->next)
|
|
;
|
|
|
|
if (pdma == NULL)
|
|
{
|
|
// can't find this guy!
|
|
TraceMsg(TF_MEMORY_LEAK, "%s: MemReAlloc'ing a bogus pointer %x!", s_Dbg_MemStats.pszName, pv);
|
|
Assert(0); // bogus pointer
|
|
|
|
pv = NULL;
|
|
}
|
|
else
|
|
{
|
|
// we blow away the original pv here, but we're not free'ing it so that's ok
|
|
#pragma prefast(suppress:308)
|
|
pv = LocalReAlloc((HLOCAL)pv, uCount, LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
}
|
|
|
|
if (pv != NULL)
|
|
{
|
|
// update the stats
|
|
pdma->pvAlloc = pv;
|
|
s_Dbg_MemStats.uTotalAlloc += (uCount - pdma->uCount);
|
|
pdma->uCount = uCount;
|
|
pdma->pszFile = pszFile;
|
|
pdma->iLine = iLine;
|
|
}
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
if (pv == s_Dbg_pvBreak)
|
|
Assert(0);
|
|
|
|
return pv;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemSize
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" UINT Dbg_MemSize(void *pv)
|
|
{
|
|
UINT uiSize;
|
|
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
uiSize = (UINT)LocalSize((HLOCAL)pv);
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
return uiSize;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemSetName
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL Dbg_MemSetName(void *pv, const TCHAR *pszName)
|
|
{
|
|
return Dbg_MemSetNameIDCounter(pv, pszName, (DWORD)-1, (ULONG)-1);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemSetNameID
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL Dbg_MemSetNameID(void *pv, const TCHAR *pszName, DWORD dwID)
|
|
{
|
|
return Dbg_MemSetNameIDCounter(pv, pszName, dwID, (ULONG)-1);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemSetNameID
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL Dbg_MemSetNameIDCounter(void *pv, const TCHAR *pszName, DWORD dwID, ULONG iCounter)
|
|
{
|
|
DBG_MEMALLOC *pdma;
|
|
BOOL f = FALSE;
|
|
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
for (pdma = s_Dbg_MemStats.pMemAllocList; pdma != NULL && pdma->pvAlloc != pv; pdma = pdma->next)
|
|
;
|
|
|
|
if (pdma != NULL)
|
|
{
|
|
if (s_rgCounters != NULL && iCounter != (ULONG)-1)
|
|
{
|
|
s_rgCounters[iCounter].uCount++;
|
|
}
|
|
LocalFree(pdma->pszName);
|
|
pdma->pszName = Dbg_CopyString(pszName);
|
|
pdma->dwID = dwID;
|
|
f = TRUE;
|
|
}
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
return f;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Dbg_MemGetName
|
|
//
|
|
// Pass in ccBuffer == 0 to get size of string only.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" int Dbg_MemGetName(void *pv, TCHAR *pch, int ccBuffer)
|
|
{
|
|
DBG_MEMALLOC *pdma;
|
|
int cc;
|
|
|
|
if (ccBuffer <= 0)
|
|
return 0;
|
|
|
|
EnterCriticalSection(s_Dbg_cs);
|
|
|
|
for (pdma = s_Dbg_MemStats.pMemAllocList; pdma != NULL && pdma->pvAlloc != pv; pdma = pdma->next)
|
|
;
|
|
|
|
if (pdma != NULL)
|
|
{
|
|
cc = lstrlen(pdma->pszName);
|
|
cc = min(cc, ccBuffer-1);
|
|
memcpy(pch, pdma->pszName, cc*sizeof(TCHAR));
|
|
}
|
|
else
|
|
{
|
|
cc = 0;
|
|
}
|
|
pch[cc] = '\0';
|
|
|
|
LeaveCriticalSection(s_Dbg_cs);
|
|
|
|
return cc;
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|