热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++:重载全局new/delete实现跨平台多线程内存检测

Reference: https://blog.csdn.net/u014023615/article/details/39551191

 

Reference: https://blog.csdn.net/u014023615/article/details/39551191

 

实现类:

DumpMemoryLeaks.h

 


/**

* @file DumpMemoryLeaks.h

* @brief 跟踪内存分配并定时输到文件,以协助检查有无内存泄漏

*

* 修订记录

* @author jack3z

* @version 1.00

* @date 2014-05-18

*

*/



#ifndef DUMPMEMORYLEAKS_H

#define DUMPMEMORYLEAKS_H



#ifdef __linux__

# include

#else

# include

#endif



#include

#include



#include



#include



#include

#include

#include



#include



#ifndef DUMP_MEM_REPORT_FREQUENCY

//# define DUMP_MEM_REPORT_FREQUENCY (30*60) //每隔30分钟输出一次内存分配情况

//# define DUMP_MEM_REPORT_FREQUENCY 60 //每隔一分钟输出一次内存分配情况

# define DUMP_MEM_REPORT_FREQUENCY 10 //测试时,10秒输出一次内存分配情况

#endif



struct StMemAllocRec

{

void* addr;

size_t nSize;

};



class CAllocLocalInfo

{

public:

CAllocLocalInfo()

{

m_nLine = -1;

}



std::string m_strFile;

int m_nLine;



bool operator<(const CAllocLocalInfo& other) const

{

return m_strFile
}

};





class CDumpMemoryLeaks

{

private:

CDumpMemoryLeaks(void);

~CDumpMemoryLeaks(void);



public:

static CDumpMemoryLeaks& GetInstance()

{

static CDumpMemoryLeaks inst;

return inst;

}



void Init();



void AddTrack(void* addr, size_t asize, const char *fname, int lnum);



void RemoveTrack(void* addr);



protected:



bool IsTheTime2Dump()

{

return m_timeDump
}



void ResetDumpTime()

{

m_timeDump = time(NULL) + DUMP_MEM_REPORT_FREQUENCY;

}



void Dump();



void lock()

{

#ifdef __linux__

pthread_mutex_lock(&m_mtx);

#else

EnterCriticalSection(&m_mtx);

#endif

}



void unlock()

{

#ifdef __linux__

pthread_mutex_unlock(&m_mtx);

#else

LeaveCriticalSection(&m_mtx);

#endif

}



protected:

bool m_bInit;



FILE* m_fpDumpFile;

std::string m_strDumpFile;



std::map > m_mapAllocRec;



std::map m_mapAddr2AllocLocal;



time_t m_timeDump;



std::string m_strMsg;



char m_szBuf[1024];



#ifdef __linux__

pthread_mutex_t m_mtx;

#else

CRITICAL_SECTION m_mtx;

#endif

};



//#ifdef DEBUG_REPORT_NEW_ALLOC



inline void * operator new(size_t size, const char* file, const size_t line)

{

void *ptr = (void*)malloc(size);

CDumpMemoryLeaks::GetInstance().AddTrack(ptr, size, file, line);

return(ptr);

}



inline void * operator new [](size_t size, const char* file, const size_t line)

{

void *ptr = (void*)malloc(size);

CDumpMemoryLeaks::GetInstance().AddTrack(ptr, size, file, line);

return ptr;

}



inline void operator delete(void *p)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



inline void operator delete(void *p, size_t size)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



inline void operator delete(void *p, const char* file, const size_t line)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



inline void operator delete [](void *p)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



inline void operator delete [](void *p, size_t size)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



inline void operator delete [](void *p, const char* file, const size_t line)

{

CDumpMemoryLeaks::GetInstance().RemoveTrack(p);

free(p);

}



#define malloc(s) ((void*)new unsigned char[s])



#define free(p) (delete [] (char*)(p));



#define new new(__FILE__, __LINE__) //1st parameter:size is not needed, passed by the compiler



//#endif





#endif

DumpMemoryLeaks.cpp

 

 


#include "DumpMemoryLeaks.h"



#ifndef localtime_r



#if _MSC_VER >= 1400

//Visual C++ 2005 以及更高版本

# define localtime_r(_Time_ptr,_Tm_ptr) (localtime_s((_Tm_ptr),(_Time_ptr)) == 0 ? (_Tm_ptr) : NULL)

#else

# define localtime_r(_Time_ptr,_Tm_ptr) ( *(_Tm_ptr) = *localtime(_Time_ptr), (_Tm_ptr))

