tinymush-3.1p2/game/backups/
tinymush-3.1p2/game/bin/
tinymush-3.1p2/game/data/
tinymush-3.1p2/game/modules/
tinymush-3.1p2/game/modules/old/
tinymush-3.1p2/src/modules/comsys/
tinymush-3.1p2/src/modules/hello/
tinymush-3.1p2/src/modules/mail/
tinymush-3.1p2/src/tools/
/* alloc.c - memory allocation subsystem */
/* $Id: alloc.c,v 1.23 2002/09/23 08:42:08 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */

POOL pools[NUM_POOLS];
const char *poolnames[] =
{"Sbufs", "Mbufs", "Lbufs", "Bools", "Descs", "Qentries", "Pcaches"};

#define POOL_MAGICNUM 0xdeadbeef

void pool_init(poolnum, poolsize)
int poolnum, poolsize;
{
	pools[poolnum].pool_size = poolsize;
	pools[poolnum].free_head = NULL;
	pools[poolnum].chain_head = NULL;
	pools[poolnum].tot_alloc = 0;
	pools[poolnum].num_alloc = 0;
	pools[poolnum].max_alloc = 0;
	pools[poolnum].num_lost = 0;
	return;
}

static void pool_err(logsys, logflag, poolnum, tag, ph, action, reason)
int logflag, poolnum;
const char *logsys, *tag, *action, *reason;
POOLHDR *ph;
{
	if (!mudstate.logging) {
		STARTLOG(logflag, logsys, "ALLOC")
			log_printf("%s[%d] (tag %s) %s at %lx. (%s)",
				   action, pools[poolnum].pool_size, tag,
				   reason, (long)ph, mudstate.debug_cmd);
		ENDLOG
	} else if (logflag != LOG_ALLOCATE) {
		log_printf("\n***< %s[%d] (tag %s) %s at %lx. >***",
			   action, pools[poolnum].pool_size, tag, reason,
			   (long)ph);
	}
}

static void pool_vfy(poolnum, tag)
int poolnum;
const char *tag;
{
	POOLHDR *ph, *lastph;
	POOLFTR *pf;
	char *h;
	int psize;

	lastph = NULL;
	psize = pools[poolnum].pool_size;
	for (ph = pools[poolnum].chain_head; ph; lastph = ph, ph = ph->next) {
		h = (char *)ph;
		h += sizeof(POOLHDR);
		h += pools[poolnum].pool_size;
		pf = (POOLFTR *) h;

		if (ph->magicnum != POOL_MAGICNUM) {
			pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph,
			  "Verify", "header corrupted (clearing freelist)");

			/*
			 * Break the header chain at this point so we don't
			 * generate an error for EVERY alloc and free, 
			 * also we can't continue the scan because the
			 * next pointer might be trash too. 
			 */

			if (lastph)
				lastph->next = NULL;
			else
				pools[poolnum].chain_head = NULL;
			return;	/* not safe to continue */
		}
		if (pf->magicnum != POOL_MAGICNUM) {
			pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph,
				 "Verify", "footer corrupted");
			pf->magicnum = POOL_MAGICNUM;
		}
		if (ph->pool_size != psize) {
			pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph,
				 "Verify", "header has incorrect size");
		}
	}
}

void pool_check(tag)
const char *tag;
{
	int poolnum;

	for (poolnum = 0; poolnum < NUM_POOLS; ++poolnum) {
		pool_vfy(poolnum, tag);
	}
}

