1041 lines
33 KiB
C++
1041 lines
33 KiB
C++
|
|
|||
|
// LeakFinder.cpp
|
|||
|
|
|||
|
// Finds memory leaks rather effectively
|
|||
|
|
|||
|
// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**********************************************************************
|
|||
|
*
|
|||
|
* LEAKFINDER.CPP
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
* History:
|
|||
|
* 2010-04-15 RC10 - Updated to VC10 RTM
|
|||
|
* Fixed Bug: Application Verifier, thanks to handsinmypocket!
|
|||
|
* http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx
|
|||
|
* 2008-08-04 RC6 - Updated to VC9 RTM
|
|||
|
* Fixed Bug: Missing "ole32.lib" LIB
|
|||
|
* http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx
|
|||
|
* Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
|
|||
|
* http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
|
|||
|
* Fixed Bug: Compiling with "/Wall"
|
|||
|
* http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
|
|||
|
* Removed "#pragma init_seg (compiler)" from h-file
|
|||
|
*
|
|||
|
* 2005-12-30 RC5 - Now again VC8 RTM compatible
|
|||
|
* - Added Xml-Output (like in the old Leakfinder)
|
|||
|
* YOu need to define XML_LEAK_FINDER to activate it
|
|||
|
* So you can use the LeakAnalyseTool from
|
|||
|
* http://www.codeproject.com/tools/leakfinder.asp
|
|||
|
*
|
|||
|
* 2005-12-13 RC4 - Merged with the new "StackWalker"-project on
|
|||
|
* http://www.codeproject.com/threads/StackWalker.asp
|
|||
|
*
|
|||
|
* 2005-08-01 RC3 - Merged with the new "StackWalker"-project on
|
|||
|
* http://www.codeproject.com/threads/StackWalker.asp
|
|||
|
*
|
|||
|
* 2005-07-05 RC2 - First version with x86, IA64 and x64 support
|
|||
|
*
|
|||
|
* 2005-07-04 RC1 - Added "OutputOptions"
|
|||
|
* - New define "INIT_LEAK_FINDER_VERBOSE" to
|
|||
|
* display more info (for error reporting)
|
|||
|
*
|
|||
|
* 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll
|
|||
|
* (version 6.5.3.7 from 2005-05-30; StakWalk64 no
|
|||
|
* refused to produce an callstack on x86 systems
|
|||
|
* if the context is NULL or has some registers set
|
|||
|
* to 0 (for example Esp). This is against the
|
|||
|
* documented behaviour of StackWalk64...)
|
|||
|
* - First version with x64-support
|
|||
|
*
|
|||
|
* 2005-06-16 Beta1 First public release with the following features:
|
|||
|
* - Completely rewritten in C++ (object oriented)
|
|||
|
* - CRT-Leak-Report
|
|||
|
* - COM-Leak-Report
|
|||
|
* - Report is done via "OutputDebugString" so
|
|||
|
* the line can directly selected in the debugger
|
|||
|
* and is opening the corresponding file/line of
|
|||
|
* the allocation
|
|||
|
* - Tried to support x64 systems, bud had some
|
|||
|
* trouble wih StackWalk64
|
|||
|
* See: http://blog.kalmbachnet.de/?postid=43
|
|||
|
*
|
|||
|
* LICENSE (http://www.opensource.org/licenses/bsd-license.php)
|
|||
|
*
|
|||
|
* Copyright (c) 2005-2010, Jochen Kalmbach
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Redistribution and use in source and binary forms, with or without modification,
|
|||
|
* are permitted provided that the following conditions are met:
|
|||
|
*
|
|||
|
* Redistributions of source code must retain the above copyright notice,
|
|||
|
* this list of conditions and the following disclaimer.
|
|||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|||
|
* this list of conditions and the following disclaimer in the documentation
|
|||
|
* and/or other materials provided with the distribution.
|
|||
|
* Neither the name of Jochen Kalmbach nor the names of its contributors may be
|
|||
|
* used to endorse or promote products derived from this software without
|
|||
|
* specific prior written permission.
|
|||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
*
|
|||
|
**********************************************************************/
|
|||
|
|
|||
|
#include <windows.h>
|
|||
|
#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN"
|
|||
|
#include <tchar.h>
|
|||
|
#include <crtdbg.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#include <string>
|
|||
|
#include <vector>
|
|||
|
|
|||
|
|
|||
|
#include "LeakFinder.h"
|
|||
|
|
|||
|
// Currently only tested with MS VC++ 5 to 10
|
|||
|
#if (_MSC_VER < 1100) || (_MSC_VER > 1600)
|
|||
|
#error Only MS VC++ 5/6/7/7.1/8/9 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler!
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
// Controlling the callstack depth
|
|||
|
#define MAX_CALLSTACK_LEN_BUF 0x2000
|
|||
|
|
|||
|
#define IGNORE_CRT_ALLOC
|
|||
|
|
|||
|
// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!)
|
|||
|
#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size
|
|||
|
#pragma warning(disable:4826)
|
|||
|
|
|||
|
|
|||
|
// secure-CRT_functions are only available starting with VC8
|
|||
|
#if _MSC_VER < 1400
|
|||
|
#define _snprintf_s _snprintf
|
|||
|
#define _tcscat_s _tcscat
|
|||
|
#endif
|
|||
|
|
|||
|
static std::string SimpleXMLEncode(LPCSTR szText)
|
|||
|
{
|
|||
|
std::string szRet;
|
|||
|
for (size_t i=0; i<strlen(szText); i++)
|
|||
|
{
|
|||
|
switch(szText[i])
|
|||
|
{
|
|||
|
case '&':
|
|||
|
szRet.append("&");
|
|||
|
break;
|
|||
|
case '<':
|
|||
|
szRet.append("<");
|
|||
|
break;
|
|||
|
case '>':
|
|||
|
szRet.append(">");
|
|||
|
break;
|
|||
|
case '"':
|
|||
|
szRet.append(""");
|
|||
|
break;
|
|||
|
case '\'':
|
|||
|
szRet.append("'");
|
|||
|
break;
|
|||
|
default:
|
|||
|
szRet += szText[i];
|
|||
|
}
|
|||
|
}
|
|||
|
return szRet;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath)
|
|||
|
: StackWalker(options, szSymPath)
|
|||
|
{
|
|||
|
}
|
|||
|
void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName)
|
|||
|
{
|
|||
|
CHAR buffer[1024];
|
|||
|
_snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName);
|
|||
|
this->OnOutput(buffer);
|
|||
|
}
|
|||
|
void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
|
|||
|
{
|
|||
|
CHAR buffer[1024];
|
|||
|
_snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize);
|
|||
|
this->OnOutput(buffer);
|
|||
|
}
|
|||
|
void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
|
|||
|
{
|
|||
|
if ( (eType != lastEntry) && (entry.offset != 0) )
|
|||
|
{
|
|||
|
if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && (
|
|||
|
(strstr(entry.lineFileName, "afxmem.cpp") != NULL) ||
|
|||
|
(strstr(entry.lineFileName, "dbgheap.c") != NULL) ||
|
|||
|
(strstr(entry.lineFileName, "new.cpp") != NULL) ||
|
|||
|
(strstr(entry.lineFileName, "newop.cpp") != NULL) ||
|
|||
|
(strstr(entry.lineFileName, "leakfinder.cpp") != NULL) ||
|
|||
|
(strstr(entry.lineFileName, "stackwalker.cpp") != NULL)
|
|||
|
) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
StackWalker::OnCallstackEntry(eType, entry);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// ####################################################################
|
|||
|
// XML-Output
|
|||
|
LeakFinderXmlOutput::LeakFinderXmlOutput()
|
|||
|
{
|
|||
|
TCHAR szXMLFileName[1024];
|
|||
|
|
|||
|
GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR));
|
|||
|
_tcscat_s(szXMLFileName, _T(".mem.xml-leaks"));
|
|||
|
#if _MSC_VER < 1400
|
|||
|
m_fXmlFile = _tfopen(szXMLFileName, _T("w"));
|
|||
|
#else
|
|||
|
m_fXmlFile = NULL;
|
|||
|
_tfopen_s(&m_fXmlFile, szXMLFileName, _T("w"));
|
|||
|
#endif
|
|||
|
if (m_fXmlFile != NULL)
|
|||
|
{
|
|||
|
SYSTEMTIME st;
|
|||
|
GetLocalTime(&st);
|
|||
|
fprintf(m_fXmlFile, "<MEMREPORT date=\"%.2d/%.2d/%.4d\" time=\"%.2d:%.2d:%.2d\">\n",
|
|||
|
st.wMonth, st.wDay, st.wYear,
|
|||
|
st.wHour, st.wMinute, st.wSecond);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
|
|||
|
}
|
|||
|
}
|
|||
|
LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName)
|
|||
|
{
|
|||
|
#if _MSC_VER < 1400
|
|||
|
m_fXmlFile = _tfopen(szFileName, _T("w"));
|
|||
|
#else
|
|||
|
m_fXmlFile = NULL;
|
|||
|
_tfopen_s(&m_fXmlFile, szFileName, _T("w"));
|
|||
|
#endif
|
|||
|
if (m_fXmlFile == NULL)
|
|||
|
{
|
|||
|
MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
|
|||
|
}
|
|||
|
}
|
|||
|
LeakFinderXmlOutput::~LeakFinderXmlOutput()
|
|||
|
{
|
|||
|
if (m_fXmlFile != NULL)
|
|||
|
{
|
|||
|
// Write the ending-tags and close the file
|
|||
|
fprintf(m_fXmlFile, "</MEMREPORT>\n");
|
|||
|
fclose(m_fXmlFile);
|
|||
|
}
|
|||
|
m_fXmlFile = NULL;
|
|||
|
}
|
|||
|
void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName)
|
|||
|
{
|
|||
|
}
|
|||
|
void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
|
|||
|
{
|
|||
|
if (m_fXmlFile != NULL)
|
|||
|
{
|
|||
|
fprintf(m_fXmlFile, " <LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize);
|
|||
|
}
|
|||
|
}
|
|||
|
void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
|
|||
|
{
|
|||
|
if (m_fXmlFile != NULL)
|
|||
|
{
|
|||
|
if (eType != lastEntry)
|
|||
|
{
|
|||
|
fprintf(m_fXmlFile, " <STACKENTRY decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(entry.undName).c_str(), entry.offsetFromSmybol);
|
|||
|
fprintf(m_fXmlFile, "srcfile=\"%s\" line=\"%d\" line_offset=\"%+ld\" ", SimpleXMLEncode(entry.lineFileName).c_str(), entry.lineNumber, entry.offsetFromLine);
|
|||
|
fprintf(m_fXmlFile, "module=\"%s\" base=\"%08lx\" ", SimpleXMLEncode(entry.moduleName).c_str(), entry.baseOfImage);
|
|||
|
fprintf(m_fXmlFile, "/>\n");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fprintf(m_fXmlFile, " </LEAK>\n");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// Base class for storing contexts in a hashtable
|
|||
|
template <typename HASHTABLE_KEY> class ContextHashtableBase
|
|||
|
{
|
|||
|
public:
|
|||
|
ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName)
|
|||
|
{
|
|||
|
SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType);
|
|||
|
m_hHeap = HeapCreate(0, 10*1024 + s, 0);
|
|||
|
if (m_hHeap == NULL)
|
|||
|
throw;
|
|||
|
pAllocHashTable = (AllocHashEntryType*) own_malloc(s);
|
|||
|
sAllocEntries = sizeOfHastable;
|
|||
|
m_finderName = own_strdup(finderName);
|
|||
|
}
|
|||
|
|
|||
|
protected:
|
|||
|
virtual ~ContextHashtableBase()
|
|||
|
{
|
|||
|
if (pAllocHashTable != NULL)
|
|||
|
own_free(pAllocHashTable);
|
|||
|
pAllocHashTable = NULL;
|
|||
|
|
|||
|
own_free(m_finderName);
|
|||
|
m_finderName = NULL;
|
|||
|
|
|||
|
if (m_hHeap != NULL)
|
|||
|
HeapDestroy(m_hHeap);
|
|||
|
}
|
|||
|
|
|||
|
__inline LPVOID own_malloc(SIZE_T size)
|
|||
|
{
|
|||
|
return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size);
|
|||
|
}
|
|||
|
__inline VOID own_free(LPVOID memblock)
|
|||
|
{
|
|||
|
HeapFree(m_hHeap, 0, memblock);
|
|||
|
}
|
|||
|
__inline CHAR *own_strdup(const char *str)
|
|||
|
{
|
|||
|
size_t len = strlen(str)+1;
|
|||
|
CHAR *c = (CHAR*)own_malloc(len);
|
|||
|
#if _MSC_VER >= 1400
|
|||
|
strcpy_s(c, len, str);
|
|||
|
#else
|
|||
|
strcpy(c, str);
|
|||
|
#endif
|
|||
|
return c;
|
|||
|
}
|
|||
|
|
|||
|
// Disables this leak-finder
|
|||
|
virtual LONG Disable() = 0;
|
|||
|
// enables the leak-finder again...
|
|||
|
virtual LONG Enable() = 0;
|
|||
|
|
|||
|
private:
|
|||
|
// Entry for each allocation
|
|||
|
typedef struct AllocHashEntryType {
|
|||
|
HASHTABLE_KEY key;
|
|||
|
SIZE_T nDataSize; // Size of the allocated memory
|
|||
|
struct AllocHashEntryType *Next;
|
|||
|
CONTEXT c;
|
|||
|
PVOID pStackBaseAddr;
|
|||
|
SIZE_T nMaxStackSize;
|
|||
|
|
|||
|
PVOID pCallstackOffset;
|
|||
|
SIZE_T nCallstackLen;
|
|||
|
char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values...
|
|||
|
} AllocHashEntryType;
|
|||
|
|
|||
|
protected:
|
|||
|
virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0;
|
|||
|
virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0;
|
|||
|
virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0;
|
|||
|
virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0;
|
|||
|
//virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; }
|
|||
|
|
|||
|
public:
|
|||
|
VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize)
|
|||
|
{
|
|||
|
SIZE_T HashIdx;
|
|||
|
AllocHashEntryType *pHashEntry;
|
|||
|
|
|||
|
// generate hash-value
|
|||
|
HashIdx = HashFunction(key);
|
|||
|
|
|||
|
pHashEntry = &pAllocHashTable[HashIdx];
|
|||
|
if (IsKeyEmpty(pHashEntry->key) != FALSE) {
|
|||
|
// Entry is empty...
|
|||
|
}
|
|||
|
else {
|
|||
|
// Entry is not empy! make a list of entries for this hash value...
|
|||
|
while(pHashEntry->Next != NULL) {
|
|||
|
pHashEntry = pHashEntry->Next;
|
|||
|
}
|
|||
|
|
|||
|
pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType));
|
|||
|
pHashEntry = pHashEntry->Next;
|
|||
|
}
|
|||
|
pHashEntry->key = key;
|
|||
|
pHashEntry->nDataSize = nDataSize;
|
|||
|
pHashEntry->Next = NULL;
|
|||
|
#ifdef _M_IX86
|
|||
|
pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp);
|
|||
|
#elif _M_X64
|
|||
|
pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp);
|
|||
|
#elif _M_IA64
|
|||
|
pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP);
|
|||
|
#else
|
|||
|
#error "Platform not supported!"
|
|||
|
#endif
|
|||
|
pHashEntry->c = context;
|
|||
|
|
|||
|
// Query the max. stack-area:
|
|||
|
MEMORY_BASIC_INFORMATION MemBuffer;
|
|||
|
if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0)
|
|||
|
{
|
|||
|
pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress;
|
|||
|
pHashEntry->nMaxStackSize = MemBuffer.RegionSize;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pHashEntry->pStackBaseAddr = 0;
|
|||
|
pHashEntry->nMaxStackSize = 0;
|
|||
|
}
|
|||
|
|
|||
|
SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF;
|
|||
|
if (pHashEntry->nMaxStackSize > 0)
|
|||
|
{
|
|||
|
SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset;
|
|||
|
bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF);
|
|||
|
}
|
|||
|
// Now read the callstack:
|
|||
|
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0)
|
|||
|
{
|
|||
|
// Could not read memory...
|
|||
|
pHashEntry->nCallstackLen = 0;
|
|||
|
pHashEntry->pCallstackOffset = 0;
|
|||
|
} // read callstack
|
|||
|
} // Insert
|
|||
|
|
|||
|
BOOL Remove(HASHTABLE_KEY &key)
|
|||
|
{
|
|||
|
SIZE_T HashIdx;
|
|||
|
AllocHashEntryType *pHashEntry, *pHashEntryLast;
|
|||
|
|
|||
|
// get the Hash-Value
|
|||
|
HashIdx = HashFunction(key);
|
|||
|
|
|||
|
pHashEntryLast = NULL;
|
|||
|
pHashEntry = &pAllocHashTable[HashIdx];
|
|||
|
while(pHashEntry != NULL) {
|
|||
|
if (pHashEntry->key == key) {
|
|||
|
// release my memory
|
|||
|
if (pHashEntryLast == NULL) {
|
|||
|
// It is an entry in the table, so do not release this memory
|
|||
|
if (pHashEntry->Next == NULL) {
|
|||
|
// It was the last entry, so empty the table entry
|
|||
|
SetEmptyKey(pAllocHashTable[HashIdx].key);
|
|||
|
//memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx]));
|
|||
|
}
|
|||
|
else {
|
|||
|
// There are some more entries, so shorten the list
|
|||
|
AllocHashEntryType *pTmp = pHashEntry->Next;
|
|||
|
*pHashEntry = *(pHashEntry->Next);
|
|||
|
own_free(pTmp);
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
// now, I am in an dynamic allocated entry (it was a collision)
|
|||
|
pHashEntryLast->Next = pHashEntry->Next;
|
|||
|
own_free(pHashEntry);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
pHashEntryLast = pHashEntry;
|
|||
|
pHashEntry = pHashEntry->Next;
|
|||
|
}
|
|||
|
|
|||
|
// if we are here, we could not find the RequestID
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
AllocHashEntryType *Find(HASHTABLE_KEY &key)
|
|||
|
{
|
|||
|
SIZE_T HashIdx;
|
|||
|
AllocHashEntryType *pHashEntry;
|
|||
|
|
|||
|
// get the Hash-Value
|
|||
|
HashIdx = HashFunction(key);
|
|||
|
|
|||
|
pHashEntry = &pAllocHashTable[HashIdx];
|
|||
|
while(pHashEntry != NULL) {
|
|||
|
if (pHashEntry->key == key) {
|
|||
|
return pHashEntry;
|
|||
|
}
|
|||
|
pHashEntry = pHashEntry->Next;
|
|||
|
}
|
|||
|
|
|||
|
// entry was not found!
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
// For the followong static-var See comment in "ShowCallstack"...
|
|||
|
static BOOL CALLBACK ReadProcessMemoryFromHashEntry64(
|
|||
|
HANDLE hProcess, // hProcess must be a pointer to an hash-entry!
|
|||
|
DWORD64 lpBaseAddress,
|
|||
|
PVOID lpBuffer,
|
|||
|
DWORD nSize,
|
|||
|
LPDWORD lpNumberOfBytesRead,
|
|||
|
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
|
|||
|
)
|
|||
|
{
|
|||
|
*lpNumberOfBytesRead = 0;
|
|||
|
AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData;
|
|||
|
if (pHashEntry == NULL)
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) {
|
|||
|
// Memory is located in saved Callstack:
|
|||
|
// Calculate the offset
|
|||
|
DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset);
|
|||
|
DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset);
|
|||
|
memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize);
|
|||
|
*lpNumberOfBytesRead = dwSize;
|
|||
|
if (dwSize != nSize)
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
*lpNumberOfBytesRead = nSize;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (*lpNumberOfBytesRead == 0) // Memory could not be found
|
|||
|
{
|
|||
|
if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) )
|
|||
|
{
|
|||
|
// Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code)
|
|||
|
SIZE_T st = 0;
|
|||
|
BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st);
|
|||
|
*lpNumberOfBytesRead = (DWORD) st;
|
|||
|
return bRet;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID ShowLeaks(LeakFinderOutput &leakFinderOutput)
|
|||
|
{
|
|||
|
SIZE_T ulTemp;
|
|||
|
AllocHashEntryType *pHashEntry;
|
|||
|
ULONG ulCount = 0;
|
|||
|
SIZE_T ulLeaksByte = 0;
|
|||
|
|
|||
|
leakFinderOutput.OnLeakSearchStart(this->m_finderName);
|
|||
|
|
|||
|
// Move throu every entry
|
|||
|
CHAR keyName[1024];
|
|||
|
for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) {
|
|||
|
pHashEntry = &pAllocHashTable[ulTemp];
|
|||
|
if (IsKeyEmpty(pHashEntry->key) == FALSE) {
|
|||
|
while(pHashEntry != NULL) {
|
|||
|
ulCount++;
|
|||
|
CONTEXT c;
|
|||
|
memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT));
|
|||
|
|
|||
|
this->GetKeyAsString(pHashEntry->key, keyName, 1024);
|
|||
|
|
|||
|
leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize);
|
|||
|
leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry);
|
|||
|
|
|||
|
// Count the number of leaky bytes
|
|||
|
ulLeaksByte += pHashEntry->nDataSize;
|
|||
|
|
|||
|
pHashEntry = pHashEntry->Next;
|
|||
|
} // while
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
AllocHashEntryType *pAllocHashTable;
|
|||
|
SIZE_T sAllocEntries;
|
|||
|
HANDLE m_hHeap;
|
|||
|
LPSTR m_finderName;
|
|||
|
bool m_bSupressUselessLines;
|
|||
|
}; // template <typename HASHTABLE_KEY> class ContextHashtableBase
|
|||
|
|
|||
|
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// Specialization for CRT-Leaks:
|
|||
|
// VC5 has excluded all types in release-builds
|
|||
|
#ifdef _DEBUG
|
|||
|
|
|||
|
// The follwoing is copied from dbgint.h:
|
|||
|
// <CRT_INTERNALS>
|
|||
|
/*
|
|||
|
* For diagnostic purpose, blocks are allocated with extra information and
|
|||
|
* stored in a doubly-linked list. This makes all blocks registered with
|
|||
|
* how big they are, when they were allocated, and what they are used for.
|
|||
|
*/
|
|||
|
|
|||
|
// forward declaration:
|
|||
|
#ifndef _M_CEE_PURE
|
|||
|
#define MyAllocHookCallingConvention __cdecl
|
|||
|
#endif
|
|||
|
#if _MSC_VER >= 1400
|
|||
|
#ifdef _M_CEE
|
|||
|
#define MyAllocHookCallingConvention __clrcall
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData,
|
|||
|
size_t nSize, int nBlockUse, long lRequest,
|
|||
|
#if _MSC_VER <= 1100 // Special case for VC 5 and before
|
|||
|
const char * szFileName,
|
|||
|
#else
|
|||
|
const unsigned char * szFileName,
|
|||
|
#endif
|
|||
|
int nLine);
|
|||
|
|
|||
|
static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL;
|
|||
|
static LONG s_CrtDisableCount = 0;
|
|||
|
static LONG s_lMallocCalled = 0;
|
|||
|
|
|||
|
|
|||
|
class CRTTable : public ContextHashtableBase<LONG>
|
|||
|
{
|
|||
|
public:
|
|||
|
CRTTable() : ContextHashtableBase<LONG>(1021, "CRT-Leaks")
|
|||
|
{
|
|||
|
// save the previous alloc hook
|
|||
|
s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);
|
|||
|
}
|
|||
|
|
|||
|
virtual ~CRTTable()
|
|||
|
{
|
|||
|
_CrtSetAllocHook(s_pfnOldCrtAllocHook);
|
|||
|
}
|
|||
|
|
|||
|
virtual LONG Disable()
|
|||
|
{
|
|||
|
return InterlockedIncrement(&s_CrtDisableCount);
|
|||
|
}
|
|||
|
virtual LONG Enable()
|
|||
|
{
|
|||
|
return InterlockedDecrement(&s_CrtDisableCount);
|
|||
|
}
|
|||
|
|
|||
|
virtual SIZE_T HashFunction(LONG &key)
|
|||
|
{
|
|||
|
// I couldn<64>t find any better and faster
|
|||
|
return key % sAllocEntries;
|
|||
|
}
|
|||
|
virtual BOOL IsKeyEmpty(LONG &key)
|
|||
|
{
|
|||
|
if (key == 0)
|
|||
|
return TRUE;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
virtual VOID SetEmptyKey(LONG &key)
|
|||
|
{
|
|||
|
key = 0;
|
|||
|
}
|
|||
|
virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen)
|
|||
|
{
|
|||
|
#if _MSC_VER < 1400
|
|||
|
_snprintf_s(szName, nBufferLen, "%d", key);
|
|||
|
#else
|
|||
|
_snprintf_s(szName, nBufferLen, nBufferLen, "%d", key);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
protected:
|
|||
|
CHAR *m_pBuffer;
|
|||
|
SIZE_T m_maxBufferLen;
|
|||
|
SIZE_T m_bufferLen;
|
|||
|
}; // class CRTTable
|
|||
|
|
|||
|
|
|||
|
#define nNoMansLandSize 4
|
|||
|
|
|||
|
typedef struct _CrtMemBlockHeader
|
|||
|
{
|
|||
|
struct _CrtMemBlockHeader * pBlockHeaderNext;
|
|||
|
struct _CrtMemBlockHeader * pBlockHeaderPrev;
|
|||
|
char * szFileName;
|
|||
|
int nLine;
|
|||
|
#ifdef _WIN64
|
|||
|
/* These items are reversed on Win64 to eliminate gaps in the struct
|
|||
|
* and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
|
|||
|
* maintained in the debug heap.
|
|||
|
*/
|
|||
|
int nBlockUse;
|
|||
|
size_t nDataSize;
|
|||
|
#else /* _WIN64 */
|
|||
|
size_t nDataSize;
|
|||
|
int nBlockUse;
|
|||
|
#endif /* _WIN64 */
|
|||
|
long lRequest;
|
|||
|
unsigned char gap[nNoMansLandSize];
|
|||
|
/* followed by:
|
|||
|
* unsigned char data[nDataSize];
|
|||
|
* unsigned char anotherGap[nNoMansLandSize];
|
|||
|
*/
|
|||
|
} _CrtMemBlockHeader;
|
|||
|
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
|
|||
|
#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
|
|||
|
// </CRT_INTERNALS>
|
|||
|
|
|||
|
static CRTTable *g_pCRTTable = NULL;
|
|||
|
|
|||
|
|
|||
|
// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
|
|||
|
static int MyAllocHook(int nAllocType, void *pvData,
|
|||
|
size_t nSize, int nBlockUse, long lRequest,
|
|||
|
#if _MSC_VER <= 1100 // Special case for VC 5
|
|||
|
const char * szFileName,
|
|||
|
#else
|
|||
|
const unsigned char * szFileName,
|
|||
|
#endif
|
|||
|
int nLine)
|
|||
|
{
|
|||
|
//static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
|
|||
|
//static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
|
|||
|
|
|||
|
#ifdef IGNORE_CRT_ALLOC
|
|||
|
if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
|
|||
|
return TRUE;
|
|||
|
#endif
|
|||
|
extern int _crtDbgFlag;
|
|||
|
if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
|
|||
|
{
|
|||
|
// Someone has disabled that the runtime should log this allocation
|
|||
|
// so we do not log this allocation
|
|||
|
if (s_pfnOldCrtAllocHook != NULL)
|
|||
|
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
// Handle the Disable/Enable setting
|
|||
|
if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0)
|
|||
|
return TRUE;
|
|||
|
|
|||
|
// Prevent from reentrat calls
|
|||
|
if (InterlockedIncrement(&s_lMallocCalled) > 1) { // I was already called
|
|||
|
InterlockedDecrement(&s_lMallocCalled);
|
|||
|
// call the previous alloc hook
|
|||
|
if (s_pfnOldCrtAllocHook != NULL)
|
|||
|
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
_ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
|
|||
|
_ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
|
|||
|
|
|||
|
if (nAllocType == _HOOK_FREE) { // freeing
|
|||
|
// Try to get the header information
|
|||
|
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
|
|||
|
// get the ID
|
|||
|
_CrtMemBlockHeader *pHead;
|
|||
|
// get a pointer to memory block header
|
|||
|
pHead = pHdr(pvData);
|
|||
|
nSize = pHead->nDataSize;
|
|||
|
lRequest = pHead->lRequest; // This is the ID!
|
|||
|
|
|||
|
if (pHead->nBlockUse == _IGNORE_BLOCK)
|
|||
|
{
|
|||
|
InterlockedDecrement(&s_lMallocCalled);
|
|||
|
if (s_pfnOldCrtAllocHook != NULL)
|
|||
|
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
if (lRequest != 0) { // RequestID was found
|
|||
|
g_pCRTTable->Remove(lRequest);
|
|||
|
}
|
|||
|
} // freeing
|
|||
|
|
|||
|
if (nAllocType == _HOOK_REALLOC) { // re-allocating
|
|||
|
// Try to get the header information
|
|||
|
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
|
|||
|
BOOL bRet;
|
|||
|
LONG lReallocRequest;
|
|||
|
// get the ID
|
|||
|
_CrtMemBlockHeader *pHead;
|
|||
|
// get a pointer to memory block header
|
|||
|
pHead = pHdr(pvData);
|
|||
|
// Try to find the RequestID in the Hash-Table, mark it that it was freed
|
|||
|
lReallocRequest = pHead->lRequest;
|
|||
|
bRet = g_pCRTTable->Remove(lReallocRequest);
|
|||
|
} // ValidHeapPointer
|
|||
|
} // re-allocating
|
|||
|
|
|||
|
//if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
|
|||
|
if (nAllocType == _HOOK_FREE) {
|
|||
|
InterlockedDecrement(&s_lMallocCalled);
|
|||
|
// call the previous alloc hook
|
|||
|
if (s_pfnOldCrtAllocHook != NULL)
|
|||
|
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
CONTEXT c;
|
|||
|
GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
|
|||
|
|
|||
|
// Only insert in the Hash-Table if it is not a "freeing"
|
|||
|
if (nAllocType != _HOOK_FREE) {
|
|||
|
if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
|
|||
|
g_pCRTTable->Insert(lRequest, c, nSize);
|
|||
|
}
|
|||
|
|
|||
|
InterlockedDecrement(&s_lMallocCalled);
|
|||
|
// call the previous alloc hook
|
|||
|
if (s_pfnOldCrtAllocHook != NULL)
|
|||
|
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
|||
|
return TRUE; // allow the memory operation to proceed
|
|||
|
} // MyAllocHook
|
|||
|
|
|||
|
#endif // _DEBUG
|
|||
|
|
|||
|
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// Specialization for COM-Leaks:
|
|||
|
|
|||
|
// forwards:
|
|||
|
class COMTable;
|
|||
|
class CMallocSpy : public IMallocSpy
|
|||
|
{
|
|||
|
public:
|
|||
|
CMallocSpy() { m_cbRequest = 0; m_cRef = 0; m_disableCount = 0; }
|
|||
|
virtual ~CMallocSpy() {}
|
|||
|
// IUnknown methods
|
|||
|
STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk);
|
|||
|
STDMETHOD_(ULONG, AddRef) ();
|
|||
|
STDMETHOD_(ULONG, Release) ();
|
|||
|
// IMallocSpy methods
|
|||
|
STDMETHOD_(SIZE_T, PreAlloc) (SIZE_T cbRequest);
|
|||
|
STDMETHOD_(void *, PostAlloc) (void *pActual);
|
|||
|
STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed);
|
|||
|
STDMETHOD_(void, PostFree) (BOOL fSpyed) { return; };
|
|||
|
STDMETHOD_(SIZE_T, PreRealloc) (void *pRequest, SIZE_T cbRequest, void **ppNewRequest, BOOL fSpyed);
|
|||
|
STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed);
|
|||
|
STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) { return pRequest; }
|
|||
|
STDMETHOD_(SIZE_T, PostGetSize) (SIZE_T cbActual, BOOL fSpyed) { return cbActual; }
|
|||
|
STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) { return pRequest; }
|
|||
|
STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) { return fActual; }
|
|||
|
STDMETHOD_(void, PreHeapMinimize) (void) { return; }
|
|||
|
STDMETHOD_(void, PostHeapMinimize) (void) { return; }
|
|||
|
private:
|
|||
|
LONG m_cRef;
|
|||
|
SIZE_T m_cbRequest;
|
|||
|
protected:
|
|||
|
COMTable *m_pComTable;
|
|||
|
LONG m_disableCount;
|
|||
|
friend COMTable;
|
|||
|
};
|
|||
|
|
|||
|
class COMTable : public ContextHashtableBase<LPVOID>
|
|||
|
{
|
|||
|
public:
|
|||
|
COMTable() : ContextHashtableBase<LPVOID>(1021, "COM-Leaks")
|
|||
|
{
|
|||
|
m_pMallocSpy = new CMallocSpy(); // wird sp<73>ter durch Release freigegeben
|
|||
|
if (m_pMallocSpy != NULL)
|
|||
|
{
|
|||
|
m_pMallocSpy->m_pComTable = this;
|
|||
|
// CoInitilize(); // ??? Is this necessary ?
|
|||
|
HRESULT hr = CoRegisterMallocSpy(m_pMallocSpy);
|
|||
|
if FAILED(hr)
|
|||
|
{
|
|||
|
_tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
virtual ~COMTable()
|
|||
|
{
|
|||
|
if (m_pMallocSpy != NULL)
|
|||
|
m_pMallocSpy->m_pComTable = NULL;
|
|||
|
CoRevokeMallocSpy();
|
|||
|
}
|
|||
|
|
|||
|
virtual LONG Disable()
|
|||
|
{
|
|||
|
return InterlockedIncrement(&(m_pMallocSpy->m_disableCount));
|
|||
|
}
|
|||
|
virtual LONG Enable()
|
|||
|
{
|
|||
|
return InterlockedDecrement(&(m_pMallocSpy->m_disableCount));
|
|||
|
}
|
|||
|
|
|||
|
virtual SIZE_T HashFunction(LPVOID &key)
|
|||
|
{
|
|||
|
// I couldn<64>t find any better and faster
|
|||
|
#ifdef _M_IX86
|
|||
|
#if _MSC_VER > 1100
|
|||
|
#pragma warning (push)
|
|||
|
#endif
|
|||
|
#pragma warning (disable: 4311)
|
|||
|
DWORD llP = (DWORD) key;
|
|||
|
#if _MSC_VER > 1100
|
|||
|
#pragma warning (pop)
|
|||
|
#endif
|
|||
|
#else
|
|||
|
ULONGLONG llP = (ULONGLONG) key;
|
|||
|
#endif
|
|||
|
return (SIZE_T) llP % sAllocEntries;
|
|||
|
}
|
|||
|
virtual BOOL IsKeyEmpty(LPVOID &key)
|
|||
|
{
|
|||
|
if (key == 0)
|
|||
|
return TRUE;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
virtual VOID SetEmptyKey(LPVOID &key)
|
|||
|
{
|
|||
|
key = 0;
|
|||
|
}
|
|||
|
virtual VOID GetKeyAsString(LPVOID &key, CHAR *szName, SIZE_T nBufferLen)
|
|||
|
{
|
|||
|
#if _MSC_VER < 1400
|
|||
|
_snprintf_s(szName, nBufferLen, "%p", key);
|
|||
|
#else
|
|||
|
_snprintf_s(szName, nBufferLen, nBufferLen, "%p", key);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
CMallocSpy *m_pMallocSpy;
|
|||
|
friend CMallocSpy;
|
|||
|
}; // class COMTable
|
|||
|
|
|||
|
|
|||
|
STDMETHODIMP CMallocSpy::QueryInterface(REFIID riid, LPVOID *ppUnk) {
|
|||
|
HRESULT hr = S_OK;
|
|||
|
if (IsEqualIID(riid, IID_IUnknown)) {
|
|||
|
*ppUnk = (IUnknown *) this;
|
|||
|
}
|
|||
|
else if (IsEqualIID(riid, IID_IMallocSpy)) {
|
|||
|
*ppUnk = (IMalloc *) this;
|
|||
|
}
|
|||
|
else {
|
|||
|
*ppUnk = NULL;
|
|||
|
hr = E_NOINTERFACE;
|
|||
|
}
|
|||
|
AddRef();
|
|||
|
return hr;
|
|||
|
}
|
|||
|
STDMETHODIMP_(ULONG) CMallocSpy::AddRef(void) {
|
|||
|
return (ULONG) InterlockedIncrement(&m_cRef);
|
|||
|
}
|
|||
|
STDMETHODIMP_(ULONG) CMallocSpy::Release(void) {
|
|||
|
LONG cRef;
|
|||
|
cRef = InterlockedDecrement(&m_cRef);
|
|||
|
if (cRef == 0)
|
|||
|
{
|
|||
|
delete this;
|
|||
|
}
|
|||
|
return (ULONG) cRef;
|
|||
|
}
|
|||
|
// IMallocSpy methods
|
|||
|
STDMETHODIMP_(SIZE_T) CMallocSpy::PreAlloc(SIZE_T cbRequest) {
|
|||
|
m_cbRequest = cbRequest;
|
|||
|
return cbRequest;
|
|||
|
}
|
|||
|
STDMETHODIMP_(void *) CMallocSpy::PostAlloc(void *pActual) {
|
|||
|
if (m_pComTable != NULL)
|
|||
|
{
|
|||
|
CONTEXT c;
|
|||
|
GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
|
|||
|
m_pComTable->Insert(pActual, c, m_cbRequest);
|
|||
|
}
|
|||
|
return pActual;
|
|||
|
}
|
|||
|
STDMETHODIMP_(void *) CMallocSpy::PreFree(void *pRequest, BOOL fSpyed) {
|
|||
|
if (m_pComTable != NULL)
|
|||
|
{
|
|||
|
m_pComTable->Remove(pRequest);
|
|||
|
}
|
|||
|
return pRequest;
|
|||
|
}
|
|||
|
STDMETHODIMP_(SIZE_T) CMallocSpy::PreRealloc(void *pRequest, SIZE_T cbRequest,
|
|||
|
void **ppNewRequest, BOOL fSpyed) {
|
|||
|
if (m_pComTable != NULL)
|
|||
|
{
|
|||
|
m_pComTable->Remove(pRequest);
|
|||
|
}
|
|||
|
*ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber
|
|||
|
return cbRequest;
|
|||
|
}
|
|||
|
STDMETHODIMP_(void *) CMallocSpy::PostRealloc(void *pActual, BOOL fSpyed) {
|
|||
|
if (m_pComTable != NULL)
|
|||
|
{
|
|||
|
CONTEXT c;
|
|||
|
GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
|
|||
|
m_pComTable->Insert(pActual, c, m_cbRequest);
|
|||
|
}
|
|||
|
return pActual;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// ##########################################################################
|
|||
|
// Init/Deinit functions
|
|||
|
|
|||
|
|
|||
|
static COMTable *g_pCOMTable;
|
|||
|
HRESULT InitLeakFinder()
|
|||
|
{
|
|||
|
// _X: Disabled COM monitoring: g_pCOMTable = new COMTable();
|
|||
|
#ifdef _DEBUG
|
|||
|
g_pCRTTable = new CRTTable();
|
|||
|
#endif
|
|||
|
return S_OK;
|
|||
|
}
|
|||
|
|
|||
|
void DeinitLeakFinder(LeakFinderOutput *output)
|
|||
|
{
|
|||
|
LeakFinderOutput *pLeakFinderOutput = output;
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
g_pCRTTable->Disable();
|
|||
|
#endif
|
|||
|
// _X: Disabled COM monitoring: g_pCOMTable->Disable();
|
|||
|
|
|||
|
if (pLeakFinderOutput == NULL)
|
|||
|
pLeakFinderOutput = new LeakFinderOutput();
|
|||
|
|
|||
|
// explicite load the modules:
|
|||
|
pLeakFinderOutput->LoadModules();
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
g_pCRTTable->ShowLeaks(*pLeakFinderOutput);
|
|||
|
if (g_pCRTTable != NULL)
|
|||
|
delete g_pCRTTable;
|
|||
|
g_pCRTTable = NULL;
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
// _X: Disabled COM monitoring:
|
|||
|
g_pCOMTable->ShowLeaks(*pLeakFinderOutput);
|
|||
|
if (g_pCOMTable != NULL)
|
|||
|
delete g_pCOMTable;
|
|||
|
g_pCOMTable = NULL;
|
|||
|
*/
|
|||
|
|
|||
|
if (output == NULL)
|
|||
|
delete pLeakFinderOutput;
|
|||
|
}
|
|||
|
void DeinitLeakFinder()
|
|||
|
{
|
|||
|
DeinitLeakFinder(NULL);
|
|||
|
}
|