【内存泄漏检测】调式运行时堆的多模块检测工具

这篇具有很好参考价值的文章主要介绍了【内存泄漏检测】调式运行时堆的多模块检测工具。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

//.h
#pragma once
/*
* 开启调式模式 (定义了_DEBUG)
* 配置进程工作目录下的MemCheckConfig目录中的memConfig.ini 文件,这个文件的配置对所有检测模块生效
* 把MemSentry.h和MemSentry.cpp 文件添加到想要进行内存泄漏检测的模块的项目工程中
* 如果要同时检测一个进程的多个模块的内存情况,就需要在每个模块的工程中添加.h .cpp
* 在当前进程工作目录下 会为每一个被检测的模块生成一个内存日志文件
*/
#include<iostream>
#include<Windows.h>
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <psapi.h>


typedef enum _THREADINFOCLASS {
	ThreadBasicInformation,
	ThreadTimes,
	ThreadPriority,
	ThreadBasePriority,
	ThreadAffinityMask,
	ThreadImpersonationToken,
	ThreadDescriptorTableEntry,
	ThreadEnableAlignmentFaultFixup,
	ThreadEventPair_Reusable,
	ThreadQuerySetWin32StartAddress,
	ThreadZeroTlsCell,
	ThreadPerformanceCount,
	ThreadAmILastThread,
	ThreadIdealProcessor,
	ThreadPriorityBoost,
	ThreadSetTlsArrayAddress,
	ThreadIsIoPending,
	ThreadHideFromDebugger,
	ThreadBreakOnTermination,
	MaxThreadInfoClass
}THREADINFOCLASS;
typedef struct _CLIENT_ID {
	HANDLE UniqueProcess;
	HANDLE UniqueThread;
}CLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION {
	LONG ExitStatus;
	PVOID TebBaseAddress;
	CLIENT_ID ClientId;
	LONG AffinityMask;
	LONG Priority;
	LONG BasePriority;
}THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
using ZwQueryInformationThread=  LONG(__stdcall * )(
	IN HANDLE ThreadHandle,
	IN THREADINFOCLASS ThreadInformationClass,
	OUT PVOID ThreadInformation,
	IN ULONG ThreadInformationLength,
	OUT PULONG ReturnLength OPTIONAL
	) ;

#ifdef _DEBUG
#define DBG_NEW new ( _CLIENT_BLOCK , __FILE__ , __LINE__ )
#define new  DBG_NEW
#else
#define DBG_NEW new
#endif

#define KB 1024
#define MB (KB*KB)
#define MEMINIFILENAME "memConfig.ini"

typedef struct _HOOK_PRO_FILE
{
	BOOL bViewBlock;	//是否显示内存块信息
	BOOL bClientHook;	//是否挂钩客户块
	BOOL bAllocHook;	//是否分配挂钩
	size_t nMemlimit;	//警戒内存水位
	HANDLE hLogFile;	//日志文件
	TCHAR szlogfilename[MAX_PATH];			//reserve

	_HOOK_PRO_FILE()
	{
		hLogFile = NULL;
		memset(szlogfilename, 0, MAX_PATH);
	}
}HOOK_PRO_FILE,*PHOOK_PRO_FILE;


class MemLog
{
public:
	MemLog();
	
	~MemLog()
	{
		CloseHandle(MemConfigInfo.hLogFile);
		MemConfigInfo.hLogFile = NULL;
	}
	PHOOK_PRO_FILE GetConfig()
	{
		return &MemConfigInfo;
	}

	void init()
	{
		MemConfigInfo.bViewBlock = GetMemCheckConfigInt("MemPro", "ObserveBlock", 0, MEMINIFILENAME);
		MemConfigInfo.bClientHook = GetMemCheckConfigInt("MemPro", "ClientHook", 0, MEMINIFILENAME);
		MemConfigInfo.bAllocHook = GetMemCheckConfigInt("MemPro", "bAllocHook", 0, MEMINIFILENAME);
		MemConfigInfo.nMemlimit = GetMemCheckConfigInt("MemPro", "Memlimit", 800, MEMINIFILENAME);
	}