#endif //#if _MSC_VER >= 1500



#endif //#ifndef localtime_r



#ifdef _MSC_VER

# ifndef snprintf

# define snprintf _snprintf

# endif//snprintf

#endif //_MSC_VER



CDumpMemoryLeaks::CDumpMemoryLeaks(void)

{

m_bInit = false;



m_fpDumpFile = NULL;



m_timeDump = NULL;



memset(m_szBuf,0,sizeof(m_szBuf));



#ifdef __linux__

pthread_mutexattr_t mattr;

pthread_mutexattr_init(&mattr);

pthread_mutexattr_settype(&mattr , PTHREAD_MUTEX_RECURSIVE);

pthread_mutex_init(&m_mtx,&mattr);

#else

InitializeCriticalSection(&m_mtx);

#endif



}



CDumpMemoryLeaks::~CDumpMemoryLeaks(void)

{

if (m_fpDumpFile)

{

fclose(m_fpDumpFile);

m_fpDumpFile = NULL;

}



#ifdef __linux__

pthread_mutex_destroy(&m_mtx);

#else

DeleteCriticalSection(&m_mtx);

#endif

}



void CDumpMemoryLeaks::Init()

{

lock();



if (!m_bInit)

{

ResetDumpTime();



m_bInit = true;

}



unlock();



}



void CDumpMemoryLeaks::AddTrack(void* addr, size_t asize, const char *fname, int lnum)

{

lock();



if (!m_bInit)

{

Init();

}



CAllocLocalInfo alloc_local;

alloc_local.m_strFile = fname;

alloc_local.m_nLine = lnum;



StMemAllocRec mem_alloc_rec;

mem_alloc_rec.addr = addr;

mem_alloc_rec.nSize = asize;



m_mapAllocRec[alloc_local].push_back(mem_alloc_rec);



m_mapAddr2AllocLocal[addr] = alloc_local;



if (IsTheTime2Dump())

{

Dump();



ResetDumpTime();

}



unlock();

}



void CDumpMemoryLeaks::RemoveTrack(void* addr)

{

lock();



if (!m_bInit)

{

Init();

}



bool bRemoveSuccess = false;



std::map::iterator itorAddr2AllocLocal = m_mapAddr2AllocLocal.find(addr);



if (itorAddr2AllocLocal == m_mapAddr2AllocLocal.end())

{

unlock();

return;

}

else

{

std::map >::iterator itorAllocRec = m_mapAllocRec.find(itorAddr2AllocLocal->second);

assert(itorAllocRec != m_mapAllocRec.end());



std::list& listAllocRec = itorAllocRec->second;



for (std::list::iterator itor = listAllocRec.begin();

itor != listAllocRec.end();

++itor)

{

if ((*itor).addr == addr)

{

listAllocRec.erase(itor);

bRemoveSuccess = true;



break;

}

}



if (listAllocRec.empty())

{

m_mapAllocRec.erase(itorAllocRec);

m_mapAddr2AllocLocal.erase(itorAddr2AllocLocal);

}

}



assert(bRemoveSuccess);



if (IsTheTime2Dump())

{

Dump();



ResetDumpTime();

}



unlock();

}



void CDumpMemoryLeaks::Dump()

