Some things seem like a good idea - and then there's the reality.
Using C/C++ means dealing with pointers. You can trick them up with smart pointers to reduce the chance of error, but sooner or later you're going to get memory leaks - little bits of memory you've allocated and never freed.
For software that needs to run for a long time reliably, these are death. You can watch the memory use of the process increase and increase over hours, days or weeks, until the server falls over.
To catch these, you need a system that can track memory use and help you find where the leaks are occurring.
Something else that is really useful, is a system to help you track when you stomp memory outside your allocated blocks.
I already had my own memory manager, so adding a memory tracker to it was straightforward because all allocations and frees were already routed through the memory manager. If you don't have a memory manager, you can override the default new/delete malloc/free in most modern runtime libraries to add these hooks yourself.
The memory tracker I implemented is deliberately simple. The major design criteria are that it has to be fast, and it can't rely on the memory manager. Memory use wasn't really an issue since I am working on PC where memory is more or less unlimited - at least in the development environment.
The system I implemented it is a giant hash table of pointers which point into to a giant static array of MemoryDebugBlockInfo structures. The hash table pointer array is two times the number of MemoryDebugBlockInfo structures to reduce collisions. Both of those arrays are allocated at startup so when an allocation or deallocation occurs, there is very little overhead. Free memory tracking blocks are linked together in a singly linked list which is set up during initialization, so to get a new block or free an existing one, it is one pointer change. The hash table is indexed using a DJB5 hash of the pointer returned by the memory manager, so lookups are quick and hash collisions minimal. It uses simple linear probing in the case of collisions. If a pointer doesn't exist in the table (a free with no allocation), it will search the entire table and then ASSERT.
To track memory stomps, each block gets small areas before and after each allocation which are set to known values which are checked when the block is freed to detect when the program has written beyond a block boundary.
The MemoryTracker interface:
class MemoryTracker
{
public:
MemoryTracker( void );
~MemoryTracker( void );
// Called with full debug info for block being allocated
void Allocating( MemoryDebugBlockInfo& block );
// Called with mReturnedPtr to remove block on deallocation.
void Freeing( const void *ptr );
// Tag a memory block with file and line info. Sometimes you want to tag allocations a little more specifically.
void Tag( const void *ptr, const char *fileName, unsigned int line );
// Print out a memory report, check all blocks for validity.
void Report( void );
private:
static const unsigned int MAX_TRACKED_BLOCKS = 10000; // Maximum number of blocks to track. If block count exceeds this, MemoryTracker shuts down.
unsigned int mUsedBlocks; // Number of memory tracker blocks in use
unsigned int mHashTableSize; // Size of hash table (twice number of tracker blocks)
unsigned int mBlockArraySize; // Size of block array - MAX_TRACKED_BLOCKS
unsigned int mAllocations; // Total number of allocations processed
unsigned int mFrees; // Total number of frees processed
unsigned int mMaxUsedBlocks; // Peak number of memory tracker blocks used
sizet mMemUse; // Memory use of memory tracker - this is big!
MemoryDebugBlockInfo **mHashTable; // Array of pointers, most are NULL until a block is allocated and put into the hash table.
MemoryDebugBlockInfo *mBlockArray; // Array of blocks.
MemoryDebugBlockInfo *mFreeList; // Array of free blocks, singly linked list linked together by mReturnPtr as a 'next' pointer
bool mDisabled; // True if memory tracking is temporarily disabled
Spinlock mLock; // Spinlock to protect access to memory tracker data
};
struct MemoryDebugBlockInfo
{
char mFileName[ FILE_NAME_SIZE ]; // Name of file where allocation occurred
fourcc mPool; // four character code name of module which allocated block.
byte *mBlockPtr; // Pointer to start of allocated memory manager block
sizet mBlockSize; // Size of allocated memory manager block
byte *mReturnedPtr; // Pointer returned by new/malloc
sizet mAllocatedSize; // Size of allocated memory (header + block)
sizet mHeaderSize; // Header size before mReturnedPtr
byte mDebugSuffixSize; // Size of known value block suffix for overwrite detection
byte mDebugPrefixSize; // Size of known value block prefix for overwrite detection
bool mStartupAllocation; // If block was allocated before start of main()
uint16 mLine; // Line of file where allocation occurred
uint16 mCrc; // CRC of this data, used to verify validity of memory tracker data
OS::Time::timevalue mTime; // Time of allocation
}
MemoryTracker Report
Allocations: 203068 Frees: 203024
Tracking 44 blocks, 9956 available. Peak used blocks was 2215. Tracker using 781K RAM.
Address Size[BlockSize] (File:Line) Pool
========= Normal ==========
2063912 1484[1548] includenetworkgenericpacket.h(16) dflt ............test................ B0DA800000000000050000007465737400A3A3A3A3A3A3A3A3A3A3A3A3A3A3A30
2082256 1484[1548] includenetworkgenericpacket.h(16) dflt ............test................ B0DA800000000000050000007465737400A3A3A3A3A3A3A3A3A3A3A3A3A3A3A30
...
========= Startup =========
39341016 6[70] braryutilitysourcestring.cpp(193) dflt n.A.n. 6E0041006E000
39341152 6[70] braryutilitysourcestring.cpp(193) dflt I.n.f. 49006E0066000
...
Tracking 44 blocks, 9956 available. Peak used blocks was 2215. Tracker using 781K RAM.
We were unable to retrieve our cookie from your web browser. If pressing F5 once to reload this page does not get rid of this message, please read this to learn more.
You will not be able to post until you resolve this problem.