I know there are quite a few threads on this, but I wrote something that meets my needs, and I wanted to put it out here for the world to see partially so that people can use it, and partially because I'd like to know if there are any serious problems with this approach.
edit: tiny fix
#ifndef __MEMTRACKER_H__
#define __MEMTRACKER_H__
#include <cstdlib>
#include <string>
#include <list>
#include <bitset>
#include <iostream>
#include <iomanip>
#ifdef USE_MEMTRACKER
enum MemState { MS_ARRAY=0, MS_DELETED, MS_INC_DELETED };
class MemBlock
{
public:
MemBlock(size_t address, size_t size, const std::string &file, const std::string &func, size_t line, bool array) :
mAddress(address), mSize(size), mFile(file), mFunc(func), mLine(line)
{
mState[MS_ARRAY] = array;
}
size_t mAddress;
size_t mSize;
std::string mFile;
std::string mFunc;
size_t mLine;
std::bitset<3> mState;
friend std::ostream& operator<<(std::ostream &os, const MemBlock &block);
};
std::ostream& operator<<(std::ostream &os, const MemBlock &block)
{
return os << std::setw(9) << block.mSize << " bytes allocated at 0x" << std::ios::hex << block.mAddress
<< " in " << block.mFunc << " (" << block.mFile << ":" << std::setw(4) << block.mLine << ") "
<< (block.mState[MS_ARRAY]?"Array ":" ")
<< (block.mState[MS_DELETED]?"Deleted ":"LEAKED ")
<< (block.mState[MS_INC_DELETED]?"Incorrectly Deleted ":"");
}
class MemTracker
{
typedef std::list<MemBlock> BlockList;
typedef BlockList::iterator BlockIterator;
private:
static BlockList sMemBlocks;
static size_t sCurMem;
static size_t sMaxMem;
public:
static void newBlock(const MemBlock &block)
{
sMemBlocks.push_back(block);
sCurMem += block.mSize;
sMaxMem = std::max(sCurMem,sMaxMem);
}
static void deleteBlock(size_t address, bool arrayDelete)
{
for(BlockIterator it=sMemBlocks.begin(); it != sMemBlocks.end(); ++it)
{
if(it->mAddress == address)
{
sCurMem -= it->mSize;
if(it->mState[MS_ARRAY])
{
if(arrayDelete)
it->mState[MS_DELETED] = true;
else
it->mState[MS_INC_DELETED] = true;
}
else
{
if(!arrayDelete)
it->mState[MS_DELETED] = true;
else
it->mState[MS_INC_DELETED] = true;
}
}
}
}
static void showAllMem(std::ostream &os)
{
os << "--Memory Allocated--" << std::endl;
for(BlockIterator it=sMemBlocks.begin(); it != sMemBlocks.end(); ++it)
os << *it << std::endl;
}
static bool leaksExist()
{
for(BlockIterator it=sMemBlocks.begin(); it != sMemBlocks.end(); ++it)
{
if(!it->mState[MS_DELETED])
return true;
}
return false;
}
static void showLeaks(std::ostream &os)
{
os << "--Memory Leaked--" << std::endl;
for(BlockIterator it=sMemBlocks.begin(); it != sMemBlocks.end(); ++it)
{
if(!it->mState[MS_DELETED])
os << *it << std::endl;
}
}
static void showVitals(std::ostream &os)
{
os << "--Memory Report--" << std::endl
<< "Currently Allocated: " << std::setw(9) << sCurMem << " bytes" << std::endl
<< "Maximum Allocated: " << std::setw(9) << sMaxMem << " bytes" << std::endl;
}
};
MemTracker::BlockList MemTracker::sMemBlocks;
size_t MemTracker::sCurMem;
size_t MemTracker::sMaxMem;
inline void* operator new(size_t size, const char* file, const char* func, int line)
{
void *ptr = std::malloc(size);
MemTracker::newBlock(MemBlock((size_t)ptr, size, file, func, line, false));
return ptr;
}
inline void* operator new[](size_t size, const char* file, const char* func, int line)
{
void *ptr = std::malloc(size);
MemTracker::newBlock(MemBlock((size_t)ptr, size, file, func, line, true));
return ptr;
}
inline void operator delete(void* ptr)
{
MemTracker::deleteBlock((size_t)ptr, false);
std::free(ptr);
}
inline void operator delete[](void* ptr)
{
MemTracker::deleteBlock((size_t)ptr, true);
std::free(ptr);
}
#define DEBUG_NEW new(__FILE__, __FUNCTION__, __LINE__)
#define new DEBUG_NEW
#else //USE_MEMTRACKER
class MemTracker
{
public:
static void showAllMem(std::ostream &os) {}
static void showLeaks(std::ostream &os) {}
static void showVitals(std::ostream &os) {}
static bool leaksExist() { return false; }
};
#endif //USE_MEMTRACKER
#endif //__MEMTRACKER_H__
I meant to add a flag for new/new[] to make sure that the proper delete/delete[] works.
Oh and if you're looking to test this:
#define USE_MEMTRACKER
#include "MemTracker.h"
#include <iostream>
int main()
{
int *a = new int;
int *b = new int[40];
int *c = new int;
int *d = new int[40];
int *e = new int;
int *f = new int[40];
delete a;
delete b;
delete []c;
delete []d;
MemTracker::showVitals(std::cout);
std::cout << std::endl;
MemTracker::showAllMem(std::cout);
std::cout << std::endl;
if(MemTracker::leaksExist())
MemTracker::showLeaks(std::cout);
}
You should get:
--Memory Report--
Currently Allocated: 164 bytes
Maximum Allocated: 492 bytes
--Memory Allocated--
4 bytes allocated at 0x84014000 in main (new.cpp: 6) Deleted
160 bytes allocated at 0x84008312 in main (new.cpp: 7) Array LEAKED Incorrectly Deleted
4 bytes allocated at 0x84014032 in main (new.cpp: 8) LEAKED Incorrectly Deleted
160 bytes allocated at 0x84008480 in main (new.cpp: 9) Array Deleted
4 bytes allocated at 0x84014048 in main (new.cpp: 10) LEAKED
160 bytes allocated at 0x84008648 in main (new.cpp: 11) Array LEAKED
--Memory Leaked--
160 bytes allocated at 0x84008312 in main (new.cpp: 7) Array LEAKED Incorrectly Deleted
4 bytes allocated at 0x84014032 in main (new.cpp: 8) LEAKED Incorrectly Deleted
4 bytes allocated at 0x84014048 in main (new.cpp: 10) LEAKED
160 bytes allocated at 0x84008648 in main (new.cpp: 11) Array LEAKED
[Edited by - cozman on August 6, 2004 2:33:42 AM]