444 lines
11 KiB
C
444 lines
11 KiB
C
/*
|
|
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
|
|
|
|
/* 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;
|
|
#ifdef MEM_DEBUG
|
|
static DICTIONARY *allocations = NULL;
|
|
static FILE *trace_file = NULL;
|
|
#endif
|
|
|
|
/* 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;
|
|
}
|