tmuck2.4/
tmuck2.4/admin/scripts/
tmuck2.4/docs/
tmuck2.4/minimal-db/
tmuck2.4/minimal-db/data/
tmuck2.4/minimal-db/logs/
tmuck2.4/minimal-db/muf/
tmuck2.4/old/
tmuck2.4/src/
tmuck2.4/src/compile/
tmuck2.4/src/editor/
tmuck2.4/src/game/
tmuck2.4/src/interface/
tmuck2.4/src/scripts/
tmuck2.4/src/utilprogs/
/* Copyright (c) 1992 by David Moore.  All rights reserved. */
/* debug_malloc.c,v 2.7 1996/01/26 01:50:33 dmoore Exp */
/* written by dmoore 12/26/91, to reduce the MALLOC, FREE, etc macros
   into subroutines. */

#include "config.h"

#ifdef DEBUG_MALLOCS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "externs.h"


/* The trick is, we will always ask malloc for the requested memory
   PLUS enough memory to hold this stucture at the beginning.  Then
   we will fill in the appropriate fields, and return the pointer
   just past the structure to the caller.  The magic numbers will
   hopefully be useful to track illegal frees/reallocs.  By keeping
   a linked list of the things we have malloced, as well as the file
   and line number where they were malloced, memory leaks can be
   tracked. */

/* You want the size of this structure to be a multiple of your
   alignment size.  That way, when the real malloc returns something aligned
   the result these routines return will be aligned. */
union debug_malloc_info {
    struct {
	int magic1;
	const char *file;
	int line;
#ifdef DEBUG_MALLOCS_RCHECK
	size_t size;
#endif /* DEBUG_MALLOCS_RCHECK */
#ifdef DEBUG_MALLOCS_TRACE
	union debug_malloc_info *next, *prev;
#endif /* DEBUG_MALLOCS_TRACE */
	int magic2;
    } info;
    char align_force[ALIGN_BYTES];
};

/* Two magic numbers, one to specify a valid pointer that our malloc
   has generated.  The other set on freed objects.  Note that the freed
   information can get lost, though, because a later call to malloc might
   reuse that space. */
#define MAGIC_NUMBER 0xEEEEEEEE
/* #define MAGIC_NUMBER 15000  *//* Number of students. :) */
#define MAGIC_NUMBER2 91973 /* For me to know, and you to guess at! */

/* Magic number used to fill out freed memory space, use 0 to try to
   make things dereference them badly.  Use a large character to
   flush out things which check if 0 and then use it anyways, though
   they shouldn't (since it was freed). */
#define MAGIC_FREE_NUMBER 0xCC

union debug_malloc_info *info_head = 0;


static int align_size(int size)
{
    if (!(size % ALIGN_BYTES)) return size;
    else return size + ALIGN_BYTES - (size % ALIGN_BYTES);
}


/* Verify that the pointer we've just recieved is one generated by an
   earlier call to malloc.  This is done by checking for some magic
   numbers that have been stored just in front of the pointer the user has. */
static void check_magic(const void *cp, const char *file, const int line)
{
    union debug_malloc_info *info, *info2;
    
    if (!cp) {
	log_status("REALLOC/FREE: null pointer at %d in %s.",
		   line, file);
	panic("Bad realloc/free.");
	/* NEVER REACHED */
    }
    
    info = (union debug_malloc_info *) cp;
    info--;
    
    if ((info->info.magic1 == MAGIC_NUMBER2) || (info->info.magic2 == MAGIC_NUMBER2)) {
	log_status("REALLOC/FREE: already freed pointer at %d in %s. Originally freed at %d in %s.", line, file, info->info.line, info->info.file);
	panic("Bad realloc/free.");
	/* NEVER REACHED */
    }
    
    if ((info->info.magic1 != MAGIC_NUMBER) || (info->info.magic2 != MAGIC_NUMBER)) {
	log_status("REALLOC/FREE: illegal pointer at %d in %s.",
		   line, file);
	panic("Bad realloc/free.");
	/* NEVER REACHED */
    }
    
#ifdef DEBUG_MALLOCS_RCHECK
    info2 = (union debug_malloc_info *) (((char *) cp) + info->info.size);
    
    if ((info2->info.magic1 != info->info.magic1) ||
	(info2->info.magic2 != info->info.magic2) ||
	(info2->info.file != info->info.file) ||
	(info2->info.line != info->info.line) ||
	(info2->info.size != info->info.size)) {
	log_status("REALLOC/FREE: range check failed at %d in %s.",
		   line, file);
	log_status("REALLOC/FREE: (could be invalid) malloced at %d in %s.",
		   info->info.line, info->info.file);
	panic("Bad realloc/free.");
	/* NEVER REACHED */
    }
#endif /* DEBUG_MALLOCS_RCHECK */
}