char *pool_alloc(poolnum, tag)
int poolnum;
const char *tag;
{
	int *p;
	char *h;
	POOLHDR *ph;
	POOLFTR *pf;

	if (mudconf.paranoid_alloc)
		pool_check(tag);
	do {
		if (pools[poolnum].free_head == NULL) {
			h = (char *)RAW_MALLOC(pools[poolnum].pool_size +
					    sizeof(POOLHDR) + sizeof(POOLFTR),
					    "pool_alloc.ph");
			if (h == NULL) {
			    fprintf(mainlog_fp, "ABORT! alloc.c, pool_alloc() failed to get memory.\n");
			    abort();
			}
			ph = (POOLHDR *) h;
			h += sizeof(POOLHDR);
			p = (int *)h;
			h += pools[poolnum].pool_size;
			pf = (POOLFTR *) h;
			ph->next = pools[poolnum].chain_head;
			ph->nxtfree = NULL;
			ph->magicnum = POOL_MAGICNUM;
			ph->pool_size = pools[poolnum].pool_size;
			pf->magicnum = POOL_MAGICNUM;
			*p = POOL_MAGICNUM;
			pools[poolnum].chain_head = ph;
			pools[poolnum].max_alloc++;
		} else {
			ph = (POOLHDR *) (pools[poolnum].free_head);
			h = (char *)ph;
			h += sizeof(POOLHDR);
			p = (int *)h;
			h += pools[poolnum].pool_size;
			pf = (POOLFTR *) h;
			pools[poolnum].free_head = ph->nxtfree;

			/* If corrupted header we need to throw away the
			 * freelist as the freelist pointer may be corrupt. 
			 */

			if (ph->magicnum != POOL_MAGICNUM) {
				pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph,
					 "Alloc", "corrupted buffer header");

				/* Start a new free list and record stats */

				p = NULL;
				pools[poolnum].free_head = NULL;
				pools[poolnum].num_lost +=
					(pools[poolnum].tot_alloc -
					 pools[poolnum].num_alloc);
				pools[poolnum].tot_alloc =
					pools[poolnum].num_alloc;
			}
			/* Check for corrupted footer, just report and fix
			 * it 
			 */

			if (pf->magicnum != POOL_MAGICNUM) {
				pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph,
					 "Alloc", "corrupted buffer footer");
				pf->magicnum = POOL_MAGICNUM;
			}
		}
	} while (p == NULL);

	ph->buf_tag = (char *)tag;
	pools[poolnum].tot_alloc++;
	pools[poolnum].num_alloc++;

	pool_err("DBG", LOG_ALLOCATE, poolnum, tag, ph, "Alloc", "buffer");

	/* If the buffer was modified after it was last freed, log it. */

	if ((*p != POOL_MAGICNUM) && (!mudstate.logging)) {
		pool_err("BUG", LOG_PROBLEMS, poolnum, tag, ph, "Alloc",
			 "buffer modified after free");
	}
	*p = 0;
	return (char *)p;
}

void pool_free(poolnum, buf)
int poolnum;
char **buf;
{
	int *ibuf;
	char *h;
	POOLHDR *ph;
	POOLFTR *pf;

	ibuf = (int *)*buf;
	h = (char *)ibuf;
	h -= sizeof(POOLHDR);
	ph = (POOLHDR *) h;
	h = (char *)ibuf;
	h += pools[poolnum].pool_size;
	pf = (POOLFTR *) h;
	if (mudconf.paranoid_alloc)
		pool_check(ph->buf_tag);

	/* Make sure the buffer header is good.  If it isn't, log the error
	 * and throw away the buffer. 
	 */

	if (ph->magicnum != POOL_MAGICNUM) {
		pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
			 "corrupted buffer header");
		pools[poolnum].num_lost++;
		pools[poolnum].num_alloc--;
		pools[poolnum].tot_alloc--;
		return;
	}
	/* Verify the buffer footer.  Don't unlink if damaged, just repair */

	if (pf->magicnum != POOL_MAGICNUM) {
		pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
			 "corrupted buffer footer");
		pf->magicnum = POOL_MAGICNUM;
	}
	/* Verify that we are not trying to free someone else's buffer */

	if (ph->pool_size != pools[poolnum].pool_size) {
		pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
			 "Attempt to free into a different pool.");
		return;
	}
	pool_err("DBG", LOG_ALLOCATE, poolnum, ph->buf_tag, ph, "Free",
		 "buffer");

	/* Make sure we aren't freeing an already free buffer.  If we are,
	 * log an error, otherwise update the pool header and stats  
	 */

	if (*ibuf == POOL_MAGICNUM) {
		pool_err("BUG", LOG_BUGS, poolnum, ph->buf_tag, ph, "Free",
			 "buffer already freed");
	} else {
		*ibuf = POOL_MAGICNUM;
		ph->nxtfree = pools[poolnum].free_head;
		pools[poolnum].free_head = ph;
		pools[poolnum].num_alloc--;
	}
}

