From 55ff2558d7a88cba8ba9c1af9c4c26831aaadb82 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 23 Mar 2013 19:04:39 +0000 Subject: 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 --- source/LeakFinder.cpp | 221 +++++++++++++++++++++++++++++++------------------- source/LeakFinder.h | 3 +- source/Server.cpp | 8 ++ 3 files changed, 147 insertions(+), 85 deletions(-) diff --git a/source/LeakFinder.cpp b/source/LeakFinder.cpp index 2702fb47a..aa7848b65 100644 --- a/source/LeakFinder.cpp +++ b/source/LeakFinder.cpp @@ -252,7 +252,8 @@ LeakFinderXmlOutput::LeakFinderXmlOutput() -LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) +LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) : + m_Progress(10) { #if _MSC_VER < 1400 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); } + else + { + fprintf(m_fXmlFile, "\n"); + } } @@ -297,7 +302,12 @@ void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) { if (m_fXmlFile != NULL) { - fprintf(m_fXmlFile, " \n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); + fprintf(m_fXmlFile, "\t\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) { - fprintf(m_fXmlFile, " \n"); } else { - fprintf(m_fXmlFile, " \n"); + fprintf(m_fXmlFile, "\t\n"); } } } @@ -754,6 +764,11 @@ typedef struct _CrtMemBlockHeader 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! 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 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; + 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; + } - // 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; - } + // Handle the Disable/Enable setting + if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) + { + return TRUE; + } - _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); - _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); + // 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; + } - 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! + _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); + _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); - 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; - } + 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_CurrentMemUsage -= nSize; + 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; + g_CurrentMemUsage -= pHead->nDataSize; + 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); + 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); - } + // 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_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); - // 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 + 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 diff --git a/source/LeakFinder.h b/source/LeakFinder.h index 42af4f910..e63b9ec5d 100644 --- a/source/LeakFinder.h +++ b/source/LeakFinder.h @@ -98,7 +98,8 @@ protected: virtual void OnOutput(LPCSTR szText) { } virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } - FILE *m_fXmlFile; + FILE * m_fXmlFile; + int m_Progress; }; // C++ interface: diff --git a/source/Server.cpp b/source/Server.cpp index 24d999b31..ce631f754 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -452,6 +452,14 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd) DumpUsedMemory(&Output); 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 if (cPluginManager::Get()->ExecuteConsoleCommand(split)) -- cgit v1.2.3