最近工作挺忙,博客也好久没更新,这可不是什么好事,写博客的习惯还是得坚持的。
前几天无聊,就写了一个简单的内存泄露检测工具。
原理很简单,直接上代码吧。
.h文件
#ifndef _Z_NEW_H_
#define _Z_NEW_H_
#if defined MEM_DEBUG
#include <iostream>
#include <map>
#include <string>
class MemRec
{
private:
struct Mem_Node
{
Mem_Node()
{
memset(this,0,sizeof(*this));
}
Mem_Node(const char *_filename,int _num)
{
strncpy(filename,_filename,sizeof(filename));
num = _num;
}
char filename[32];
int num;
};
public:
MemRec()
{
}
~MemRec()
{
if(_M_mem_info.empty())
{
std::cout << "------------------- no memory leak ----------------------" << std::endl;
}
else
{
std::cout << "------------------- memory leak start ----------------------" << std::endl;
int i = 0;
for(MEMITER it = _M_mem_info.begin(); it != _M_mem_info.end(); ++it,++i)
{
std::cout << i << " : " << it->second.filename << " " << it->second.num << std::endl;
}
std::cout << "------------------- memory leak end ----------------------" << std::endl;
}
}
void Insert(void* _p,const char* _filename,int _num)
{
_M_mem_info[_p] = Mem_Node(_filename,_num);
}
void Delete(void* _p)
{
MEMITER it = _M_mem_info.find(_p);
if(it != _M_mem_info.end())
{
_M_mem_info.erase(it);
}
}
private:
typedef std::map<void*,Mem_Node> MEMINFO;
typedef MEMINFO::iterator MEMITER;
MEMINFO _M_mem_info;
};
void* operator new(size_t sz,const char* filename,int num);
void* operator new[](size_t sz,const char* filename,int num);
void operator delete(void *p);
void operator delete[](void *p);
#endif
#if defined MEM_DEBUG
#define zNew new(__FILE__,__LINE__)
#else
#define zNew new
#endif
#endif
#include "zNew.h"
#if defined MEM_DEBUG
//global
MemRec appMemRec;
void* operator new(size_t sz,const char* filename,int num)
{
void* ret = ::operator new(sz);
if(ret)
{
appMemRec.Insert(ret,filename,num);
}
return ret;
}
void* operator new[](size_t sz,const char* filename,int num)
{
return operator new(sz,filename,num);
}
void operator delete(void* p)
{
if(NULL == p)
{
return ;
}
else
{
appMemRec.Delete(p);
free(p);
}
}
void operator delete[](void* p)
{
operator delete(p);
}
#endif
测试代码
#include "zNew.h"
struct _T
{
_T()
{
x = 10;
name[1] = 'x';
};
int x;
char name[32];
};
int main()
{
_T *p = zNew _T;
_T *parray = zNew _T[3];
return 0;
}
用gdb调试可以很详细的看到整个函数调用流程:
new operator调用operator new(我们将此函数重载),后者调用全局operator new分配内存,然后编译器负责调用类构造函数
0x08048bf2 <main+18>: movl $0x1a,0x8(%esp)
0x08048bfa <main+26>: movl $0x804a4d4,0x4(%esp)
0x08048c02 <main+34>: movl $0x24,(%esp)
0x08048c09 <main+41>: call 0x8048eb0 <_ZnwjPKci>
0x08048c0e <main+46>: mov %eax,%ebx
0x08048c10 <main+48>: mov %ebx,(%esp)
0x08048c13 <main+51>: call 0x8048c78 <_T>