检测内存泄露的工具:debugnew
网上有一个流传甚广的检测内存泄露的工具: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); }

|