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/.