#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <time.h> #include "externs.h" #include "db.h" /* The next should be defined as either 1 or 2 */ /* 1 - The stack is only used for temporary allocation. */ /* 2 - The stack is used for temporary and permanent allocation. */ #define STACK_USAGE 1 typedef struct { unsigned long size; void *heap; } STACK; typedef struct perm_stack { char *file; int line; time_t time; STACK mem; struct perm_stack *next; } PERM_STACK; typedef struct temp_stack { STACK mem; struct temp_stack *next; } TEMP_STACK; static PERM_STACK *perm_stack = NULL; static TEMP_STACK *temp_stack = NULL; static unsigned long maze_stack_size = 0L; static unsigned long maze_stack_highest = 0L; /* The free_stack is designed for efficiency. It allows for the game to */ /* make fewer malloc() calls which are slow. If you set the */ /* the stack limit too low, you're not using it to it's full potential */ /* and if you set it too high, some of it will never be used and will */ /* waste memory. */ static TEMP_STACK *temp_free_stack = NULL; static PERM_STACK *perm_free_stack = NULL; static int temp_free_stack_limit = 200; static int perm_free_stack_limit = 200; static int temp_free_stack_size = 0; static int perm_free_stack_size = 0; static int temp_free_size_highest = 0; static int perm_free_size_highest = 0; static TEMP_STACK *get_temp_free_stack(void) { TEMP_STACK *s; if(!(s = temp_free_stack)) s = (TEMP_STACK *)malloc(sizeof(TEMP_STACK)); else { temp_free_stack = temp_free_stack->next; temp_free_stack_size--; } return(s); } static PERM_STACK *get_perm_free_stack(void) { PERM_STACK *s; if(!(s = perm_free_stack)) s = (PERM_STACK *)malloc(sizeof(PERM_STACK)); else { perm_free_stack = perm_free_stack->next; perm_free_stack_size--; } return(s); } static void make_temp_free_stack(TEMP_STACK *stack) { if(temp_free_stack_size >= temp_free_stack_limit) free(stack); else { stack->next = temp_free_stack; temp_free_stack = stack; if(++temp_free_stack_size > temp_free_size_highest) temp_free_size_highest = temp_free_stack_size; } } static void make_perm_free_stack(PERM_STACK *stack) { if(perm_free_stack_size >= perm_free_stack_limit) free(stack); else { stack->next = perm_free_stack; perm_free_stack = stack; if(++perm_free_stack_size > perm_free_size_highest) perm_free_size_highest = perm_free_stack_size; } } /* size is the number of bytes to allocate, permanent is either 1 or 0. */ /* 1 indicates that the memory allocated should not be freed by */ /* stack_unalloc(). If clear is 1 the allocated memory will be cleared */ /* (filled with 0s) to simulate a calloc() call. */ void *stack_alloc2(char *file, int line, unsigned long size, bool permanent, bool clear) { STACK *s; PERM_STACK *ps; TEMP_STACK *ts; if(STACK_USAGE == 1 && permanent) return(((clear)?calloc(1, size):malloc(size))); if(permanent) { ps = get_perm_free_stack(); s = &ps->mem; ps->file = (char *)malloc(strlen(file)+1); strcpy(ps->file, file); ps->line = line; ps->time = now; ps->next = perm_stack; perm_stack = ps; } else { ts = get_temp_free_stack(); s = &ts->mem; ts->next = temp_stack; temp_stack = ts; } s->heap = (clear)?calloc(1, size):malloc(size); s->size = size; if((maze_stack_size += size) > maze_stack_highest) maze_stack_highest = maze_stack_size; return(s->heap); } void *stack_realloc2(char *file, int line, void *ptr, unsigned long newsize, bool chktemp) { STACK *s; PERM_STACK *ps, *psprev = NULL; TEMP_STACK *ts = NULL, *tsprev = NULL; unsigned long oldsize; if(chktemp) for(ts = temp_stack;ts;ts = ts->next) { if(ts->mem.heap == ptr) break; tsprev = ts; } if(!ts && STACK_USAGE == 1) return(realloc(ptr, newsize)); if(!ts) { for(ps = perm_stack;ps;ps = ps->next) { if(ps->mem.heap == ptr) break; psprev = ps; } if(!ps) { log_error(tprintf("stack_realloc() %s %d: Attempt to realloc nonexistent pointer. ptr = %p, newsize = %ld", file, line, ptr, newsize)); return(stack_alloc(newsize, 1, 0)); } } if(ts) { oldsize = ts->mem.size; if(!newsize) { if(!tsprev) temp_stack = ts->next; else tsprev->next = ts->next; free(ts->mem.heap); make_temp_free_stack(ts); maze_stack_size -= oldsize; return(NULL); } s = &ts->mem; } else { oldsize = ps->mem.size; if(!newsize) { if(psprev) psprev->next = ps->next; else perm_stack = ps->next; free(ps->file); free(ps->mem.heap); make_perm_free_stack(ps); maze_stack_size -= oldsize; return(NULL); } s = &ps->mem; } s->heap = realloc(s->heap, newsize); s->size = newsize; if((maze_stack_size += newsize-oldsize) > maze_stack_highest) maze_stack_highest = maze_stack_size; return(s->heap); } void stack_free2(char *file, int line, void *ptr, bool chktemp) { PERM_STACK *ps, *psprev = NULL; TEMP_STACK *ts = NULL, *tsprev = NULL; if(chktemp) for(ts = temp_stack;ts;ts = ts->next) { if(ts->mem.heap == ptr) break; tsprev = ts; } if(!ts && STACK_USAGE == 1) { free(ptr); return; } if(!ts) { for(ps = perm_stack;ps;ps = ps->next) { if(ps->mem.heap == ptr) break; psprev = ps; } if(!ps) { log_error(tprintf("stack_free(): %s %d: Attempt to free a nonexistent pointer. ptr = %p", file, line, ptr)); return; } } if(ts) { if(tsprev) tsprev->next = ts->next; else temp_stack = ts->next; maze_stack_size -= ts->mem.size; free(ts->mem.heap); make_temp_free_stack(ts); } else { if(psprev) psprev->next = ps->next; else perm_stack = ps->next; maze_stack_size -= ps->mem.size; free(ps->file); free(ps->mem.heap); make_perm_free_stack(ps); } } void stack_unalloc(void) { TEMP_STACK *ts, *tsnext; for(ts = temp_stack;ts;ts = tsnext) { tsnext = ts->next; maze_stack_size -= ts->mem.size; free(ts->mem.heap); make_temp_free_stack(ts); } temp_stack = NULL; } char *stack_string_alloc2(char *file, int line, char *str, bool permanent) { char *ptr; ptr = (char *)stack_alloc2(file, line, strlen(str)+1, permanent, 0); strcpy(ptr, str); return(ptr); } char *stack_string_realloc2(char *file, int line, char *ptr, char *str, bool chktemp) { STACK *s = NULL; TEMP_STACK *ts = NULL; PERM_STACK *ps; if(chktemp) for(ts = temp_stack;ts;ts = ts->next) if(ts->mem.heap == ptr) { s = &ts->mem; break; } if(!ts && STACK_USAGE == 1) { ptr = (char *)realloc(ptr, strlen(str)+1); strcpy(ptr, str); return(ptr); } if(!ts) { for(ps = perm_stack;ps;ps = ps->next) if(ps->mem.heap == ptr) { s = &ps->mem; break; } if(!ps) { log_error(tprintf("stack_string_realloc() %s %d: Attempt to realloc a nonexistent pointer (ptr = %p)", file, line, ptr)); return(stack_string_alloc(str, 1)); } } s->heap = (char *)realloc(s->heap, strlen(str)+1); if((maze_stack_size += (strlen(str)+1)-(s->size)) > maze_stack_highest) maze_stack_highest = maze_stack_size; s->size = strlen(str)+1; strcpy(s->heap, str); return(s->heap); } void info_stack(OBJ *player) { unsigned long size, highest, num_temporary = 0L, num_permanent = 0L; unsigned long temp_size = 0L, perm_size = 0L, temp_overhead, perm_overhead; unsigned long temp_free_overhead, perm_free_overhead; int num_temp_free = 0, num_perm_free = 0; TEMP_STACK *ts; PERM_STACK *ps; size = maze_stack_size; highest = maze_stack_highest; for(ts = temp_stack;ts;ts = ts->next) { temp_size += ts->mem.size; num_temporary++; } for(ps = perm_stack;ps;ps = ps->next) { perm_size += ps->mem.size; num_permanent++; } for(ts = temp_free_stack;ts;ts = ts->next) num_temp_free++; for(ps = perm_free_stack;ps;ps = ps->next) num_perm_free++; temp_overhead = (sizeof(TEMP_STACK)+sizeof(STACK))*num_temporary; perm_overhead = (sizeof(PERM_STACK)+sizeof(STACK))*num_permanent; temp_free_overhead = (sizeof(TEMP_STACK)+sizeof(STACK))*num_temp_free; perm_free_overhead = (sizeof(PERM_STACK)+sizeof(STACK))*num_perm_free; notify(player, tprintf("|+Y|%s stack status:", config.maze_name)); notify(player, tprintf("|+B|Stack Size |+W|: |+C|%s bytes", comma(tprintf("%ld", size)))); notify(player, tprintf("|+B|Stack Peak Size|+W|: |+C|%s bytes\n", comma(tprintf("%ld", highest)))); if(STACK_USAGE == 2) notify(player, "|+Y|Temporary Stack|+W|:"); notify(player, tprintf("|+B|Current Stack Allocs|+W|: |+C|%s |+W|(|+C|%s bytes|+W|)", comma(tprintf("%ld", num_temporary)), comma(tprintf("%ld", temp_size)))); notify(player, tprintf("|+B|Stack Overhead |+W|: |+C|%s bytes |+W|(|+C|%s total bytes|+W|)", comma(tprintf("%d", sizeof(TEMP_STACK)+sizeof(STACK))), comma(tprintf("%d", temp_overhead)))); notify(player, tprintf("|+B|Free Links |+W|(|+B|highest|+W|): |+C|%s |+W|(|+C|%s|+W|)", comma(tprintf("%d", num_temp_free)), comma(tprintf("%d", temp_free_size_highest)))); notify(player, tprintf("|+B|Free Stack Use |+W|: |+C|%s bytes", comma(tprintf("%d", temp_free_overhead)))); if(STACK_USAGE == 1) return; notify(player, "\n|+Y|Permanent Stack|+W|:"); notify(player, tprintf("|+B|Current Stack Allocs|+W|: |+C|%s |+W|(|+C|%s bytes|+W|)", comma(tprintf("%ld", num_permanent)), comma(tprintf("%ld", perm_size)))); notify(player, tprintf("|+B|Stack Overhead |+W|: |+C|%s bytes |+W|(|+C|%s total bytes|+W|)", comma(tprintf("%d", sizeof(PERM_STACK)+sizeof(STACK))), comma(tprintf("%d", perm_overhead)))); notify(player, tprintf("|+B|Free Links |+W|(|+B|highest|+W|): |+C|%s |+W|(|+C|%s|+W|)", comma(tprintf("%d", num_perm_free)), comma(tprintf("%d", perm_free_size_highest)))); notify(player, tprintf("|+B|Free Stack Use |+W|: |+C|%s bytes", comma(tprintf("%d", perm_free_overhead)))); } void stack_report() { FILE *fp; PERM_STACK *ps; unsigned long total = 0L, lines = 0L; char *b, *p, buf[100]; if(!(fp = fopen("stack_report", "w"))) return; for(ps = perm_stack;ps;ps = ps->next) { b = buf; for(p = ps->mem.heap;*p && p-(char *)(ps->mem.heap) < 15;p++) { if(isprint(*p)) *b++ = *p; else if(b-buf) *b++ = '.'; else break; } *b = '\0'; fprintf(fp, "%s %s %d: %ld bytes: %s\n", mktm(ps->time, "D", root_obj), ps->file, ps->line, ps->mem.size, buf); total += ps->mem.size; lines++; } fprintf(fp, "-----\n%ld total bytes left unallocated.\n", total); fprintf(fp, "%ld total allocations not freed.\n", lines); fclose(fp); }