/* dircproxy * Copyright (C) 2002 Scott James Remnant . * All Rights Reserved. * * memdebug.c * - wrappers to memory allocation functions * - memory leak tracing * - buffer overrun detection * * The idea of these is that lots of extra memory is used to store * information about memory allocations, and to check things don't * get accidentally overrun. This is purely debug, you should * NEVER use this in a real program. * -- * @(#) $Id: memdebug.c,v 1.8 2001/12/21 20:15:55 keybuk Exp $ * * This file is distributed according to the GNU General Public * License. For full details, read the top of 'main.c' or the * file called COPYING that was distributed with this code. */ #include #include #include /* Define MIN() */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif /* MIN */ /* This is the structure that goes in front of EVERY chunk or hunk of memory alloc'd. Take a good long look at how big it is, imagine your memory full of these. I think its best to only use this for debugging purposes. Okay? */ struct memstamp { unsigned short magic; unsigned long allocnum; size_t size; char *file; int line; struct memstamp *next; }; /* For statistics */ struct memcount { char *file; unsigned long count; struct memcount *next; }; /* definition of the magic number */ #define MEMMAGIC 0xDC10 /* data put before and after the memory to detect overruns */ #define MEMPREBUFF "yankydoodledandy" #define MEMPOSTBUFF "itwasacolddayinhell" /* function prototypes - don't include the .h as it'll all go *boom* */ void *mem_malloc(size_t, char *, int); void *mem_realloc(void *, size_t, char *, int); void mem_report(char *); /* variables */ static unsigned long memalloccount = 0L; static unsigned long memfreecount = 0L; static unsigned long memusage = 0L; static struct memstamp *memstamplist = NULL; static struct memcount *memcounts = 0; /* Insert a new memory stamp onto the list */ static void _mem_insert(struct memstamp *ms) { ms->next = memstamplist; memstamplist = ms; } /* Delete a memory stamp from the list */ static void _mem_delete(struct memstamp *ms) { struct memstamp *msptr; if (memstamplist != ms) { msptr = memstamplist; while (msptr->next != NULL) { if (msptr->next == ms) { msptr->next = ms->next; return; } msptr = msptr->next; } fprintf(stderr, "MEM: bugger! attempted delete of phantom stamp.\n"); } else { memstamplist = ms->next; } } /* Check for overruns */ static void _mem_checkpad(struct memstamp *ms, char *file, int line) { char *ptr; ptr = (char *)ms + sizeof(struct memstamp); if (strncmp(ptr, MEMPREBUFF, strlen(MEMPREBUFF))) { char *data; data = (char *)malloc(strlen(MEMPREBUFF) + 1); strncpy(data, ptr, strlen(MEMPREBUFF)); data[strlen(MEMPREBUFF)] = 0; fprintf(stderr, "MEM: possible underun detected by (%s/%d) " "alloc at (%s/%d).\n", file ? file : "debug code", file ? line : 0, ms->file ? ms->file : "debug code", ms->file ? ms->line : 0); fprintf(stderr, " [%s:%s]\n", MEMPREBUFF, data); free(data); } ptr = (char *)ptr + strlen(MEMPREBUFF) + ms->size; if (strncmp(ptr, MEMPOSTBUFF, strlen(MEMPOSTBUFF))) { char *data; data = (char *)malloc(strlen(MEMPOSTBUFF) + 1); strncpy(data, ptr, strlen(MEMPOSTBUFF)); data[strlen(MEMPOSTBUFF)] = 0; fprintf(stderr, "MEM: possible overun detected by (%s/%d) " "alloc at (%s/%d).\n", file ? file : "debug code", file ? line : 0, ms->file ? ms->file : "debug code", ms->file ? ms->line : 0); fprintf(stderr, " [%s:%s]\n", MEMPOSTBUFF, data); free(data); } } /* Wrapper around malloc() which adds a memstamp to the front */ void *mem_malloc(size_t size, char *file, int line) { struct memstamp *ms; struct memcount *mc; if (size > 0) { unsigned long malloc_sz; char *preptr, *postptr; malloc_sz = (sizeof(struct memstamp) + strlen(MEMPREBUFF) + size + strlen(MEMPOSTBUFF)); if (!(ms = (struct memstamp *)malloc(malloc_sz))) { fprintf(stderr, "MEM: malloc failed to alloc %lu(%lu) bytes (%s/%d)\n", (unsigned long)malloc_sz, (unsigned long)size, file ? file : "debug code", file ? line : 0); abort(); } if (file && strlen(file)) { mc = memcounts; while (mc) { if (!strcmp(mc->file, file)) { mc->count++; break; } mc = mc->next; } if (!mc) { if (!(mc = (struct memcount *)malloc(sizeof(struct memcount)))) { fprintf(stderr, "MEM: malloc failed to alloc %lu bytes (%s/%d)\n", (unsigned long)(sizeof(struct memcount)), file, line); abort(); } if (!(mc->file = (char *)malloc(strlen(file) + 1))) { fprintf(stderr, "MEM: malloc failed to alloc %lu bytes (%s/%d)\n", (unsigned long)(strlen(file) + 1), file, line); abort(); } strcpy(mc->file, file); mc->count = 1; mc->next = memcounts; memcounts = mc; } if (!(ms->file = (char *)malloc(strlen(file) + 1))) { fprintf(stderr, "MEM: malloc failed to alloc %lu bytes (%s/%d)\n", (unsigned long)(strlen(file) + 1), file, line); abort(); } strcpy(ms->file, file); ms->allocnum = memalloccount++; ms->line = line; #if 0 fprintf(stderr, "MEM: malloc of %lu bytes (%s/%d)\n", (unsigned long)size, file, line); #endif } else { ms->file = 0; ms->allocnum = 0; ms->line = 0; } ms->magic = MEMMAGIC; ms->size = size; memusage += size; _mem_insert(ms); preptr = (char *)ms; preptr += sizeof(struct memstamp); strncpy(preptr, MEMPREBUFF, strlen(MEMPREBUFF)); preptr += strlen(MEMPREBUFF); postptr = preptr; postptr += size; strncpy(postptr, MEMPOSTBUFF, strlen(MEMPOSTBUFF)); return (void *)preptr; } else { return NULL; } } /* Wrapper around realloc() which adds a memstamp to the front */ void *mem_realloc(void *ptr, size_t size, char *file, int line) { unsigned long data_off; struct memstamp *ms; void *block; if (ptr == NULL) return mem_malloc(size, file, line); data_off = sizeof(struct memstamp) + strlen(MEMPREBUFF); ms = (struct memstamp *)((char *)ptr - data_off); if (ms->magic != MEMMAGIC) { fprintf(stderr, "MEM: %s of illegal block (%s/%d)\n", (size > 0) ? "realloc" : "free", file ? file : "debug code", file ? line : 0); return NULL; } _mem_checkpad(ms, file, line); block = NULL; if (size > 0) { block = mem_malloc(size, file, line); if (size < ms->size) memcpy(block, (char *)ms + data_off, size); else memcpy(block, (char *)ms + data_off, ms->size); } if (ms->file) memfreecount++; memusage -= ms->size; _mem_delete(ms); free(ms->file); free(ms); return block; } /* Reports current memory usage and shows what was malloc()d where */ void mem_report(char *message) { struct memstamp *msptr; struct memcount *mc; msptr = memstamplist; printf("MEM:REPORT%s%s%s %lu bytes in use [%lu alloc][%lu free]\n", message ? " (" : "", message ? message : "", message ? ")" : "", memusage, memalloccount, memfreecount); mc = memcounts; while (mc) { unsigned long i, t; printf("%16s %8lu ", mc->file, mc->count); t = ((double)mc->count / (double)memalloccount) * 40; for (i = 0; i < t; i++) printf("#"); printf("\n"); mc = mc->next; } printf("\n"); while (msptr) { if (message) { int i; printf(" %s/%d - %lu bytes alloc'd [an:%lu]\n", msptr->file ? msptr->file : "debug code", msptr->file ? msptr->line : 0, (unsigned long)msptr->size, msptr->file ? msptr->allocnum : 0); printf(" ["); for (i = 0; i <= MIN(msptr->size, 70); i++) { int c; c = *((char *)msptr + sizeof(struct memstamp) + strlen(MEMPREBUFF) + i); if ((c >= 32) && (c <= 127)) { printf("%c", c); } else if (c) { printf("£"); } else { break; } } printf("]\n"); } _mem_checkpad(msptr, "report", 0); msptr = msptr->next; } }