mux2.0/game/
mux2.0/game/data/
mux2.0/src/tools/
/*
 * alloc.cpp - memory allocation subsystem 
 */
/*
 * $Id: alloc.cpp,v 1.4 2000/06/02 19:15:08 sdennis Exp $ 
 */
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include "db.h"
#include "alloc.h"
#include "mudconf.h"

// The following structure is 64-bit aligned.
//
typedef struct pool_header
{
    unsigned int magicnum;          // For consistency check 
    int    pool_size;               // For consistency check
    struct pool_header *next;       // Next pool header in chain 
    struct pool_header *nxtfree;    // Next pool header in freelist 
    char  *buf_tag;                 // Debugging/trace tag 
    int    UnusedFieldForAlignmentToINT64Boundary;
} POOLHDR;

typedef struct pool_footer
{
    unsigned int magicnum;          // For consistency check 
} POOLFTR;

typedef struct pooldata
{
    int pool_size;                  // Size in bytes of a buffer 
    POOLHDR *free_head;             // Buffer freelist head 
    POOLHDR *chain_head;            // Buffer chain head
    int tot_alloc;                  // Total buffers allocated 
    int num_alloc;                  // Number of buffers currently allocated 
    int max_alloc;                  // Max # buffers allocated at one time 
    int num_lost;                   // Buffers lost due to corruption 
} POOL;

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

#define POOL_MAGICNUM 0xdeadbeefU

void pool_init(int poolnum, int 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;
}

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

static void pool_vfy(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(const char *tag)
{
    pool_vfy(POOL_LBUF, tag);
    pool_vfy(POOL_MBUF, tag);
    pool_vfy(POOL_SBUF, tag);
    pool_vfy(POOL_BOOL, tag);
    pool_vfy(POOL_DESC, tag);
    pool_vfy(POOL_QENTRY, tag);
}

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

    if (mudconf.paranoid_alloc)
    {
        pool_check(tag);
    }

    do
    {
        if (pools[poolnum].free_head == NULL)
        {
            h = (char *)MEMALLOC(pools[poolnum].pool_size + sizeof(POOLHDR) + sizeof(POOLFTR));
            ISOUTOFMEMORY(h);
            ph = (POOLHDR *) h;
            h += sizeof(POOLHDR);
            p = (unsigned 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 = (unsigned 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(int poolnum, char **buf)
{
    unsigned int *ibuf;
    char *h;
    POOLHDR *ph;
    POOLFTR *pf;

    ibuf = (unsigned 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(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(dbref player, int poolnum, const char *text)
{
    POOLHDR *ph;
    int numfree;
    unsigned int *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 = (unsigned int *)h;
        if (*ibuf != POOL_MAGICNUM)
        {
            notify(player, ph->buf_tag);
        }
        else
        {
            numfree++;
        }
    }
    notify(player, tprintf("%d free %s", numfree, text));
}

static void list_bufstat(dbref player, int poolnum)
{
    char *buff = pool_stats(poolnum, poolnames[poolnum]);
    notify(player, buff);
    free_mbuf(buff);
}

void list_bufstats(dbref player)
{
    int i;

    notify(player, "Buffer Stats     Size    InUse    Total   Allocs     Lost");
    for (i = 0; i < NUM_POOLS; i++)
    {
        list_bufstat(player, i);
    }
}

void list_buftrace(dbref player)
{
    int i;

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

void pool_reset(void)
{
    POOLHDR *ph, *phnext, *newchain;
    int i;
    unsigned int *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 = (unsigned int *)h;
            if (*ibuf == POOL_MAGICNUM)
            {
                MEMFREE(ph);
            }
            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;
    }
}