/* This stores the appropriate magic numbers for a new memory allocation
   and chains this node into a linked list of all of the malloced nodes. */
static void set_magic(void *cp, const char *file, const int line, const size_t size)
{
    union debug_malloc_info *info, *info2;
    
    info = (union debug_malloc_info *) cp;
    info->info.magic1 = MAGIC_NUMBER;
    info->info.file = file;
    info->info.line = line;
    info->info.magic2 = MAGIC_NUMBER;
    
#ifdef DEBUG_MALLOCS_TRACE
    /* Chain it into the linked list. */
    info->info.next = info_head;
    info->info.prev = 0;
    if (info_head) info_head->info.prev = info;
    info_head = info;
#endif /* DEBUG_MALLOCS_TRACE */
    
#ifdef DEBUG_MALLOCS_RCHECK
    info->info.size = size;
    
    info2 = (union debug_malloc_info *) (((char *) cp) + size);
    info2++;
    
    info2->info.magic1 = info2->info.magic2 = MAGIC_NUMBER;
    info2->info.file = file;
    info2->info.line = line;
    info2->info.size = size;
#endif /* DEBUG_MALLOCS_RCHECK */
}


static void reset_size(void *cp, const size_t size)
{
    union debug_malloc_info *info, *info2;
    
    info = (union debug_malloc_info *) cp;
    info--;

    /* Fix up the linked list,in case of a moved realloc. */
    if (!info->info.prev) {
        /* It was info_head. */
        info_head = info;
    }
    if (info->info.next) info->info.next->info.prev = info;
    if (info->info.prev) info->info.prev->info.next = info;

#ifdef DEBUG_MALLOCS_RCHECK
    info2 = (union debug_malloc_info *) (((char *) cp) + size);
    
    info->info.size = size;
    info2->info.magic1 = info2->info.magic2 = MAGIC_NUMBER;
    info2->info.file = info->info.file;
    info2->info.line = info->info.line;
    info2->info.size = size; 
#endif /* DEBUG_MALLOCS_RCHECK */
}


/* This routine removes the node from the list of malloced memories,
   and resets the magic numbers so we can hopefully detect illegal frees
   to the same address again.  It also zeroes out the memory that was
   allocated, hopeing to flush out problems. */
static void clear_node(void *cp, const char *file, const int line)
{
    union debug_malloc_info *info, *info2;
    char *ptr;
    
    info = (union debug_malloc_info *) cp;
    info--;
    
#ifdef DEBUG_MALLOCS_TRACE
    /* Remove it from the linked list. */
    if (info->info.next) info->info.next->info.prev = info->info.prev;
    if (info == info_head) {
	info_head = info->info.next;
    } else {
	info->info.prev->info.next = info->info.next;
    }
    info->info.next = 0;
    info->info.prev = 0;
#endif /* DEBUG_MALLOCS_TRACE */
    
    /* Set magic so we know if they try to free it again! */
    info->info.magic1 = MAGIC_NUMBER2;
    info->info.magic2 = MAGIC_NUMBER2;
    info->info.file = file;
    info->info.line = line;
    
#ifdef DEBUG_MALLOCS_RCHECK
    info2 = (union debug_malloc_info *) (((char *) cp) + info->info.size);
    
    info2->info.magic1 = info2->info.magic2 = MAGIC_NUMBER2;
    info2->info.file = file;
    info2->info.line = line;
    
    for (ptr = (char *) cp; ptr != (char *) info2; ptr++)
	*ptr = MAGIC_FREE_NUMBER;
#endif /* DEBUG_MALLOCS_RCHECK */
}


