当前位置:网站首页>VC开发非MFC程序内存泄漏跟踪代码

VC开发非MFC程序内存泄漏跟踪代码

2022-07-05 13:56:00 joshua0137

在可执行模块源码中重写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("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, "%016I64X: ", 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);

		strStack[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
}

原网站

版权声明
本文为[joshua0137]所创,转载请带上原文链接,感谢
https://blog.csdn.net/joshua0137/article/details/125587567