static char *pool_stats(poolnum, text)
int poolnum;
const char *text;
{
	char *buf;

	buf = alloc_mbuf("pool_stats");
	sprintf(buf, "%-15s %5d%9d%9d%9d%9d", text, pools[poolnum].pool_size,
		pools[poolnum].num_alloc, pools[poolnum].max_alloc,
		pools[poolnum].tot_alloc, pools[poolnum].num_lost);
	return buf;
}

static void pool_trace(player, poolnum, text)
dbref player;
int poolnum;
const char *text;
{
	POOLHDR *ph;
	int numfree, *ibuf;
	char *h;

	numfree = 0;
	notify(player, tprintf("----- %s -----", text));
	for (ph = pools[poolnum].chain_head; ph != NULL; ph = ph->next) {
		if (ph->magicnum != POOL_MAGICNUM) {
			notify(player, "*** CORRUPTED BUFFER HEADER, ABORTING SCAN ***");
			notify(player,
			       tprintf("%d free %s (before corruption)",
				       numfree, text));
			return;
		}
		h = (char *)ph;
		h += sizeof(POOLHDR);
		ibuf = (int *)h;
		if (*ibuf != POOL_MAGICNUM)
			notify(player, ph->buf_tag);
		else
			numfree++;
	}
	notify(player, tprintf("%d free %s", numfree, text));
}

void list_bufstats(player)
dbref player;
{
    int i;
    char *buff;

    notify(player,
	   "Buffer Stats     Size    InUse    Total   Allocs     Lost");

    for (i = 0; i < NUM_POOLS; i++) {
	buff = pool_stats(i, poolnames[i]);
	notify(player, buff);
	free_mbuf(buff);
    }
}

void list_buftrace(player)
dbref player;
{
	int i;

	for (i = 0; i < NUM_POOLS; i++)
		pool_trace(player, i, poolnames[i]);
}

void pool_reset()
{
	POOLHDR *ph, *phnext, *newchain;
	int i, *ibuf;
	char *h;

	
	for (i = 0; i < NUM_POOLS; i++) {
		newchain = NULL;
		for (ph = pools[i].chain_head; ph != NULL; ph = phnext) {
			h = (char *)ph;
			phnext = ph->next;
			h += sizeof(POOLHDR);
			ibuf = (int *)h;
			if (*ibuf == POOL_MAGICNUM) {
				RAW_FREE(ph, "pool_reset.ph");
			} else {
				if (!newchain) {
					newchain = ph;
					ph->next = NULL;
				} else {
					ph->next = newchain;
					newchain = ph;
				}
				ph->nxtfree = NULL;
			}
		}
		pools[i].chain_head = newchain;
		pools[i].free_head = NULL;
		pools[i].max_alloc = pools[i].num_alloc;
	}
}

/* ---------------------------------------------------------------------------
 * Track allocations that don't use the memory pools.
 */

#ifdef RAW_MEMTRACKING

static int sort_memtable(p1, p2)
    const void *p1, *p2;
{
    return strcmp((*(MEMTRACK **)p1)->buf_tag,
		  (*(MEMTRACK **)p2)->buf_tag);
}

