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:
parent
40bbe4df3a
commit
55ff2558d7
@ -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;
|
||||||
|
|
||||||
InterlockedDecrement(&s_lMallocCalled);
|
if (g_CurrentMemUsage > 1024 * 1024 * 1024)
|
||||||
// call the previous alloc hook
|
{
|
||||||
if (s_pfnOldCrtAllocHook != NULL)
|
printf("******************************************\n");
|
||||||
s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
|
printf("** Server reached 1 GiB memory usage, **\n");
|
||||||
return TRUE; // allow the memory operation to proceed
|
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);
|
||||||
|
// 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
|
} // MyAllocHook
|
||||||
|
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
|
@ -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:
|
||||||
|
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user