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;
}
 
生成的报告:

--------------------- 
作者:jack3z 
来源:CSDN 
原文:https://blog.csdn.net/u014023615/article/details/39551191 
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://my.oschina.net/u/4000302/blog/3057686

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值