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

实现类:

DumpMemoryLeaks.h

/**
* @file DumpMemoryLeaks.h
* @brief 跟踪内存分配并定时输到文件,以协助检查有无内存泄漏
*
* 修订记录 
* @author   jack3z
* @version  1.00
* @date 2014-05-18
*
*/

#ifndef DUMPMEMORYLEAKS_H
#define DUMPMEMORYLEAKS_H

#ifdef __linux__
# include <pthread.h>
#else
# include <Windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include <malloc.h>

#include <time.h>

#include <string>
#include <map>
#include <list>

#include <assert.h>

#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 < other.m_strFile || (m_strFile==other.m_strFile && m_nLine < other.m_nLine);
	}
};


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 < time(NULL);
	}

	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< CAllocLocalInfo, std::list<StMemAllocRec> > m_mapAllocRec;

	std::map<void *,CAllocLocalInfo> 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<void *, CAllocLocalInfo>::iterator itorAddr2AllocLocal = m_mapAddr2AllocLocal.find(addr);

	if (itorAddr2AllocLocal == m_mapAddr2AllocLocal.end())
	{
		unlock();
		return;
	}
	else
	{
		std::map< CAllocLocalInfo, std::list<StMemAllocRec> >::iterator itorAllocRec = m_mapAllocRec.find(itorAddr2AllocLocal->second);
		assert(itorAllocRec != m_mapAllocRec.end());

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

		for (std::list<StMemAllocRec>::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<CAllocLocalInfo> listLocal;//按内存大到小排序

		std::map<CAllocLocalInfo,size_t> mapLocal2Size;

		for (std::map< CAllocLocalInfo, std::list<StMemAllocRec> >::iterator itor = m_mapAllocRec.begin();
			itor != m_mapAllocRec.end();
			++itor)
		{
			const CAllocLocalInfo& local = itor->first;

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

			for (std::list<StMemAllocRec>::iterator itor = listAllocRec.begin();
				itor != listAllocRec.end();
				++itor)
			{
				nTotalAlloc += itor->nSize;

				mapLocal2Size[local] += itor->nSize;
			}
		}

		for (std::map<CAllocLocalInfo,size_t>::iterator itor = mapLocal2Size.begin();
			itor != mapLocal2Size.end();
			++itor)
		{
			std::list<CAllocLocalInfo>::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<CAllocLocalInfo>::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 <list>
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;
}

生成的报告:





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值