	int GetMemCheckConfigInt(LPCSTR lpszField, LPCSTR lpszKey, int nDefault, LPCSTR lpszmoduleName)
	{
		char szDirectory[MAX_PATH] = "";
		GetCurrentDirectory(MAX_PATH, szDirectory);
		char cmodulefigFilePath[MAX_PATH] = "";
		sprintf(cmodulefigFilePath, "%s\\MemCheckConfig\\%s", szDirectory, lpszmoduleName);
		return GetPrivateProfileInt(lpszField, lpszKey, nDefault, cmodulefigFilePath);
	}

	DWORD GetMemCheckConfigStr(LPCSTR lpszField, LPCSTR lpszKey, LPSTR lpszValue, DWORD dwMaxLen, LPCSTR lpszDefault, LPCSTR lpszmoduleName)
	{
		char szDirectory[MAX_PATH] = "";
		GetCurrentDirectory(MAX_PATH, szDirectory);
		char cmodulefigFilePath[MAX_PATH] = "";
		sprintf(cmodulefigFilePath, "%s\\MemCheckConfig\\%s", szDirectory, lpszmoduleName);
		return GetPrivateProfileString(lpszField, lpszKey, lpszDefault, lpszValue, dwMaxLen, cmodulefigFilePath);
	}

	void GetCurrentModule(TCHAR* reallymodname);
	
	

	HOOK_PRO_FILE MemConfigInfo;
};
/*
* 二种方法 观测堆内存 调用 watch 或者定义局部 CMemSentry对象
* CMemSentry是一个调式版内存块观查器,观测当前模块的运行时堆; 
* 若设置了局部哨兵对象,但是报告文件中没有出现那次的历史记录,就说明局部观测点的析构是没有被调用;侧面说明了卡主,崩溃问题 可能就出现在那个本该出现的局部观测点位置
*/

class CMemSentry
{
	
public:
	CMemSentry() 
	{
		static MemLog logob;
		if (!pMemConfig)
			pMemConfig = logob.GetConfig();	
		_CrtMemCheckpoint(&mBeg);
	}
	//析构时查看当前模块内存泄漏情况
	~CMemSentry()
	{
		_CrtMemCheckpoint(&mEnd);
		if (_CrtMemDifference(&mResult, &mBeg, &mEnd))
		{
			//内存块分析
			if (pMemConfig&& pMemConfig->bViewBlock)
				_CrtMemDumpStatistics(&mResult);
			//内存泄漏转储
			_CrtDumpMemoryLeaks();
		}
	}

	//主动查看当前模块的内存泄漏情况
	void Watch(const char* szfile,int nline)
	{
	
		if (pMemConfig&& pMemConfig->hLogFile)
		{
			DWORD nWritten;
			char szinfo[2][MAX_PATH]{};
			strcat(szinfo[0], szfile);
			sprintf(szinfo[1], "\n观测行:%d\n", nline);
			strcat(szinfo[0], szinfo[1]);
			WriteFile(pMemConfig->hLogFile, szinfo, strlen(szinfo[0]), &nWritten, NULL);
			_CrtDumpMemoryLeaks();
		}
	}

	//客户端块挂钩回调
	static void DumpClientHook(void* userPortion, size_t blockSize)
	{

		
	}

