Memory leak detector ( C++)
This is a simple solution for memory leak detector.
For any C++ software source under windows, please follow these steps:
1. You need to put MemoryTracker.h and MemoryAllocateTracker.h into include folder, and force all *.cpp file to include MemoryTracker.h first(when using MVS C++/Using precompiled headers, we usually include it within stdafx.h, and remove all MVS C++ generated DEBUG_NEW if exists);
2. Besides, modify the log file location(which is now c:\temp\) in MemoryAllocateTracker.cpp, and rebuild the version of MemoryAllocateTracker.dll;
3. Also make sure no other 'replace new' exists in your C++ source;
For any issues, please feel free to send email to health_163@163.com.
MemoryTracker.h
#ifdef _DEBUG
#pragma comment(lib, "MemoryAllocateTracker.lib")
#include " MemoryAllocateTracker.h "
#ifdef new
#undef new
#endif
inline void * __cdecl operator new (size_t size, const char * file, int line)
{
return MemoryAllocateTracker::GetInstance().re_malloc_dbg(size, _NORMAL_BLOCK, file, line);
}
inline void * __cdecl operator new [](size_t size, const char * file, int line)
{
return operator new (size, file, line);
}
inline void __cdecl operator delete( void * ptr)
{
MemoryAllocateTracker::GetInstance().re_free_dbg(ptr, _NORMAL_BLOCK);
}
inline void __cdecl operator delete[]( void * ptr)
{
delete ptr;
}
#ifdef DEBUG_NEW
#undef DEBUG_NEW
#endif
#define DEBUG_NEW new (__FILE__, __LINE__)
#define new DEBUG_NEW
#ifdef malloc
#undef malloc
#endif
#define malloc(s) MemoryAllocateTracker::GetInstance().re_malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#ifdef calloc
#undef calloc
#endif
#define calloc(c, s) MemoryAllocateTracker::GetInstance().re_calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#ifdef realloc
#undef realloc
#endif
#define realloc(p, s) MemoryAllocateTracker::GetInstance().re_realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#ifdef free
#undef free
#endif
#define free(p) MemoryAllocateTracker::GetInstance().re_free_dbg(p, _NORMAL_BLOCK)
// #define _msize(p) _msize_dbg(p, _NORMAL_BLOCK)
// #define _aligned_msize(p, a, o) _aligned_msize_dbg(p, a, o)
// #define _aligned_malloc(s, a) _aligned_malloc_dbg(s, a, __FILE__, __LINE__)
// #define _aligned_realloc(p, s, a) _aligned_realloc_dbg(p, s, a, __FILE__, __LINE__)
// #define _aligned_recalloc(p, c, s, a) _aligned_recalloc_dbg(p, c, s, a, __FILE__, __LINE__)
// #define _aligned_offset_malloc(s, a, o) _aligned_offset_malloc_dbg(s, a, o, __FILE__, __LINE__)
// #define _aligned_offset_realloc(p, s, a, o) _aligned_offset_realloc_dbg(p, s, a, o, __FILE__, __LINE__)
// #define _aligned_offset_recalloc(p, c, s, a, o) _aligned_offset_recalloc_dbg(p, c, s, a, o, __FILE__, __LINE__)
// #define _aligned_free(p) _aligned_free_dbg(p)
//
// #define _malloca(s) _malloca_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _freea(p) _freea_dbg(p, _NORMAL_BLOCK)
//
// #define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wcsdup(s) _wcsdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _mbsdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _tempnam(s1, s2) _tempnam_dbg(s1, s2, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wtempnam(s1, s2) _wtempnam_dbg(s1, s2, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _fullpath(s1, s2, le) _fullpath_dbg(s1, s2, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wfullpath(s1, s2, le) _wfullpath_dbg(s1, s2, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _getcwd(s, le) _getcwd_dbg(s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wgetcwd(s, le) _wgetcwd_dbg(s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _getdcwd(d, s, le) _getdcwd_dbg(d, s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wgetdcwd(d, s, le) _wgetdcwd_dbg(d, s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _getdcwd_nolock(d, s, le) _getdcwd_lk_dbg(d, s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wgetdcwd_nolock(d, s, le) _wgetdcwd_lk_dbg(d, s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _dupenv_s(ps1, size, s2) _dupenv_s_dbg(ps1, size, s2, _NORMAL_BLOCK, __FILE__, __LINE__)
// #define _wdupenv_s(ps1, size, s2) _wdupenv_s_dbg(ps1, size, s2, _NORMAL_BLOCK, __FILE__, __LINE__)
#if !__STDC__
#ifdef strdup
#undef strdup
#endif
#define strdup(s) MemoryAllocateTracker::GetInstance().re_strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#ifdef wcsdup
#undef wcsdup
#endif
#define wcsdup(s) MemoryAllocateTracker::GetInstance().re_wcsdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#ifdef tempnam
#undef tempnam
#endif
#define tempnam(s1, s2) MemoryAllocateTracker::GetInstance().re_tempnam_dbg(s1, s2, _NORMAL_BLOCK, __FILE__, __LINE__)
// #ifdef getcwd
// #undef getcwd
// #endif
// #define getcwd(s, le) MemoryAllocateTracker::GetInstance().re_getcwd_dbg(s, le, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif
MemoryAllocateTracker.h
#ifdef MEMORY_ALLOC_TRACKER
#define MEMORY_ALLOC_TRACKER_CLASS _declspec(dllexport)
#define MEMORY_ALLOC_TRACKER_FUNC _declspec(dllexport)
#else
#define MEMORY_ALLOC_TRACKER_CLASS _declspec(dllimport)
#define MEMORY_ALLOC_TRACKER_FUNC _declspec(dllimport)
#endif
class MEMORY_ALLOC_TRACKER_CLASS MemoryAllocateTracker
{
public :
enum AllocState
{
AS_NOW,
AS_MIN,
AS_MAX
};
public :
static MemoryAllocateTracker & GetInstance();
int GetAllocatedSize(AllocState state) const ;
void Alloc( void * addr, size_t size, const char * file, long line);
void Dealloc( void * addr);
void WriteLog( const char * file = " C:\\temp\\MemoryLeakTracker.txt " );
public :
void re_free_dbg( void * addr, int blockType);
void * re_malloc_dbg(size_t size, int blockType, const char * file, int line);
void * re_calloc_dbg(size_t num, size_t size, int blockType, const char * file, int line);
void * re_realloc_dbg( void * addr, size_t size, int blockType, const char * file, int line);
char * re_strdup_dbg(
const char * str,
int blockType,
const char * file,
int line
);
wchar_t * re_wcsdup_dbg(
const wchar_t * str,
int blockType,
const char * file,
int line
);
char * re_tempnam_dbg(
const char * dirname,
const char * filePrefix,
int blockType,
const char * file,
int line
);
char * re_getcwd_dbg(
char * buf,
int size,
int blockType,
const char * file,
int line
);
private :
MemoryAllocateTracker();
~ MemoryAllocateTracker();
};
MemoryAllocateTracker.pp
#include < iomanip >
#include < fstream >
#include < map >
#include < set >
#include " MemoryAllocateTracker.h "
#ifndef _CRTBLD
#define _CRTBLD
#include < dbgint.h >
#endif
#pragma data_seg("SharedData")
namespace
{
long g_TotalAllocCount = 0 ;
HANDLE g_hProcHeap = 0 ;
// TEMPLATE CLASS allocator
template < class _Ty >
class SpecAllocator
{ // generic allocator for objects of class _Ty
public :
typedef typename _Ty value_type;
typedef value_type _FARQ * pointer;
typedef value_type _FARQ & reference;
typedef const value_type _FARQ * const_pointer;
typedef const value_type _FARQ & const_reference;
typedef _SIZT size_type;
typedef _PDFT difference_type;
template < class _Other >
struct rebind
{ // convert an allocator<_Ty> to an allocator <_Other>
typedef SpecAllocator < _Other > other;
};
pointer address(reference _Val) const
{ // return address of mutable _Val
return ( & _Val);
}
const_pointer address(const_reference _Val) const
{ // return address of nonmutable _Val
return ( & _Val);
}
SpecAllocator() _THROW0()
{ // construct default allocator (do nothing)
}
SpecAllocator( const SpecAllocator < _Ty >& ) _THROW0()
{ // construct by copying (do nothing)
}
template < class _Other >
SpecAllocator( const SpecAllocator < _Other >& ) _THROW0()
{ // construct from a related allocator (do nothing)
}
template < class _Other >
SpecAllocator < _Ty >& operator = ( const SpecAllocator < _Other >& )
{ // assign from a related allocator (do nothing)
return ( * this );
}
void deallocate(pointer _Ptr, size_type)
{ // deallocate object at _Ptr, ignore size
// ::HeapFree(g_hProcHeap, 0, _Ptr);
free(_Ptr);
}
pointer allocate(size_type _Count)
{ // allocate array of _Count elements
g_TotalAllocCount += _Count;
// check for integer overflow
if (_Count <= 0 )
_Count = 0 ;
else if (((_SIZT)( - 1 ) / _Count) < sizeof (_Ty))
_THROW_NCEE(std::bad_alloc, NULL);
// return (pointer)::HeapAlloc(g_hProcHeap, 0, _Count);
return (pointer)_malloc_dbg(_Count * sizeof (_Ty), 1 , __FILE__, __LINE__);
}
pointer allocate(size_type _Count, const void _FARQ * )
{ // allocate array of _Count elements, ignore hint
return (allocate(_Count));
}
void construct(pointer _Ptr, const _Ty & _Val)
{ // construct object at _Ptr with value _Val
_Construct(_Ptr, _Val);
}
void destroy(pointer _Ptr)
{ // destroy object at _Ptr
_Destroy(_Ptr);
}
_SIZT max_size() const _THROW0()
{ // estimate maximum array size
_SIZT _Count = (_SIZT)( - 1 ) / sizeof (_Ty);
return ( 0 < _Count ? _Count : 1 );
}
};
// typedef std::basic_string<char, std::char_traits<char>, SpecAllocator<char> > SpecString;
typedef char * SpecString;
// typedef std::string SpecString;
typedef std:: set < int , std::less < int > , SpecAllocator < int > > SpecSet;
struct TrackerNode
{
SpecString file;
long lineNum;
long allocSize;
long count;
long totalCount;
long leakCount;
TrackerNode( const char * f = 0 , long l = 0 , long s = 0 , long c = 1 )
: file(f != 0 ? f : " (No file path!) " ), lineNum(l),allocSize(s), count(c), totalCount( 1 ), leakCount( 0 )
{
}
TrackerNode( const TrackerNode & o)
: file(o.file), lineNum(o.lineNum),allocSize(o.allocSize), count(o.count), totalCount( 1 ), leakCount( 0 )
{
}
~ TrackerNode()
{
}
static void * operator new (size_t size)
{
return _malloc_dbg(size, 1 , __FILE__, __LINE__);
// return ::HeapAlloc(g_hProcHeap, 0, size);
}
static void operator delete( void * ptr)
{
// ::HeapFree(g_hProcHeap, 0, ptr);
free(ptr);
}
};
// todo: It should be changed it to shared ptr later.
typedef TrackerNode * TrackerNodePtr;
typedef const TrackerNode * TrackerNodeConstPtr;
inline bool operator < ( const TrackerNode & lt, const TrackerNode & rh)
{
if (lt.lineNum < rh.lineNum)
{
return true ;
}
else if (lt.lineNum == rh.lineNum
&& lt.file < rh.file)
{
return true ;
}
else if (lt.lineNum == rh.lineNum
&& lt.file == rh.file
&& lt.allocSize < rh.allocSize)
{
return true ;
}
return false ;
}
template < class _Ty = TrackerNode >
struct LessForSort:
public std::binary_function < _Ty, _Ty, bool >
{
bool operator () ( const _Ty & lt, const _Ty & rh) const
{
return lt.allocSize * lt.count > rh.allocSize * rh.count;
}
};
}
template <>
struct std::less < TrackerNodePtr > :
public std::binary_function < TrackerNodePtr, TrackerNodePtr, bool >
{
bool operator () ( const TrackerNodePtr & lt, const TrackerNodePtr & rh) const
{
if (lt -> lineNum < rh -> lineNum)
{
return true ;
}
else if (lt -> lineNum == rh -> lineNum
&& lt -> file < rh -> file)
{
return true ;
}
else if (lt -> lineNum == rh -> lineNum
&& lt -> file == rh -> file
&& lt -> allocSize < rh -> allocSize)
{
return true ;
}
return false ;
}
};
namespace
{
struct AddrCountPair
{
long count;
TrackerNodePtr addr;
AddrCountPair( long c = 0 , TrackerNodePtr ptr = 0 ): count(c), addr(ptr)
{}
};
typedef std::map < int *
,AddrCountPair
,std::less < int *>
,SpecAllocator < std::pair < const int * , AddrCountPair > >
> TrackerPtrMap;
typedef TrackerPtrMap::iterator TrackerPtrMapItr;
typedef std:: set < TrackerNodePtr
,std::less < TrackerNodePtr >
,SpecAllocator < TrackerNodePtr >
> TrackerPtrSet;
typedef TrackerPtrSet::iterator TrackerPtrSetItr;
typedef std:: set < TrackerNode
,std::less < TrackerNode >
,SpecAllocator < TrackerNode >
> TrackerSet;
typedef TrackerSet::iterator TrackerSetItr;
typedef std:: set < TrackerNode
,LessForSort <>
,SpecAllocator < TrackerNode >
> TrackerSortedSet;
typedef TrackerSortedSet::iterator TrackerSortedSetItr;
long g_totalAlloc = 0 ;
TrackerPtrMap g_allocMap;
TrackerPtrSet g_allocSet;
CMemoryState g_mfcMemStateBegin, g_mfcMemStateEnd;
}
namespace
{
class Mutex
{
friend class Lock;
public :
Mutex () { InitializeCriticalSection ( & _critSection); }
~ Mutex () { DeleteCriticalSection ( & _critSection); }
private :
void Acquire ()
{
EnterCriticalSection ( & _critSection);
}
void Release ()
{
LeaveCriticalSection ( & _critSection);
}
private :
CRITICAL_SECTION _critSection;
};
Mutex _mutex;
class Lock
{
public :
// Acquire the state of the semaphore
Lock ()
{
_mutex.Acquire();
}
// Release the state of the semaphore
~ Lock ()
{
_mutex.Release();
}
};
}
// see bellow...
{
if (g_hProcHeap == 0 )
{
// g_hProcHeap = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
}
g_mfcMemStateBegin.Checkpoint();
}
MemoryAllocateTracker:: ~ MemoryAllocateTracker()
{
Lock threadlock;
WriteLog();
if (g_hProcHeap != 0 )
{
// ::HeapDestroy(g_hProcHeap);
g_hProcHeap = 0 ;
}
}
int MemoryAllocateTracker::GetAllocatedSize(AllocState state) const
{
return 0 ;
}
void MemoryAllocateTracker::Alloc( void * addr, size_t size, const char * file, long line)
{
Lock threadlock;
g_totalAlloc += size;
TrackerNode node(file, line, size, 1 ) ;
TrackerPtrSetItr it = g_allocSet.find( & node);
if (it != g_allocSet.end())
{
( * it) -> count ++ ;
( * it) -> totalCount ++ ;
g_allocMap[( int * )addr] = AddrCountPair(( * it) -> totalCount, ( * it));
}
else
{
TrackerNodePtr nodeptr = new TrackerNode(node);
g_allocSet.insert(nodeptr);
g_allocMap[( int * )addr] = AddrCountPair(nodeptr -> totalCount, nodeptr);
}
}
void MemoryAllocateTracker::Dealloc( void * addr)
{
Lock threadlock;
TrackerPtrMapItr it = g_allocMap.find(( int * )addr);
if (it != g_allocMap.end())
{
g_totalAlloc -= it -> second.addr -> allocSize;
-- (it -> second.addr -> count);
g_allocMap.erase(it);
g_TotalAllocCount -- ;
}
}
MemoryAllocateTracker & MemoryAllocateTracker::GetInstance()
{
static MemoryAllocateTracker tracker;
return tracker;
}
void MemoryAllocateTracker::WriteLog( const char * file)
{
double totalSizeAllocatedBySelf = double (g_TotalAllocCount) / 1024 ;
if ( true )
{
std::ofstream out ( " C:\\temp\\_MemoryLeakTracker.txt " );
if ( out .fail())
{
return ;
}
out << std::setw( 24 )
<< std::setprecision( 3 )
<< " \t Total Size Allocated by MemoryAllocateTracker.dll:\t " << totalSizeAllocatedBySelf << " kb \n " << std::endl
<< " \t Total Size of Memory Leak(without MFC):\t " << double (g_totalAlloc) / 1024 << " kb \n " << std::endl
<< " \t Size(bytes) \t Count(times) \t Total(kb) \t First Leak Count \t Line number \t file location\n\n " ;
for (TrackerPtrMapItr it = g_allocMap.begin(); it != g_allocMap.end(); ++ it)
{
AddrCountPair & pair = it -> second;
if (pair.addr -> leakCount <= 0 || pair.count < pair.addr -> leakCount)
{
pair.addr -> leakCount = pair.count;
}
}
for (TrackerPtrSetItr it = g_allocSet.begin(); it != g_allocSet.end();)
{
TrackerNodePtr ptr = * it;
if (ptr -> count > 0 )
{
out << " \t " << ptr -> allocSize
<< " \t\t " << ptr -> count
<< " \t\t " << double (ptr -> count * ptr -> allocSize) / 1024
<< " \t\t " << ptr -> leakCount
<< " \t\t " << ptr -> lineNum
<< " \t\t " << ptr -> file
<< std::endl;
}
it = g_allocSet.erase(it);
// Do not use free(ptr), else memory of (*ptr)->file is not released.
delete ptr;
}
g_allocMap.clear();
out .flush();
out .close();
}
g_mfcMemStateEnd.Checkpoint();
TrackerSet trackerSet ;
_CrtMemBlockHeader * ptr = g_mfcMemStateEnd.m_memState.pBlockHeader;
long mfcLeakSize = 0 ;
while (ptr && ptr != g_mfcMemStateBegin.m_memState.pBlockHeader)
{
TrackerNode node(ptr -> szFileName, ptr -> nLine, ptr -> nDataSize, 1 );
mfcLeakSize += ptr -> nDataSize;
TrackerSetItr it = trackerSet.find(node);
if (it != trackerSet.end())
{
it -> count ++ ;
}
else
{
trackerSet.insert(node);
}
ptr = ptr -> pBlockHeaderNext;
}
std::ofstream mfcOut(file);
mfcOut << std::setw( 16 )
<< std::setprecision( 3 );
mfcOut << " \t Total Size Allocated by MemoryAllocateTracker.dll:\t " << totalSizeAllocatedBySelf << " kb \n " << std::endl
<< " \n\t Total Size of Memory Leak:\t " << double (mfcLeakSize) / 1024 << " kb\n " << std::endl
<< " \t Size(bytes) \t Count(times) \t Total(kb) \t Line number \t file location " << std::endl;
const bool needChecked = false ;
if (needChecked && ptr)
{
_CrtMemBlockHeader * xptr = ptr;
_CrtMemBlockHeader * rexptr = g_mfcMemStateBegin.m_memState.pBlockHeader;
size_t count = 0 ;
while (xptr || rexptr)
{
if (xptr != rexptr || ++ count > 1000 )
{
mfcOut << " Got Stupid ! " << std::endl;
break ;
}
xptr = xptr ? xptr -> pBlockHeaderNext : xptr;
rexptr = rexptr ? rexptr -> pBlockHeaderNext : rexptr;
}
}
TrackerSortedSet sortedSet(trackerSet.begin(), trackerSet.end());
for (TrackerSortedSetItr it = sortedSet.begin(); it != sortedSet.end(); ++ it)
{
mfcOut << " \t " << it -> allocSize
<< " \t\t " << it -> count
<< " \t\t " << double (it -> allocSize * it -> count) / 1024
<< " \t\t " << it -> lineNum
<< " \t\t " << it -> file
<< std::endl;
}
mfcOut << " \n\n\t========================================== "
<< " File Order ==========================================\n\n " ;
for (TrackerSetItr it = trackerSet.begin(); it != trackerSet.end(); ++ it)
{
mfcOut << " \t " << it -> allocSize
<< " \t\t " << it -> count
<< " \t\t " << double (it -> allocSize * it -> count) / 1024
<< " \t\t " << it -> lineNum
<< " \t\t " << it -> file
<< std::endl;
}
mfcOut.flush();
mfcOut.close();
}
void MemoryAllocateTracker::re_free_dbg( void * addr, int blockType)
{
_free_dbg(addr, blockType);
Dealloc(addr);
}
void * MemoryAllocateTracker::re_malloc_dbg(size_t size, int blockType, const char * file, int line)
{
void * addr = _malloc_dbg(size, blockType, file, line);
Alloc(addr, size, file, line);
return addr;
}
void * MemoryAllocateTracker::re_calloc_dbg(size_t num, size_t size, int blockType, const char * file, int line)
{
void * addr = _calloc_dbg(num, size, blockType, file, line);
Alloc(addr, num * size, file, line);
return addr;
}
void * MemoryAllocateTracker::re_realloc_dbg( void * addr, size_t size, int blockType, const char * file, int line)
{
addr = _realloc_dbg(addr, size, blockType, file, line);
Alloc(addr, size, file, line);
return addr;
}
char * MemoryAllocateTracker::re_strdup_dbg(
const char * str,
int blockType,
const char * file,
int line
)
{
char * addr = _strdup_dbg(str, blockType, file, line);
Alloc(addr, strlen(addr), file, line);
return addr;
}
wchar_t * MemoryAllocateTracker::re_wcsdup_dbg(
const wchar_t * str,
int blockType,
const char * file,
int line
)
{
wchar_t * addr = _wcsdup_dbg(str, blockType, file, line);
Alloc(addr, wcslen(addr), file, line);
return addr;
}
char * MemoryAllocateTracker::re_tempnam_dbg(
const char * dirname,
const char * filePrefix,
int blockType,
const char * file,
int line
)
{
char * addr = _tempnam_dbg(dirname, filePrefix, blockType, file, line);
Alloc(addr, 123 , file, line);
return addr;
}
char * MemoryAllocateTracker::re_getcwd_dbg(
char * buf,
int size,
int blockType,
const char * file,
int line
)
{
char * addr = _getcwd_dbg(buf, size, blockType, file, line);
Alloc(addr, 123 , file, line);
return addr;
}
#pragma data_seg()
#pragma comment(linker,"/section:SharedData,rws")
For MFC memory check, please refer to Detecting memory leaks: by using CRT diagnostic functions - Part1
and Inside CRT: Debug Heap Management
To figure out the function call stack, you need to use dbhelp API within the source above under windows NT.
Or else, Visual Leak Detector is also a good choice, also see http://vld.codeplex.com/.