重写可执行模块源码new和delete函数,插入跟踪代码,在程序最终t退出前保存统计信息。以下代码基本上可以解决问题,但程序运行缓慢,可以作为测试来满足要求。
namespace foundation { std::string MemleakNewDump() { DWORD id = ::GetCurrentThreadId(); HANDLE h = OpenThread( THREAD_GET_CONTEXT, TRUE, id ); //获得真实句柄 std::string strStack; std::thread th([&] { CONTEXT ctx = { 0 }; ctx.ContextFlags = CONTEXT_ALL; ::SuspendThread(h); ::GetThreadContext(h, &ctx); MINIDUMP_EXCEPTION_INFORMATION eInfo; EXCEPTION_POINTERS excpInfo; excpInfo.ExceptionRecord = NULL; excpInfo.ContextRecord = &ctx; eInfo.ThreadId = GetCurrentThreadId(); eInfo.ExceptionPointers = &excpInfo; eInfo.ClientPointers = FALSE; strStack.clear(); // Initialize stack frame STACKFRAME64 sf; memset(&sf, 0, sizeof(STACKFRAME)); #if defined(_WIN64) sf.AddrPC.Offset = ctx.Rip; sf.AddrStack.Offset = ctx.Rsp; sf.AddrFrame.Offset = ctx.Rbp; #elif defined(WIN32) sf.AddrPC.Offset = ctx.Eip; sf.AddrStack.Offset = ctx.Esp; sf.AddrFrame.Offset = ctx.Ebp; #endif sf.AddrPC.Mode = AddrModeFlat; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Mode = AddrModeFlat; DWORD _dwMachineType = 0; char* chArchVar; size_t requiredSize; getenv_s(&requiredSize, NULL, 0, "PROCESSOR_ARCHITECTURE"); chArchVar = (char*)malloc(requiredSize * sizeof(char)); getenv_s(&requiredSize, chArchVar, requiredSize, "PROCESSOR_ARCHITECTURE"); if (chArchVar) { if ((!strcmp("EM64T", chArchVar)) || !strcmp("EM64T", chArchVar)) || !strcmp("AMD64", chArchVar)) _dwMachineType = IMAGE_FILE_MACHINE_AMD64; else if (!strcmp("x86", chArchVar)) _dwMachineType = IMAGE_FILE_MACHINE_I386; } free(chArchVar); if (0 == _dwMachineType) return; DWORD _dwCode = 0; int _nTableCount = sizeof(g_ExceptDescTable) / sizeof(g_ExceptDescTable[0]); bool _bFind = false; for (int _i = 0; _i < _nTableCount; _i) { if (_dwCode == g_ExceptDescTable[_i].dwCode) { strStack = g_ExceptDescTable[_i].szDesc; strStack = "\r\n"; _bFind = true; break; } } char _sz[256]; if (!_bFind) { sprintf_s(_sz, "cxx except code: 0x%x\r\n", _dwCode); strStack = _sz; } // Walk through the stack frames. HANDLE hProcess = GetCurrentProcess(); HANDLE hThread = GetCurrentThread(); if (!SymInitialize(hProcess, NULL, TRUE)) { SymCleanup(hProcess); return; } while (StackWalk64(_dwMachineType, hProcess, hThread, &sf, &ctx, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { if (sf.AddrFrame.Offset == 0) break; // 1. Get function name at the address const int nBuffSize = (sizeof(SYMBOL_INFO) MAX_SYM_NAME * sizeof(TCHAR) sizeof(ULONG64) - 1) / sizeof(ULONG64); ULONG64 symbolBuffer[nBuffSize]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; FunctionCall curCall; curCall.FunctionName = ""; curCall.FileName = ""; curCall.LineNumber = 0; DWORD64 dwSymDisplacement = 0; if (SymFromAddr(hProcess, sf.AddrPC.Offset, &dwSymDisplacement, pSymbol)) { curCall.FunctionName = pSymbol->Name; } //2. get line and file name at the address IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) }; DWORD dwLineDisplacement = 0; if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo)) { curCall.FileName = (lineInfo.FileName); curCall.LineNumber = lineInfo.LineNumber; } CStackDumper::_ATL_SYMBOL_INFO info; sprintf_s(_sz, "6I64X: ", sf.AddrPC.Offset); strStack = _sz; if (CStackDumper::ResolveSymbol(hProcess, UINT_PTR(sf.AddrPC.Offset), info)) { strStack = info.szModule; strStack = " "; strStack = info.szSymbol; strStack = "\r\n"; } else strStack = "symbol not found"; strStack = "File: "; strStack = curCall.FileName; strStack = "\r\n"; strStack = "Func: "; strStack = curCall.FunctionName; strStack = "\r\n"; sprintf_s(_sz, "Line: %d", curCall.LineNumber); strStack = _sz; strStack = "\r\n\r\n"; } SymCleanup(hProcess); ::ResumeThread(h); ::CloseHandle(h); }); th.join(); return strStack; } struct myMap { const void* pData; char* sz = NULL; bool deleted = false; }; static std::atomic<int> mmk = 0; myMap* mmmap = NULL;; std::mutex mtx; HANDLE hProcess = NULL; struct TWk { const void* block; HANDLE h; }; DWORD lastThreadID = 0; char strStack[8192 * 2] = { 0 }; bool inAlloc = true; bool userCreateThreadEnvNotReady = true; void allocStackString(HANDLE h, const void* block) { EnterCriticalSection(&csMemLeakDump); strtack[0] = 0;
CONTEXT ctx = { 0 };
ctx.ContextFlags = CONTEXT_ALL;
strStack[0] = 0;
::SuspendThread(h);
::GetThreadContext(h, &ctx);
MINIDUMP_EXCEPTION_INFORMATION eInfo;
EXCEPTION_POINTERS excpInfo;
excpInfo.ExceptionRecord = NULL;
excpInfo.ContextRecord = &ctx;
eInfo.ThreadId = GetCurrentThreadId();
eInfo.ExceptionPointers = &excpInfo;
eInfo.ClientPointers = FALSE;
// Initialize stack frame
STACKFRAME64 sf;
memset(&sf, 0, sizeof(STACKFRAME));
#if defined(_WIN64)
sf.AddrPC.Offset = ctx.Rip;
sf.AddrStack.Offset = ctx.Rsp;
sf.AddrFrame.Offset = ctx.Rbp;
#elif defined(WIN32)
sf.AddrPC.Offset = ctx.Eip;
sf.AddrStack.Offset = ctx.Esp;
sf.AddrFrame.Offset = ctx.Ebp;
#endif
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Mode = AddrModeFlat;
DWORD _dwMachineType = 0;
char* chArchVar;
size_t requiredSize;
getenv_s(&requiredSize, NULL, 0, "PROCESSOR_ARCHITECTURE");
chArchVar = (char*)malloc(requiredSize * sizeof(char));
getenv_s(&requiredSize, chArchVar, requiredSize, "PROCESSOR_ARCHITECTURE");
if (chArchVar)
{
if ((!strcmp("EM64T", chArchVar)) || !strcmp("AMD64", chArchVar))
_dwMachineType = IMAGE_FILE_MACHINE_AMD64;
else if (!strcmp("x86", chArchVar))
_dwMachineType = IMAGE_FILE_MACHINE_I386;
}
free(chArchVar);
if (0 == _dwMachineType)
{
inAlloc = false;
LeaveCriticalSection(&csMemLeakDump);
if (-1 != ::ResumeThread(h))
::CloseHandle(h);
return;
}
char _sz[256];
bool xmemory = false;
if (!SymInitialize(hProcess, NULL, TRUE))
{
printf("%s", strStack);
SymCleanup(hProcess);
inAlloc = false;
if (-1 != ::ResumeThread(h))
::CloseHandle(h);
LeaveCriticalSection(&csMemLeakDump);
return;
}
while (StackWalk64(_dwMachineType, hProcess, h, &sf, &ctx, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0))
{
if (sf.AddrFrame.Offset == 0)
break;
// 1. Get function name at the address
const int nBuffSize = (sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64);
ULONG64 symbolBuffer[nBuffSize];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
FunctionCall_2 curCall;
strcpy(curCall.FunctionName, "");
strcpy(curCall.FileName, "");
curCall.LineNumber = 0;
DWORD64 dwSymDisplacement = 0;
if (SymFromAddr(hProcess, sf.AddrPC.Offset, &dwSymDisplacement, pSymbol))
{
strcpy(curCall.FunctionName, pSymbol->Name);
}
//2. get line and file name at the address
IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
DWORD dwLineDisplacement = 0;
if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
{
strcpy(curCall.FileName, (lineInfo.FileName));
curCall.LineNumber = lineInfo.LineNumber;
}
CStackDumper::_ATL_SYMBOL_INFO info;
sprintf_s(_sz, "%016I64X: ", sf.AddrPC.Offset);
strcat(strStack, _sz);
if (CStackDumper::ResolveSymbol(hProcess, UINT_PTR(sf.AddrPC.Offset), info))
{
strcat(strStack, info.szModule);
strcat(strStack, " ");
strcat(strStack, info.szSymbol);
strcat(strStack, "\r\n");
}
else
strcat(strStack, "symbol not found");
strcat(strStack, "File: ");
strcat(strStack, curCall.FileName);
int n = strlen(curCall.FileName);
if (n >= 7)
{
char* p = curCall.FileName + n - 7;
if (!strcmp(p, "xmemory")) //std::string need this
xmemory = true;
}
strcat(strStack, "\r\n");
strcat(strStack, "Func: ");
strcat(strStack, curCall.FunctionName);
strcat(strStack, "\r\n");
sprintf_s(_sz, "Line: %d", curCall.LineNumber);
strcat(strStack, _sz);
strcat(strStack, "\r\n\r\n");
}
if (mmk < 102400 * 4 - 2 && strStack[0] && !xmemory)
{
(mmmap + mmk)->pData = block;
(mmmap + mmk)->deleted = false;
(mmmap + mmk)->sz = (char*)malloc(strlen(strStack) + 4);
strcpy((mmmap + mmk)->sz, strStack);
mmk++;
}
SymCleanup(hProcess);
if (-1 != ::ResumeThread(h))
::CloseHandle(h);
LeaveCriticalSection(&csMemLeakDump);
inAlloc = false;
}
void MemleakNewDump(const void* block)
{
if (hProcess == NULL)
{
InitializeCriticalSection(&csMemLeakDump);
InitializeCriticalSection(&csMemLeakFree);
}
hProcess = GetCurrentProcess();
if (mmmap == NULL)
{
mmmap = (myMap*)malloc(102400 * 4 * sizeof(myMap));
memset(mmmap, 0, 102400 * 4 * sizeof(myMap));
}
DWORD id = ::GetCurrentThreadId();
if (lastThreadID == id)
return;
lastThreadID = id;
HANDLE h = OpenThread(
THREAD_GET_CONTEXT,
TRUE,
id
); //获得真实句柄
if (h == INVALID_HANDLE_VALUE)
return;
std::thread th([&] {
allocStackString(h, block);
});
inAlloc = true;
if (userCreateThreadEnvNotReady)
{
th.detach();
int n = 20;
while (inAlloc && n--)
Sleep(60);
if (n > 0)
userCreateThreadEnvNotReady = false;
else
allocStackString(h, block);
}
else
th.join();
lastThreadID = 0;
}
void dumpMemLeak()
{
if (!mmmap)
return;
SYSTEMTIME st;
GetLocalTime(&st);
char sz[MAX_PATH + 4];
sprintf(sz, "memLeak-%d-%02d-%02d=%02d-%02d-%02d.txt", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
FILE* f = NULL;
int cnt = 0;
for (int i = 0; i < 102400 * 4 - 2; i++)
{
if (!(mmmap + i)->sz || (mmmap + i)->deleted)
continue;
printf("\n\n=============== %d =====================\n", ++cnt);
printf("%s", (mmmap + i)->sz);
if (f == NULL)
f = fopen(sz, "w");
fprintf(f, "\n\n=============== %d =====================\n", cnt);
fprintf(f, "%s", (mmmap + i)->sz);
}
if (f != NULL)
fclose(f);
}
void memLeakFree(void* db)
{
EnterCriticalSection(&csMemLeakFree);
for (int i = 0; i < 102400 * 4 - 2; i++)
{
if (db == (mmmap + i)->pData)
{
(mmmap + i)->deleted = true;
break;
}
}
LeaveCriticalSection(&csMemLeakFree);
}
};
//#define FOUNDATION_MEMLEAK_DETECT
void* __cdecl operator new(size_t const size)
{
for (;;)
{
if (void* const block = malloc(size))
{
#ifdef FOUNDATION_MEMLEAK_DETECT
foundation::TriggerDump(block);
#endif
return block;
}
if (_callnewh(size) == 0)
{
if (size == SIZE_MAX)
{
throw std::exception("bad alloc, SIZE_MAX");
}
else
{
throw std::exception("bad alloc");
}
}
}
}
void __cdecl operator delete(void* p)
{
#if !defined(_ATL_NO_DEBUG_CRT) && defined(_DEBUG)
_free_dbg(p, _NORMAL_BLOCK);
#else
free(p);
#endif
#ifdef FOUNDATION_MEMLEAK_DETECT
foundation::memLeakFree(p);
#endif
}