694 lines
21 KiB
C++
694 lines
21 KiB
C++
#include "priv.h"
|
|
|
|
#undef new // Hack! Need to resolve this. (edwardp)
|
|
|
|
#define _NO_DBGMEMORY_REDEFINITION_
|
|
#include "dbgmem.h"
|
|
|
|
#include <platform.h>
|
|
#define DM_MISC2 0 // misc stuff (verbose)
|
|
|
|
|
|
#ifdef DEBUG // {
|
|
EXTERN_C LONG g_cmem = 0;
|
|
EXTERN_C DWORD g_TlsMem;
|
|
|
|
// BUGBUG:: Gross but simply a static table of people who registered callbacks...
|
|
#define MAX_INTELLI_CALLBACKS 10
|
|
|
|
// We will psuedo register our own callback as part of this as to remove a lot of special case code...
|
|
STDAPI_(void) DumpLeakedMemory(PLEAKMEMINFO pmeminfo);
|
|
STDAPI_(LPWSTR) GetLeakSymbolicName(PLEAKMEMINFO pmeminfo, LPWSTR pwszBuf, int cchBuf);
|
|
|
|
static const INTELLILEAKDUMPCBFUNCTIONS c_ildcbf = {
|
|
DumpLeakedMemory,
|
|
GetLeakSymbolicName
|
|
};
|
|
|
|
int g_cIntelliCallbacks = 1;
|
|
struct INTELLILEAKCALLBACKENTRY
|
|
{
|
|
HMODULE hmod;
|
|
const INTELLILEAKDUMPCBFUNCTIONS *pfns;
|
|
} g_pfnLeakCallbacks[MAX_INTELLI_CALLBACKS] = {{0, &c_ildcbf}};
|
|
|
|
|
|
STDAPI_(void) _add_to_memlist( HMODULE hmod, void *pv, unsigned int nSize, UINT nType, LPCSTR pszFile, const int iLine )
|
|
{
|
|
// must cache the error so that we can restore it if necessary
|
|
DWORD dwErr = GetLastError();
|
|
HDSA hdsa = (HDSA)TlsGetValue(g_TlsMem);
|
|
|
|
if ((hdsa) && (pv)) {
|
|
LEAKMEMINFO memi = { hmod, pv, nSize, nType, pszFile, iLine };
|
|
LEAKMEMINFO *pmemi = &memi;
|
|
|
|
if (pmemi->nType == DBGMEM_UNKNOBJ) {
|
|
#if ( _X86_) // { QIStub (and GetStackBack) only work for X86
|
|
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGQI)) {
|
|
extern void * QIStub_CreateInstance(void* that, IUnknown* punk, REFIID riid);
|
|
HRESULT QISearch(void* that, LPCQITAB pqitab, REFIID riid, void ** ppv);
|
|
// if QIStub is on, our frame should look like:
|
|
// ... -> leaker -> [SUPER::QI]* -> xxx::QI -> QISearch ->
|
|
// -> QIStub_CreateInst -> ::new
|
|
// we do our best to follow it back to get to leaker. getting
|
|
// to xxx::QI is pretty easy, getting thru the SUPER::QI's a
|
|
// bit more iffy (but usually works).
|
|
|
|
// note that we need to allow for some fudge since a child
|
|
// returns into the middle of a func (i.e. func+FUDGE), not
|
|
// to the beginning (i.e. func+0).
|
|
#define FUDGE(n) (n)
|
|
|
|
// TODO: eventually we may hook up to imagehlp's GetStackBacktrace
|
|
// to give a deeper backtrace, but it's a bit heavyweight.
|
|
|
|
int fp, n, i, pfunc;
|
|
struct DBstkback sbtab[6]; // enough for a couple SUPER's
|
|
|
|
fp = pmemi->iLine;
|
|
n = DBGetStackBack(&fp, sbtab, ARRAYSIZE(sbtab));
|
|
#define INFUNC(p, pfunc, cb) ((pfunc) <= (p) && (p) <= (pfunc) + (cb))
|
|
// skip over QIStub_CI and QISearch explicitly
|
|
pfunc = (int) QIStub_CreateInstance;
|
|
i = 0;
|
|
if (INFUNC(sbtab[0].ret, (int)QIStub_CreateInstance, FUDGE(16))) {
|
|
i++;
|
|
if (INFUNC(sbtab[1].ret, (int)QISearch, FUDGE(512)))
|
|
i++;
|
|
}
|
|
|
|
// skip over any known/suspected QI's
|
|
for ( ; i < n; i++) {
|
|
if (! DBIsQIFunc(sbtab[i].ret, FUDGE(64)))
|
|
break;
|
|
}
|
|
|
|
TraceMsg(DM_MISC2, "v.atm: n=%d i=%d ret[]=%x,%x,%x,%x,%x,%x", n, i,
|
|
sbtab[0].ret, sbtab[1].ret, sbtab[2].ret, sbtab[3].ret,
|
|
sbtab[4].ret, sbtab[5].ret);
|
|
|
|
// this should be the leaker
|
|
pmemi->iLine = sbtab[i].ret;
|
|
}
|
|
else
|
|
{
|
|
// beats me if/why this works for non-x86,
|
|
// but it's what the old code did
|
|
pmemi->iLine = *((int *)iLine + 1); // aka BP_GETRET(iLine)
|
|
}
|
|
#else
|
|
// It beats the non-x86 cpus also.
|
|
// Need to fix this (edwardp).
|
|
pmemi->iLine = 0;
|
|
#endif
|
|
}
|
|
|
|
if (DSA_AppendItem(hdsa, &memi) >=0) {
|
|
InterlockedIncrement(&g_cmem);
|
|
}
|
|
}
|
|
|
|
// only restore it if it changed,
|
|
// that way people with SLE bps wont be startled
|
|
if (dwErr != GetLastError())
|
|
SetLastError(dwErr);
|
|
}
|
|
|
|
void * __cdecl operator new( size_t nSize, LPCSTR pszFile, const int iLine )
|
|
{
|
|
// Zero init just to save some headaches
|
|
void * pv = (void *)LocalAlloc(LPTR, nSize);
|
|
|
|
#ifdef DEBUG
|
|
HMODULE hmod = 0;
|
|
|
|
if (g_bUseNewLeakDetection)
|
|
hmod = HINST_THISDLL;
|
|
add_to_memlist( hmod, pv, nSize, DBGMEM_OBJECT, pszFile, iLine );
|
|
#endif
|
|
|
|
return pv;
|
|
}
|
|
|
|
void * __cdecl operator new( size_t nSize )
|
|
{
|
|
|
|
// HACK! The compiler is putting hmod as the first local (EBP - 4). So
|
|
// point pstack to &hmod. We need to get rid of this scumbag code!
|
|
// (edwardp)
|
|
|
|
void * pv;
|
|
#ifdef DEBUG
|
|
HMODULE hmod = 0;
|
|
LPDWORD pstack = (LPDWORD) &hmod; // HACK! compiler is putting hmod as first local.
|
|
#endif
|
|
|
|
// Zero init just to save some headaches
|
|
pv = (void *)LocalAlloc(LPTR, nSize);
|
|
|
|
#ifdef DEBUG
|
|
if (g_bUseNewLeakDetection)
|
|
hmod = HINST_THISDLL;
|
|
add_to_memlist( hmod, pv, nSize, DBGMEM_UNKNOBJ, "UNKNOWN", (INT_PTR) &pstack[1] );
|
|
#endif
|
|
|
|
return pv;
|
|
}
|
|
|
|
int _DSA_GetPtrIndex(HDSA hdsa, void* pv)
|
|
{
|
|
for (int i=0; i<DSA_GetItemCount(hdsa); i++) {
|
|
LEAKMEMINFO* pmemi = (LEAKMEMINFO*)DSA_GetItemPtr(hdsa, i);
|
|
if (pmemi->pv == pv) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
STDAPI_(void) _remove_from_memlist(void *pv)
|
|
{
|
|
// must cache the error so that we can restore it if necessary
|
|
DWORD dwErr = GetLastError();
|
|
|
|
HDSA hdsa = (HDSA)TlsGetValue(g_TlsMem);
|
|
if (hdsa) {
|
|
int index = _DSA_GetPtrIndex(hdsa, pv);
|
|
if (index >=0) {
|
|
DSA_DeleteItem(hdsa, index);
|
|
InterlockedDecrement(&g_cmem);
|
|
}
|
|
}
|
|
// only restore it if it changed,
|
|
// that way people with SLE bps wont be startled
|
|
if (dwErr != GetLastError())
|
|
SetLastError(dwErr);
|
|
|
|
}
|
|
|
|
static UINT g_uMemMove = 0;
|
|
STDAPI_(UINT) _mem_thread_message()
|
|
{
|
|
if ( !g_uMemMove )
|
|
g_uMemMove = RegisterWindowMessage(TEXT("Shdocvw_ThreadMemTransfer"));
|
|
|
|
return g_uMemMove;
|
|
}
|
|
|
|
STDAPI_(void) _transfer_to_thread_memlist(DWORD dwThreadId, void *pv)
|
|
{
|
|
UINT uMsg = mem_thread_message();
|
|
|
|
// must cache the error so that we can restore it if necessary
|
|
DWORD dwErr = GetLastError();
|
|
|
|
HDSA hdsa = (HDSA)TlsGetValue(g_TlsMem);
|
|
if ( hdsa )
|
|
{
|
|
int index = _DSA_GetPtrIndex( hdsa, pv );
|
|
if ( index >= 0)
|
|
{
|
|
LEAKMEMINFO *pMemBlock = (LEAKMEMINFO*) DSA_GetItemPtr(hdsa, index);
|
|
LEAKMEMINFO *pNewBlock = (LEAKMEMINFO*) LocalAlloc( LPTR, sizeof(LEAKMEMINFO ));
|
|
if ( pNewBlock )
|
|
{
|
|
*pNewBlock = *pMemBlock;
|
|
|
|
// post a message to the thread giving it the memblock
|
|
PostThreadMessage( dwThreadId, uMsg, 0, (LPARAM) pNewBlock );
|
|
}
|
|
|
|
// remove from the current thread's list...
|
|
DSA_DeleteItem( hdsa, index );
|
|
InterlockedDecrement(&g_cmem);
|
|
}
|
|
}
|
|
|
|
// only restore it if it changed,
|
|
// that way people with SLE bps wont be startled
|
|
if (dwErr != GetLastError())
|
|
SetLastError(dwErr);
|
|
}
|
|
|
|
STDAPI_(void) _remove_from_thread_memlist( DWORD dwThreadId, void * pv )
|
|
{
|
|
UINT uMsg = mem_thread_message();
|
|
|
|
PostThreadMessage( dwThreadId, uMsg, 1, (LPARAM) pv );
|
|
}
|
|
|
|
STDAPI_(void) _received_for_thread_memlist( DWORD dwFlags, void * pData )
|
|
{
|
|
|
|
// must cache the error so that we can restore it if necessary
|
|
DWORD dwErr = GetLastError();
|
|
|
|
LEAKMEMINFO * pMem = (LEAKMEMINFO *) pData;
|
|
if ( pMem ){
|
|
if ( dwFlags )
|
|
{
|
|
// we are being told to remove it from our thread list because it
|
|
// is actually being freed on the other thread....
|
|
remove_from_memlist( pMem->pv );
|
|
return;
|
|
}
|
|
|
|
HDSA hdsa = (HDSA)TlsGetValue(g_TlsMem);
|
|
|
|
if (hdsa) {
|
|
if (DSA_AppendItem(hdsa, pMem) >=0) {
|
|
InterlockedIncrement(&g_cmem);
|
|
}
|
|
}
|
|
LocalFree( pMem );
|
|
}
|
|
|
|
// only restore it if it changed,
|
|
// that way people with SLE bps wont be startled
|
|
if (dwErr != GetLastError())
|
|
SetLastError(dwErr);
|
|
}
|
|
|
|
void __cdecl operator delete(void *pv)
|
|
{
|
|
if (pv) {
|
|
remove_from_memlist(pv);
|
|
memset(pv, 0xfe, (UINT)LocalSize((HLOCAL)pv));
|
|
LocalFree((HLOCAL)pv);
|
|
}
|
|
}
|
|
|
|
EXTERN_C int __cdecl _purecall(void) {return 0;}
|
|
|
|
//** heuristics {
|
|
|
|
//** GetLeakSymbolic -- get human-readable name for object
|
|
|
|
TCHAR *GetLeakSymbolic(LEAKMEMINFO *pLeak)
|
|
{
|
|
extern TCHAR *DBGetQIStubSymbolic(void* that);
|
|
extern TCHAR *DBGetClassSymbolic(int cbSize);
|
|
TCHAR *pszSym;
|
|
|
|
#if ( _X86_) // { QIStub only works for X86
|
|
if (pLeak->nType == DBGMEM_UNKNOBJ && DBIsQIStub(pLeak->pv))
|
|
pszSym = DBGetQIStubSymbolic(pLeak->pv);
|
|
else
|
|
#endif // }
|
|
if (pszSym = DBGetClassSymbolic(pLeak->cb))
|
|
;
|
|
else {
|
|
// if you get this string, go into debdump.cpp and add a
|
|
// DBGetClassSymbolic table entry for the class
|
|
pszSym = TEXT("?debdump!DBGetClassSymbolic");
|
|
}
|
|
|
|
return pszSym;
|
|
}
|
|
|
|
//** _ConvertHModToIntelliDumpIndex
|
|
// DESCRIPTION
|
|
// main heuristics:
|
|
int _ConvertHModToIntelliDumpIndex(HMODULE hmod)
|
|
{
|
|
for (int i = 0; i < g_cIntelliCallbacks; i++) {
|
|
if ((hmod == g_pfnLeakCallbacks[i].hmod) &&
|
|
!IsBadCodePtr((FARPROC)g_pfnLeakCallbacks[i].pfns->pfnDumpLeakedMemory)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
//** _DoDumpMemLeakIntelli -- attempt to interpret leak info
|
|
// DESCRIPTION
|
|
// main heuristics:
|
|
// try to detect refs from one object to another by walking all DWORDs
|
|
// this gives us a graph description
|
|
// more importantly, it tells us which are the 'root' leaks
|
|
// identify classes so we can label them w/ a symbolic name
|
|
// identify QIStub's so we can dump them nicely
|
|
// e.g. symbolic IID and a sequence # for that IID
|
|
// the sequence # can be used to find the exact QI call
|
|
// others:
|
|
// see the code (sorry)...
|
|
// NOTES
|
|
// algorithm is n^2, so sue me...
|
|
// we assume (hopefully correctly...) that all memory is aligned to a
|
|
// 'strict' boundary so we'll never fault dereferencing DWORDs. if that
|
|
// turns out to be wrong we can wrap in a __try...__except.
|
|
void _DoDumpMemLeakIntelli(HDSA hdsa, DWORD wFlags)
|
|
{
|
|
int cLeak;
|
|
|
|
ASSERT(wFlags & DML_END);
|
|
ASSERT(hdsa);
|
|
|
|
cLeak = DSA_GetItemCount(hdsa);
|
|
if (!cLeak)
|
|
return;
|
|
|
|
TraceMsg(TF_ALWAYS, "intelli-leak heuristics...");
|
|
|
|
for (int iDef = 0; iDef < cLeak; iDef++) {
|
|
LEAKMEMINFO* pDef = (LEAKMEMINFO*)DSA_GetItemPtr(hdsa, iDef);
|
|
BOOL fHasRef;
|
|
int iDumpModule;
|
|
|
|
if (IsBadReadPtr(pDef->pv, pDef->cb))
|
|
continue;
|
|
|
|
// see if we should hand this one off to a different dumper?
|
|
if ((iDumpModule = _ConvertHModToIntelliDumpIndex(pDef->hmod)) >= 0) {
|
|
g_pfnLeakCallbacks[iDumpModule].pfns->pfnDumpLeakedMemory(pDef);
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
}
|
|
// find all (likely) refs to this leak. we do this by walking
|
|
// the full set of leaks and looking for DWORDs that point to
|
|
// the base of this leak. we only look at the base on the
|
|
// assumption that ptrs will go thru QIStub's (and this bounds
|
|
// the search which is nice).
|
|
fHasRef = FALSE;
|
|
for (int iRef = 0; iRef < cLeak; iRef++) {
|
|
LEAKMEMINFO* pRef = (LEAKMEMINFO*)DSA_GetItemPtr(hdsa, iRef);
|
|
int dOff;
|
|
|
|
if (IsBadReadPtr(pRef->pv, pRef->cb))
|
|
continue;
|
|
|
|
#define TRUNCPOW2(n, p) ((n) & ~((p) - 1))
|
|
dOff = SearchDW((DWORD*)pRef->pv, TRUNCPOW2(pRef->cb, SIZEOF(DWORD)), PtrToLong(pDef->pv));
|
|
if (dOff != -1) {
|
|
// found a ref to the leak def!
|
|
if ((iDumpModule = _ConvertHModToIntelliDumpIndex(pRef->hmod)) >= 0) {
|
|
WCHAR szBuf[80];
|
|
LPWSTR pwszRef = g_pfnLeakCallbacks[iDumpModule].pfns->pfnGetLeakSymbolicName(pDef,
|
|
szBuf, ARRAYSIZE(szBuf));
|
|
|
|
TraceMsg(TF_ALWAYS, "\tref=%x,%x+%x(%ls)",
|
|
pRef->pv, pRef->cb, dOff, pwszRef);
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
// a fair number of objects actually point back to themselves
|
|
// print out the ref just in case, but don't count it as a
|
|
// real one (which will make us look like a non-root)
|
|
if (pRef->pv != pDef->pv)
|
|
fHasRef = TRUE;
|
|
else
|
|
TraceMsg(TF_ALWAYS, "\tref=self");
|
|
|
|
// (we could iterate on pRef->pv+dOff+SIZEOF(DWORD),
|
|
// but probably the 1st hit is all we need)
|
|
}
|
|
}
|
|
if (!fHasRef) {
|
|
// these are the guys we really care about
|
|
TraceMsg(TF_ALWAYS, "\tref=root **");
|
|
}
|
|
}
|
|
|
|
// drop into debugger for further analysis
|
|
ASSERT_MSG(0, "Take this opportunity to debug the leak");
|
|
|
|
return;
|
|
}
|
|
|
|
// }
|
|
|
|
void _DoDumpMemLeak(HDSA hdsa, DWORD wFlags)
|
|
{
|
|
BOOL fLeaked = FALSE;
|
|
|
|
ASSERT(wFlags & DML_END);
|
|
ASSERT(hdsa);
|
|
|
|
if (DSA_GetItemCount(hdsa)) {
|
|
// Let's always dump them.
|
|
TraceMsg(TF_ALWAYS, "**");
|
|
TraceMsg(TF_ALWAYS, "* !!!!! WARNING : MEMORY LEAK DETECTED !!!!! *");
|
|
TraceMsg(TF_ALWAYS, "**");
|
|
TraceMsg(TF_ALWAYS, "* For Object: address: Vtbl, ...Vtbl, _cRef *");
|
|
TraceMsg(TF_ALWAYS, "* For StrDup: address: 'text' *");
|
|
TraceMsg(TF_ALWAYS, "* For Traced: address: *");
|
|
TraceMsg(TF_ALWAYS, "* For Memory: address: *");
|
|
TraceMsg(TF_ALWAYS, "**");
|
|
{
|
|
for (int i=0; i<DSA_GetItemCount(hdsa); i++) {
|
|
LEAKMEMINFO* pmemi = (LEAKMEMINFO*)DSA_GetItemPtr(hdsa, i);
|
|
DWORD* pdw = (DWORD*)pmemi->pv;
|
|
|
|
// sometimes we think we have leaked something and its really been freed,
|
|
// so check here first
|
|
if (IsBadReadPtr((void *)pdw, pmemi->cb))
|
|
continue;
|
|
|
|
switch( pmemi->nType ) {
|
|
case DBGMEM_STRDUP:
|
|
TraceMsgA(TF_ALWAYS, "StrDup: %8x: \"%s\"\n\t\t size=%d (%s, line %d)",
|
|
pdw, pdw, pmemi->cb, PathFindFileNameA(pmemi->pszFile), pmemi->iLine);
|
|
break;
|
|
|
|
case DBGMEM_UNKNOBJ:
|
|
case DBGMEM_OBJECT:
|
|
{
|
|
if ( pmemi->cb >= 32 )
|
|
{
|
|
TraceMsg(TF_ALWAYS, "Object: %8x: %8x %8x %8x %8x",
|
|
pdw, pdw[0], pdw[1], pdw[2], pdw[3] );
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_ALWAYS, "Object: %8x: Size<32 bytes. No Vtbl.", pdw );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DBGMEM_TRACED:
|
|
TraceMsg(TF_ALWAYS, "Traced: %8x:", pdw );
|
|
break;
|
|
|
|
case DBGMEM_MEMORY:
|
|
default:
|
|
TraceMsg(TF_ALWAYS, "Memory: %8x:", pdw );
|
|
break;
|
|
}
|
|
if ( pmemi->nType == DBGMEM_UNKNOBJ )
|
|
{
|
|
TraceMsg(TF_ALWAYS, "\t size=%d, created from %8x (return address)",
|
|
pmemi->cb, pmemi->iLine);
|
|
}
|
|
else
|
|
{
|
|
TraceMsgA(TF_ALWAYS, "\t size=%d (%s, line %d)",
|
|
pmemi->cb, PathFindFileNameA(pmemi->pszFile), pmemi->iLine);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
TraceMsg(TF_ALWAYS, "**");
|
|
AssertMsg(0, TEXT("** ALL MEMORY LEAK MUST BE FIXED BEFORE WE RELEASE (continue for intelli-leak on x86) **"));
|
|
fLeaked = TRUE;
|
|
}
|
|
|
|
if (!fLeaked) {
|
|
TraceMsg(TF_GENERAL, "Thread Terminated: -- No Memory Leak Detected for this thread --");
|
|
}
|
|
}
|
|
|
|
void _DumpMemLeak(DWORD wFlags)
|
|
{
|
|
HDSA hdsa;
|
|
if (wFlags & DML_END) {
|
|
hdsa = (HDSA)TlsGetValue(g_TlsMem);
|
|
if (hdsa) {
|
|
_DoDumpMemLeak(hdsa, wFlags);
|
|
_DoDumpMemLeakIntelli(hdsa, wFlags);
|
|
|
|
DSA_Destroy(hdsa);
|
|
TlsSetValue(g_TlsMem, NULL);
|
|
}
|
|
} else {
|
|
hdsa = DSA_Create(SIZEOF(LEAKMEMINFO),8);
|
|
TlsSetValue(g_TlsMem, (void *)hdsa);
|
|
}
|
|
}
|
|
|
|
STDAPI_(void) _DebugMemLeak(UINT wFlags, LPCTSTR pszFile, UINT iLine)
|
|
{
|
|
LPCTSTR pszSuffix = (wFlags & DML_END) ? TEXT("END") : TEXT("BEGIN");
|
|
|
|
switch(wFlags & DML_TYPE_MASK) {
|
|
case DML_TYPE_MAIN:
|
|
_DumpMemLeak(wFlags);
|
|
if (g_cmem) {
|
|
AssertMsg(0, TEXT("MAIN: %s, line %d: %d blocks of C++ objects left in memory. Read shdocvw") TEXT(FILENAME_SEPARATOR_STR) TEXT("memleak.txt for detail"),
|
|
PathFindFileName(pszFile), iLine, g_cmem);
|
|
}
|
|
break;
|
|
|
|
case DML_TYPE_THREAD:
|
|
if (g_tidParking != GetCurrentThreadId())
|
|
_DumpMemLeak(wFlags);
|
|
break;
|
|
|
|
case DML_TYPE_NAVIGATE:
|
|
TraceMsg(TF_SHDLIFE, "NAVIGATEW_%s: %s, line %d: %d blocks of C++ objects in memory",
|
|
pszSuffix, PathFindFileName(pszFile), iLine, g_cmem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// LocalXXXXX functions
|
|
STDAPI_(HLOCAL) _TrcLocalAlloc(
|
|
UINT uFlags, // flags used in LocalAlloc
|
|
UINT uBytes, // number of bytes to be allocated
|
|
LPCSTR pszFile, // file which allocced memory
|
|
const int iLine // line which allocced memory
|
|
)
|
|
{
|
|
HLOCAL lpv = (HLOCAL) LocalAlloc( uFlags, uBytes );
|
|
if ( lpv )
|
|
add_to_memlist( 0, lpv, uBytes, DBGMEM_MEMORY, pszFile, iLine );
|
|
|
|
return lpv;
|
|
}
|
|
|
|
|
|
STDAPI_(LPTSTR) _TrcStrDup(
|
|
LPTSTR lpSrch, // pointer to string to StrDup
|
|
LPCSTR pszFile, // file which allocced memory
|
|
const int iLine // line which allocced memory
|
|
)
|
|
{
|
|
UINT uBytes = 0;
|
|
if ( lpSrch )
|
|
uBytes = lstrlen( lpSrch ) + 1;
|
|
|
|
LPTSTR lpstr = StrDup( lpSrch );
|
|
if ( lpstr )
|
|
add_to_memlist( 0, lpstr, uBytes, DBGMEM_STRDUP, pszFile, iLine );
|
|
|
|
return lpstr;
|
|
}
|
|
|
|
STDAPI_(HLOCAL) _TrcLocalFree(
|
|
HLOCAL hMem // memory to be freed
|
|
)
|
|
{
|
|
if ( hMem )
|
|
{
|
|
remove_from_memlist( hMem );
|
|
memset( hMem, 0xfe, (UINT)LocalSize( hMem ));
|
|
}
|
|
return LocalFree( hMem );
|
|
}
|
|
|
|
STDAPI_(void) _register_intelli_dump(HMODULE hmod, const INTELLILEAKDUMPCBFUNCTIONS *pfns)
|
|
{
|
|
if (g_cIntelliCallbacks < (MAX_INTELLI_CALLBACKS - 1))
|
|
{
|
|
g_pfnLeakCallbacks[g_cIntelliCallbacks].hmod = hmod;
|
|
g_pfnLeakCallbacks[g_cIntelliCallbacks].pfns = pfns;
|
|
g_cIntelliCallbacks++;
|
|
}
|
|
}
|
|
|
|
#else // }{
|
|
#define CPP_FUNCTIONS
|
|
#include <crtfree.h>
|
|
#endif // }
|
|
|
|
|
|
BOOL GetLeakDetectionFunctionTable(LEAKDETECTFUNCS *pTable)
|
|
{
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if(pTable)
|
|
{
|
|
pTable->pfnDumpMemLeak = _DumpMemLeak;
|
|
pTable->pfnTrcLocalAlloc = _TrcLocalAlloc;
|
|
|
|
pTable->pfnTrcLocalFree = _TrcLocalFree;
|
|
|
|
pTable->pfnTrcStrDup = _TrcStrDup;
|
|
|
|
pTable->pfnDumpMemLeak = _DumpMemLeak;
|
|
pTable->pfnDebugMemLeak = MemLeakInit; //_DebugMemLeak;
|
|
pTable->pfnreceived_for_thread_memlist = received_for_thread_memlist;
|
|
pTable->pfnremove_from_thread_memlist = remove_from_thread_memlist;
|
|
pTable->pfnmem_thread_message = mem_thread_message;
|
|
pTable->pfnremove_from_memlist = remove_from_memlist;
|
|
pTable->pfnadd_to_memlist = add_to_memlist;
|
|
pTable->pfnregister_hmod_intelli_dump = register_intelli_dump;
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
// We export all the memory leak detection functions in the form of a table of
|
|
// pointers so that all other shell components can share in the same leak
|
|
// detection code
|
|
|
|
|
|
// These two functions are our DLLS callback functions to dump the acutal leaks...
|
|
#ifdef DEBUG
|
|
//** GetLeakSymbolicName
|
|
// DESCRIPTION
|
|
STDAPI_(LPWSTR) GetLeakSymbolicName(PLEAKMEMINFO pmeminfo, LPWSTR pwszBuf, int cchBuf)
|
|
{
|
|
LPTSTR pszDef = GetLeakSymbolic(pmeminfo); // human-readable name
|
|
|
|
// they only want the class name...
|
|
return pszDef;
|
|
}
|
|
|
|
//** Dump LeakedMemory
|
|
// DESCRIPTION
|
|
STDAPI_(void) DumpLeakedMemory(LEAKMEMINFO* pmeminfo)
|
|
{
|
|
LPTSTR pszDef = GetLeakSymbolic(pmeminfo); // human-readable name
|
|
|
|
TraceMsg(TF_ALWAYS, "leak=0x%x,%x(%s)", pmeminfo->pv, pmeminfo->cb, pszDef);
|
|
|
|
if (pmeminfo->nType == DBGMEM_UNKNOBJ)
|
|
TraceMsg(TF_ALWAYS, "\tcreated from 0x%x", pmeminfo->iLine);
|
|
else
|
|
TraceMsgA(TF_ALWAYS, "\tcreated from %hs:%d",
|
|
PathFindFileNameA(pmeminfo->pszFile), pmeminfo->iLine);
|
|
|
|
#if ( _X86_) // { QIStub only works for X86
|
|
if (pmeminfo->nType == DBGMEM_UNKNOBJ && DBIsQIStub(pmeminfo->pv)) {
|
|
// it's a QIStub, dump it
|
|
// of particular interest is the 'sequence #' (esp. for roots)
|
|
DBDumpQIStub(pmeminfo->pv);
|
|
}
|
|
#endif // }
|
|
}
|
|
|
|
#endif
|