forked from kyonshi/grenzland-mud
Fixed top_of_helpt bug being off by one. (thanks Jamdog) Fixed trigedit copy bug where it used real_room and not real_trigger. (thanks Jamdog) [Dec 21 2007] - Rumble Fixed dg_affect crash caused by a typo on my part. Renamed shit again! No really in zmalloc.c unsigned char * shit. [Dec 15 2007] - Rumble Showvnums shows [T#] for a single attached trig or [TRIGS] for multiple attached trigs (except for rooms since there is plenty of room to list all attached trigs). Fixed bug where showvnums would not show if attached. (thanks Sryth)
403 lines
10 KiB
C
403 lines
10 KiB
C
/**************************************************************************
|
|
* File: zmalloc.c Part of tbaMUD *
|
|
* Usage: A simple memory allocation monitor. *
|
|
* *
|
|
* Version 2. Copyright 1996, 1998, 1999, 2000 Eric Murray ericm@lne.com *
|
|
**************************************************************************/
|
|
|
|
/* Usage: To run tbaMUD in debug mode change the flags line in your Makefile
|
|
* as below, make clean, and reboot.
|
|
*
|
|
* Makefile: # Any special flags you want to pass to the compiler
|
|
* Makefile: MYFLAGS = -Wall -DMEMORY_DEBUG */
|
|
|
|
/* protect our calloc() and free() calls from recursive redefinition: */
|
|
#define ZMALLOC_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#define NUM_ZBUCKETS 256
|
|
#define GET_ZBUCKET(addr) (((int)(addr) >> 3) & 0xFF)
|
|
|
|
//#define NO_MEMORY_PADDING
|
|
|
|
#ifndef NO_MEMORY_PADDING
|
|
static unsigned char beginPad[4] = {
|
|
0xde, 0xad, 0xde, 0xad };
|
|
|
|
static unsigned char endPad[4] = {
|
|
0xde, 0xad, 0xde, 0xad };
|
|
#endif
|
|
|
|
FILE *zfd = NULL;
|
|
|
|
typedef struct meminfo {
|
|
struct meminfo *next;
|
|
int size; /* number of bytes malloced */
|
|
unsigned char *addr; /* address of memory returned */
|
|
int frees; /* number of times that 'free' was called on this memory */
|
|
char *file; /* file where malloc was called */
|
|
int line; /* line in the code where malloc was called */
|
|
} meminfo;
|
|
|
|
static meminfo *memlist[NUM_ZBUCKETS];
|
|
|
|
/*
|
|
* 0 = only end summary
|
|
* 1 = show errors
|
|
* 2 = errors with dumps
|
|
* 3 = all of the above plus all mallocs/frees
|
|
*/
|
|
int zmalloclogging = 2;
|
|
|
|
/* functions: */
|
|
unsigned char *zmalloc(int len, char *file, int line);
|
|
unsigned char *zrealloc(unsigned char *what, int len, char *file, int line);
|
|
void zdump(meminfo *m);
|
|
void zfree(unsigned char *what, char *file, int line);
|
|
char *zstrdup(const char *src, char *file, int line);
|
|
void zmalloc_init(void);
|
|
void zmalloc_check(void);
|
|
void pad_check(meminfo *m);
|
|
void zmalloc_free_list(meminfo *m);
|
|
|
|
|
|
void zmalloc_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_ZBUCKETS; i++)
|
|
memlist[i] = NULL;
|
|
|
|
zfd = fopen("zmalloc.log","w+");
|
|
}
|
|
|
|
|
|
void zdump(meminfo *m)
|
|
{
|
|
#define MAX_ZDUMP_SIZE 32
|
|
const unsigned char *hextab = (unsigned char *)"0123456789ABCDEF";
|
|
unsigned char hexline[37], ascline[17], *hexp, *ascp, *inp;
|
|
int len, c = 1;
|
|
|
|
if (m->addr == NULL || m->size <= 0)
|
|
return;
|
|
|
|
hexp = hexline;
|
|
ascp = ascline;
|
|
inp = m->addr;
|
|
len = (m->size > MAX_ZDUMP_SIZE ? MAX_ZDUMP_SIZE : m->size);
|
|
|
|
for ( ; len > 0; len--, inp++, c++) {
|
|
*(hexp++) = hextab[(int) (*inp & 0xF0) >> 4]; /* high 4 bit */
|
|
*(hexp++) = hextab[(int) (*inp & 0x0F)]; /* low 4 bit */
|
|
if (c % 4 == 0) *(hexp++) = ' ';
|
|
*(ascp++) = isprint(*inp) ? *inp : '.';
|
|
if (c % 16 == 0 || len <= 1) {
|
|
*hexp = '\0';
|
|
*ascp = '\0';
|
|
fprintf(zfd, " %-40.40s%s\n", hexline, ascline);
|
|
hexp = hexline;
|
|
ascp = ascline;
|
|
}
|
|
}
|
|
fprintf(zfd, "\n");
|
|
}
|
|
|
|
|
|
unsigned char *zmalloc(int len, char *file, int line)
|
|
{
|
|
unsigned char *ret;
|
|
meminfo *m;
|
|
|
|
#ifndef NO_MEMORY_PADDING
|
|
ret = (unsigned char *) calloc(1, len + sizeof(beginPad) + sizeof(endPad));
|
|
#else
|
|
ret = (unsigned char *) calloc(1, len);
|
|
#endif
|
|
|
|
if (!ret) {
|
|
fprintf(zfd,"zmalloc: malloc FAILED");
|
|
return NULL;
|
|
}
|
|
#ifndef NO_MEMORY_PADDING
|
|
/* insert begin and end padding to detect buffer under/overruns: */
|
|
memcpy(ret, beginPad, sizeof(beginPad));
|
|
ret += sizeof(beginPad); /* make ret skip begin pad */
|
|
memcpy(ret + len, endPad, sizeof(endPad));
|
|
#endif
|
|
|
|
if (zmalloclogging > 2)
|
|
fprintf(zfd,"zmalloc: 0x%4.4x %d bytes %s:%d\n",(int)ret,len,file,line);
|
|
|
|
m = (meminfo *) calloc(1, sizeof(meminfo));
|
|
if (!m) {
|
|
fprintf(zfd,"zmalloc: FAILED mem alloc for zmalloc struct... bailing!\n");
|
|
return NULL;
|
|
}
|
|
m->addr = ret;
|
|
m->size = len;
|
|
m->frees = 0;
|
|
m->file = strdup(file);
|
|
if (!m->file) {
|
|
fprintf(zfd,"zmalloc: FAILED mem alloc for zmalloc struct... bailing!\n");
|
|
free(m);
|
|
return NULL;
|
|
}
|
|
m->line = line;
|
|
m->next = memlist[GET_ZBUCKET(ret)];
|
|
memlist[GET_ZBUCKET(ret)] = m;
|
|
return (ret);
|
|
}
|
|
|
|
unsigned char *zrealloc(unsigned char *what, int len, char *file, int line)
|
|
{
|
|
unsigned char *ret;
|
|
meminfo *m, *prev_m;
|
|
|
|
if (what) {
|
|
for (prev_m = NULL, m = memlist[GET_ZBUCKET(what)]; m; prev_m = m, m = m->next) {
|
|
if (m->addr == what) {
|
|
#ifndef NO_MEMORY_PADDING
|
|
ret = (unsigned char *) realloc(what - sizeof(beginPad), len + sizeof(beginPad) + sizeof(endPad));
|
|
#else
|
|
ret = (unsigned char *) realloc(what, len);
|
|
#endif
|
|
if (!ret) {
|
|
fprintf(zfd,"zrealloc: FAILED for 0x%4.4x %d bytes mallocd at %s:%d,\n"
|
|
" %d bytes reallocd at %s:%d.\n",
|
|
(int)m->addr, m->size, m->file, m->line, len, file, line);
|
|
if (zmalloclogging > 1) zdump(m);
|
|
return NULL;
|
|
}
|
|
#ifndef NO_MEMORY_PADDING
|
|
/* insert begin and end padding to detect buffer under/overruns: */
|
|
memcpy(ret, beginPad, sizeof(beginPad));
|
|
ret += sizeof(beginPad); /* make ret skip begin pad */
|
|
memcpy(ret + len, endPad, sizeof(endPad));
|
|
#endif
|
|
if (zmalloclogging > 2)
|
|
fprintf(zfd,"zrealloc: 0x%4.4x %d bytes mallocd at %s:%d, %d bytes reallocd at %s:%d.\n",
|
|
(int)m->addr, m->size, m->file, m->line, len, file, line);
|
|
|
|
m->addr = ret;
|
|
m->size = len;
|
|
if (m->file) free(m->file);
|
|
m->file = strdup(file);
|
|
m->line = line;
|
|
|
|
/* detach node */
|
|
if (prev_m)
|
|
prev_m->next = m->next;
|
|
else
|
|
memlist[GET_ZBUCKET(what)] = m->next;
|
|
|
|
/* readd to proper bucket */
|
|
m->next = memlist[GET_ZBUCKET(ret)];
|
|
memlist[GET_ZBUCKET(ret)] = m;
|
|
|
|
/* could continue the loop to check for multiply-allocd memory */
|
|
/* but that's highly improbable so lets just return instead. */
|
|
return (ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* NULL or invalid pointer given */
|
|
fprintf(zfd,"zrealloc: invalid pointer 0x%4.4x, %d bytes to realloc at %s:%d.\n",
|
|
(int)what, len, file, line);
|
|
|
|
return (zmalloc(len, file, line));
|
|
}
|
|
|
|
/* doesn't actually free memory */
|
|
void zfree(unsigned char *what, char *file, int line)
|
|
{
|
|
meminfo *m;
|
|
int gotit = 0;
|
|
|
|
if (!what) {
|
|
fprintf(zfd,"zfree: ERR: Null pointer free'd: %s:%d.\n", file, line);
|
|
return;
|
|
}
|
|
|
|
/* look up allocated mem in list: */
|
|
for (m = memlist[GET_ZBUCKET(what)]; m; m = m->next) {
|
|
if (m->addr == what) {
|
|
/* got it. Print it if verbose: */
|
|
if (zmalloclogging > 2) {
|
|
fprintf(zfd,"zfree: Freed 0x%4.4x %d bytes mallocd at %s:%d, freed at %s:%d\n",
|
|
(int)m->addr, m->size, m->file, m->line, file, line);
|
|
}
|
|
/* check the padding: */
|
|
pad_check(m);
|
|
|
|
/* note that we freed the memory */
|
|
m->frees++;
|
|
|
|
/* check to see if it was freed > once */
|
|
if (m->frees > 1) {
|
|
fprintf(zfd,"zfree: ERR: multiple frees! 0x%4.4x %d bytes\n"
|
|
" mallocd at %s:%d, freed at %s:%d.\n",
|
|
(int)m->addr, m->size, m->file, m->line, file, line);
|
|
if (zmalloclogging > 1) zdump(m);
|
|
}
|
|
gotit++;
|
|
}
|
|
} /* for.. */
|
|
|
|
if (!gotit) {
|
|
fprintf(zfd,"zfree: ERR: attempt to free unallocated memory 0x%4.4x at %s:%d.\n",
|
|
(int)what, file, line);
|
|
}
|
|
if (gotit > 1) {
|
|
/* this shouldn't happen, eh? */
|
|
fprintf(zfd,"zfree: ERR: Multiply-allocd memory 0x%4.4x.\n", (int)what);
|
|
}
|
|
}
|
|
|
|
|
|
char *zstrdup(const char *src, char *file, int line)
|
|
{
|
|
char *result;
|
|
#ifndef NO_MEMORY_STRDUP
|
|
result = (char*)zmalloc(strlen(src) + 1, file, line);
|
|
if (!result)
|
|
return NULL;
|
|
strcpy(result, src);
|
|
return result;
|
|
#else
|
|
result = (char*)malloc(strlen(src) + 1);
|
|
if (!result)
|
|
return NULL;
|
|
strcpy(result, src); /* strcpy ok, size checked above */
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
|
|
void zmalloc_check()
|
|
{
|
|
meminfo *m, *next_m;
|
|
char *admonishemnt;
|
|
int total_leak = 0, num_leaks = 0, i;
|
|
|
|
fprintf(zfd, "\n------------ Checking leaks ------------\n\n");
|
|
|
|
for (i = 0; i < NUM_ZBUCKETS; i++) {
|
|
for (m = memlist[i]; m; m = next_m) {
|
|
next_m = m->next;
|
|
if (m->addr != 0 && m->frees <= 0) {
|
|
fprintf(zfd,"zmalloc: UNfreed memory 0x%4.4x %d bytes mallocd at %s:%d\n",
|
|
(int)m->addr, m->size, m->file, m->line);
|
|
if (zmalloclogging > 1) zdump(m);
|
|
|
|
/* check padding on un-freed memory too: */
|
|
pad_check(m);
|
|
|
|
total_leak += m->size;
|
|
num_leaks++;
|
|
}
|
|
#ifndef NO_MEMORY_PADDING
|
|
if (m->addr) free(m->addr - sizeof(beginPad));
|
|
#else
|
|
if (m->addr) free(m->addr);
|
|
#endif
|
|
if (m->file) free(m->file);
|
|
free(m);
|
|
}
|
|
}
|
|
|
|
if (total_leak) {
|
|
if (total_leak > 10000)
|
|
admonishemnt = "you must work for Microsoft.";
|
|
else if (total_leak > 5000)
|
|
admonishemnt = "you should be ashamed!";
|
|
else if (total_leak > 2000)
|
|
admonishemnt = "you call yourself a programmer?";
|
|
else if (total_leak > 1000)
|
|
admonishemnt = "the X consortium has a job for you...";
|
|
else
|
|
admonishemnt = "close, but not there yet.";
|
|
fprintf(zfd,"zmalloc: %d leaks totalling %d bytes... %s\n",
|
|
num_leaks, total_leak, admonishemnt);
|
|
}
|
|
else {
|
|
fprintf(zfd,"zmalloc: Congratulations: leak-free code!\n");
|
|
}
|
|
|
|
if (zfd) {
|
|
fflush(zfd);
|
|
fclose(zfd);
|
|
}
|
|
}
|
|
|
|
|
|
void pad_check(meminfo *m)
|
|
{
|
|
#ifndef NO_MEMORY_PADDING
|
|
if (memcmp(m->addr - sizeof(beginPad), beginPad, sizeof(beginPad)) != 0) {
|
|
fprintf(zfd,"pad_check: ERR: beginPad was modified! (mallocd@ %s:%d)\n", m->file, m->line);
|
|
if (zmalloclogging > 1) zdump(m);
|
|
}
|
|
if (memcmp(m->addr + m->size, endPad, sizeof(endPad)) != 0) {
|
|
fprintf(zfd,"pad_check: ERR: endPad was modified! (mallocd@ %s:%d)\n", m->file, m->line);
|
|
if (zmalloclogging > 1) zdump(m);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef ZTEST
|
|
#undef ZMALLOC_H
|
|
|
|
#include "zmalloc.h"
|
|
|
|
int main()
|
|
{
|
|
unsigned char * tmp;
|
|
|
|
zmalloc_init();
|
|
|
|
/* You should see no error here. */
|
|
tmp = (unsigned char*)malloc(200);
|
|
free(tmp);
|
|
|
|
/* Multiple frees test */
|
|
tmp = (unsigned char*)malloc(200);
|
|
strcpy(tmp, "This should show up in the dump but truncated to MAX_ZDUMP_SIZE chars");
|
|
free(tmp);
|
|
free(tmp);
|
|
|
|
/* Free unallocated mem test */
|
|
tmp += 4;
|
|
free(tmp);
|
|
|
|
/* Unfreed mem test... You should see "UNfreed mem at line 370" (at end) because of this */
|
|
tmp = (unsigned char*)malloc(200);
|
|
strcpy(tmp, "This is unfreed memory!");
|
|
|
|
/* Buffer overrun test... You should see an ERR:endPad here */
|
|
tmp = (unsigned char*)malloc(200);
|
|
tmp[202] = 0xbb;
|
|
free(tmp);
|
|
|
|
/* Buffer underrun test... You should see an ERR:beginPad here */
|
|
tmp = (unsigned char*)malloc(200);
|
|
tmp[-3] = 0x0f;
|
|
free(tmp);
|
|
|
|
/* Free NULL pointer test... */
|
|
tmp = NULL;
|
|
free(tmp);
|
|
|
|
printf("Test completed. See zmalloc.log for the messages.\n");
|
|
|
|
zmalloc_check();
|
|
exit(0);
|
|
}
|
|
#endif /* ZTEST */
|