401 lines
7.1 KiB
C++
401 lines
7.1 KiB
C++
|
/*
|
||
|
* @doc INTERNAL
|
||
|
*
|
||
|
* @module utilmem.cpp - Debug memory tracking/allocation routines
|
||
|
*
|
||
|
* History: <nl>
|
||
|
* 8/17/99 KeithCu Move to a separate module to prevent errors.
|
||
|
*
|
||
|
* Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved.
|
||
|
*/
|
||
|
|
||
|
#define W32SYS_CPP
|
||
|
|
||
|
#include "_common.h"
|
||
|
|
||
|
#undef PvAlloc
|
||
|
#undef PvReAlloc
|
||
|
#undef FreePv
|
||
|
#undef new
|
||
|
|
||
|
|
||
|
#if defined(DEBUG)
|
||
|
|
||
|
#undef PvSet
|
||
|
#undef ZeroMemory
|
||
|
#undef strcmp
|
||
|
|
||
|
MST vrgmst[100];
|
||
|
|
||
|
typedef struct tagPVH //PV Header
|
||
|
{
|
||
|
char *szFile;
|
||
|
int line;
|
||
|
tagPVH *ppvhNext;
|
||
|
int cbAlloc; //On Win'95, the size returned is not the size allocated.
|
||
|
int magicPvh; //Should be last
|
||
|
} PVH;
|
||
|
#define cbPvh (sizeof(PVH))
|
||
|
|
||
|
typedef struct //PV Tail
|
||
|
{
|
||
|
int magicPvt; //Must be first
|
||
|
} PVT;
|
||
|
|
||
|
#define cbPvt (sizeof(PVT))
|
||
|
#define cbPvDebug (cbPvh + cbPvt)
|
||
|
|
||
|
void *vpHead = 0;
|
||
|
|
||
|
/*
|
||
|
* UpdateMst(void)
|
||
|
*
|
||
|
* @func Fills up the vrgmst structure with summary information about our memory
|
||
|
* usage.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void UpdateMst(void)
|
||
|
{
|
||
|
W32->ZeroMemory(vrgmst, sizeof(vrgmst));
|
||
|
|
||
|
PVH *ppvh;
|
||
|
MST *pmst;
|
||
|
|
||
|
ppvh = (PVH*) vpHead;
|
||
|
|
||
|
while (ppvh != 0)
|
||
|
{
|
||
|
pmst = vrgmst;
|
||
|
|
||
|
//Look for entry in list...
|
||
|
while (pmst->szFile)
|
||
|
{
|
||
|
if (W32->strcmp(pmst->szFile, ppvh->szFile) == 0)
|
||
|
{
|
||
|
pmst->cbAlloc += ppvh->cbAlloc;
|
||
|
break;
|
||
|
}
|
||
|
pmst++;
|
||
|
}
|
||
|
|
||
|
if (pmst->szFile == 0)
|
||
|
{
|
||
|
pmst->szFile = ppvh->szFile;
|
||
|
pmst->cbAlloc = ppvh->cbAlloc;
|
||
|
}
|
||
|
|
||
|
ppvh = ppvh->ppvhNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* PvDebugValidate(void)
|
||
|
*
|
||
|
* @func Verifies the the node is proper. Pass in a pointer to the users data
|
||
|
* (after the header node.)
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void PvDebugValidate(void *pv)
|
||
|
{
|
||
|
PVH *ppvh;
|
||
|
UNALIGNED PVT *ppvt;
|
||
|
|
||
|
ppvh = (PVH*) ((char*) pv - cbPvh);
|
||
|
ppvt = (PVT*) ((char*) pv + ppvh->cbAlloc);
|
||
|
|
||
|
AssertSz(ppvh->magicPvh == 0x12345678, "PvDebugValidate: header bytes are corrupt");
|
||
|
AssertSz(ppvt->magicPvt == 0xfedcba98, "PvDebugValidate: tail bytes are corrupt");
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CW32System::PvSet(pv, szFile, line)
|
||
|
*
|
||
|
* @mfunc Sets a different module and line number for
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void CW32System::PvSet(void *pv, char *szFile, int line)
|
||
|
{
|
||
|
if (pv == 0)
|
||
|
return;
|
||
|
|
||
|
PvDebugValidate(pv);
|
||
|
PVH *ppvh = (PVH*) ((char*) pv - cbPvh);
|
||
|
|
||
|
ppvh->szFile = szFile;
|
||
|
ppvh->line = line;
|
||
|
}
|
||
|
/*
|
||
|
* CW32System::PvAllocDebug(cb, uiMemFlags, szFile, line)
|
||
|
*
|
||
|
* @mfunc Allocates a generic (void*) pointer. This is a debug only routine which
|
||
|
* tracks the allocation.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void* CW32System::PvAllocDebug(ULONG cb, UINT uiMemFlags, char *szFile, int line)
|
||
|
{
|
||
|
void *pv;
|
||
|
|
||
|
pv = PvAlloc(cb + cbPvDebug, uiMemFlags);
|
||
|
if (!pv)
|
||
|
return 0;
|
||
|
|
||
|
PVH *ppvh;
|
||
|
UNALIGNED PVT *ppvt;
|
||
|
|
||
|
ppvt = (PVT*) ((char*) pv + cb + cbPvh);
|
||
|
ppvh = (PVH*) pv;
|
||
|
|
||
|
ZeroMemory(ppvh, sizeof(PVH));
|
||
|
ppvh->magicPvh = 0x12345678;
|
||
|
ppvt->magicPvt = 0xfedcba98;
|
||
|
ppvh->szFile = szFile;
|
||
|
ppvh->line = line;
|
||
|
ppvh->cbAlloc = cb;
|
||
|
|
||
|
ppvh->ppvhNext = (PVH*) vpHead;
|
||
|
vpHead = pv;
|
||
|
|
||
|
return (char*) pv + cbPvh;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CW32System::PvReAllocDebug(pv, cb, szFile, line)
|
||
|
*
|
||
|
* @mfunc ReAllocates a generic (void*) pointer. This is a debug only routine which
|
||
|
* tracks the allocation.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void* CW32System::PvReAllocDebug(void *pv, ULONG cb, char *szFile, int line)
|
||
|
{
|
||
|
void *pvNew;
|
||
|
PVH *ppvh, *ppvhHead, *ppvhTail;
|
||
|
UNALIGNED PVT *ppvt;
|
||
|
ppvh = (PVH*) ((char*) pv - cbPvh);
|
||
|
|
||
|
if (!pv)
|
||
|
return PvAllocDebug(cb, 0, szFile, line);
|
||
|
|
||
|
PvDebugValidate(pv);
|
||
|
|
||
|
pvNew = PvReAlloc((char*) pv - cbPvh, cb + cbPvDebug);
|
||
|
|
||
|
if (!pvNew)
|
||
|
return 0;
|
||
|
|
||
|
ppvt = (PVT*) ((char*) pvNew + cb + cbPvh);
|
||
|
ppvh = (PVH*) pvNew;
|
||
|
ppvh->cbAlloc = cb;
|
||
|
|
||
|
//Put the new trailer bytes in.
|
||
|
ppvt->magicPvt = 0xfedcba98;
|
||
|
|
||
|
//Make the pointer list up to date again
|
||
|
if (pv != pvNew)
|
||
|
{
|
||
|
ppvhTail = 0;
|
||
|
ppvhHead = (PVH*) vpHead;
|
||
|
|
||
|
while ((char*)ppvhHead != (char*)pv - cbPvh)
|
||
|
{
|
||
|
AssertSz(ppvhHead, "entry not found in list.");
|
||
|
ppvhTail = ppvhHead;
|
||
|
ppvhHead = (PVH*) ppvhHead->ppvhNext;
|
||
|
}
|
||
|
|
||
|
if (ppvhTail == 0)
|
||
|
vpHead = pvNew;
|
||
|
else
|
||
|
ppvhTail->ppvhNext = (PVH*) pvNew;
|
||
|
}
|
||
|
|
||
|
return (char*) pvNew + cbPvh;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CW32System::FreePvDebug(pv)
|
||
|
*
|
||
|
* @mfunc Returns a pointer when you are done with it.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void CW32System::FreePvDebug(void *pv)
|
||
|
{
|
||
|
if (!pv)
|
||
|
return;
|
||
|
|
||
|
PvDebugValidate(pv);
|
||
|
|
||
|
PVH *ppvhHead, *ppvhTail, *ppvh;
|
||
|
|
||
|
AssertSz(vpHead, "Deleting from empty free list.");
|
||
|
|
||
|
ppvh = (PVH*) ((char*) pv - cbPvh);
|
||
|
|
||
|
//Search and remove the entry from the list
|
||
|
ppvhTail = 0;
|
||
|
ppvhHead = (PVH*) vpHead;
|
||
|
|
||
|
while ((char*) ppvhHead != ((char*) pv - cbPvh))
|
||
|
{
|
||
|
AssertSz(ppvhHead, "entry not found in list.");
|
||
|
ppvhTail = ppvhHead;
|
||
|
ppvhHead = (PVH*) ppvhHead->ppvhNext;
|
||
|
}
|
||
|
|
||
|
if (ppvhTail == 0)
|
||
|
vpHead = ppvhHead->ppvhNext;
|
||
|
else
|
||
|
ppvhTail->ppvhNext = ppvhHead->ppvhNext;
|
||
|
|
||
|
FreePv((char*) pv - cbPvh);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CatchLeaks(void)
|
||
|
*
|
||
|
* @func Displays any memory leaks in a dialog box.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* void
|
||
|
*/
|
||
|
void CatchLeaks(void)
|
||
|
{
|
||
|
PVH *ppvh;
|
||
|
char szLeak[512];
|
||
|
|
||
|
ppvh = (PVH*) vpHead;
|
||
|
while (ppvh != 0)
|
||
|
{
|
||
|
#ifndef NOFULLDEBUG
|
||
|
wsprintfA(szLeak, "Memory Leak of %d bytes: -- File: %s, Line: %d", ppvh->cbAlloc, ppvh->szFile, ppvh->line);
|
||
|
#endif
|
||
|
if (NULL != pfnAssert)
|
||
|
{
|
||
|
// if we have an assert hook, give the user a chance to process the leak message
|
||
|
if (pfnAssert(szLeak, ppvh->szFile, &ppvh->line))
|
||
|
{
|
||
|
#ifdef NOFULLDEBUG
|
||
|
DebugBreak();
|
||
|
#else
|
||
|
// hook returned true, show the message box
|
||
|
MessageBoxA(NULL, szLeak, "", MB_OK);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifdef NOFULLDEBUG
|
||
|
DebugBreak();
|
||
|
#else
|
||
|
MessageBoxA(NULL, szLeak, "", MB_OK);
|
||
|
#endif
|
||
|
}
|
||
|
ppvh = ppvh->ppvhNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void* _cdecl operator new (size_t size, char *szFile, int line)
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "new");
|
||
|
|
||
|
return W32->PvAllocDebug(size, GMEM_ZEROINIT, szFile, line);
|
||
|
}
|
||
|
|
||
|
void _cdecl operator delete (void* pv)
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "delete");
|
||
|
|
||
|
W32->FreePvDebug(pv);
|
||
|
}
|
||
|
|
||
|
#else //DEBUG
|
||
|
|
||
|
void* _cdecl operator new (size_t size)
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "new");
|
||
|
|
||
|
return W32->PvAlloc(size, GMEM_ZEROINIT);
|
||
|
}
|
||
|
|
||
|
void _cdecl operator delete (void* pv)
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "delete");
|
||
|
|
||
|
W32->FreePv(pv);
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif //DEBUG
|
||
|
|
||
|
HANDLE g_hHeap;
|
||
|
|
||
|
/*
|
||
|
* PvAlloc (cbBuf, uiMemFlags)
|
||
|
*
|
||
|
* @mfunc memory allocation. Similar to GlobalAlloc.
|
||
|
*
|
||
|
* @comm The only flag of interest is GMEM_ZEROINIT, which
|
||
|
* specifies that memory should be zeroed after allocation.
|
||
|
*/
|
||
|
PVOID CW32System::PvAlloc(
|
||
|
ULONG cbBuf, //@parm Count of bytes to allocate
|
||
|
UINT uiMemFlags) //@parm Flags controlling allocation
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "PvAlloc");
|
||
|
if (g_hHeap == 0)
|
||
|
{
|
||
|
CLock lock;
|
||
|
g_hHeap = HeapCreate(0, 0, 0);
|
||
|
}
|
||
|
|
||
|
void *pv = HeapAlloc(g_hHeap, (uiMemFlags & GMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0, cbBuf);
|
||
|
|
||
|
return pv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* PvReAlloc (pv, cbBuf)
|
||
|
*
|
||
|
* @mfunc memory reallocation.
|
||
|
*
|
||
|
*/
|
||
|
PVOID CW32System::PvReAlloc(
|
||
|
PVOID pv, //@parm Buffer to reallocate
|
||
|
DWORD cbBuf) //@parm New size of buffer
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "PvReAlloc");
|
||
|
|
||
|
if(pv)
|
||
|
return HeapReAlloc(g_hHeap, 0, pv, cbBuf);
|
||
|
|
||
|
return PvAlloc(cbBuf, 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FreePv (pv)
|
||
|
*
|
||
|
* @mfunc frees memory
|
||
|
*
|
||
|
* @rdesc void
|
||
|
*/
|
||
|
void CW32System::FreePv(
|
||
|
PVOID pv) //@parm Buffer to free
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "FreePv");
|
||
|
|
||
|
if(pv)
|
||
|
HeapFree(g_hHeap, 0, pv);
|
||
|
}
|
||
|
|