/*
memory.c
UltraRogue: The Ultimate Adventure in the Dungeons of Doom
Copyright (C) 1995 Herb Chong
All rights reserved.
See the file LICENSE.TXT for full copyright and licensing information.
*/
#include <stdio.h>
#include <stdlib.h>
#include "dict.h"
#include "memory.h"
#include "rogue.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
static char sccsid[] = "%W%\t%G%";
/* Debugging memory allocation code that tries to trap common memory problems
like overwriting storage and stepping on memory pointer chains. If code
doesn't use malloc, free, and realloc a lot, these routines can be left in
as added protection against undetected storage bugs.
*/
/* FENCE_SIZE should be a multiple of sizeof(size_t) to prevent alignment problems.
The code assumes that malloc and realloc return pointers aligned at least on size_t
sized boundaries and that a pointer needs alignment no more strict than that of an
object needed to hold a size_t.
*/
#define FENCE_SIZE (sizeof(size_t) * 1024)
static int memdebug_level = 0;
static DICTIONARY *allocations = NULL;
static FILE *trace_file = NULL;
/* set the debug level */
void mem_debug(const int level)
{
memdebug_level = level;
#ifdef MEM_DEBUG
if (trace_file == NULL)
trace_file = fopen("trace", "w");
/* all except 0, 1, and unknown fall through */
switch(memdebug_level) {
case 2:
fprintf(trace_file, "+++ Memory tracking possible, ");
case 1:
fprintf(trace_file, "+++ Memory debugging enabled, ");
break;
case 0:
fprintf(trace_file, "+++ Memory debugging disabled, ");
break;
default:
fprintf(trace_file, "!!! Unknown memory debug level set, enabling level 1, ");
memdebug_level = 1;
break;
}
fprintf(trace_file, "fence size = %d\n", FENCE_SIZE);
#endif
}
/* set memory tracking on or off */
/* turning it off deletes all tracking data */
void mem_tracking(int flag)
{
#ifdef MEM_DEBUG
/* do nothing if debuglevel is too low */
if (memdebug_level < 2)
return;
/* turn on tracking */
if (flag > 0) {
if (allocations != NULL) {
dict_destroy(allocations);
allocations = NULL;
}
allocations = dict_create(8, 100, 4, 20);
if (allocations == NULL) {
fprintf(trace_file, "!!! Unable to allocate tracking table!\n");
abort();
}
}
/* turn off tracking */
else if (allocations != NULL) {
dict_destroy(allocations);
allocations = NULL;
}
#endif
}
/* go through all pointers and see if they are OK, aborting if not */
/* always returns 1 if not aborting so that it can be included in */
/* if statement boolean expressions */
int mem_check(char *fname, int linenum)
{
#ifdef MEM_DEBUG
STRING_ENTRY *se;
/* scan of a NULL dictionary always succeeds */
if (allocations == NULL)
return TRUE;
if (!dict_scan_begin(allocations)) {
fprintf(trace_file, "!!! Dictionary scan initialization failed!\n");
abort();
}
fprintf(trace_file, "\n+++ --- Starting pointer scan\n");
fprintf(trace_file, "+++ --- At %s, %d\n", fname, linenum);
/* mem_validate aborts if there is a problem */
while((se = dict_scan_next(allocations)) != NULL)
mem_validate(se->any_ptr);
fprintf(trace_file, "+++ --- Done pointer scan\n\n");
#endif
/* always return a good value if execution arrives here */
return 1;
}
/* allocate some memory and initialize header and trailer */
void *mem_malloc(const size_t bytes)
{
#ifdef MEM_DEBUG
char *mem_temp;
size_t real_size = bytes + (FENCE_SIZE << 1);
/* allocate including guard bytes to detect some ways of overwriting of memory areas */
mem_temp = (void *)malloc(real_size);
if (memdebug_level > 0) {
fprintf(trace_file, "+++ Requested size of %ld bytes\n", bytes);
fprintf(trace_file, "+++ Actual malloc of %ld bytes located at %p\n", real_size, mem_temp);
}
/* if allocation succeeded, set management data */
if (mem_temp != NULL) {
size_t i;
char *end;
/* do beginning marker bytes */
for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++)
*mem_temp++ = 145;
/* save size in header too */
if (memdebug_level > 0)
fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp);
*(size_t *)mem_temp = bytes;
/* finally, point to storage we are going to hand out */
mem_temp += sizeof(size_t);
/* now, point to trailer bytes and do them */
end = mem_temp + bytes;
for (i = 0; i < FENCE_SIZE; i++)
*end++ = 145;
/* now zap contents to zero */
for (i = 0; i < bytes; i++)
mem_temp[i] = 0;
}
/* track pointer if needed */
if (memdebug_level > 1 && allocations != NULL) {
char key[16];
long temp;
sprintf(key, "%p", mem_temp);
if (dict_insert(allocations, key, 1, (const unsigned long) bytes, mem_temp, &temp) == NULL) {
fprintf(trace_file, "!!! Insert of pointer tracking info failed\n");
abort();
}
}
/* allow caller to do error handling */
if (memdebug_level > 0) {
fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp);
fflush(trace_file);
}
return (void *)mem_temp;
#else
return malloc(bytes);
#endif
}
/* release some memory, making sure that it was properly allocated */
void mem_free(const void *ptr)
{
#ifdef MEM_DEBUG
char *mem_temp;
size_t mem_size;
size_t i;
if (memdebug_level > 0)
fprintf(trace_file, "+++ Free of memory located at %p\n", ptr);
if (ptr == NULL) {
if (memdebug_level > 0) {
fprintf(trace_file, "!!! Freeing NULL pointer\n");
fflush(trace_file);
}
abort();
}
mem_validate(ptr); /* doesn't return on error */
/* get location of size of area */
mem_temp = (char *)ptr - sizeof(size_t);
/* get and calculate real size */
mem_size = *(size_t *)mem_temp + (FENCE_SIZE << 1);
/* if doing memory tracking */
if (memdebug_level > 1 && allocations != NULL) {
char key[16];
STRING_ENTRY *se;
long temp;
sprintf(key, "%p", ptr);
if ((se = dict_search(allocations, key, &temp)) == NULL) {
fprintf(trace_file, "!!! Deleting pointer not found in tracking info\n");
abort();
}
if (se->count == 0) {
fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n");
abort();
}
else if (se->flags != mem_size - (FENCE_SIZE << 1)) {
fprintf(trace_file, "!!! Stored size different from tracking size!\n");
abort();
}
/* remember deleted stuff by zeroing the allocation count */
se->count = 0;
se->flags = 0;
}
/* zap bytes being freed */
for (i = 0, mem_temp = (char *)ptr - FENCE_SIZE; i < mem_size; i++, mem_temp++)
*mem_temp = 243;
if (memdebug_level > 0)
fflush(trace_file);
mem_temp = (char *)ptr - FENCE_SIZE;
free((void *)mem_temp);
#else
free((void *) ptr);
#endif
}
/* reallocate some memory, making sure that it was properly allocated */
void *mem_realloc(const void *ptr, const size_t new_size)
{
#ifdef MEM_DEBUG
char *mem_temp = (char *)ptr;
size_t real_size = new_size + (FENCE_SIZE << 1);
size_t mem_size;
long i;
if (memdebug_level > 0) {
fprintf(trace_file, "+++ Requested size of %ld bytes\n", new_size);
fprintf(trace_file, "+++ Actual realloc of %ld bytes located at %p\n", real_size, mem_temp);
}
if (ptr == NULL) {
if (memdebug_level > 0) {
fprintf(trace_file, "!!! Reallocating NULL pointer\n");
fflush(trace_file);
}
abort();
}
mem_validate(ptr); /* doesn't return on error */
/* if doing memory tracking */
if (memdebug_level > 1 && allocations != NULL) {
char key[16];
STRING_ENTRY *se;
long temp;
sprintf(key, "%p", ptr);
if ((se = dict_search(allocations, key, &temp)) == NULL) {
fprintf(trace_file, "!!! Deleting a pointer not found in tracking info!\n");
abort();
}
/* point to size bytes */
mem_temp = (char *)ptr - sizeof(size_t);
/* get user size */
mem_size = *(size_t *)mem_temp;
if (se->count == 0) {
fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n");
abort();
}
else if (se->flags != mem_size) {
fprintf(trace_file, "!!! Stored size different from tracking size!\n");
abort();
}
/* remember deleted stuff by zeroing the allocation count */
se->count = 0;
se->flags = 0;
}
/* header marker bytes will be copied by the realloc */
mem_temp = (char *)ptr - FENCE_SIZE;
mem_temp = realloc((void *)mem_temp, real_size);
if (mem_temp != NULL) {
char *end;
/* save size in header too */
mem_temp += FENCE_SIZE - sizeof(size_t);
if (memdebug_level > 0)
fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp);
*(size_t *)mem_temp = new_size;
/* finally, point to storage we are going to hand out */
mem_temp += sizeof(size_t);
/* now, point to trailer bytes and do them */
end = mem_temp + new_size;
for (i = 0; i < FENCE_SIZE; i++)
*end++ = 145;
}
if (memdebug_level > 1 && allocations != NULL) {
char key[16];
long temp;
sprintf(key, "%p", mem_temp);
if (dict_insert(allocations, key, 1, (const unsigned long)new_size, mem_temp, &temp) == NULL) {
fprintf(trace_file, "!!! Insert of pointer tracking info failed\n");
abort();
}
}
if (memdebug_level > 0) {
fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp);
fflush(trace_file);
}
return (void *)mem_temp;
#else
return realloc((void *) ptr, new_size);
#endif
}
/* check a pointer to be sure all check bytes are OK. abort if not */
/* always returns 1 if not aborting so that it can be included in */
/* if statement boolean expressions */
int mem_validate(const void *ptr)
{
#ifdef MEM_DEBUG
unsigned char *mem_temp = (unsigned char *)ptr;
size_t mem_size;
size_t i;
/* NULL pointers are always valid */
if (ptr == NULL)
return 1;
if (memdebug_level > 0)
fprintf(trace_file, "+++ Checking %p as pointer\n", ptr);
if (memdebug_level > 1 && allocations != NULL) {
char key[16];
STRING_ENTRY *se;
long temp;
sprintf(key, "%p", ptr);
if ((se = dict_search(allocations, key, &temp)) == NULL) {
fprintf(trace_file, "!!! Pointer not found in tracking info!\n");
abort();
}
/* point to size bytes */
mem_temp = (unsigned char *)ptr - sizeof(size_t);
/* get user size */
mem_size = *(size_t *)mem_temp;
if (se->count == 0) {
fprintf(trace_file, "!!! Checking pointer has been freed!\n");
abort();
}
else if (se->flags != mem_size) {
fprintf(trace_file, "!!! Stored size different from tracking size!\n");
abort();
}
}
/* check the header bytes */
mem_temp = (unsigned char *) ptr - FENCE_SIZE;
if (memdebug_level > 0)
fprintf(trace_file, "+++ Real pointer at %p\n", mem_temp);
for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++)
if (*mem_temp++ != 145) {
if (memdebug_level > 0) {
fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr);
fprintf(trace_file, "!!! Header offset %ld has been changed\n", i - 1);
fflush(trace_file);
}
abort();
}
/* check size */
i = *(size_t *)mem_temp;
if (memdebug_level > 0)
fprintf(trace_file, "*** Stored memory size of %ld bytes in header\n", i);
/* now point to where trailer should be */
mem_temp = (unsigned char *)ptr + i;
for (i = 0; i < FENCE_SIZE; i++)
if (*mem_temp++ != 145) {
if (memdebug_level > 0) {
fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr);
fprintf(trace_file, "!!! Trailer offset %ld has been changed\n", i - 1);
fflush(trace_file);
}
abort();
}
if (memdebug_level > 0)
fflush(trace_file);
#endif
return 1;
}