/* * 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; } }