{

time_t timeNow = time(NULL);

struct tm tmNow;

if (NULL == localtime_r(&timeNow,&tmNow))

{

assert(false);

}



if (m_strMsg.empty())

{//生成信息



size_t nTotalAlloc = 0;



std::list listLocal;//按内存大到小排序



std::map mapLocal2Size;



for (std::map >::iterator itor = m_mapAllocRec.begin();

itor != m_mapAllocRec.end();

++itor)

{

const CAllocLocalInfo& local = itor->first;



std::list& listAllocRec = itor->second;



for (std::list::iterator itor = listAllocRec.begin();

itor != listAllocRec.end();

++itor)

{

nTotalAlloc += itor->nSize;



mapLocal2Size[local] += itor->nSize;

}

}



for (std::map::iterator itor = mapLocal2Size.begin();

itor != mapLocal2Size.end();

++itor)

{

std::list::iterator itorLocalList = listLocal.begin();



for (;

itorLocalList != listLocal.end();

++itorLocalList)

{

if (itor->second >= mapLocal2Size[*itorLocalList])

{

break;

}

}



listLocal.insert(itorLocalList,itor->first);

}



snprintf(m_szBuf,sizeof(m_szBuf)-1,"Total unfree:%lu \n",(unsigned long)nTotalAlloc);



m_strMsg += "\n";



m_strMsg += m_szBuf;



m_strMsg += "--------------------------------------------------------------------------\n";



snprintf(m_szBuf,sizeof(m_szBuf)-1,

"Time: %04u-%02u-%02u %02u:%02u:%02u\n\n",

tmNow.tm_year + 1900,

tmNow.tm_mon + 1,

tmNow.tm_mday,

tmNow.tm_hour,

tmNow.tm_min,

tmNow.tm_sec

);



m_strMsg += m_szBuf;



double dTotalReciprocal = 1.0/(double)nTotalAlloc;



for (std::list::iterator itorLocalList = listLocal.begin();

itorLocalList != listLocal.end();

++itorLocalList)

{

size_t nSize = mapLocal2Size[*itorLocalList];



snprintf(m_szBuf,sizeof(m_szBuf)-1,

"%s:line %d, unfreed size:%lu, percentage:%lf %%; alloc times:%lu\n",

itorLocalList->m_strFile.c_str(),

itorLocalList->m_nLine,

nSize,

nSize*dTotalReciprocal*100,

(unsigned long)m_mapAllocRec[*itorLocalList].size()

);



m_strMsg += m_szBuf;

}



m_strMsg += "--------------------------------------------------------------------------\n";



}



//////////////////////////////////////////////////////////////////////////

//文件操作:打开文件,分割文件并写入



snprintf(m_szBuf,sizeof(m_szBuf)-1,

"MemoryAllocReport_%04u-%02u-%02u.txt",

tmNow.tm_year + 1900,

tmNow.tm_mon + 1,

tmNow.tm_mday

);



if (!m_fpDumpFile)

{

m_fpDumpFile = fopen(m_szBuf,"a");



m_strDumpFile = m_szBuf;



fprintf(m_fpDumpFile, "\n\n---------------- DumpMemoryLeaks begin! ----------------\n");



}

else

{

if (m_strDumpFile != m_szBuf)

{

fclose(m_fpDumpFile);



m_fpDumpFile = fopen(m_szBuf,"w");



m_strDumpFile = m_szBuf;

}

}



fwrite(m_strMsg.c_str(),m_strMsg.length(),1,m_fpDumpFile);



//文件操作结束

//////////////////////////////////////////////////////////////////////////



m_strMsg.clear();

}




 

实现方法:把文件保存到项目源文件目录下并在全局头文件添加# include "DumpMemoryLeaks.h"

如在win32工程中的stdafx.h文件内添加:


#ifdef _DEBUG

# include "DumpMemoryLeaks.h"

#endif // _DEBUG

测试:

 


// DbgMemLeak.cpp : 定义控制台应用程序的入口点。

//



#include "stdafx.h"

#include

using namespace std;



class A

{

public:

A()

{

m_i = 9;

}



~A()

{

if (m_i != 9)

{

assert(false);

}



m_i = 1;

}



int m_i;

};



class B

{

public:

B()

{

printf("new B instance %p \n", this);

}



~B()

{

printf("delete B instance %p \n", this);

}

};



int main(int argc, _TCHAR* argv[])

{

/*

{//测试构造和析构

B* pB = new B();



delete pB;



Sleep(3*1000);//方便观察终端输出内容

}

*/



{//测试数组构造和析构

B* pArray = new B[4];

delete[] pArray;

pArray = NULL;



Sleep(3*1000);//方便观察终端输出内容

}



/*

{//测试new和delete基础类型数组

char* pChArr = new char[1024];

delete pChArr;

}

*/



//模拟内存泄漏

for (int i = 0; i <60*30*10; ++i)

{

A* p = new A ;



Sleep(100);



if (i%2 == 0)

{

delete p;

}

}





for (int i = 0; ; ++i)

{

A* p = new A ;



Sleep(1);



if (i%4 == 0)

{

delete p;

}



}



return 0;

}


 

 

 

C++:重载全局new/delete实现跨平台多线程内存检测

 

--------------------- 本文来自 jack3z 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u014023615/article/details/39551191?utm_source=copy 


推荐阅读
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文详细介绍了PHP中与URL处理相关的三个函数:http_build_query、parse_str和查询字符串的解析。通过示例和语法说明,讲解了这些函数的使用方法和作用,帮助读者更好地理解和应用。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在Kubernetes上部署JupyterHub的步骤和实验依赖
    本文介绍了在Kubernetes上部署JupyterHub的步骤和实验所需的依赖,包括安装Docker和K8s,使用kubeadm进行安装,以及更新下载的镜像等。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
author-avatar
手机用户2502919063
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有