1
0

Use leakfinder in debug builds to dump all currently used memory via the "dumpmem" console command.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@984 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com 2012-10-19 22:09:33 +00:00
parent 6a5ebcd6ca
commit d2780443c6
4 changed files with 143 additions and 218 deletions

View File

@ -129,6 +129,10 @@
#define _tcscat_s _tcscat
#endif
static std::string SimpleXMLEncode(LPCSTR szText)
{
std::string szRet;
@ -159,22 +163,40 @@ static std::string SimpleXMLEncode(LPCSTR szText)
}
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) )
@ -195,6 +217,9 @@ void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry
}
// ####################################################################
// XML-Output
LeakFinderXmlOutput::LeakFinderXmlOutput()
@ -222,6 +247,11 @@ LeakFinderXmlOutput::LeakFinderXmlOutput()
MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
}
}
LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName)
{
#if _MSC_VER < 1400
@ -235,6 +265,11 @@ LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName)
MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
}
}
LeakFinderXmlOutput::~LeakFinderXmlOutput()
{
if (m_fXmlFile != NULL)
@ -245,9 +280,19 @@ LeakFinderXmlOutput::~LeakFinderXmlOutput()
}
m_fXmlFile = NULL;
}
void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName)
{
}
void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
{
if (m_fXmlFile != NULL)
@ -255,6 +300,11 @@ void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
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)
@ -273,6 +323,10 @@ void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEn
}
}
// ##########################################################################
// ##########################################################################
// ##########################################################################
@ -567,6 +621,9 @@ public:
}; // template <typename HASHTABLE_KEY> class ContextHashtableBase
// ##########################################################################
// ##########################################################################
// ##########################################################################
@ -802,186 +859,6 @@ static int MyAllocHook(int nAllocType, void *pvData,
#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ä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´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;
}
@ -990,51 +867,67 @@ STDMETHODIMP_(void *) CMallocSpy::PostRealloc(void *pActual, BOOL fSpyed) {
// ##########################################################################
// 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;
#ifdef _DEBUG
g_pCRTTable = new CRTTable();
#endif
return S_OK;
}
void DumpUsedMemory(LeakFinderOutput * output)
{
LeakFinderOutput *pLeakFinderOutput = output;
#ifdef _DEBUG
g_pCRTTable->Disable();
#endif
if (pLeakFinderOutput == NULL)
{
pLeakFinderOutput = new LeakFinderOutput();
}
// explicitly load the modules:
pLeakFinderOutput->LoadModules();
#ifdef _DEBUG
g_pCRTTable->ShowLeaks(*pLeakFinderOutput);
#endif
if (output == NULL)
{
delete pLeakFinderOutput;
}
}
void DeinitLeakFinder(LeakFinderOutput *output)
{
LeakFinderOutput *pLeakFinderOutput = output;
DumpUsedMemory(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;
#ifdef _DEBUG
delete g_pCRTTable;
g_pCRTTable = NULL;
#endif
}
void DeinitLeakFinder()
{
DeinitLeakFinder(NULL);
DeinitLeakFinder(NULL);
}

View File

@ -143,3 +143,13 @@ ZZZ_LeakFinder zzz_LeakFinder;
#endif
#endif // __cplusplus
extern void DumpUsedMemory(LeakFinderOutput * output = NULL);

View File

@ -38,6 +38,20 @@ extern "C" {
// For the "dumpmem" server command:
/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
#define ENABLE_LEAK_FINDER
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
#pragma warning(push)
#pragma warning(disable:4100)
#include "LeakFinder.h"
#pragma warning(pop)
#endif
typedef std::list< cClientHandle* > ClientList;
@ -528,6 +542,15 @@ void cServer::ServerCommand(const AString & a_Cmd)
return;
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
if (split[0].compare("dumpmem") == 0)
{
LeakFinderXmlOutput Output("memdump.xml");
DumpUsedMemory(&Output);
return;
}
#endif
if (split.size() > 1)
{
if (split[0].compare("say") == 0)

View File

@ -27,7 +27,6 @@
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
#define XML_LEAK_FINDER
#pragma warning(push)
#pragma warning(disable:4100)
#include "LeakFinder.h"