917 lines
18 KiB
C++
917 lines
18 KiB
C++
/***
|
|
*dballoc.cpp
|
|
*
|
|
* Copyright (C) 1992-93, Microsoft Corporation. All Rights Reserved.
|
|
* Information Contained Herein Is Proprietary and Confidential.
|
|
*
|
|
*Purpose:
|
|
* This file contains a debug implementation of the IMalloc interface.
|
|
*
|
|
* This implementation is basically a simple wrapping of the C runtime,
|
|
* with additional work to detect memory leakage, and memory overwrite.
|
|
*
|
|
* Leakage is detected by tracking each allocation in an address
|
|
* instance table, and then checking to see if the table is empty
|
|
* when the last reference to the allocator is released.
|
|
*
|
|
* Memory overwrite is detected by placing a signature at the end
|
|
* of every allocated block, and checking to make sure the signature
|
|
* is unchanged when the block is freed.
|
|
*
|
|
* This implementation also has additional param validation code, as
|
|
* well as additional check make sure that instances that are passed
|
|
* to Free() were actually allocated by the corresponding instance
|
|
* of the allocator.
|
|
*
|
|
*
|
|
* Creating an instance of this debug allocator that uses the default
|
|
* output interface would look like the following,
|
|
*
|
|
*
|
|
* int init_application_instance()
|
|
* {
|
|
* HRESULT hresult;
|
|
* IMalloc FAR* pmalloc;
|
|
*
|
|
* pmalloc = NULL;
|
|
*
|
|
* if((hresult = CreateDbAlloc(DBALLOC_NONE, NULL, &pmalloc)) != NOERROR)
|
|
* goto LReturn;
|
|
*
|
|
* hresult = OleInitialize(pmalloc);
|
|
*
|
|
* LReturn:;
|
|
* if(pmalloc != NULL)
|
|
* pmalloc->Release();
|
|
*
|
|
* return (hresult == NOERROR) ? TRUE : FALSE;
|
|
* }
|
|
*
|
|
*
|
|
* CONSIDER: could add an option to force error generation, something
|
|
* like DBALLOC_ERRORGEN, that works along the lines of OB's
|
|
* DebErrorNow.
|
|
*
|
|
* CONSIDER: add support for heap-checking. say for example,
|
|
* DBALLOC_HEAPCHECK would do a heapcheck every free? every 'n'
|
|
* calls to free? ...
|
|
*
|
|
*
|
|
*Revision History:
|
|
*
|
|
* [00] 25-Feb-92 bradlo: Created.
|
|
*
|
|
*Implementation Notes:
|
|
*
|
|
* The method IMalloc::DidAlloc() is allowed to always return
|
|
* "Dont Know" (-1). This method is called by Ole, and they take
|
|
* some appropriate action when they get this answer. UNDONE -- elaborate.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "common.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
|
|
#if !HC_MPW
|
|
# include <malloc.h>
|
|
#endif
|
|
|
|
#include "dballoc.h"
|
|
|
|
#if defined(WIN32)
|
|
#define OUTPUTDEBUGSTRING OutputDebugStringA
|
|
#else
|
|
#define OUTPUTDEBUGSTRING OutputDebugString
|
|
#endif
|
|
|
|
#if OE_MAC
|
|
# define MALLOC(CB) NewPtr(CB)
|
|
# define REALLOC(PV, CB) ERROR
|
|
# define FREE(PV) DisposePtr((Ptr)PV)
|
|
# define HEAPMIN() CompactMem(maxSize)
|
|
#elif OE_WIN32
|
|
# define MALLOC(CB) malloc(CB)
|
|
# define REALLOC(PV, CB) realloc((PV), (CB))
|
|
# define FREE(PV) free(PV)
|
|
# define HEAPMIN()
|
|
#else
|
|
# define MALLOC(CB) _fmalloc(CB)
|
|
# define REALLOC(PV, CB) _frealloc(PV, CB)
|
|
# define FREE(PV) _ffree(PV)
|
|
# define HEAPMIN() _fheapmin()
|
|
|
|
|
|
void
|
|
hmemset(void HUGEP* pv, int val, unsigned long size)
|
|
{
|
|
size_t cb;
|
|
char HUGEP* pdata;
|
|
|
|
pdata = (char HUGEP*)pv;
|
|
|
|
// compute the # of bytes to the end of the current segment.
|
|
cb = (size_t)((unsigned long)UINT_MAX + (unsigned long)1 - ((unsigned long)pdata&(unsigned long)UINT_MAX));
|
|
|
|
if(size <= cb){
|
|
// easy, the entire area fits within the current segment
|
|
MEMSET(pdata, val, (size_t)size);
|
|
return;
|
|
}
|
|
|
|
// clear out to the end of the current segment.
|
|
MEMSET(pdata, val, cb);
|
|
size -= cb;
|
|
pdata += cb;
|
|
|
|
// loop through the remaining segments
|
|
while(size > 0){
|
|
|
|
if(size <= UINT_MAX){
|
|
MEMSET(pdata, val, (size_t)size);
|
|
break;
|
|
}
|
|
|
|
// otherwise, fill as much as we can with one call to memset
|
|
|
|
MEMSET(pdata, val, UINT_MAX);
|
|
|
|
// the following leaves us pointing @ the last byte of the segment.
|
|
pdata += UINT_MAX;
|
|
|
|
// fill the last byte of the segment, and move on to the next segment.
|
|
*pdata++ = (char)val;
|
|
|
|
size -= ((unsigned long)UINT_MAX+1UL);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
class FAR CStdDbOutput : public IDbOutput {
|
|
public:
|
|
static IDbOutput FAR* Create();
|
|
|
|
// IUnknown methods
|
|
|
|
STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppv);
|
|
STDMETHOD_(unsigned long, AddRef)(void);
|
|
STDMETHOD_(unsigned long, Release)(void);
|
|
|
|
|
|
// IDbOutput methods
|
|
|
|
STDMETHOD_(void, Printf)(char FAR* szFmt, ...);
|
|
|
|
STDMETHOD_(void, Assertion)(
|
|
int cond,
|
|
char FAR* szFile,
|
|
unsigned int uLine,
|
|
char FAR* szMsg);
|
|
|
|
|
|
void FAR* operator new(size_t cb){
|
|
return MALLOC(cb);
|
|
}
|
|
void operator delete(void FAR* pv){
|
|
FREE(pv);
|
|
}
|
|
|
|
CStdDbOutput(){
|
|
m_refs = 0;
|
|
}
|
|
|
|
private:
|
|
unsigned long m_refs;
|
|
|
|
char m_rgch[128]; // buffer for output formatting
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// implementation of the debug allocator
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
class FAR CAddrNode
|
|
{
|
|
public:
|
|
void FAR* m_pv; // instance
|
|
unsigned long m_cb; // size of allocation in BYTES
|
|
unsigned long m_nAlloc; // the allocation pass count
|
|
CAddrNode FAR* m_next;
|
|
|
|
void FAR* operator new(size_t cb){
|
|
return MALLOC(cb);
|
|
}
|
|
void operator delete(void FAR* pv){
|
|
FREE(pv);
|
|
}
|
|
};
|
|
|
|
|
|
class FAR CDbAlloc : public IMalloc
|
|
{
|
|
public:
|
|
static HRESULT Create(
|
|
unsigned long options, IDbOutput FAR* pdbout, IMalloc FAR* FAR* ppmalloc);
|
|
|
|
// IUnknown methods
|
|
|
|
STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppv);
|
|
STDMETHOD_(unsigned long, AddRef)(void);
|
|
STDMETHOD_(unsigned long, Release)(void);
|
|
|
|
// IMalloc methods
|
|
|
|
STDMETHOD_(void FAR*, Alloc)(unsigned long cb);
|
|
STDMETHOD_(void FAR*, Realloc)(void FAR* pv, unsigned long cb);
|
|
STDMETHOD_(void, Free)(void FAR* pv);
|
|
STDMETHOD_(unsigned long, GetSize)(void FAR* pv);
|
|
STDMETHOD_(int, DidAlloc)(void FAR* pv);
|
|
STDMETHOD_(void, HeapMinimize)(void);
|
|
|
|
void IncAllocCalls(void);
|
|
|
|
void FAR* operator new(size_t cb){
|
|
return MALLOC(cb);
|
|
}
|
|
void operator delete(void FAR* pv){
|
|
FREE(pv);
|
|
}
|
|
|
|
CDbAlloc();
|
|
|
|
private:
|
|
|
|
unsigned long m_refs;
|
|
unsigned long m_options;
|
|
unsigned long m_cAllocCalls; // total count of allocation calls
|
|
IDbOutput FAR* m_pdbout; // output interface
|
|
CAddrNode FAR* m_rganode[64]; // address instance table
|
|
|
|
|
|
// instance table methods
|
|
|
|
int IsEmpty(void);
|
|
|
|
void AddInst(void FAR* pv, unsigned long nAlloc, unsigned long cb);
|
|
void DelInst(void FAR* pv);
|
|
CAddrNode FAR* GetInst(void FAR* pv);
|
|
|
|
void ReportLeakage(void);
|
|
void DumpInst(CAddrNode FAR* pn);
|
|
|
|
inline unsigned int HashInst(void FAR* pv) const {
|
|
return ((unsigned int)((unsigned long)pv >> 4)) % DIM(m_rganode);
|
|
}
|
|
|
|
|
|
// output method(s)
|
|
|
|
inline void Assertion(
|
|
int cond,
|
|
char FAR* szFile,
|
|
unsigned int uLine,
|
|
char FAR* szMsg)
|
|
{
|
|
m_pdbout->Assertion(cond, szFile, uLine, szMsg);
|
|
}
|
|
|
|
#undef ASSERT
|
|
#define ASSERT(X) Assertion(X, __FILE__, __LINE__, NULL)
|
|
|
|
#undef ASSERTSZ
|
|
#define ASSERTSZ(X, SZ) Assertion(X, __FILE__, __LINE__, SZ)
|
|
};
|
|
|
|
CDbAlloc::CDbAlloc()
|
|
{
|
|
m_refs = 1;
|
|
m_pdbout = NULL;
|
|
m_cAllocCalls = 0;
|
|
MEMSET(m_rganode, 0, sizeof(m_rganode));
|
|
}
|
|
|
|
unsigned char g_rgchSig[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
|
|
#if OE_WIN16
|
|
// answer if an allocation of the given size would be a "huge" allocation
|
|
int
|
|
IsHuge(unsigned long cb)
|
|
{
|
|
return (cb + sizeof(g_rgchSig)) >= UINT_MAX;
|
|
}
|
|
#endif
|
|
|
|
// check the signature on the given instance
|
|
int
|
|
CheckSig(CAddrNode FAR* pn)
|
|
{
|
|
int i;
|
|
unsigned char HUGEP* pchSig;
|
|
|
|
pchSig = ((unsigned char HUGEP*)pn->m_pv) + pn->m_cb;
|
|
|
|
for(i = 0; i < DIM(g_rgchSig); ++i){
|
|
if(pchSig[i] != g_rgchSig[i])
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ApplySig(unsigned char HUGEP* pch)
|
|
{
|
|
for(int i = 0; i < DIM(g_rgchSig); ++i)
|
|
pch[i] = g_rgchSig[i];
|
|
}
|
|
|
|
|
|
/***
|
|
*HRESULT CreateDbAlloc(unsigned long, IDbOutput*, IMalloc**)
|
|
*Purpose:
|
|
* Create an instance of CDbAlloc -- a debug implementation
|
|
* of IMalloc.
|
|
*
|
|
*Entry:
|
|
* pdbout = optional IDbOutput interface to use for ouput
|
|
* (if NULL, then the default debug output interface will be used)
|
|
* options =
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* *ppmalloc = pointer to an IMalloc interface
|
|
*
|
|
***********************************************************************/
|
|
STDAPI
|
|
CreateDbAlloc(
|
|
unsigned long options,
|
|
IDbOutput FAR* pdbout,
|
|
IMalloc FAR* FAR* ppmalloc)
|
|
{
|
|
return CDbAlloc::Create(options, pdbout, ppmalloc);
|
|
}
|
|
|
|
HRESULT
|
|
CDbAlloc::Create(
|
|
unsigned long options,
|
|
IDbOutput FAR* pdbout,
|
|
IMalloc FAR* FAR* ppmalloc)
|
|
{
|
|
HRESULT hresult;
|
|
CDbAlloc FAR* pmalloc;
|
|
|
|
|
|
// default the instance of IDbOutput if the user didn't supply one
|
|
if(pdbout == NULL && ((pdbout = CStdDbOutput::Create()) == NULL)){
|
|
hresult = ResultFromScode(E_OUTOFMEMORY);
|
|
goto LError0;
|
|
}
|
|
|
|
pdbout->AddRef();
|
|
|
|
if((pmalloc = new FAR CDbAlloc()) == NULL){
|
|
hresult = ResultFromScode(E_OUTOFMEMORY);
|
|
goto LError1;
|
|
}
|
|
|
|
pmalloc->m_pdbout = pdbout;
|
|
pmalloc->m_options = options;
|
|
|
|
*ppmalloc = pmalloc;
|
|
|
|
return NOERROR;
|
|
|
|
LError1:;
|
|
pdbout->Release();
|
|
pmalloc->Release();
|
|
|
|
LError0:;
|
|
return hresult;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDbAlloc::QueryInterface(REFIID riid, void FAR* FAR* ppv)
|
|
{
|
|
if(!IsEqualIID(riid,IID_IUnknown))
|
|
if(IsEqualIID(riid, IID_IMalloc))
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
*ppv = this;
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CDbAlloc::AddRef()
|
|
{
|
|
return ++m_refs;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CDbAlloc::Release()
|
|
{
|
|
if(--m_refs == 0){
|
|
|
|
if(m_options & DBALLOC_DETECTLEAKS)
|
|
ReportLeakage();
|
|
|
|
m_pdbout->Release();
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_refs;
|
|
}
|
|
|
|
STDMETHODIMP_(void FAR*)
|
|
CDbAlloc::Alloc(unsigned long cb)
|
|
{
|
|
size_t size;
|
|
|
|
IncAllocCalls();
|
|
|
|
#if OE_WIN16
|
|
if(IsHuge(cb))
|
|
{
|
|
unsigned char HUGEP* pch;
|
|
|
|
if((pch = (unsigned char HUGEP*)_halloc(cb+sizeof(g_rgchSig), 1)) == NULL)
|
|
return NULL;
|
|
|
|
hmemset(pch, -1, cb);
|
|
|
|
ApplySig(pch + cb);
|
|
|
|
AddInst((void FAR*)pch, m_cAllocCalls, cb);
|
|
|
|
return (void FAR*)pch;
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
void FAR* pv;
|
|
|
|
size = (size_t)cb + sizeof(g_rgchSig);
|
|
|
|
if((pv = MALLOC(size)) == NULL)
|
|
return NULL;
|
|
|
|
// set allocated block to some non-zero value
|
|
MEMSET(pv, -1, size);
|
|
|
|
// put signature at end of allocated block
|
|
ApplySig((unsigned char HUGEP*)pv + cb);
|
|
|
|
AddInst(pv, m_cAllocCalls, cb);
|
|
|
|
return pv;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(void FAR*)
|
|
CDbAlloc::Realloc(void FAR* pv, unsigned long cb)
|
|
{
|
|
CAddrNode FAR* pn;
|
|
|
|
if(pv == NULL){
|
|
return Alloc(cb);
|
|
}
|
|
|
|
if(cb == 0){
|
|
Free(pv);
|
|
return NULL;
|
|
}
|
|
|
|
pn = GetInst(pv);
|
|
ASSERTSZ(pn != NULL, "pointer realloc'd by wrong allocator");
|
|
|
|
#if OE_WIN16
|
|
// if either the new or the old allocation is huge, then just
|
|
// allocate a new block and copy. This is the easiest implementation.
|
|
if(IsHuge(cb) || IsHuge(pn->m_cb))
|
|
{
|
|
void HUGEP* pvNew;
|
|
|
|
if((pvNew = (void HUGEP*)Alloc(cb)) == NULL)
|
|
return NULL;
|
|
|
|
// copy in the existing data
|
|
hmemcpy(pvNew, (void HUGEP*)pv, MIN(cb, pn->m_cb));
|
|
|
|
Free(pv);
|
|
|
|
return (void FAR*)pvNew;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
void FAR* pvNew;
|
|
|
|
IncAllocCalls();
|
|
|
|
size_t size = (size_t)cb + sizeof(g_rgchSig);
|
|
#if OE_MAC
|
|
SetPtrSize((Ptr)pv, size);
|
|
if(MemError() != noErr)
|
|
return NULL;
|
|
pvNew = pv;
|
|
#else
|
|
if((pvNew = REALLOC(pv, size)) == NULL)
|
|
return NULL;
|
|
#endif
|
|
|
|
DelInst(pv);
|
|
AddInst(pvNew, m_cAllocCalls, cb);
|
|
|
|
// put signature at end of allocated block
|
|
ApplySig((unsigned char HUGEP*)pvNew + cb);
|
|
|
|
return pvNew;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CDbAlloc::Free(void FAR* pv)
|
|
{
|
|
#if OE_WIN16
|
|
int fIsHuge;
|
|
#endif
|
|
CAddrNode FAR* pn;
|
|
static char szSigMsg[] = "Signature Check Failed";
|
|
|
|
// REVIEW: is the following correct?
|
|
if(pv == NULL)
|
|
return;
|
|
|
|
pn = GetInst(pv);
|
|
|
|
// check for attempt to free an instance we didnt allocate
|
|
if(pn == NULL){
|
|
ASSERTSZ(FALSE, "pointer freed by wrong allocator");
|
|
return;
|
|
}
|
|
|
|
#if OE_WIN16
|
|
fIsHuge = IsHuge(pn->m_cb);
|
|
#endif
|
|
|
|
// verify the signature
|
|
if(!CheckSig(pn)){
|
|
m_pdbout->Printf(szSigMsg); m_pdbout->Printf("\n");
|
|
DumpInst(pn);
|
|
ASSERTSZ(FALSE, szSigMsg);
|
|
}
|
|
|
|
// stomp on the contents of the block
|
|
#if OE_WIN16
|
|
if(fIsHuge)
|
|
{
|
|
hmemset(pv, 0xCC, pn->m_cb + sizeof(g_rgchSig));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
MEMSET(pv, 0xCC, (size_t)pn->m_cb + sizeof(g_rgchSig));
|
|
}
|
|
|
|
DelInst(pv);
|
|
|
|
#if OE_WIN16
|
|
if(fIsHuge)
|
|
{
|
|
_hfree(pv);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
FREE(pv);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CDbAlloc::GetSize(void FAR* pv)
|
|
{
|
|
CAddrNode FAR* pn;
|
|
|
|
pn = GetInst(pv);
|
|
|
|
ASSERT(pn != NULL);
|
|
|
|
return pn->m_cb;
|
|
}
|
|
|
|
|
|
/***
|
|
*PUBLIC HRESULT CDbAlloc::DidAlloc
|
|
*Purpose:
|
|
* Answer if the given address belongs to a block allocated by
|
|
* this allocator.
|
|
*
|
|
*Entry:
|
|
* pv = the instance to lookup
|
|
*
|
|
*Exit:
|
|
* return value = int
|
|
* 1 - did alloc
|
|
* 0 - did *not* alloc
|
|
* -1 - dont know (according to the ole2 spec it is always legal
|
|
* for the allocator to answer "dont know")
|
|
*
|
|
***********************************************************************/
|
|
STDMETHODIMP_(int)
|
|
CDbAlloc::DidAlloc(void FAR* pv)
|
|
{
|
|
UNUSED(pv);
|
|
|
|
return -1; // answer "I dont know"
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(void)
|
|
CDbAlloc::HeapMinimize()
|
|
{
|
|
HEAPMIN();
|
|
}
|
|
|
|
void CDbAlloc::IncAllocCalls(void)
|
|
{
|
|
++m_cAllocCalls;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// instance table methods
|
|
//---------------------------------------------------------------------
|
|
|
|
/***
|
|
*PRIVATE CDbAlloc::AddInst
|
|
*Purpose:
|
|
* Add the given instance to the address instance table.
|
|
*
|
|
*Entry:
|
|
* pv = the instance to add
|
|
* nAlloc = the allocation passcount of this instance
|
|
*
|
|
*Exit:
|
|
* None
|
|
*
|
|
***********************************************************************/
|
|
void
|
|
CDbAlloc::AddInst(void FAR* pv, unsigned long nAlloc, unsigned long cb)
|
|
{
|
|
unsigned int hash;
|
|
CAddrNode FAR* pn;
|
|
|
|
|
|
ASSERT(pv != NULL);
|
|
|
|
pn = (CAddrNode FAR*)new FAR CAddrNode();
|
|
|
|
ASSERT(pn != NULL);
|
|
|
|
pn->m_pv = pv;
|
|
pn->m_cb = cb;
|
|
pn->m_nAlloc = nAlloc;
|
|
|
|
hash = HashInst(pv);
|
|
pn->m_next = m_rganode[hash];
|
|
m_rganode[hash] = pn;
|
|
}
|
|
|
|
|
|
/***
|
|
*UNDONE
|
|
*Purpose:
|
|
* Remove the given instance from the address instance table.
|
|
*
|
|
*Entry:
|
|
* pv = the instance to remove
|
|
*
|
|
*Exit:
|
|
* None
|
|
*
|
|
***********************************************************************/
|
|
void
|
|
CDbAlloc::DelInst(void FAR* pv)
|
|
{
|
|
CAddrNode FAR* FAR* ppn, FAR* pnDead;
|
|
|
|
for(ppn = &m_rganode[HashInst(pv)]; *ppn != NULL; ppn = &(*ppn)->m_next){
|
|
if((*ppn)->m_pv == pv){
|
|
pnDead = *ppn;
|
|
*ppn = (*ppn)->m_next;
|
|
delete pnDead;
|
|
// make sure it doesnt somehow appear twice
|
|
ASSERT(GetInst(pv) == NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// didnt find the instance
|
|
ASSERT(UNREACHED);
|
|
}
|
|
|
|
|
|
CAddrNode FAR*
|
|
CDbAlloc::GetInst(void FAR* pv)
|
|
{
|
|
CAddrNode FAR* pn;
|
|
|
|
for(pn = m_rganode[HashInst(pv)]; pn != NULL; pn = pn->m_next){
|
|
if(pn->m_pv == pv)
|
|
return pn;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
CDbAlloc::DumpInst(CAddrNode FAR* pn)
|
|
{
|
|
m_pdbout->Printf("[%lp] nAlloc=%ld size=%ld\n",
|
|
pn->m_pv, pn->m_nAlloc, GetSize(pn->m_pv));
|
|
}
|
|
|
|
|
|
/***
|
|
*PRIVATE int IsEmpty
|
|
*Purpose:
|
|
* Answer if the address instance table is empty.
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* return value = int, TRUE if empty, FALSE otherwise
|
|
*
|
|
***********************************************************************/
|
|
int
|
|
CDbAlloc::IsEmpty()
|
|
{
|
|
unsigned int u;
|
|
|
|
for(u = 0; u < DIM(m_rganode); ++u){
|
|
if(m_rganode[u] != NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***
|
|
*PRIVATE CDbAlloc::Dump
|
|
*Purpose:
|
|
* Print the current contents of the address instance table,
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* None
|
|
*
|
|
***********************************************************************/
|
|
void
|
|
CDbAlloc::ReportLeakage()
|
|
{
|
|
unsigned int u, cInst;
|
|
unsigned long cbTotal;
|
|
CAddrNode FAR* pn;
|
|
|
|
// check for memory leakage
|
|
if(IsEmpty()){
|
|
m_pdbout->Printf("No Memory Leaks.\n");
|
|
return;
|
|
}
|
|
|
|
cInst = 0;
|
|
cbTotal = 0;
|
|
for(u = 0; u < DIM(m_rganode); ++u){
|
|
for(pn = m_rganode[u]; pn != NULL; pn = pn->m_next){
|
|
++cInst;
|
|
cbTotal += pn->m_cb;
|
|
}
|
|
}
|
|
|
|
m_pdbout->Printf(
|
|
"Memory Leak Detected: %d Allocations / %ld Bytes,\n", cInst, cbTotal);
|
|
|
|
cInst = 0;
|
|
for(u = 0; u < DIM(m_rganode); ++u){
|
|
for(pn = m_rganode[u]; pn != NULL; pn = pn->m_next){
|
|
if(++cInst > 16){
|
|
m_pdbout->Printf("etc...\n");
|
|
return;
|
|
}
|
|
DumpInst(pn);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// implementation of CStdDbOutput
|
|
//---------------------------------------------------------------------
|
|
|
|
IDbOutput FAR*
|
|
CStdDbOutput::Create()
|
|
{
|
|
return (IDbOutput FAR*)new FAR CStdDbOutput();
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CStdDbOutput::QueryInterface(REFIID riid, void FAR* FAR* ppv)
|
|
{
|
|
if(IsEqualIID(riid, IID_IUnknown)){
|
|
*ppv = this;
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CStdDbOutput::AddRef()
|
|
{
|
|
return ++m_refs;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CStdDbOutput::Release()
|
|
{
|
|
if(--m_refs == 0){
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_refs;
|
|
}
|
|
|
|
#if OE_MAC
|
|
extern "C" void DbPrintf(char *szFmt, ...);
|
|
#endif
|
|
|
|
STDMETHODIMP_(void)
|
|
CStdDbOutput::Printf(char FAR* szFmt, ...)
|
|
{
|
|
va_list args;
|
|
static char rgchOutputBuf[128];
|
|
|
|
#if OE_MAC
|
|
va_start(args, szFmt);
|
|
vsprintf(rgchOutputBuf, szFmt, args);
|
|
DbPrintf(rgchOutputBuf);
|
|
|
|
#else
|
|
char *pn, FAR* pf;
|
|
static char rgchFmtBuf[128];
|
|
|
|
// copy the 'far' format string to a near buffer so we can use
|
|
// a medium model vsprintf, which only supports near data pointers.
|
|
//
|
|
pn = rgchFmtBuf, pf=szFmt;
|
|
while(*pf != '\0')
|
|
*pn++ = *pf++;
|
|
*pn = '\0';
|
|
|
|
va_start(args, szFmt);
|
|
vsprintf(rgchOutputBuf, rgchFmtBuf, args);
|
|
OUTPUTDEBUGSTRING(rgchOutputBuf);
|
|
#endif
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CStdDbOutput::Assertion(
|
|
int cond,
|
|
char FAR* szFile,
|
|
unsigned int uLine,
|
|
char FAR* szMsg)
|
|
{
|
|
if(cond)
|
|
return;
|
|
|
|
# if OE_MAC
|
|
|
|
char *szFmt;
|
|
char buf[128];
|
|
szFmt = (szMsg == NULL)
|
|
? "Assertion failed: %s(%d)"
|
|
: "Assertion failed: %s(%d) '%s'";
|
|
sprintf(&buf[1], szFmt, szFile, uLine, szMsg);
|
|
buf[0] = strlen(&buf[1]);
|
|
DebugStr((unsigned char *)buf);
|
|
|
|
# else
|
|
|
|
DispAssert(szMsg, szFile, uLine);
|
|
|
|
# endif
|
|
}
|
|
|