//.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
文章来源:https://www.toymoban.com/news/detail-599490.html
到了这里,关于【内存泄漏检测】调式运行时堆的多模块检测工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!