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

#pragma once

#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

#pragma once

#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 <string>
#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...
MemoryAllocateTracker::MemoryAllocateTracker()
{
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/.

 posted on 2011-05-20 15:16  sun_kang  阅读(2117)  评论(0编辑  收藏  举报