1
0

Added auto-kill-with-dump to LeakFinder if the allocated memory grows over 1 GiB.

Only works on Windows and only present in Debug mode. Produces memdump.xml for analysis.
http://forum.mc-server.org/showthread.php?tid=826&pid=6948#pid6948

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1302 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com 2013-03-23 19:04:39 +00:00
parent 40bbe4df3a
commit 55ff2558d7
3 changed files with 144 additions and 82 deletions

View File

@ -252,7 +252,8 @@ LeakFinderXmlOutput::LeakFinderXmlOutput()
LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) :
m_Progress(10)
{ {
#if _MSC_VER < 1400 #if _MSC_VER < 1400
m_fXmlFile = _tfopen(szFileName, _T("w")); m_fXmlFile = _tfopen(szFileName, _T("w"));
@ -264,6 +265,10 @@ LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName)
{ {
MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
} }
else
{
fprintf(m_fXmlFile, "<MEMREPORT>\n");
}
} }
@ -297,7 +302,12 @@ void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
{ {
if (m_fXmlFile != NULL) if (m_fXmlFile != NULL)
{ {
fprintf(m_fXmlFile, " <LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); fprintf(m_fXmlFile, "\t<LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize);
}
if (--m_Progress == 0)
{
m_Progress = 100;
putc('.', stdout);
} }
} }
@ -311,14 +321,14 @@ void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEn
{ {
if (eType != lastEntry) if (eType != lastEntry)
{ {
fprintf(m_fXmlFile, " <STACKENTRY decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(entry.undName).c_str(), entry.offsetFromSmybol); fprintf(m_fXmlFile, "\t\t<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, "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, "module=\"%s\" base=\"%08lx\" ", SimpleXMLEncode(entry.moduleName).c_str(), entry.baseOfImage);
fprintf(m_fXmlFile, "/>\n"); fprintf(m_fXmlFile, "/>\n");
} }
else else
{ {
fprintf(m_fXmlFile, " </LEAK>\n"); fprintf(m_fXmlFile, "\t</LEAK>\n");
} }
} }
} }
@ -754,6 +764,11 @@ typedef struct _CrtMemBlockHeader
static CRTTable *g_pCRTTable = NULL; static CRTTable *g_pCRTTable = NULL;
size_t g_CurrentMemUsage = 0;
// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function! // MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
static int MyAllocHook(int nAllocType, void *pvData, static int MyAllocHook(int nAllocType, void *pvData,
@ -772,93 +787,131 @@ static int MyAllocHook(int nAllocType, void *pvData,
if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
return TRUE; return TRUE;
#endif #endif
extern int _crtDbgFlag; extern int _crtDbgFlag;
if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) ) if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
{ {
// Someone has disabled that the runtime should log this allocation // Someone has disabled that the runtime should log this allocation
// so we do not log this allocation // so we do not log this allocation
if (s_pfnOldCrtAllocHook != NULL) if (s_pfnOldCrtAllocHook != NULL)
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE; return TRUE;
} }
// Handle the Disable/Enable setting // Handle the Disable/Enable setting
if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0)
return TRUE; {
return TRUE;
}
// Prevent from reentrat calls // Prevent from reentrat calls
if (InterlockedIncrement(&s_lMallocCalled) > 1) { // I was already called if (InterlockedIncrement(&s_lMallocCalled) > 1)
InterlockedDecrement(&s_lMallocCalled); {
// call the previous alloc hook // I was already called
if (s_pfnOldCrtAllocHook != NULL) InterlockedDecrement(&s_lMallocCalled);
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); // call the previous alloc hook
return TRUE; 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( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
_ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
if (nAllocType == _HOOK_FREE) { // freeing if (nAllocType == _HOOK_FREE)
// Try to get the header information {
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer // freeing
// get the ID // Try to get the header information
_CrtMemBlockHeader *pHead; if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
// get a pointer to memory block header // get the ID
pHead = pHdr(pvData); _CrtMemBlockHeader *pHead;
nSize = pHead->nDataSize; // get a pointer to memory block header
lRequest = pHead->lRequest; // This is the ID! pHead = pHdr(pvData);
nSize = pHead->nDataSize;
lRequest = pHead->lRequest; // This is the ID!
if (pHead->nBlockUse == _IGNORE_BLOCK) if (pHead->nBlockUse == _IGNORE_BLOCK)
{ {
InterlockedDecrement(&s_lMallocCalled); InterlockedDecrement(&s_lMallocCalled);
if (s_pfnOldCrtAllocHook != NULL) if (s_pfnOldCrtAllocHook != NULL)
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); {
return TRUE; s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
} }
} return TRUE;
if (lRequest != 0) { // RequestID was found }
g_pCRTTable->Remove(lRequest); }
} if (lRequest != 0)
} // freeing {
// RequestID was found
g_CurrentMemUsage -= nSize;
g_pCRTTable->Remove(lRequest);
}
} // freeing
if (nAllocType == _HOOK_REALLOC) { // re-allocating if (nAllocType == _HOOK_REALLOC)
// Try to get the header information {
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer // re-allocating
BOOL bRet; // Try to get the header information
LONG lReallocRequest; if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
// get the ID BOOL bRet;
_CrtMemBlockHeader *pHead; LONG lReallocRequest;
// get a pointer to memory block header // get the ID
pHead = pHdr(pvData); _CrtMemBlockHeader *pHead;
// Try to find the RequestID in the Hash-Table, mark it that it was freed // get a pointer to memory block header
lReallocRequest = pHead->lRequest; pHead = pHdr(pvData);
bRet = g_pCRTTable->Remove(lReallocRequest); // Try to find the RequestID in the Hash-Table, mark it that it was freed
} // ValidHeapPointer lReallocRequest = pHead->lRequest;
} // re-allocating g_CurrentMemUsage -= pHead->nDataSize;
bRet = g_pCRTTable->Remove(lReallocRequest);
} // ValidHeapPointer
} // re-allocating
//if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) { //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
if (nAllocType == _HOOK_FREE) { if (nAllocType == _HOOK_FREE)
InterlockedDecrement(&s_lMallocCalled); {
// call the previous alloc hook InterlockedDecrement(&s_lMallocCalled);
if (s_pfnOldCrtAllocHook != NULL) // call the previous alloc hook
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); if (s_pfnOldCrtAllocHook != NULL)
return TRUE; {
} s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
}
return TRUE;
}
CONTEXT c; CONTEXT c;
GET_CURRENT_CONTEXT(c, CONTEXT_FULL); GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
// Only insert in the Hash-Table if it is not a "freeing" // Only insert in the Hash-Table if it is not a "freeing"
if (nAllocType != _HOOK_FREE) { 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); if (lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
} {
g_CurrentMemUsage += nSize;
if (g_CurrentMemUsage > 1024 * 1024 * 1024)
{
printf("******************************************\n");
printf("** Server reached 1 GiB memory usage, **\n");
printf("** something is probably wrong. **\n");
printf("** Writing memory dump into memdump.xml **\n");
printf("******************************************\n");
printf("Please wait\n");
LeakFinderXmlOutput Output("memdump.xml");
DumpUsedMemory(&Output);
printf("\nMemory dump complete. Server will now abort.\n");
abort();
}
g_pCRTTable->Insert(lRequest, c, nSize);
}
}
InterlockedDecrement(&s_lMallocCalled); InterlockedDecrement(&s_lMallocCalled);
// call the previous alloc hook // call the previous alloc hook
if (s_pfnOldCrtAllocHook != NULL) if (s_pfnOldCrtAllocHook != NULL)
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE; // allow the memory operation to proceed return TRUE; // allow the memory operation to proceed
} // MyAllocHook } // MyAllocHook
#endif // _DEBUG #endif // _DEBUG

View File

@ -98,7 +98,8 @@ protected:
virtual void OnOutput(LPCSTR szText) { } virtual void OnOutput(LPCSTR szText) { }
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { }
FILE *m_fXmlFile; FILE * m_fXmlFile;
int m_Progress;
}; };
// C++ interface: // C++ interface:

View File

@ -452,6 +452,14 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd)
DumpUsedMemory(&Output); DumpUsedMemory(&Output);
return; return;
} }
if (split[0].compare("killmem") == 0)
{
while (true)
{
new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS
}
}
#endif #endif
if (cPluginManager::Get()->ExecuteConsoleCommand(split)) if (cPluginManager::Get()->ExecuteConsoleCommand(split))