void list_rawmemory(player)
    dbref player;
{
    MEMTRACK *tptr, **t_array;
    int n_tags, total, c_tags, c_total, u_tags, i, j;
    const char *ntmp;

    n_tags = total = 0;

    raw_notify(player,
	       "Memory Tag                           Allocs      Bytes");

    for (tptr = mudstate.raw_allocs; tptr != NULL; tptr = tptr->next) {
	n_tags++;
	total += (int) tptr->alloc;
    }

    t_array = (MEMTRACK **) RAW_CALLOC(total, sizeof(MEMTRACK *),
				       "list_rawmemory");

    for (i = 0, tptr = mudstate.raw_allocs; tptr != NULL;
	 i++, tptr = tptr->next) {
	t_array[i] = tptr; 
    }

    qsort((void *) t_array, n_tags, sizeof(MEMTRACK *),
	  (int (*)(const void *, const void *))sort_memtable);

    for (i = 0; i < n_tags;  ) {
	u_tags++;
	ntmp = t_array[i]->buf_tag;
	if ((i < n_tags - 1) &&
	    !strcmp(ntmp, t_array[i+1]->buf_tag)) {
	    c_tags = 2;
	    c_total = (int) t_array[i]->alloc + (int) t_array[i+1]->alloc;
	    for (j = i + 2; (j < n_tags) &&
		     !strcmp(ntmp, t_array[j]->buf_tag);
		 j++) {
		c_tags++;
		c_total += (int) t_array[j]->alloc;
	    }
	    i = j;
	} else {
	    c_tags = 1;
	    c_total = (int) t_array[i]->alloc;
	    i++;
	}
	raw_notify(player,
		   tprintf("%-35.35s  %6d   %8d", ntmp, c_tags, c_total));
    }

    RAW_FREE(t_array, "list_rawmemory");

    raw_notify(player,
       tprintf("Total: %d raw allocations in %d unique tags. %d bytes (%d K).",
	       n_tags, u_tags, total, (int) total / 1024));
}

static void trace_alloc(amount, name, ptr)
    size_t amount;
    const char *name;
    void *ptr;
{
    /* We maintain an unsorted list, most recently-allocated things at
     * the head, based on the belief that it's basically a stack --
     * when we go to free something it's usually the most recent thing
     * that we've allocated.
     */

    MEMTRACK *tptr;

    tptr = (MEMTRACK *) RAW_MALLOC(sizeof(MEMTRACK), "trace_alloc.tptr");
    if (!tptr)
	return;

    tptr->buf_tag = name;
    tptr->bptr = ptr;
    tptr->alloc = amount;
    tptr->next = mudstate.raw_allocs;
    mudstate.raw_allocs = tptr;
}

static void trace_free(name, ptr)
    const char *name;
    void *ptr;
{
    MEMTRACK *tptr, *prev;

    prev = NULL;
    for (tptr = mudstate.raw_allocs; tptr != NULL; tptr = tptr->next) {
	if (tptr->bptr == ptr) {
	    if (strcmp(tptr->buf_tag, name)) {
		STARTLOG(LOG_BUGS, "MEM", "TRACE")
		    log_printf("Free mismatch, tag %s allocated as %s",
			       name, tptr->buf_tag);
		ENDLOG
	    }
	    if (mudstate.raw_allocs == tptr)
		mudstate.raw_allocs = tptr->next;
	    if (prev)
		prev->next = tptr->next;
	    RAW_FREE(tptr, "trace_alloc.tptr");
	    return;
	}
	prev = tptr;
    }

    STARTLOG(LOG_BUGS, "MEM", "TRACE")
	log_printf("Attempt to free unknown with tag %s", name);
    ENDLOG
}

void *track_malloc(amount, name)
    size_t amount;
    const char *name;
{
    void *r;

    r = malloc(amount);
    trace_alloc(amount, name, r);
    return (r);
}

void *track_calloc(elems, esize, name)
    size_t elems, esize;
    const char *name;
{
    void *r;

    r = calloc(elems, esize);
    trace_alloc(elems * esize, name, r);
    return (r);
}

void *track_realloc(ptr, amount, name)
    void *ptr;
    size_t amount;
    const char *name;
{
    void *r;

    trace_free(name, r);
    r = realloc(ptr, amount);
    trace_alloc(amount, name, r);
    return (r);
}

char *track_strdup(str, name)
    const char *str, *name;
{
    char *r;

    r = strdup(str);
    trace_alloc(strlen(str) + 1, name, r);
    return (r);
}

void track_free(ptr, name)
    void *ptr;
    const char *name;
{
    trace_free(name, ptr);
    free(ptr);
}

#else

void list_rawmemory(player)
    dbref player;
{
    notify(player, "Feature not supported.");
}

#endif /* RAW_MEMTRACKING */