/*
Module : MemTrace
Purpose: All memory (re)allocations take place here. Keeps it tidy.
Version: 0.01
Date : 11th July 2005
Started: 11th July 2005
Author : Rick Murray
http://www.heyrick.co.uk/eurovision/
*/
#include "EuroScore.h"
/* ALLOW THE FOLLOWING DEFINITION TO WRITE MEMTRACE INFORMATION TO STDERR
(you can't turn off the unreleased claim output - it's there for a REASON) */
// #define MTDEBUG 1
typedef struct mtdef
{
int address;
int size;
char allocmethod[1];
char parentfunc[23];
};
static struct mtdef *memtrace = NULL;
static int mtcount = 0;
#define MTMAX 32
#define CALLOC 1
#define MALLOC 2
#define REALLOC 3
void memtrace_init(void);
void memtrace_finish(void);
void memtrace_addentry(int base, int size, int method, char *parent);
void memtrace_removeentry(int base);
void *memtrace_calloc(size_t nmemb, size_t size);
void *memtrace_namedcalloc(size_t nmemb, size_t size, char *parent);
void memtrace_free(void *ptr);
void *memtrace_malloc(size_t size);
void *memtrace_namedmalloc(size_t size, char *parent);
void *memtrace_realloc(void *ptr, size_t size);
void *memtrace_namedrealloc(void *ptr, size_t size, char *parent);
void memtrace_checkclaims(FILE *stream);
void memtrace_dumpsixtyfour(int address, FILE *stream);
void memtrace_init(void)
{
/* Sets up memtrace */
memtrace = calloc(MTMAX, sizeof(struct mtdef));
if (memtrace == NULL)
{
fprintf(stderr, "Couldn't perform memtrace_init, out of memory.\n\n");
exit(EXIT_FAILURE);
}
mtcount = 0;
atexit(memtrace_finish);
return;
}
void memtrace_finish(void)
{
if (memtrace == NULL)
{
fprintf(stderr, "memtrace_finish() called while 'memtrace' struct NULL - is it being called twice?\n\n");
exit(EXIT_FAILURE);
}
memtrace_checkclaims(stderr);
/* Now free it */
if (memtrace != NULL)
{
free(memtrace);
memtrace = NULL;
}
return;
}
void memtrace_addentry(int base, int size, int method, char *parent)
{
int slot = -1;
int loop = 0;
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: memtrace_addentry()\n");
#endif
/* Try to find an unused slot */
loop = 0;
while ( (slot == -1) && (loop < MTMAX) )
{
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: Checking slot %d : %d\n", loop, memtrace[loop].address);
#endif
if (memtrace[loop].address == 0)
{
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: Slot %d is available.\n", loop);
#endif
slot = loop;
}
loop++;
}
/* If not found, make a new one */
if (slot == -1)
{
mtcount++;
slot = mtcount;
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: No slot, adding another slot, slots now %d.\n", slot);
#endif
if (mtcount >= MTMAX)
{
fprintf(stderr, "Too many claims for memtrace to follow.\n\n");
exit(EXIT_FAILURE); // may cause lots of unresolved claims
}
}
/* Set up base, size, method, and owner */
memtrace[slot].address = base;
memtrace[slot].size = size;
memtrace[slot].allocmethod[0] = method;
strcpy(memtrace[slot].parentfunc, parent);
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: Added entry at &%08X (slot %d) from %s\n", \
base, slot, memtrace[slot].parentfunc);
#endif
return;
}
void memtrace_removeentry(int base)
{
int loop = 0;
int slot = -1;
/* Find the entry */
for (loop = 0; loop < MTMAX; loop++)
{
if (memtrace[loop].address == base)
slot = loop;
}
if (slot == -1)
{
/* It isn't one we know of! */
fprintf(stderr, "memtrace_removeentry() called with address &%08X, unknown claim!\n(memory dump of area follows)\n", \
base);
memtrace_dumpsixtyfour(base, stderr);
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: memtrace_removeentry() called with address &%08X, UNKNOWN!\n", \
base);
memtrace_dumpsixtyfour(base, stderr);
#endif
exit(1);
}
#ifdef MTDEBUG
fprintf(stderr, "DEBUG: Removing entry at address &%08X (slot %d)\n", base, slot);
#endif
memtrace[slot].address = 0;
memtrace[slot].size = 0;
memtrace[slot].allocmethod[0] = 0;
/* Reduce the count, if this was the last item */
if (slot == mtcount)
mtcount--;
/* We don't bother to garbage collect (ie, if freeing item 7 and the previous 6 are free
we might consider reducing mtcount to zero, but we won't...) because the allocator will
look for blanks to fill FIRST. */
return;
}
void *memtrace_calloc(size_t nmemb, size_t size)
{
/* This looks like "calloc()" */
void *ptr;
int extent;
ptr = calloc(nmemb, size);
extent = (int)nmemb * (int)size;
memtrace_addentry( (int)ptr, extent, CALLOC, "");
return ptr;
}
void *memtrace_namedcalloc(size_t nmemb, size_t size, char *parent)
{
/* This looks like "calloc()", but includes "parent" function pointer */
void *ptr;
int extent;
ptr = calloc(nmemb, size);
extent = (int)nmemb * (int)size;
memtrace_addentry( (int)ptr, extent, CALLOC, parent);
return ptr;
}
void memtrace_free(void *ptr)
{
/* This looks like "free()" */
free(ptr);
memtrace_removeentry( (int)ptr );
return;
}
void *memtrace_malloc(size_t size)
{
/* This looks like "malloc()" */
void *ptr;
ptr = malloc(size);
memtrace_addentry( (int)ptr, (int)size, MALLOC, "");
return ptr;
}
void *memtrace_namedmalloc(size_t size, char *parent)
{
/* This looks like "malloc()", but includes "parent" function pointer */
void *ptr;
ptr = malloc(size);
memtrace_addentry( (int)ptr, (int)size, MALLOC, parent);
return ptr;
}
void *memtrace_realloc(void *ptr, size_t size)
{
/* This looks like "realloc()" */
void *newptr;
newptr = realloc(ptr, size);
if (newptr != NULL)
{
/* Only update it if it was updated... */
if (ptr != NULL)
memtrace_removeentry( (int)ptr ); /* Only if non-null, else we malloc'd */
memtrace_addentry( (int)newptr, (int)size, REALLOC, "");
}
return newptr;
}
void *memtrace_namedrealloc(void *ptr, size_t size, char *parent)
{
/* This looks like "realloc()", but includes "parent" function pointer */
void *newptr;
newptr = realloc(ptr, size);
if (newptr != NULL)
{
/* Only update it if it was updated... */
if (ptr != NULL)
memtrace_removeentry( (int)ptr ); /* Only if non-null, else we malloc'd */
memtrace_addentry( (int)newptr, (int)size, REALLOC, parent);
}
return newptr;
}
void memtrace_checkclaims(FILE *stream)
{
int loop = 0;
int first = TRUE;
for (loop = 0; loop < MTMAX; loop++)
{
if ( memtrace[loop].address != 0 )
{
if (first)
{
fprintf(stream, "memtrace claims not released:\n=============================\n\n");
first = FALSE;
}
fprintf(stream, "Claim entry %d:\n", loop);
fprintf(stream, " Base address : &%08X\n Area extent : %d bytes\n Claimed with : ",\
memtrace[loop].address, memtrace[loop].size);
switch (memtrace[loop].allocmethod[0])
{
case 0 : fprintf(stream, "!unknown!\n");
break;
case 1 : fprintf(stream, "calloc()\n");
break;
case 2 : fprintf(stream, "malloc()\n");
break;
case 3 : fprintf(stream, "realloc()\n");
break;
default: fprintf(stream, "!error - method %d!\n", memtrace[loop].allocmethod[0]);
break;
}
fprintf(stream, " Claimed by : %s\n\n", memtrace[loop].parentfunc);
memtrace_dumpsixtyfour(memtrace[loop].address, stream);
fprintf(stream, "\n\n");
}
}
return;
}
void memtrace_dumpsixtyfour(int address, FILE *stream)
{
char *offset = NULL;
int byte = 0;
int prloopouter = 0;
int prloopinner = 0;
offset = (char *)address;
fprintf(stream, "Address : 00 01 02 03 04 05 06 07 : ASCII data\n");
for (prloopouter = 0; prloopouter < 8; prloopouter++)
{
fprintf(stream, "%08X : ", (int)offset);
for (prloopinner = 0; prloopinner < 8; prloopinner++)
{
byte = offset[prloopinner];
fprintf(stream, "%02X ", byte);
}
fprintf(stream, ": ");
for (prloopinner = 0; prloopinner < 8; prloopinner++)
{
byte = offset[prloopinner];
if ( (byte < 32) || (byte == 127) )
byte = '.';
fprintf(stream, "%c", byte);
}
fprintf(stream, "\n");
offset += 8;
}
return;
}