检测内存泄露的工具:debugnew
http://dev.csdn.net/article/58/58407.shtm
网上有一个流传甚广的检测内存泄露的工具:debugnew(debugnew.h/debugnew.cpp)
用法很简单,把debugnew.cpp放在项目里一起编译,需要检测的文件把debugnew.h嵌在文件的最前面。
为方便使用,对源代码做了一些小的改动。
下面是一些简单的说明:
1、new 的重载
void* operator new (size_t size, const char* file, int line); ⑴
void* operator new [] (size_t size, const char* file, int line); ⑵
在需要检测的文件里,重定义new
#define new new(__FILE__, __LINE__)
造成的结果:
ClassName *p = new ClassName; => ClassName *p = new(__FILE__, __LINE__) ClassName;
// 实际会调用void* operator new (size_t size, const char* file, int line);
ClassName **pp = new classname[count]; => ClassName **pp = new(__FILE__, __LINE__) ClassName[count];
// 实际会调用void* operator new [] (size_t size, const char* file, int line);
这其实是利用了placement new的语法,通过一个简单的宏,就可以把普通的new操作对应到相应的重载( ⑴,⑵ )上去。
2、delete 的重载
void operator delete (void* p, const char* file, int line); ⑶
void operator delete [] (void* p, const char* file, int line); ⑷
void operator delete (void* p); ⑸
void operator delete [] (void* p); ⑹
因为没有类似于placement new的语法,所以就不能用一个宏来替换替换delete了。要调用带有更多信息的delete操作符,只能修改源代码了。
delete p; => delete ( p, __FILE__, __LINE__ );
delete []pp; => delete [] ( pp, __FILE__, __LINE__ );
但这个工作很烦琐,如果并不需要多余的信息的话,简单地重载delete( ⑸,⑹ )就可以了。
3、检测和统计
程序开始时,在debugnew.cpp中会创建一个DebugNewTracer对象
在重载的new操作符( ⑴,⑵ )中,每一次内存分配都会被记录,而在delete( ⑶,⑷,⑸,⑹ )中则会删除相应的记录。
当程序结束,DebugNewTracer对象被销毁,它的析构函数会dump剩余的记录,这就是泄露的内存了。
在原有代码的基础上,增加了记录size的功能,这样可以在每次new和delete时,看到实际占用的内存。所有信息可以dump出来,也可以写入log。
5、源代码
********************************************************
debugnew.h:
/*
filename: debugnew.h
This code is based on code retrieved from a web site. The
author was not identified, so thanks go to anonymous.
This is used to substitute a version of the new operator that
can be used for debugging memory leaks. To use it:
- In any (all?) code files #include debugnew.h. Make sure all
system files (i.e. those in <>'s) are #included before
debugnew.h, and that any header files for your own code
are #included after debugnew.h. The reason is that some
system files refer to ::new, and this will not compile
if debugnew is in effect. You may still have problems
if any of your own code refers to ::new, or if any
of your own files #include system files that use ::new
and which have not already been #included before
debugnew.h.
- Add debugnew.cpp to the CodeWarrior project or compile
it into your Linux executable. If debugnew.cpp is in the
project, then debugnew.h must be #included in at least
one source file
*/
#ifndef __DEBUGNEW_H__
#define __DEBUGNEW_H__
#include <map>
#define LOG_FILE
#if defined(LOG_FILE)
#define LOG_FILE_NAME "./debugnew.log"
#endif
void* operator new (std::size_t size, const char* file, int line);
void operator delete (void* p, const char* name, int line);
void* operator new [] (std::size_t size, const char* file, int line);
void operator delete [] (void* p, const char* name, int line);
class DebugNewTracer {
private:
class Entry {
public:
Entry (char const* file, int line) : _file (file), _line (line) {}
Entry (char const* file, int line, int size) : _file (file), _line (line), _size (size) {}
Entry () : _file (0), _line (0), _size (0) {}
const char* File () const { return _file; }
int Line () const { return _line; }
size_t Size () const { return _size; }
private:
char const* _file;
int _line;
size_t _size;
};
class Lock {
public:
Lock (DebugNewTracer & DebugNewTracer) : _DebugNewTracer (DebugNewTracer) {
_DebugNewTracer.lock ();
}
~Lock () {
_DebugNewTracer.unlock ();
}
private:
DebugNewTracer & _DebugNewTracer;
};
typedef std::map<void*, Entry>::iterator iterator;
friend class Lock;
public:
DebugNewTracer ();
~DebugNewTracer ();
void Add (void* p, const char* file, int line);
void Add (void* p, const char* file, int line, size_t size);
void Remove (void* p);
void Dump ();
static bool Ready;
private:
void lock () { _lockCount++; }
void unlock () { _lockCount--; }
private:
std::map<void*, Entry> _map;
int _lockCount;
size_t _totalsize;
#if defined(LOG_FILE)
FILE* _logfp;
#endif
};
// The file that implements class DebugNewTracer
// does NOT want the word "new" expanded.
// The object DebugNewTrace is defined in that
// implementation file but only declared in any other file.
#ifdef DEBUGNEW_CPP
DebugNewTracer DebugNewTrace;
#else
#define new new(__FILE__, __LINE__)
extern DebugNewTracer DebugNewTrace;
#endif
#endif//#ifndef __DEBUGNEW_H__
********************************************************
debugnew.cpp:
/*
filename: debugnew.cpp
This is used to substitute a version of the new operator that
can be used for debugging memory leaks. In any (all?) code
files #include debugnew.h. Add debugnew.cpp to the project.
*/
#include <iostream>
#include <map>
using namespace std;
// This disables macro expansion of "new".
// This statement should only appear in this file.
#define DEBUGNEW_CPP
#include "debugnew.h"
DebugNewTracer::DebugNewTracer () : _lockCount (0)
{
// Once this object is constructed all calls to
// new should be traced.
Ready = true;
_totalsize = 0;
#if defined(LOG_FILE)
if( (_logfp=fopen(LOG_FILE_NAME,"wt")) == NULL )
{
printf(" Error! file: debugnew.log can not open@!\n");
return;
}
fprintf(_logfp,"new, delete list:\n");
fflush(_logfp);
#endif
}
void DebugNewTracer::Add (void* p, const char* file, int line)
{
// Tracing must not be done a second time
// while it is already
// in the middle of executing
if (_lockCount > 0)
return;
// Tracing must be disabled while the map
// is in use in case it calls new.
DebugNewTracer::Lock lock (*this);
_map [p] = Entry (file, line);
}
void DebugNewTracer::Add (void* p, const char* file, int line, size_t size)
{
// Tracing must not be done a second time
// while it is already
// in the middle of executing
if (_lockCount > 0)
return;
// Tracing must be disabled while the map
// is in use in case it calls new.
DebugNewTracer::Lock lock (*this);
#if 1
//´Óȫ·¾¶ÖÐÌáÈ¡ÎļþÃû
//linuxϵÄgcc,__FILE__½ö½ö°üÀ¨ÎļþÃû£¬windowsϵÄvc,__FILE__°üÀ¨È«Â·¾¶,ËùÒÔÓÐÕâÑùµÄ´¦Àí
file = strrchr(file,'\\')== NULL?file:strrchr(file,'\\')+1;
#endif
_map [p] = Entry (file, line, size);
_totalsize += size;
#if defined(LOG_FILE)
fprintf(_logfp,"*N p = 0x%08x, size = %6d, %-22s, Line: %4d. totalsize =%8d\n", p, size, file, line, _totalsize);
fflush(_logfp);
#endif
}
void DebugNewTracer::Remove (void* p)
{
// Tracing must not be done a second time
// while it is already
// in the middle of executing
if (_lockCount > 0)
return;
// Tracing must be disabled while the map
// is in use in case it calls new.
DebugNewTracer::Lock lock (*this);
iterator it = _map.find (p);
if (it != _map.end ())
{
size_t size = (*it).second.Size();
_totalsize -= size;
#if defined(LOG_FILE)
fprintf(_logfp,"#D p = 0x%08x, size = %6u.%39stotalsize =%8d\n", p, size, "----------------------------------- ", _totalsize );
fflush(_logfp);
#endif
_map.erase (it);
}
else
{
#if defined(LOG_FILE)
fprintf(_logfp,"#D p = 0x%08x. error point!!!\n", p );
fflush(_logfp);
#endif
}
}
DebugNewTracer::~DebugNewTracer ()
{
// Trace must not be called if Dump indirectly
// invokes new, so it must be disabled before
// Dump is called. After this destructor executes
// any other global objects that get destructed
// should not do any tracing.
Ready = false;
Dump ();
#if defined(LOG_FILE)
fclose(_logfp);
#endif
}
// If some global object is destructed after DebugNewTracer
// and if that object calls new, it should not trace that
// call to new.
void DebugNewTracer::Dump ()
{
if (_map.size () != 0)
{
std::cout << _map.size () << " memory leaks detected\n";
#if defined(LOG_FILE)
fprintf(_logfp, "\n\n***%d memory leaks detected\n", _map.size ());
fflush(_logfp);
#endif
for (iterator it = _map.begin (); it != _map.end (); ++it)
{
char const * file = it->second.File ();
int line = it->second.Line ();
int size = it->second.Size ();
std::cout << file << ", " << line << std::endl;
#if defined(LOG_FILE)
fprintf(_logfp,"%s, %d, size=%d\n", file, line, size);
fflush(_logfp);
#endif
}
}
else
{
std::cout << "no leaks detected\n";
#if defined(LOG_FILE)
fprintf(_logfp,"no leaks detected\n");
fflush(_logfp);
#endif
}
}
// If some global object is constructed before DebugNewTracer
// and if that object calls new, it should not trace that
// call to new.
bool DebugNewTracer::Ready = false;
void* operator new (size_t size, const char* file, int line)
{
void * p = malloc (size);
if (DebugNewTracer::Ready)
DebugNewTrace.Add (p, file, line, size);
return p;
}
void operator delete (void* p, const char* file, int line)
{
file = 0; // avoid a warning about argument not used in function
line = 0; // avoid a warning about argument not used in function
if (DebugNewTracer::Ready)
DebugNewTrace.Remove (p);
free (p);
}
void* operator new [] (size_t size, const char* file, int line)
{
void * p = malloc (size);
if (DebugNewTracer::Ready)
DebugNewTrace.Add (p, file, line, size);
return p;
}
void operator delete [] (void* p, const char* file, int line)
{
file = 0; // avoid a warning about argument not used in function
line = 0; // avoid a warning about argument not used in function
if (DebugNewTracer::Ready)
DebugNewTrace.Remove (p);
free (p);
}
void* operator new (size_t size)
{
void * p = malloc (size);
// When uncommented these lines cause entries in the map for calls to new
// that were not altered to the debugnew version. These are likely calls
// in library functions and the presence in the dump of these entries
// is usually misleading.
// if (DebugNewTracer::Ready)
// DebugNewTrace.Add (p, "?", 0);
return p;
}
void operator delete (void* p)
{
if (DebugNewTracer::Ready)
DebugNewTrace.Remove (p);
free (p);
}
//add by yugang
void operator delete [] (void* p)
{
if (DebugNewTracer::Ready)
DebugNewTrace.Remove (p);
free (p);
}