Mercurial > hg > early-roguelike
diff urogue/memory.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 31 Jan 2017 19:56:04 -0500 |
parents | |
children | c4b12d2d1dcd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/memory.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,420 @@ +/* + 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" + +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; + + 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); +} + +/* set memory tracking on or off */ +/* turning it off deletes all tracking data */ +void mem_tracking(int flag) +{ + /* 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; + } +} + +/* 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) +{ + 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"); + + /* 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) +{ + 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; +} + +/* release some memory, making sure that it was properly allocated */ +void mem_free(const void *ptr) +{ + 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); +} + +/* reallocate some memory, making sure that it was properly allocated */ +void *mem_realloc(const void *ptr, const size_t new_size) +{ + 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; +} + +/* 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(void *ptr) +{ + 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); + return 1; +}