void *debug_realloc(void *cp, size_t size, const char *file, const int line)
{
    union debug_malloc_info *result, *arg;
    
    check_magic(cp, file, line);
    
    size = align_size(size);
    
    if (size == 0) {
	log_status("REALLOC: tried to realloc(0) at %d in %s.",
		   line, file);
	debug_free(cp, file, line);  /* Know it's not null from check_magic. */
	return NULL;
    }
    
    /* realloc will copy over the header struct for us. */
    arg = (union debug_malloc_info *) cp;
    arg--;
    
#ifndef DEBUG_MALLOCS_RCHECK
    result = realloc(arg, size + sizeof(union debug_malloc_info));
#else
    result = realloc(arg, size + 2*sizeof(union debug_malloc_info));
#endif /* DEBUG_MALLOCS_RCHECK */
    
    if (result == NULL) {
	log_status("REALLOC: out of memory at %d in %s.",
		   line, file);
	panic("Out of memory.");
	/* NEVER REACHED */
    }
    
    result++;
    reset_size(result, size);
    return (void *) result;
}


void *debug_malloc(size_t size, const char *file, const int line)
{
    union debug_malloc_info *result;
    
    size = align_size(size);
    
    if (size == 0) {
	log_status("MALLOC: tried to malloc(0) at %d in %s.",
		   line, file);
	return NULL;
    }
    
#ifndef DEBUG_MALLOCS_RCHECK
    result = malloc(size + sizeof(union debug_malloc_info));
#else
    result = malloc(size + 2*sizeof(union debug_malloc_info));
#endif /* DEBUG_MALLOCS_RCHECK */
    
    if (result == NULL) {
	log_status("MALLOC: out of memory at %d in %s.",
		line, file);
	panic("Out of memory.");
	/* NEVER REACHED */
    }
    
    set_magic(result, file, line, size);
    
    result++;
    return (void *) result;
}


void debug_free(void *cp, const char *file, const int line)
{
    union debug_malloc_info *arg;
    
    check_magic(cp, file, line);
    
    clear_node(cp, file, line);
    
    arg = (union debug_malloc_info *) cp;
    arg--;
    
#ifndef DEBUG_MALLOCS_WASTE_MEMORY
    /* free the memory, unless we have chosen not to free anything,
       which is the only guaranteed way to detect multiple frees of the same
       data, and will most likely waste memory like mad. */
    free((void *) arg);
#endif /* DEBUG_MALLOCS_WASTE_MEMORY */
}


void *debug_calloc(size_t size, const char *file, const int line)
{
    void *result;

    result = debug_malloc(size, file, line);

    if (result)
	memset(result, '\0', size);

    return result;
    
}


void debug_mstats(const char *s)
{
#ifdef DEBUG_MALLOCS_TRACE
    union debug_malloc_info *temp, *ptr;
    int counter = 0;
    
    fprintf(stderr, "(%s): Unfreed resources allocated by:\n", s);
    for (temp = info_head; temp; temp = temp->info.next) {
	ptr = temp + 1;
	check_magic(ptr, temp->info.file, temp->info.line);
	if (temp->info.file)
	    fprintf(stderr, "%s %d\n", temp->info.file, temp->info.line);
	else
	    fprintf(stderr, "NULL file field\n");
	if (counter++ > 1000) {
	    fprintf(stderr, "possible loop in malloc list, stopping.\n");
	    break;
	}
    }
    fprintf(stderr, "(%s): End of list.\n", s);
#endif /* DEBUG_MALLOCS_TRACE */
}

#endif /* DEBUG_MALLOCS */