Program: To track memory allocations in C or C++
Include following header file to track memory allocations in C or C++
/* MemTracker.h - a program to track memory allocations in C or C++ */
/* Copyright (C) 2010 Marshall Thomas */
/* Copyright (C) 2012 James McClain (I love the GPL) */
/* Version 0.1.6 9/21/2010 PRE-RELEASE DEV VERSION !!!!! */
/* - added support for MS __FUNCTION__ vs C99 __func__ */
/* - unknown problem with MS error 4291 cropped up */
/* - added 'C' 64 bit address support ... or I think so... */
/* will have to do more testing/inspection to see that was */
/* done right and also works in the Microsoft case... */
/* Version 0.1.7 9/23/2010 PRE-RELEASE DEV VERSION !!!!! */
/* - Clare: removed inline */
/* - Grant: bugs - rewording of reports */
/* - crash when FinalReport call too early */
/* MT.h couldn't handle susequent deletes */
/* - also pointer error in in MT_FreeAllMyMemory() */
/* - Realloc() didn't have proto to call the MT function */
/* - Dara: use different hash function (sprintf is slow) */
/* -fix bugs in MT_Deallocator_is_wrong (stcmp) */
/* -make MT_free friendlier (don't free memory that */
/* wasn't allocated in the first place */
/* -attempt to make realloc work */
/* -add globals to allow delete to report all info */
/*
-use atexit to automatically report status
*/
/*
right before exiting
*/
/* new version underway.........changed #defines..... */
/* fix minor goofs in my malloc, calloc stuff */
/*
* Version 0.2 11/26/2012 Open beta version.
* - Improved output greatly. Making it eaiser for
* students to read.
* -Improved the way this program works with visual studio.
*
*/
/*Usage:*/
/*
* #include "MemTracker.h" // put this at the start of your program
*
* MemTrackerFinalReport(); // prints final report and frees the tool's memory
*
// called at exit via atexit()
*
* MT_report_blocks_allocated(); // dumps current list of blocks allocated
* // If used, of course call this before
* // MemTrackerFinalReport()!!!
* // this is an optional debug method
*/
/* GNU General Public License 3 */
/* This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* The prefix MT_ or simply _ are the decorators chosen for name space separation */
/* this software program is intended to be included as a .h file */
/* LIMITATIONS:
* - this won't work with threads
* - will work with 64 bit address architecture - but total memory tracked
* must be <4GB.
* - some performance enhancements could be made, but don't appear to be
* necessary. However this is GPL3 licensed, so go for it if you want!
* - storage space of function names could be reduced, but see previous
* point!
*/
#ifndef MEMTRACKER_H
#define MEMTRACKER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_HASH_SZ 64 /*needs to be power of 2: 32,64,128,256...etc */
#define REDZONE_SIZE 4
#define REDZONE_STR "xxxx"
#ifdef _MSC_VER
/*Microsoft 4996 is a non-thread safe warning for various C library funcs*/
#pragma warning (disable : 4996)
/*Microsoft 4291 is not easy to understand about what happens if new fails*/
#pragma warning (disable : 4291)
/*Microsoft doesn't support __func__ or __FUNCTION in Visual Studio 6.x or older */
#if _MSC_VER <= 1200
#define __func__ "no name"
#else
/*Microsoft doesn't support C99 __func__ macro yet in Visual Studio 2008 */
#define __func__ __FUNCTION__
#endif
#endif
typedef struct _HASH_ENTRY
{
unsigned long int address; /* address of memory allocated */
unsigned int size_mem; /* size of memory allocated (bytes) */
char allocation_TLA[4]; /* MAL,CAL,RAL,NEW,VEC */
unsigned int sequence_num; /* sequential number of creation */
unsigned int source_line; /* actual line in the code */
char* function_name; /* function name containing the alloc */
char* file_name; /* name of file containing the alloc */
unsigned int hash_allbits; /* for re-masking and expansion later */
struct _HASH_ENTRY* next; /* a simple stack */
} _HASH_ENTRY;
typedef struct _ERRORS {
char* allocation_TLA; // If this is empty the deallocater was called but
// no allocater was.
char* deallocation_TLA;
unsigned int source_line;
char* function_name;
char* file_name;
struct _ERRORS* next;
} _ERRORS;
typedef struct _LEAKED_LIST { // Holds memory leaks so they can be printed nicely.
unsigned int source_line;
int mul; // How many times has this line been called and triggered a leak.
unsigned int size_mem;
char* function_name;
char* file_name;
struct _LEAKED_LIST* next;
} _LEAKED_LIST;
typedef struct _HASH_TABLE
{
unsigned int total_rows;
unsigned int rows_used;
unsigned int total_entries;
unsigned int total_bytes; /* sum of all bytes allocated */
unsigned int next_serial_num;
unsigned int err_total_incompatible; /*MAL,CAL,RAL uses FRE; NEW uses DEL; VEC uses DVE */
unsigned int err_total_unallocated; /*delete of unallocated memory attempted */
unsigned int current_bit_mask; /*total_rows -1 (0xF for 16 slots) */
struct _HASH_ENTRY **hash_array;
struct _ERRORS *error_array; /* Record Errors such as using the wrong type
of delete or deallocating stuff that has
already been allocated, so the errors
can be reported at the end.*/
struct _LEAKED_LIST *leaked_list;
} _HASH_TABLE;
/*****************************/
/* prototypes */
/*****************************/
void MT_init_hash();
void MT_free_hentry (_HASH_ENTRY* ptr);
unsigned int MT_gen_hash_bits (unsigned long int address);
unsigned int MT_gen_hash_array_index (unsigned long int address);
int MT_should_double();
_HASH_ENTRY* MT_find_entry(unsigned long int address);
int MT_is_deallocater_wrong(const char* alloc_TLA, const char* dealloc_TLA);
int MT_delete_entry (unsigned long int address, const char* TLA, int line,
const char* func, const char* file_name);
void MT_double_hash();
int MT_add_entry(unsigned long int address, unsigned int size, const char * TLA,
unsigned int source_line, const char* func_name,
const char* file_name);
int MT_modify_entry (unsigned long int old_address, unsigned long int new_address,
unsigned int size, const char *TLA,
unsigned int source_line, const char* function_name,
const char* file_name);
void MT_dump_stats();
void MT_dump_hash();
_HASH_ENTRY** MT_all_entries();
int MT_compare_seq(const void* vpa, const void* vpb);
void MT_dump_he(_HASH_ENTRY *p);
int MT_report_blocks_allocated(int speak);
void MT_FreeAllMyMemory();
void MemTrackerFinalReport();
void *MT_Malloc(size_t size, int line, const char* func_name, const char* file_name);
void *MT_Calloc(size_t numMembers, size_t size, int line, const char* func_name,
const char* file_name);
void *MT_Realloc(void *ptr, size_t size, int line, const char* func_name,
const char* file_name);
void MT_Free (void* ptr, int line, const char *func, const char *file_name);
void MT_AddError(const char* allocation_TLA,const char* deallocation_TLA,unsigned int source_line,const char* function_name,const char* file_name);
void MT_AddLeak(const char* file_name,const char * function_name,unsigned int source_line,unsigned int size_mem);
/*****************************/
/* main static hash table */
/*****************************/
static _HASH_TABLE _hashtable;
/* a hack to work around limitations with overriding the C++ delete operator */
const char *_mt_src_file;
const char *_mt_src_func;
int _mt_src_line;
void MT_init_hash()
{
_hashtable.total_rows = INITIAL_HASH_SZ;
_hashtable.rows_used = 0;
_hashtable.total_entries = 0;
_hashtable.next_serial_num = 1;
_hashtable.total_bytes = 0;
_hashtable.err_total_incompatible = 0;
_hashtable.err_total_unallocated = 0;
_hashtable.current_bit_mask = INITIAL_HASH_SZ-1;
_hashtable.hash_array = (_HASH_ENTRY**) calloc(INITIAL_HASH_SZ, sizeof (_HASH_ENTRY*));
_hashtable.error_array = NULL;
_hashtable.leaked_list = NULL;
if (!_hashtable.hash_array)
{
fprintf (stderr, "MemTracker: Internal Error: main hash table init error - calloc()");
exit (2);
}
atexit(MemTrackerFinalReport);
}
void MT_AddLeak(const char* file_name,const char * function_name,unsigned int source_line,unsigned int size_mem) {
_LEAKED_LIST *ptr = NULL;
ptr = (_LEAKED_LIST *) calloc(1,sizeof(_LEAKED_LIST));
ptr->file_name = (char *) calloc(strlen(file_name)+1,sizeof(char));
strcpy(ptr->file_name,file_name);
ptr->function_name = (char *) calloc(strlen(function_name)+1,sizeof(char));
strcpy(ptr->function_name,function_name);
ptr->source_line = source_line;
ptr->size_mem = size_mem;
ptr->mul = 1;
_LEAKED_LIST *walker = NULL;
_LEAKED_LIST *last = NULL;
if(! _hashtable.leaked_list) {// first call
_hashtable.leaked_list = ptr;
_hashtable.leaked_list->next = NULL;
return;
}
for(walker = _hashtable.leaked_list;walker;walker = walker->next) {
if(source_line == walker->source_line) {
if(walker->mul == -1) {
walker->size_mem += size_mem;
} else if(walker->size_mem == size_mem) {
walker->mul += 1;
} else {
walker->size_mem += walker->size_mem * (walker->mul -1);
walker->mul = -1;
}
free(ptr); //b We didn't actually need it here.
// it's possible to check this stuff before hand and be more
// efficient, be my guest if that is what you like to do.
return;
}
if(source_line < walker->source_line) {
if(!last) { // The line is less then the first one.
ptr->next = walker;
_hashtable.leaked_list = ptr;
} else {
last->next = ptr;
ptr->next = walker;
}
return;
}
last = walker;
}
// If it's the biggest line so far.
last->next = ptr;
ptr->next = NULL;
}
void MT_AddError(const char* allocation_TLA,const char* deallocation_TLA,unsigned int source_line,const char* function_name,const char* file_name) {
_ERRORS *ptr = NULL;
ptr = (_ERRORS *) calloc(1,sizeof(_ERRORS));
if(! (allocation_TLA)) {
// Then this is not a mismatch, it is a double deallocation call.
ptr->allocation_TLA = NULL;
ptr->deallocation_TLA =
(char *) calloc(strlen(deallocation_TLA)+1,sizeof(char));
strcpy(ptr->deallocation_TLA,deallocation_TLA);
} else {
ptr->allocation_TLA = (char *) calloc(strlen(allocation_TLA)+1,sizeof(char));
strcpy(ptr->allocation_TLA,allocation_TLA);
ptr->deallocation_TLA =
(char *) calloc(strlen(deallocation_TLA)+1,sizeof(char));
strcpy(ptr->deallocation_TLA,deallocation_TLA);
}
ptr->source_line = source_line;
ptr->function_name = (char *) calloc(strlen(function_name)+1,sizeof(char));
strcpy(ptr->function_name,function_name);
ptr->file_name = (char *) calloc(strlen(file_name)+1,sizeof(char));
strcpy(ptr->file_name,file_name);
_ERRORS *walker = NULL;
_ERRORS *last = NULL;
if(! _hashtable.error_array) {// first call
_hashtable.error_array = ptr;
_hashtable.error_array->next = NULL;
return;
}
for(walker = _hashtable.error_array;walker;walker = walker->next) {
if(source_line < walker->source_line) {
if(!last) { // The line is less then the first one.
ptr->next = walker;
_hashtable.error_array = ptr;
} else {
last->next = ptr;
ptr->next = walker;
}
return;
}
last = walker;
}
// If it's the biggest line so far.
last->next = ptr;
ptr->next = NULL;
}
void MT_free_hentry (_HASH_ENTRY* ptr)
{
if (ptr == NULL) {printf ("DIE\n"); exit(5);}
if (ptr->function_name)
free (ptr->function_name);
if (ptr->file_name)
free (ptr->file_name);
free(ptr);
}
/* MT_gen_hash_bits2()
* Thomas Wang's 64-bit hash function - works well for integers, and is significantly
* faster than the DJB function since the key is not ASCII. It is also slightly
* better in distributing keys.
* http://www.concentric.net/~Ttwang/tech/inthash.htm
*/
unsigned int MT_gen_hash_bits (unsigned long int address)
{
address = (~address) + (address << 21); // address = (address << 21) - address - 1;
address = address ^ (address >> 24);
address = (address + (address << 3)) + (address << 8); // address * 265
address = address ^ (address >> 14);
address = (address + (address << 2)) + (address << 4); // address * 21
address = address ^ (address >> 28);
address = address + (address << 31);
return (unsigned int)address;
}
unsigned int MT_gen_hash_array_index (unsigned long int address)
{
unsigned int all_bits = MT_gen_hash_bits(address);
return( all_bits & _hashtable.current_bit_mask );
}
int MT_should_double() /* hash table automagically doubles when needed */
{
if(_hashtable.total_entries > _hashtable.total_rows-1) return 1;
return 0;
}
_HASH_ENTRY* MT_find_entry(unsigned long int address)
{
unsigned int i = MT_gen_hash_array_index(address);
_HASH_ENTRY* mover = _hashtable.hash_array[i];
while (mover)
{
if ( mover->address == address)
return mover; /* found!! */
mover = mover -> next;
}
return NULL; /* not found */
}
/* MemTracker keeps a record of what kind of allocation method was
* used to allocate the memory in the first place.
* The user's program must use a compatible method to deallocate the
* memory otherwise an error will be reported.
*
* The C allocation methods of malloc(), calloc(), realloc() must use free()
* to de-allocate that memory.
*
* The C++ allocation method scalar "new" must use scalar "delete".
* The C++ allocation method of the vector form of "new" must use the
* vector form of "delete".
* This is an important error as although calling free() on a C++ array
* of objects will indeed free memory for those objects, the destructors
* for those objects will not be called! Oops!
*
* I use TLA's (Three Letter Acronymns) to track this stuff.
*/
int MT_is_deallocater_wrong(const char* alloc_TLA, const char* dealloc_TLA)
{
int ok =0;
int bad=1;
if ( ( strcmp(dealloc_TLA,"FRE") == 0 || strcmp(dealloc_TLA,"RAL") == 0)
&& ( strcmp(alloc_TLA,"MAL") == 0 || strcmp(alloc_TLA,"CAL") == 0
|| strcmp(alloc_TLA,"RAL") == 0)
) { return ok; }
if ( strcmp(dealloc_TLA,"DEL") == 0 /* C++ scalar delete */
&& strcmp(alloc_TLA,"NEW") == 0
) return ok;
if ( strcmp(dealloc_TLA,"VDE") == 0 /* C++ vector delete */
&& strcmp(alloc_TLA,"VEC") == 0
) return ok;
_hashtable.err_total_incompatible++;
return bad;
}
void MT_init_redzone( void *address, unsigned long int size )
{
char *start = (char *)address + size - REDZONE_SIZE;
strncpy(start, REDZONE_STR, REDZONE_SIZE);
}
int MT_check_redzone( void *address, unsigned long int size )
{
char *start = (char *)address + size - REDZONE_SIZE;
if(strncmp(start, REDZONE_STR, REDZONE_SIZE) != 0) return 0;
return 1;
}
int MT_delete_entry ( unsigned long int address, const char* TLA, int line,
const char* func, const char* file_name )
{
_HASH_ENTRY* mover = NULL;
_HASH_ENTRY* prev = NULL;
unsigned int i;
if (_hashtable.hash_array == NULL )
return (1); /* Probable cause: MemTrackerFinalReport() called too early */
/* C++ is continuing to run destructor methods */
/* I consider that "ok" */
i = MT_gen_hash_array_index(address);
if (!_hashtable.hash_array[i])
{
/* fprintf (stderr, "MemTracker: %s:%u func:%s() Deallocator called but 0x%lX not allocated!\n", */
/* file_name, line, func, address); */
MT_AddError(NULL,TLA,line,func,file_name);
_hashtable.err_total_unallocated++;
return 0; /* fail */
}
mover = _hashtable.hash_array[i];
/* zap the entry on the stack if we can */
while (mover)
{
if (mover->address == address)
{
if ( !MT_check_redzone((void *)address, mover->size_mem))
{
fprintf (stderr, "MemTracker: %s:%u func:%s() Heap corruption before deallocator called!\n",
file_name, line, func);
}
if ( MT_is_deallocater_wrong(mover->allocation_TLA, TLA) )
{
/* fprintf (stderr, "MemTracker: %s:%u func:%s() Deallocator %s not compatible with %s!\n", */
/* file_name, line, func, mover->allocation_TLA, TLA); */
MT_AddError(mover->allocation_TLA,TLA,line,func,file_name);
_hashtable.err_total_incompatible++;
/* keep going..the error has been reported..user code may still run! */
/* we let the user program do what it can */
}
_hashtable.total_bytes -= mover->size_mem;
if (!prev)
_hashtable.hash_array[i] = mover->next;
else prev->next = mover->next;
if ( !_hashtable.hash_array[i] )
_hashtable.rows_used--;
_hashtable.total_entries--;
MT_free_hentry (mover);
return 1; /* success */
}
prev = mover;
mover = mover->next;
}
fprintf (stderr, "MemTracker: %s:%u func:%s() Deallocator called but 0x%lX not allocated!\n",
file_name, line, func, address);
_hashtable.err_total_unallocated++;
return 0; /* fail */
}
void MT_double_hash()
{
_HASH_ENTRY **old_hash = _hashtable.hash_array;
_HASH_ENTRY *mover = NULL;
int n_rows = _hashtable.total_rows;
int i;
_hashtable.total_rows = n_rows * 2;
_hashtable.current_bit_mask = _hashtable.total_rows -1;
_hashtable.rows_used =0;
_hashtable.hash_array =(_HASH_ENTRY**) calloc(_hashtable.total_rows, sizeof (_HASH_ENTRY*));
if (!_hashtable.hash_array)
{
fprintf (stderr, "MemTracker: Internal error: calloc failed for main hash table\n");
exit(2);
}
for ( i=0; i<n_rows; i++)
{
mover = old_hash[i];
while (mover)
{
_HASH_ENTRY *mover_next = mover -> next;
unsigned int new_row = ( mover->hash_allbits
& _hashtable.current_bit_mask);
/*add to front of new stack */
_HASH_ENTRY* temp = _hashtable.hash_array[new_row];
if (!temp)
{
_hashtable.hash_array[new_row] = mover;
mover -> next = NULL;
_hashtable.rows_used++;
}
else
{
mover->next = temp;
_hashtable.hash_array[new_row] = mover;
}
mover = mover_next;
}
}
free (old_hash);
}
int MT_add_entry(unsigned long int address, unsigned int size, const char * TLA,
unsigned int source_line, const char* func_name, const char* file_name)
{
unsigned int all_bits, i;
_HASH_ENTRY* p_new_entry, *temp;
if (!_hashtable.hash_array) MT_init_hash (); /* automagic init() */
if (MT_should_double()) MT_double_hash();
if ( MT_find_entry(address) ) /* O/S or internal error */
{
fprintf (stderr, "MemTracker: Table Error: Address %lu already in use! %s:%u func:%s()\n",
address, file_name, source_line, func_name);
exit(2);
}
all_bits = MT_gen_hash_bits (address);
i = all_bits & _hashtable.current_bit_mask;
p_new_entry = (_HASH_ENTRY*) calloc(1, sizeof (_HASH_ENTRY) );
if (!p_new_entry)
{
fprintf (stderr, "MemTracker: Internal error: calloc failed for new hash entry\n");
exit (2);
}
p_new_entry->address = address;
p_new_entry->hash_allbits = all_bits;
p_new_entry->sequence_num = _hashtable.next_serial_num++;
p_new_entry->size_mem = size;
p_new_entry->source_line = source_line;
p_new_entry->next = NULL;
strncpy (p_new_entry->allocation_TLA, TLA,3);
/* insert new entry on top of stack*/
temp = _hashtable.hash_array[i];
if (!temp)
{
_hashtable.rows_used++;
_hashtable.hash_array[i] = p_new_entry;
}
else
{
p_new_entry->next = temp;
_hashtable.hash_array[i] = p_new_entry;
}
p_new_entry->function_name =(char*) malloc(strlen(func_name)+1);
if (!p_new_entry->function_name)
{
fprintf (stderr, "MemTracker: Internal error: malloc failed for function name\n");
exit (2);
}
strcpy(p_new_entry->function_name, func_name);
p_new_entry->file_name =(char*) malloc(strlen(file_name)+1);
if (!p_new_entry->file_name)
{
fprintf (stderr, "MemTracker: Internal error: malloc failed for file name\n");
exit (2);
}
strcpy(p_new_entry->file_name, file_name);
_hashtable.total_entries++;
_hashtable.total_bytes += size;
return 1; /* success */
}
int MT_modify_entry (unsigned long int old_address, unsigned long int new_address,
unsigned int size, const char* TLA,
unsigned int source_line, const char* function_name,
const char* file_name)
{
/* this is the realloc case */
_HASH_ENTRY* ptr;
if (! (ptr = MT_find_entry(old_address) ) )
{
fprintf (stderr, "MemTracker: %s:%u func:%s() realloc called but 0x%lX not allocated!\n",
file_name, source_line, function_name, old_address);
return 1; /* fail */
}
MT_delete_entry(old_address, "RAL", source_line, function_name, file_name);
MT_add_entry(new_address, size, TLA , source_line, function_name, file_name);
return 0; /* success */
}
void MT_dump_stats()
{
printf ("\nDump stats called\n");
printf ("total_rows %u\n", _hashtable.total_rows);
printf ("rows_used: %u\n", _hashtable.rows_used);
printf ("total_entries: %u\n", _hashtable.total_entries);
printf ("last_serial_num: %u\n", _hashtable.next_serial_num-1);
printf ("total bytes alloc %u\n", _hashtable.total_bytes);
printf ("bit mask: %X\n", _hashtable.current_bit_mask);
}
void MT_dump_hash()
{
unsigned int i;
for ( i =0; i< _hashtable.total_rows; i++)
{
_HASH_ENTRY* mover = _hashtable.hash_array[i];
printf ("\nrow[%u]",i);
while (mover)
{
printf ("n=%u:a=0x%lX:size=%u ", mover->sequence_num,
mover->address,
mover->size_mem);
mover = mover->next;
}
}
}
_HASH_ENTRY** MT_all_entries() /* list of all current hash table entries */
{
_HASH_ENTRY *mover;
_HASH_ENTRY **lmover;
_HASH_ENTRY ** list;
unsigned int i;
if (!_hashtable.total_entries) return NULL;
list = (_HASH_ENTRY**) calloc(_hashtable.total_entries +1, sizeof(_HASH_ENTRY*));
if (!list)
{
fprintf (stderr, "MemTracker: Internal error: malloc failed in MT_all_entries()\n");
exit(2);
}
lmover = list;
for (i=0; i<_hashtable.total_rows; i++)
{
if (_hashtable.hash_array != NULL)
{
mover = _hashtable.hash_array[i];
while (mover)
{
*lmover++ = mover;
mover = mover -> next;
}
}
}
return list;
}
int MT_compare_seq(const void* vpa, const void* vpb)
{
_HASH_ENTRY** a = (_HASH_ENTRY**)vpa;
_HASH_ENTRY** b = (_HASH_ENTRY**)vpb;
return ( ((*a)->sequence_num) - ((*b)->sequence_num) );
}
void MT_dump_he(_HASH_ENTRY *p) /* dump a hash entry */
{
/* printf ("n=%u:a=0x%lX:size=%u:type=%s:line=%u:func=%s:file=%s\n", p->sequence_num, */
/* p->address, */
/* p->size_mem - REDZONE_SIZE, */
/* p->allocation_TLA, */
/* p->source_line, */
/* p->function_name, */
/* p->file_name); */
// printf("%s:%-3d - %u bytes lost",p->file_name,p->source_line,p->size_mem - REDZONE_SIZE);
MT_AddLeak(p->file_name,p->function_name,p->source_line,p->size_mem - REDZONE_SIZE);
}
int MT_report_blocks_allocated(int speak) /* dumps sorted list by serial_num of all hash entries */
{
_HASH_ENTRY **list = MT_all_entries();
_HASH_ENTRY **mover = list;
qsort (list, _hashtable.total_entries, sizeof(_HASH_ENTRY**), MT_compare_seq);
// printf ("\n");
int leakNumber = 0;
if (mover)
{
while (*mover)
{
if(speak) {
MT_dump_he(*mover);
}
leakNumber++;
mover++;
}
}
free(list);
return leakNumber;
}
void MT_FreeAllMyMemory() /* avoid embarassing leak ourselves! */
{
_HASH_ENTRY** list = MT_all_entries();
_HASH_ENTRY** lmover = list;
if (list)
{
while (*lmover)
{
MT_free_hentry(*lmover);
lmover++;
}
free (list);
}
free (_hashtable.hash_array);
_hashtable.hash_array = NULL;
// Time to free ERRORS and LEAKED_LIST
_ERRORS *ePtr;
_ERRORS *toFree;
ePtr = _hashtable.error_array;
while(ePtr) {
toFree = ePtr;
ePtr = ePtr->next;
if(toFree->allocation_TLA) {
free(toFree->allocation_TLA);
}
if(toFree->deallocation_TLA) {
free(toFree->deallocation_TLA);
}
if(toFree->function_name) {
free(toFree->function_name);
}
if(toFree->file_name) {
free(toFree->file_name);
}
free(toFree);
}
_LEAKED_LIST *lPtr;
_LEAKED_LIST *ltoFree;
lPtr = _hashtable.leaked_list;
while(lPtr) {
ltoFree = lPtr;
lPtr = lPtr->next;
if(ltoFree->file_name){
free(ltoFree->file_name);
}
if(ltoFree->function_name){
free(ltoFree->function_name);
}
free(ltoFree);
}
}
void MemTrackerFinalReport()
{
const char * getTLA(char *TLA);
int size;
size = MT_report_blocks_allocated(1);
if(_hashtable.total_bytes != 0) {
printf("\nYour program contains memory leaks!\n");
printf("Total bytes lost: %d\n",_hashtable.total_bytes - REDZONE_SIZE * (size));
printf("########### START LIST OF LEAKS ###########\n");
_LEAKED_LIST *ptr2 = NULL;
char *lastFuncName2 = (char *)"fewfewgewgewgw";
int lastLineNum2 = -1;
for(ptr2 = _hashtable.leaked_list;ptr2;ptr2 = ptr2->next) {
if(strcmp(lastFuncName2,ptr2->function_name) != 0) {
printf("%s()\n",ptr2->function_name);
lastFuncName2 = ptr2->function_name;
}
if((int)ptr2->source_line != lastLineNum2) {
printf(" %s:%-3d - ",ptr2->file_name,ptr2->source_line);
if(ptr2->mul == 1 || ptr2->mul == -1) {
printf("%u bytes lost.\n",ptr2->size_mem);
} else {
printf("%u bytes lost %u times.\n",ptr2->size_mem,ptr2->mul);
}
lastLineNum2 = ptr2->source_line;
}
}
printf("########### END LIST OF LEAKS ###########\n");
}
if(_hashtable.total_bytes == 0) {
printf("\nCongratulations, your program has no memory leaks.\n");
if(_hashtable.error_array != NULL) {
printf("HOWEVER, errors have been detected!\n");
}
}
if(_hashtable.error_array != NULL) {
printf("########### START LIST OF NON-LEAK ERRORS ###########\n");
_ERRORS *ptr = NULL;
char * lastFuncName = (char *)"fewagewgreqwteqw"; // setinal value.
// please don't name your function this
// a random hiku
int lastLineNum = -1;
for(ptr = _hashtable.error_array;ptr;ptr = ptr->next) {
if(strcmp(lastFuncName,ptr->function_name) != 0) {
printf("%s()\n",ptr->function_name);
lastFuncName = ptr->function_name;
}
if((int)ptr->source_line != lastLineNum) {
printf(" %s:%-3d - ",ptr->file_name,ptr->source_line);
if(ptr->allocation_TLA) { // it's a mismatch error
printf("Using \"%s\" to free an object allocated with \"%s\".\n",
getTLA(ptr->deallocation_TLA),getTLA(ptr->allocation_TLA));
} else { // deallocation was called on a nonallocated object.
printf("\"%s\" was called but nothing was allocated at that time.\n",
getTLA(ptr->deallocation_TLA));
}
lastLineNum = ptr->source_line;
}
}
printf("########### END LIST OF NON-LEAK ERRORS ###########\n");
}
MT_FreeAllMyMemory();
printf("\n - Memory report done, press any key to exit. - \n");
getc(stdin);
return;
}
const char * getTLA(char *TLA) {
if(strcmp(TLA,"DEL") == 0) {
return "delete";
}else if (strcmp(TLA,"VDE") == 0) {
return "delete[]";
}else if (strcmp(TLA,"NEW") == 0) {
return "new";
}else if (strcmp(TLA,"VEC") == 0) {
return "new[]";
}else if (strcmp(TLA,"MAL") == 0) {
return "malloc";
}else if (strcmp(TLA,"CAL") == 0) {
return "calloc";
}else if (strcmp(TLA,"RAL") == 0) {
return "realloc";
}else if (strcmp(TLA,"FRE") == 0) {
return "free";
}
return "WTF";
}
void *MT_Malloc(size_t size, int line, const char* func_name, const char* file_name)
{
void *ptr;
size += REDZONE_SIZE;
if ((ptr = (void *) malloc(size)) == NULL)
{
perror("malloc failed to get memory!");
exit(1);
}
MT_add_entry((unsigned long int)ptr, size, "MAL", line, func_name, file_name);
MT_init_redzone(ptr, size);
return ptr;
}
void *MT_Calloc(size_t numMembers, size_t size, int line, const char* func_name,
const char *file_name)
{
void *ptr;
size = numMembers * size + REDZONE_SIZE;
if ((ptr = (void *) malloc(size)) == NULL)
{
perror("calloc failed to get memory!");
exit(1);
}
memset(ptr, 0, size);
MT_add_entry((unsigned long int)ptr, size, "CAL", line, func_name,
file_name);
MT_init_redzone(ptr, size);
return ptr;
}
void *MT_Realloc(void *ptr, size_t size, int line, const char* func_name,
const char* file_name)
{
void *newptr;
size += REDZONE_SIZE;
if(!ptr)
{
if ((newptr = (void *) realloc(ptr, size)) == NULL)
{
perror("realloc failed to resize memory!");
exit(1);
}
MT_add_entry((unsigned long int)newptr, size, "RAL", line, func_name, file_name);
}
else if(!MT_find_entry((unsigned long int)ptr))
{
fprintf (stderr, "MemTracker: %s:%u func:%s() realloc called but 0x%lX not allocated!\n",
file_name, line, func_name, (unsigned long int)ptr);
return ptr;
}
else
{
if ((newptr = (void *) realloc(ptr, size)) == NULL)
{
perror("realloc failed to resize memory!");
exit(1);
}
MT_modify_entry((unsigned long int) ptr, (unsigned long int)newptr, size, "RAL",
line, func_name, file_name);
}
MT_init_redzone(newptr, size);
return newptr;
}
void MT_Free (void* ptr, int line, const char* func, const char* file_name)
{
if(MT_delete_entry((unsigned long int)ptr, "FRE", line, func, file_name))
free(ptr);
return;
}
/******************** C++ overrides **********************/
#ifdef __cplusplus
/*this appears to be a very slightly documented interface and */
/*the ramifications of this are unknown*/
void operator delete(void* pMem, char* pszFilename, int nLine)
{
fprintf (stderr, "WOW, WOWIE ***********\n");
fprintf (stderr, "This function shouldn't be called! \n");
fprintf (stderr, "address=0x%lX name=%s, line=%d\n", (unsigned long int)pMem, pszFilename, nLine);
free(pMem); /* http://msdn.microsoft.com/en-us/library/cxdxz3x6%28VS.80%29.aspx */
}
void* operator new(size_t size, unsigned int line, const char* func_name,
const char* file_name)
{
void *ptr;
size += REDZONE_SIZE;
if ((ptr = (void *) malloc(size)) == NULL)
{
perror("C++ function \"new\" failed to get memory!");
exit(1);
}
MT_add_entry((unsigned long int)ptr, size, "NEW", line, func_name, file_name);
MT_init_redzone(ptr, size);
return ptr;
}
void* operator new [] (size_t size, unsigned int line, const char* func_name,
const char* file_name)
{
void *ptr;
size += REDZONE_SIZE;
if ((ptr = (void *) malloc(size)) == NULL)
{
perror("C++ Vector form of \"new\" failed to get memory!");
exit(1);
}
MT_add_entry((unsigned long int)ptr, size, "VEC", line, func_name, file_name);
MT_init_redzone(ptr, size);
return ptr;
}
/* C++ scalar delete */
/* The default delete "throws" (), I should claim the same */
/* even if I don't throw anything! Some compilers require this */
void operator delete(void* ptr) throw ()
{
if(MT_delete_entry((unsigned long int)ptr ,"DEL", _mt_src_line, _mt_src_func, _mt_src_file))
free(ptr);
}
/* C++ vector delete */
/* The default delete[] "throws" (), I should claim the same */
/* even if I don't throw anything! Some compilers require this */
void operator delete [] (void* ptr) throw ()
{
if(MT_delete_entry((unsigned long int)ptr, "VDE", _mt_src_line, _mt_src_func, _mt_src_file))
free(ptr);
}
#define new new(__LINE__, __func__, __FILE__)
#define delete _mt_src_file=__FILE__, _mt_src_func=__func__, _mt_src_line=__LINE__, delete
#endif
/************* end of C++ specific ************/
#define malloc(size) MT_Malloc (size, __LINE__, __func__, __FILE__)
#define calloc(n,size) MT_Calloc (n, size, __LINE__, __func__, __FILE__)
#define free(adr) MT_Free (adr, __LINE__, __func__, __FILE__)
#define realloc(ptr, size) MT_Realloc(ptr, size, __LINE__, __func__, __FILE__);
#endif