	//分配挂钩函数
	static int AllocHook(int nAllocType, void* pvData,size_t nSize, int nBlockUse, long lRequest,
		const unsigned char* szFileName, int nLine)
	{
		if (nAllocType == _HOOK_ALLOC)
		{
			if (nBlockUse == _CRT_BLOCK)
				return TRUE;
			static size_t ntotalmem = 0;
			ntotalmem += nSize;
			if (ntotalmem > 100 * MB)
			{
				//巨量累积分配查看内存泄漏情况
				if (pMemConfig && pMemConfig->hLogFile)
				{
					//发送报告
					DWORD nWritten;
					char szinfo[2][MAX_PATH]{};
					strcat(szinfo[0], (const char*)szFileName);
					sprintf(szinfo[1], "\n观测行:%d\n异常内存存量", nLine);
					strcat(szinfo[0], szinfo[1]);
					WriteFile(pMemConfig->hLogFile, szinfo, strlen(szinfo[0]), &nWritten, NULL);
					_CrtDumpMemoryLeaks();
					return FALSE;
				}	
			}
		}
		else
		{
				
		}

		return TRUE;
		
	}
private:

	_CrtMemState mBeg, mEnd, mResult;
	static PHOOK_PRO_FILE pMemConfig;

};
inline MemLog::MemLog() 
{
	//设置报告模式
	if (!MemConfigInfo.hLogFile)
	{
		TCHAR reallyModuleName[MAX_PATH]{};
		GetCurrentModule(reallyModuleName);

		strcat(reallyModuleName, ".txt");

		init();
		if(MemConfigInfo.bClientHook)
			_CrtSetDumpClient(CMemSentry::DumpClientHook);
		if(MemConfigInfo.bAllocHook)
			_CrtSetAllocHook(CMemSentry::AllocHook);

		_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
		MemConfigInfo.hLogFile = ::CreateFileA(reallyModuleName, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ,
			NULL, OPEN_ALWAYS,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		_CrtSetReportFile(_CRT_WARN, MemConfigInfo.hLogFile);
	}

}

//获取当前线程当前所在的模块名称
inline void MemLog::GetCurrentModule(TCHAR* reallymodname)
{
	ZwQueryInformationThread pZwQueryInformationThread;

	HANDLE hThread = GetCurrentThread();
	// 获取 ntdll.dll 的模块句柄
	HINSTANCE hNTDLL = ::GetModuleHandle("ntdll");

	// 从 ntdll.dll 中取出 ZwQueryInformationThread
	(FARPROC&)pZwQueryInformationThread = ::GetProcAddress(hNTDLL, "ZwQueryInformationThread");

	// 获取线程所在模块
	THREAD_BASIC_INFORMATION tbi;      // _THREAD_BASIC_INFORMATION 结构体对象

	pZwQueryInformationThread(
		hThread,              // 线程句柄
		ThreadBasicInformation,        // 线程信息类型,ThreadBasicInformation :线程基本信息
		&tbi,                // 指向缓冲区的指针
		sizeof(tbi),            // 缓冲区的大小
		NULL
	);
	TCHAR modname[MAX_PATH]{};        // 用来接收模块全路径
	if (GetMappedFileName(OpenProcess(PROCESS_ALL_ACCESS, 0,(DWORD) tbi.ClientId.UniqueProcess), CMemSentry::AllocHook, modname, MAX_PATH) == 0)
	{
		int res = GetLastError();
		exit(0);
	}

	int nlen = strlen(modname);
	int nBeg = 0xFFFFFFFF;
	int nEnd = 0xFFFFFFFF;
	while (nlen--)
	{
		if (modname[nlen] == '.')
		{
			if(nEnd== 0xFFFFFFFF)
				nEnd = nlen;
		}
		if (modname[nlen] == '\\')
		{
			nBeg = nlen+1;
			break;
		}
	}
	for (size_t i = 0; nBeg < nEnd; ++nBeg, ++i)
	{
		reallymodname[i] = modname[nBeg];
	}
}


#include"MemSentry.h"

PHOOK_PRO_FILE CMemSentry::pMemConfig =NULL;

文章来源地址https://www.toymoban.com/news/detail-599490.html

到了这里,关于【内存泄漏检测】调式运行时堆的多模块检测工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手写C语言的内存泄漏检测组件

    CC++语言中,栈空间有大小限制,所以程序员可以使用堆空间的内存。堆空间的内存是程序员自己申请的,需要程序员自己去调用释放的操作。内存管理是CC++程序员必须要注意的问题,其中包括了内存泄漏,内存泄漏的原因是程序中申请的内存没有进行释放,来看下面的例子

    2024年02月03日
    浏览(29)
  • iOS 内存泄漏检测 Instruments Leaks

    Xcode 中 按住 command + I 或者菜单栏 Product – Profile 2. 双击 Leaks 或者按 choose,打开 Leaks 面板 3. 在显示的 Leaks 面板中,点击左上角红色点,即可运行内存检测。 4. 在运行过程中如果发现Leak Checks(如图)出现红色X说明检测到内存泄露,将鼠标点击Leak Checks,在下方即可看到内存

    2024年02月01日
    浏览(34)
  • Python中的内存泄漏及其检测方法

    一、引言 内存泄漏是编程中常见的问题之一,它会导致程序在运行过程中不断消耗内存,最终可能导致程序崩溃或性能下降。在Python中,内存泄漏也是一个需要关注的问题。本文将详细介绍Python中的内存泄漏及其检测方法,以帮助读者更好地理解和解决这个问题。 二、Pyth

    2024年02月22日
    浏览(38)
  • 如何处理Flutter内存泄漏检测和优化

    处理Flutter内存泄漏问题是构建高性能、稳定的应用程序的关键部分之一。在本文中,我将详细介绍如何检测和优化Flutter内存泄漏问题,以确保应用程序的良好性能和用户体验。 1. 了解内存泄漏 在深入了解如何处理Flutter内存泄漏之前,首先需要了解什么是内存泄漏。内存泄

    2024年04月14日
    浏览(31)
  • 使用asan检测内存泄漏、堆栈溢出等问题

    操作过程参考:链接 缘起:程序在移动端崩溃,mac端复现不了,于是在写个崩溃位置函数的调用demo,使用ASAN工具进行排查。 验证过程 1、代码 main.cpp 使用附加ASAN工具的方式进行编译: 执行: 没有问题,以上是验证过程,如有问题执行时ASAN会提示有问题的相关位置。 介绍

    2024年02月11日
    浏览(48)
  • android 如何进行内存泄漏检测及解决方法

    内存泄漏是在Android开发中常见的问题之一,它可能导致应用的内存占用逐渐增加,最终影响应用的性能和稳定性。以下是一些常见的方法来进行内存泄漏检测和解决: 1. 使用工具进行内存泄漏检测: Android Profiler: Android Studio提供的Android Profiler工具可以帮助您监视应用的内

    2024年02月07日
    浏览(37)
  • Android 源码浅析:Leakcanary 内存泄漏检测的好帮手

    我们一起来分析一下大名鼎鼎的 Leakcanary, 想必作为 Android 开发都多多少少接触过,新版本的 Leakcanary 也用 Kotlin 重写了一遍,最近详细查看了下源码,分享一下。 tips:本来是只想分析下内存泄漏检测部分,但写着写着就跑偏了,因为内存泄漏的检测难点在于对对象生命周期

    2024年02月02日
    浏览(34)
  • Android内存泄漏分析及检测工具LeakCanary简介,Android进阶

    @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { if (!isEnabled()) { return } removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { \\\"Watching \\\" +

    2024年04月25日
    浏览(29)
  • 构建 ESLint 内存泄露检测插件入门:提升代码质量与防范运行时风险

    前言 本文目的是介绍如何创建开发一个自定义规则 ESLint 插件。利用其能力,检测一些代码中可能存在的内存泄露并及时进行提示,避免潜在的后期影响。 本文实现其中一部分功能–检测事件监听器的使用是否存在内存泄露为例来演示基本的 ESLint 自定义规则插件开发的过程

    2024年04月27日
    浏览(38)
  • Android之内存泄漏与内存溢出

    内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存并且分配给其他进程使用。通常少次数的内存无法及时回收并不会到程序造成什么影响,但是如果在内存本身就比较少获取多次导致内存无法正常回收时,就会导致内存

    2024年02月13日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包