Created
June 23, 2014 13:42
-
-
Save rkrishnasanka/b6f827e09d1c0c2f03a9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** This is a copyrighted work which is functionally identical to work | |
** originally published in Micro Cornucopia magazine (issue #52, March-April, | |
** 1990) and is freely licensed by the author, Walter Bright, for any use. | |
*/ | |
/*_ mem.c Fri Jan 26 1990 Modified by: Walter Bright */ | |
/* $Header: /proj/products/merlin/port/RCS/mem.c,v 1.19 89/10/20 14:36:02 bright Exp Locker: bright $ */ | |
/* Memory management package */ | |
#if defined(VAX11C) | |
#define __FILE__ "mem.c" | |
#endif | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <io.h> | |
#ifndef MEM_H | |
#include "mem.h" | |
#endif | |
#ifndef assert | |
#include <assert.h> | |
#endif | |
#if defined(_MSC_VER) | |
#include <dos.h> | |
#endif | |
#if !defined(VAX11C) | |
#ifdef BSDUNIX | |
#include <strings.h> | |
#else | |
#include <string.h> | |
#endif | |
#else | |
extern char *strcpy(),*memcpy(); | |
extern int strlen(); | |
#endif /* VAX11C */ | |
int mem_inited = 0; /* != 0 if initialized */ | |
static int mem_behavior = MEM_ABORTMSG; | |
static int (*fp)() = NULL; /* out-of-memory handler */ | |
static int mem_count; /* # of allocs that haven't been free'd */ | |
static int mem_scount; /* # of sallocs that haven't been free'd */ | |
static int near mem_exception(); /* called when out of memory */ | |
/* Determine where to send error messages */ | |
#ifdef MSDOS | |
#define ferr stdout /* stderr can't be redirected with MS-DOS */ | |
#else | |
#define ferr stderr | |
#endif | |
/*******************************/ | |
void mem_setexception(flag,handler_fp) | |
#if __cplusplus | |
enum MEM_E flag; | |
#else | |
int flag; | |
#endif | |
int (*handler_fp)(); | |
{ | |
mem_behavior = flag; | |
fp = (mem_behavior == MEM_CALLFP) ? handler_fp : 0; | |
#if MEM_DEBUG | |
assert(0 <= flag && flag <= MEM_RETRY); | |
#endif | |
} | |
/************************* | |
* This is called when we're out of memory. | |
* Returns: | |
* 1: try again to allocate the memory | |
* 0: give up and return NULL | |
*/ | |
static int near mem_exception() | |
{ int behavior; | |
behavior = mem_behavior; | |
while (1) | |
{ | |
switch (behavior) | |
{ | |
case MEM_ABORTMSG: | |
#if defined(MSDOS) || defined(__OS2__) | |
/* Avoid linking in buffered I/O */ | |
{ static char msg[] = "Fatal error: out of memory\r\n"; | |
write(1,msg,sizeof(msg) - 1); | |
} | |
#else | |
fputs("Fatal error: out of memory\n",ferr); | |
#endif | |
/* FALL-THROUGH */ | |
case MEM_ABORT: | |
exit(EXIT_FAILURE); | |
/* NOTREACHED */ | |
case MEM_CALLFP: | |
assert(fp); | |
behavior = (*fp)(); | |
break; | |
case MEM_RETNULL: | |
return 0; | |
case MEM_RETRY: | |
return 1; | |
default: | |
assert(0); | |
} | |
} | |
} | |
/****************************/ | |
#if MEM_DEBUG | |
#undef mem_strdup | |
char *mem_strdup(s) | |
const char *s; | |
{ | |
return mem_strdup_debug(s,__FILE__,__LINE__); | |
} | |
char *mem_strdup_debug(s,file,line) | |
char *file; | |
const char *s; | |
int line; | |
{ | |
char *p; | |
p = s | |
? (char *) mem_malloc_debug((unsigned) strlen(s) + 1,file,line) | |
: NULL; | |
return p ? strcpy(p,s) : p; | |
} | |
#else | |
char *mem_strdup(s) | |
const char *s; | |
{ | |
char *p; | |
p = s ? (char *) mem_malloc((unsigned) strlen(s) + 1) : NULL; | |
return p ? strcpy(p,s) : p; | |
} | |
#endif /* MEM_DEBUG */ | |
#ifdef MEM_DEBUG | |
static long mem_maxalloc; /* max # of bytes allocated */ | |
static long mem_numalloc; /* current # of bytes allocated */ | |
#define BEFOREVAL 0x12345678 /* value to detect underrun */ | |
#define AFTERVAL 0x87654321 /* value to detect overrun */ | |
#if SUN || SUN386 | |
static long afterval = AFTERVAL; /* so we can do &afterval */ | |
#endif | |
/* The following should be selected to give maximum probability that */ | |
/* pointers loaded with these values will cause an obvious crash. On */ | |
/* Unix machines, a large value will cause a segment fault. */ | |
/* MALLOCVAL is the value to set malloc'd data to. */ | |
#if MSDOS || __OS2__ | |
#define BADVAL 0xFF | |
#define MALLOCVAL 0xEE | |
#else | |
#define BADVAL 0x7A | |
#define MALLOCVAL 0xEE | |
#endif | |
/* Disable mapping macros */ | |
#undef mem_malloc | |
#undef mem_calloc | |
#undef mem_realloc | |
#undef mem_free | |
/* Create a list of all alloc'ed pointers, retaining info about where */ | |
/* each alloc came from. This is a real memory and speed hog, but who */ | |
/* cares when you've got obscure pointer bugs. */ | |
static struct mem_debug | |
{ struct mh | |
{ struct mem_debug *Mnext; /* next in list */ | |
struct mem_debug *Mprev; /* previous value in list */ | |
char *Mfile; /* filename of where allocated */ | |
int Mline; /* line number of where allocated */ | |
unsigned Mnbytes; /* size of the allocation */ | |
long Mbeforeval; /* detect underrun of data */ | |
} m; | |
char data[1]; /* the data actually allocated */ | |
} mem_alloclist = | |
{ | |
{ (struct mem_debug *) NULL, | |
(struct mem_debug *) NULL, | |
"noname", | |
11111, | |
0, | |
BEFOREVAL | |
}, | |
AFTERVAL | |
}; | |
/* Convert from a void *to a mem_debug struct. */ | |
#define mem_ptrtodl(p) ((struct mem_debug *) ((char *)p - sizeof(struct mh))) | |
/* Convert from a mem_debug struct to a mem_ptr. */ | |
#define mem_dltoptr(dl) ((void *) &((dl)->data[0])) | |
#define next m.Mnext | |
#define prev m.Mprev | |
#define file m.Mfile | |
#define line m.Mline | |
#define nbytes m.Mnbytes | |
#define beforeval m.Mbeforeval | |
/***************************** | |
* Set new value of file,line | |
*/ | |
void mem_setnewfileline(ptr,fil,lin) | |
void *ptr; | |
char *fil; | |
int lin; | |
{ | |
struct mem_debug *dl; | |
dl = mem_ptrtodl(ptr); | |
dl->file = fil; | |
dl->line = lin; | |
} | |
/**************************** | |
* Print out struct mem_debug. | |
*/ | |
static void near mem_printdl(dl) | |
struct mem_debug *dl; | |
{ | |
#if LPTR | |
fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%lx\n", | |
dl->file,dl->line,dl->nbytes,mem_dltoptr(dl)); | |
#else | |
fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%x\n", | |
dl->file,dl->line,dl->nbytes,mem_dltoptr(dl)); | |
#endif | |
} | |
/**************************** | |
* Print out file and line number. | |
*/ | |
static void near mem_fillin(fil,lin) | |
char *fil; | |
int lin; | |
{ | |
fprintf(ferr,"File '%s' line %d\n",fil,lin); | |
fflush(ferr); | |
} | |
/**************************** | |
* If MEM_DEBUG is not on for some modules, these routines will get | |
* called. | |
*/ | |
void *mem_calloc(u) | |
unsigned u; | |
{ | |
return mem_calloc_debug(u,__FILE__,__LINE__); | |
} | |
void *mem_malloc(u) | |
unsigned u; | |
{ | |
return mem_malloc_debug(u,__FILE__,__LINE__); | |
} | |
void *mem_realloc(p,u) | |
void *p; | |
unsigned u; | |
{ | |
return mem_realloc_debug(p,u,__FILE__,__LINE__); | |
} | |
void mem_free(p) | |
void *p; | |
{ | |
mem_free_debug(p,__FILE__,__LINE__); | |
} | |
/**************************/ | |
void mem_freefp(p) | |
void *p; | |
{ | |
mem_free(p); | |
} | |
/*********************** | |
* Debug versions of mem_calloc(), mem_free() and mem_realloc(). | |
*/ | |
void *mem_malloc_debug(n,fil,lin) | |
unsigned n; | |
char *fil; | |
int lin; | |
{ void *p; | |
p = mem_calloc_debug(n,fil,lin); | |
if (p) | |
memset(p,MALLOCVAL,n); | |
return p; | |
} | |
void *mem_calloc_debug(n,fil,lin) | |
unsigned n; | |
char *fil; | |
int lin; | |
{ | |
struct mem_debug *dl; | |
do | |
dl = (struct mem_debug *) | |
calloc(sizeof(*dl) + n + sizeof(AFTERVAL) - 1,1); | |
while (dl == NULL && mem_exception()); | |
if (dl == NULL) | |
{ | |
#if 0 | |
printf("Insufficient memory for alloc of %d at ",n); | |
mem_fillin(fil,lin); | |
printf("Max allocated was: %ld\n",mem_maxalloc); | |
#endif | |
return NULL; | |
} | |
dl->file = fil; | |
dl->line = lin; | |
dl->nbytes = n; | |
dl->beforeval = BEFOREVAL; | |
#if SUN || SUN386 /* bus error if we store a long at an odd address */ | |
memcpy(&(dl->data[n]),&afterval,sizeof(AFTERVAL)); | |
#else | |
*(long *) &(dl->data[n]) = AFTERVAL; | |
#endif | |
/* Add dl to start of allocation list */ | |
dl->next = mem_alloclist.next; | |
dl->prev = &mem_alloclist; | |
mem_alloclist.next = dl; | |
if (dl->next != NULL) | |
dl->next->prev = dl; | |
mem_count++; | |
mem_numalloc += n; | |
if (mem_numalloc > mem_maxalloc) | |
mem_maxalloc = mem_numalloc; | |
return mem_dltoptr(dl); | |
} | |
void mem_free_debug(ptr,fil,lin) | |
void *ptr; | |
char *fil; | |
int lin; | |
{ | |
struct mem_debug *dl; | |
if (ptr == NULL) | |
return; | |
#if 0 | |
{ fprintf(ferr,"Freeing NULL pointer at "); | |
goto err; | |
} | |
#endif | |
if (mem_count <= 0) | |
{ fprintf(ferr,"More frees than allocs at "); | |
goto err; | |
} | |
dl = mem_ptrtodl(ptr); | |
if (dl->beforeval != BEFOREVAL) | |
{ | |
#if LPTR | |
fprintf(ferr,"Pointer x%lx underrun\n",ptr); | |
#else | |
fprintf(ferr,"Pointer x%x underrun\n",ptr); | |
#endif | |
goto err2; | |
} | |
#if SUN || SUN386 /* Bus error if we read a long from an odd address */ | |
if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0) | |
#else | |
if (*(long *) &dl->data[dl->nbytes] != AFTERVAL) | |
#endif | |
{ | |
#if LPTR | |
fprintf(ferr,"Pointer x%lx overrun\n",ptr); | |
#else | |
fprintf(ferr,"Pointer x%x overrun\n",ptr); | |
#endif | |
goto err2; | |
} | |
mem_numalloc -= dl->nbytes; | |
if (mem_numalloc < 0) | |
{ fprintf(ferr,"error: mem_numalloc = %ld, dl->nbytes = %d\n", | |
mem_numalloc,dl->nbytes); | |
goto err2; | |
} | |
/* Remove dl from linked list */ | |
if (dl->prev) | |
dl->prev->next = dl->next; | |
if (dl->next) | |
dl->next->prev = dl->prev; | |
/* Stomp on the freed storage to help detect references */ | |
/* after the storage was freed. */ | |
memset((void *) dl,BADVAL,sizeof(*dl) + dl->nbytes); | |
mem_count--; | |
/* Some compilers can detect errors in the heap. */ | |
#if defined(DLC) | |
{ int i; | |
i = free(dl); | |
assert(i == 0); | |
} | |
#else | |
free((void *) dl); | |
#endif | |
return; | |
err2: | |
mem_printdl(dl); | |
err: | |
fprintf(ferr,"free'd from "); | |
mem_fillin(fil,lin); | |
assert(0); | |
/* NOTREACHED */ | |
} | |
/******************* | |
* Debug version of mem_realloc(). | |
*/ | |
void *mem_realloc_debug(oldp,n,fil,lin) | |
void *oldp; | |
unsigned n; | |
char *fil; | |
int lin; | |
{ void *p; | |
struct mem_debug *dl; | |
if (n == 0) | |
{ mem_free_debug(oldp,fil,lin); | |
p = NULL; | |
} | |
else if (oldp == NULL) | |
p = mem_malloc_debug(n,fil,lin); | |
else | |
{ | |
p = mem_malloc_debug(n,fil,lin); | |
if (p != NULL) | |
{ | |
dl = mem_ptrtodl(oldp); | |
if (dl->nbytes < n) | |
n = dl->nbytes; | |
memcpy(p,oldp,n); | |
mem_free_debug(oldp,fil,lin); | |
} | |
} | |
return p; | |
} | |
/***************************/ | |
void mem_check() | |
{ register struct mem_debug *dl; | |
for (dl = mem_alloclist.next; dl != NULL; dl = dl->next) | |
mem_checkptr(mem_dltoptr(dl)); | |
} | |
/***************************/ | |
void mem_checkptr(p) | |
register void *p; | |
{ register struct mem_debug *dl; | |
for (dl = mem_alloclist.next; dl != NULL; dl = dl->next) | |
{ | |
if (p >= (void *) &(dl->data[0]) && | |
p < (void *)((char *)dl + sizeof(struct mem_debug)-1 + dl->nbytes)) | |
goto L1; | |
} | |
assert(0); | |
L1: | |
dl = mem_ptrtodl(p); | |
if (dl->beforeval != BEFOREVAL) | |
{ | |
#if LPTR | |
fprintf(ferr,"Pointer x%lx underrun\n",p); | |
#else | |
fprintf(ferr,"Pointer x%x underrun\n",p); | |
#endif | |
goto err2; | |
} | |
#if SUN || SUN386 /* Bus error if we read a long from an odd address */ | |
if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0) | |
#else | |
if (*(long *) &dl->data[dl->nbytes] != AFTERVAL) | |
#endif | |
{ | |
#if LPTR | |
fprintf(ferr,"Pointer x%lx overrun\n",p); | |
#else | |
fprintf(ferr,"Pointer x%x overrun\n",p); | |
#endif | |
goto err2; | |
} | |
return; | |
err2: | |
mem_printdl(dl); | |
assert(0); | |
} | |
#else | |
/***************************/ | |
void *mem_malloc(numbytes) | |
unsigned numbytes; | |
{ void *p; | |
if (numbytes == 0) | |
return NULL; | |
while (1) | |
{ | |
p = malloc(numbytes); | |
if (p == NULL) | |
{ if (mem_exception()) | |
continue; | |
} | |
else | |
mem_count++; | |
break; | |
} | |
/*printf("malloc(%d) = x%lx\n",numbytes,p);*/ | |
return p; | |
} | |
/***************************/ | |
void *mem_calloc(numbytes) | |
unsigned numbytes; | |
{ void *p; | |
if (numbytes == 0) | |
return NULL; | |
while (1) | |
{ | |
p = calloc(numbytes,1); | |
if (p == NULL) | |
{ if (mem_exception()) | |
continue; | |
} | |
else | |
mem_count++; | |
break; | |
} | |
/*printf("calloc(%d) = x%lx\n",numbytes,p);*/ | |
return p; | |
} | |
/***************************/ | |
void *mem_realloc(oldmem_ptr,newnumbytes) | |
void *oldmem_ptr; | |
unsigned newnumbytes; | |
{ void *p; | |
if (oldmem_ptr == NULL) | |
p = mem_malloc(newnumbytes); | |
else if (newnumbytes == 0) | |
{ mem_free(oldmem_ptr); | |
p = NULL; | |
} | |
else | |
{ | |
do | |
p = realloc(oldmem_ptr,newnumbytes); | |
while (p == NULL && mem_exception()); | |
} | |
/*printf("realloc(x%lx,%d) = x%lx\n",oldmem_ptr,newnumbytes,p);*/ | |
return p; | |
} | |
/***************************/ | |
void mem_free(ptr) | |
void *ptr; | |
{ | |
/*printf("free(x%lx)\n",ptr);*/ | |
if (ptr != NULL) | |
{ assert(mem_count > 0); | |
mem_count--; | |
#if DLC | |
{ int i; | |
i = free(ptr); | |
assert(i == 0); | |
} | |
#else | |
free(ptr); | |
#endif | |
} | |
} | |
#endif /* MEM_DEBUG */ | |
/***************************/ | |
void mem_init() | |
{ | |
if (mem_inited == 0) | |
{ mem_count = 0; | |
#if MEM_DEBUG | |
mem_numalloc = 0; | |
mem_maxalloc = 0; | |
mem_alloclist.next = NULL; | |
#endif | |
#if defined(__ZTC__) || defined(__SC__) | |
/* Necessary if mem_sfree() calls free() before any */ | |
/* calls to malloc(). */ | |
free(malloc(1)); /* initialize storage allocator */ | |
#endif | |
mem_inited++; | |
} | |
} | |
/***************************/ | |
void mem_term() | |
{ | |
if (mem_inited) | |
{ | |
#if MEM_DEBUG | |
register struct mem_debug *dl; | |
for (dl = mem_alloclist.next; dl; dl = dl->next) | |
{ fprintf(ferr,"Unfreed pointer: "); | |
mem_printdl(dl); | |
} | |
#if 0 | |
fprintf(ferr,"Max amount ever allocated == %ld bytes\n", | |
mem_maxalloc); | |
#endif | |
#else | |
if (mem_count) | |
fprintf(ferr,"%d unfreed items\n",mem_count); | |
if (mem_scount) | |
fprintf(ferr,"%d unfreed s items\n",mem_scount); | |
#endif /* MEM_DEBUG */ | |
assert(mem_count == 0 && mem_scount == 0); | |
mem_inited = 0; | |
} | |
} | |
#undef next | |
#undef prev | |
#undef file | |
#undef line | |
#undef nbytes | |
#undef beforeval | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** This is a copyrighted work which is functionally identical to work | |
** originally published in Micro Cornucopia magazine (issue #52, March-April, | |
** 1990) and is freely licensed by the author, Walter Bright, for any use. | |
*/ | |
/*_ mem.h Fri May 26 1989 Modified by: Walter Bright */ | |
/* $Header: /proj/products/merlin/port/RCS/mem.h,v 1.11 89/10/23 11:39:00 bright Exp $ */ | |
/* Copyright 1986-1988 by Northwest Software */ | |
/* All Rights Reserved */ | |
/* Written by Walter Bright */ | |
#ifndef MEM_H | |
#define MEM_H 1 | |
#ifndef TOOLKIT_H | |
#include "toolkit.h" | |
#endif | |
/* | |
* Memory management routines. | |
* | |
* Compiling: | |
* | |
* #define MEM_DEBUG 1 when compiling to enable extended debugging | |
* features. | |
* | |
* Features always enabled: | |
* | |
* o mem_init() is called at startup, and mem_term() at | |
* close, which checks to see that the number of alloc's is | |
* the same as the number of free's. | |
* o Behavior on out-of-memory conditions can be controlled | |
* via mem_setexception(). | |
* | |
* Extended debugging features: | |
* | |
* o Enabled by #define MEM_DEBUG 1 when compiling. | |
* o Check values are inserted before and after the alloc'ed data | |
* to detect pointer underruns and overruns. | |
* o Free'd pointers are checked against alloc'ed pointers. | |
* o Free'd storage is cleared to smoke out references to free'd data. | |
* o Realloc'd pointers are always changed, and the previous storage | |
* is cleared, to detect erroneous dependencies on the previous | |
* pointer. | |
* o The routine mem_checkptr() is provided to check an alloc'ed | |
* pointer. | |
*/ | |
/********************* GLOBAL VARIABLES *************************/ | |
extern int mem_inited; /* != 0 if mem package is initialized. */ | |
/* Test this if you have other packages */ | |
/* that depend on mem being initialized */ | |
/********************* PUBLIC FUNCTIONS *************************/ | |
/*********************************** | |
* Set behavior when mem runs out of memory. | |
* Input: | |
* flag = MEM_ABORTMSG: Abort the program with the message | |
* 'Fatal error: out of memory' sent | |
* to stdout. This is the default behavior. | |
* MEM_ABORT: Abort the program with no message. | |
* MEM_RETNULL: Return NULL back to caller. | |
* MEM_CALLFP: Call application-specified function. | |
* fp must be supplied. | |
* fp Optional function pointer. Supplied if | |
* (flag == MEM_CALLFP). This function returns | |
* MEM_XXXXX, indicating what mem should do next. | |
* The function could do things like swap | |
* data out to disk to free up more memory. | |
* fp could also return: | |
* MEM_RETRY: Try again to allocate the space. Be | |
* careful not to go into an infinite loop. | |
*/ | |
#if __cplusplus | |
enum MEM_E { MEM_ABORTMSG, MEM_ABORT, MEM_RETNULL, MEM_CALLFP, MEM_RETRY }; | |
void mem_setexception P((enum MEM_E, int (*)())); | |
#else | |
#define MEM_ABORTMSG 0 | |
#define MEM_ABORT 1 | |
#define MEM_RETNULL 2 | |
#define MEM_CALLFP 3 | |
#define MEM_RETRY 4 | |
void mem_setexception P((int, int(*)())); | |
#endif | |
/**************************** | |
* Allocate space for string, copy string into it, and | |
* return pointer to the new string. | |
* This routine doesn't really belong here, but it is used so often | |
* that I gave up and put it here. | |
* Use: | |
* char *mem_strdup(const char *s); | |
* Returns: | |
* pointer to copied string if succussful. | |
* else returns NULL (if MEM_RETNULL) | |
*/ | |
char *mem_strdup P((const char *)); | |
/************************** | |
* Function so we can have a pointer to function mem_free(). | |
* This is needed since mem_free is sometimes defined as a macro, | |
* and then the preprocessor screws up. | |
* The pointer to mem_free() is used frequently with the list package. | |
* Use: | |
* void mem_freefp(void *p); | |
*/ | |
/*************************** | |
* Check for errors. This routine does a consistency check on the | |
* storage allocator, looking for corrupted data. It should be called | |
* when the application has CPU cycles to burn. | |
* Use: | |
* void mem_check(void); | |
*/ | |
void mem_check P((void )); | |
/*************************** | |
* Check ptr to see if it is in the range of allocated data. | |
* Cause assertion failure if it isn't. | |
*/ | |
void mem_checkptr P((void *ptr)); | |
/*************************** | |
* Allocate and return a pointer to numbytes of storage. | |
* Use: | |
* void *mem_malloc(unsigned numbytes); | |
* void *mem_calloc(unsigned numbytes); allocated memory is cleared | |
* Input: | |
* numbytes Number of bytes to allocate | |
* Returns: | |
* if (numbytes > 0) | |
* pointer to allocated data, NULL if out of memory | |
* else | |
* return NULL | |
*/ | |
void *mem_malloc P((unsigned)); | |
void *mem_calloc P((unsigned)); | |
/***************************** | |
* Reallocate memory. | |
* Use: | |
* void *mem_realloc(void *ptr,unsigned numbytes); | |
*/ | |
void *mem_realloc P((void *,unsigned)); | |
/***************************** | |
* Free memory allocated by mem_malloc(), mem_calloc() or mem_realloc(). | |
* Use: | |
* void mem_free(void *ptr); | |
*/ | |
void mem_free P((void *)); | |
/*************************** | |
* Initialize memory handler. | |
* Use: | |
* void mem_init(void); | |
* Output: | |
* mem_inited = 1 | |
*/ | |
void mem_init P((void )); | |
/*************************** | |
* Terminate memory handler. Useful for checking for errors. | |
* Use: | |
* void mem_term(void); | |
* Output: | |
* mem_inited = 0 | |
*/ | |
void mem_term P((void )); | |
/* The following stuff forms the implementation rather than the | |
* definition, so ignore it. | |
*/ | |
#if MEM_DEBUG /* if creating debug version */ | |
#define mem_strdup(p) mem_strdup_debug((p),__FILE__,__LINE__) | |
#define mem_malloc(u) mem_malloc_debug((u),__FILE__,__LINE__) | |
#define mem_calloc(u) mem_calloc_debug((u),__FILE__,__LINE__) | |
#define mem_realloc(p,u) mem_realloc_debug((p),(u),__FILE__,__LINE__) | |
#define mem_free(p) mem_free_debug((p),__FILE__,__LINE__) | |
char *mem_strdup_debug P((const char *,char *,int)); | |
void *mem_calloc_debug P((unsigned,char *,int)); | |
void *mem_malloc_debug P((unsigned,char *,int)); | |
void *mem_realloc_debug P((void *,unsigned,char *,int)); | |
void mem_free_debug P((void *,char *,int)); | |
void mem_freefp P((void *)); | |
void mem_setnewfileline P((void *,char *,int)); | |
#else | |
#define mem_freefp mem_free | |
#define mem_check() | |
#define mem_checkptr(p) | |
#endif /* MEM_DEBUG */ | |
#endif /* MEM_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Walter Bright's MEM Package | |
--------------------------- | |
PREFACE: | |
-------- | |
The files, MEM.H and MEM.C which constitute the MEM package were originally | |
published in the March-April 1990 edition of Micro Cornucopia magazine, now | |
sadly out of print. The files as they appear in SNIPPETS have been edited | |
somewhat to remove compiler dependencies and correct minor discrepancies. | |
For those who don't already know, Walter Bright is the author of Datalight | |
Optimum-C, the original optimizing C compiler for PC's. Through a succession | |
of sales and acquisitions plus continual improvement by Walter,, his compiler | |
became Zortech C++ and is now sold as Symantec C++. As such, it is the only | |
major PC compiler which can claim single authorship. It also compiles faster | |
than most other compilers and is still a market leader in its optimization | |
technology. | |
Like many other library and ancillary functions unique to Walter's compilers, | |
the MEM package was originally something he wrote for his own use. As noted | |
above, he published it only once but it has been included as an unheralded | |
"freebie" in Walter's compilers for the past several years. Walter was kind | |
enough to grant permission for its inclusion in SNIPPETS beginning with the | |
April, '94 release. | |
WHAT IS MEM?: | |
------------- | |
MEM is a set of functions used for debugging C pointers and memory allocation | |
problems. Quoting Walter, "Symptoms of pointer bugs include: hung machines, | |
scrambled disks, failures that occur once-in-10,000 iterations, irreprodu- | |
cible results, and male pattern baldness." After writing MEM for use in | |
developing his own compiler and tools, he reported that its use reduced | |
pointer bugs by as much as 75%. MEM is simple to add to existing programs | |
and adds little or no overhead. | |
USING MEM: | |
---------- | |
Included in the MEM package is TOOLKIT.H, which isolates compiler and | |
environmental dependencies. It should work as-is for most PC compilers and | |
the Microsoft compiler for SCO Unix. Other environments may be customized by | |
writing your own HOST.H file, using the existing definitions in TOOLKIT.H as | |
examples and modifying the values to match your system. Using these | |
techniques, the MEM package has been used successfully on Amigas, Macs, | |
VAXes, and many other non-DOS systems. | |
The MEM functions exactly parallel the standard library (plus 1 non-standard) | |
memory allocation functions. To implement MEM in your program, simply do a | |
global search-and-replace of the following functions: | |
malloc() -> mem_malloc() | |
calloc() -> mem_calloc() | |
realloc() -> mem_realloc() | |
free() -> mem_free() | |
strdup() -> mem_strdup() | |
At the beginning of main(), add the following lines: | |
mem_init(); | |
atexit(mem_term); | |
In the header section of each of your C files, add... | |
#include "mem.h" | |
...to every .C file which calls any of the above functions. | |
The final step is to compile and link MEM.C into all programs using the MEM | |
package. It really is a pretty simple procedure! | |
MEM has 2 modes of operation, debugging and non-debugging. Use debugging | |
mode during program development and then turn debugging off for final | |
production code. Control of debugging is by defining the MEM_DEBUG macro. | |
If the macro is defined, debugging is on; if undefined, debugging is off. | |
The default is non-debugging, in which case the MEM functions become trivial | |
wrappers for the standard functions, incurring virtually no overhead. | |
WHAT MEM DOES: | |
-------------- | |
1. ISO/ANSI verification: | |
When Walter wrote MEM, compiler compliance with ANSI standards was still | |
quite low. MEM verifies ISO/ANSI compliance for situations such as passing | |
NULL or size 0 to allocation/reallocation functions. | |
2. Logging of all allocations and frees: | |
All MEM's functions pass the __FILE__ and __LINE__ arguments. During alloca- | |
tion, MEM makes an entry into a linked list and stores the file and line | |
information in the list for whichever allocation or free function is called. | |
This linked list is the backbone of MEM. When MEM detects a bug, it tells | |
you where to look in which file to begin tracking the problem. | |
3. Verification of frees: | |
Since MEM knows about all allocations, when a pointer is freed, MEM can | |
verify that the pointer was allocated originally. Additionally, MEM will | |
only allow a pointer to be freed once. | |
Freed data is overwritten with a non-zero known value, flushing such problems | |
as continuing to reference data after it's been freed. The value written | |
over the data is selected to maximize the probability of a segment fault or | |
assertion failure if your application references it after it's been freed. | |
MEM obviously can't directly detect "if" instances such as... | |
mem_free(p); | |
if (p) ... | |
...but by guaranteeing that `p' points to garbage after being freed, code | |
like this will hopefully never work and will thus be easier to find. | |
4. Detection of pointer over- and under-run: | |
Pointer overrun occurs when a program stores data past the end of a buffer, | |
e.g. | |
p = malloc(strlen(s)); /* No space for terminating NUL */ | |
strcpy(p,s); /* Terminating NUL clobber memory */ | |
Pointer underrun occurs when a program stores data before the beginning of a | |
buffer. This error occurs less often than overruns, but MEM detects it | |
anyway. MEM does this by allocating a little extra at each end of every | |
buffer, which is filled with a known value, called a sentinel. MEM detects | |
overruns and underruns by verifying the sentinel value when the buffer is | |
freed. | |
5. Dependence on values in buffer obtained from malloc(): | |
When obtaining a buffer from malloc(), a program may develop erroneous and | |
creeping dependencies on whatever random (and sometimes repeatable) values | |
the buffer may contain. The mem_malloc() function prevents this by always | |
setting the data in a buffer to a known non-zero value before returning its | |
pointer. This also prevents another common error when running under MS-DOS | |
which doesn't clear unused memory when loading a program. These bugs are | |
particularly nasty to find since correct program operation may depend on what | |
was last run! | |
6. Realloc problems: | |
Common problems when using realloc() are: 1) depending on realloc() *not* | |
shifting the location of the buffer in memory, and 2) depending on finding | |
certain values in the uninitialized region of the realloc'ed buffer. | |
MEM flushes these out by *always* moving the buffer and stomping on values | |
past the initialized area. | |
7. Memory leak detection: | |
Memory "leaks" are areas that are allocated but never freed. This can become | |
a major problem in programs that must run for long periods without interrup- | |
tion (e.g. BBS's). If there are leaks, eventually the program will run out | |
of memory and fail. | |
Another form of memory leak occurs when a piece of allocated memory should | |
have been added to some central data structure, but wasn't. | |
MEM find memory leaks by keeping track of all allocations and frees. When | |
mem_term() is called, a list of all unfreed allocations is printed along with | |
the files and line numbers where the allocations occurred. | |
8. Pointer checking: | |
Sometimes it's useful to be able to verify that a pointer is actually | |
pointing into free store. MEM provides a function... | |
mem_checkptr(void *p); | |
...to do this. | |
9. Consistency checking: | |
Occasionally, even MEM's internal data structures get clobbered by a wild | |
pointer. When this happens, you can track it down by sprinkling your code | |
temporarily with calls to mem_check(), which performs a consistency check on | |
the free store. | |
10. Out of memory handling: | |
MEM can be set using mem_setexception() (see MEM.H) to handle out-of-memory | |
conditions in any one of several predefined ways: | |
1. Present an "Out of memory" message and terminate the program. | |
2. Abort the program with no message. | |
3. Mimic ISO/ANSI and return NULL. | |
4. Call a user-specified function, perhaps involving virtual memory | |
or some other "emergency reserve". | |
5. Retry (be careful to avoid infinite loops!) | |
11. Companion techniques: | |
Since MEM presets allocated and stomps on freed memory, this facilitates | |
adding your own code to add tags to your data structures when debugging. If | |
the structures are invalid, you'll know it because MEM will have clobbered | |
your verification tags. | |
SUMMARY: | |
-------- | |
Since it is, in the final analysis, a software solution, MEM is fallible. As | |
the saying goes, "Nothing is foolproof because fools are so ingenious." | |
Walter himself readily acknowledges that there are circumstances where your | |
code can do sufficient damage to MEM's internal data structures to render it | |
useless. The good news is such circumstances are few and far between. For | |
most memory debugging, MEM is a highly reliable and valuable addition to your | |
C programming toolchest. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment