您的位置:首页 > 其它

检测内存泄露的工具

2012-03-29 10:28 274 查看
检测内存泄露的工具: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);

}

转自:http://www.cppblog.com/SpringSnow/articles/65738.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: