home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / MEM.C < prev    next >
C/C++ Source or Header  |  1997-07-05  |  16KB  |  683 lines

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /*
  4. **  This is a copyrighted work which is functionally identical to work
  5. **  originally published in Micro Cornucopia magazine (issue #52, March-April,
  6. **  1990) and is freely licensed by the author, Walter Bright, for any use.
  7. */
  8.  
  9. /*_ mem.c   Fri Jan 26 1990   Modified by: Walter Bright */
  10. /* $Header: /proj/products/merlin/port/RCS/mem.c,v 1.19 89/10/20 14:36:02 bright Exp Locker: bright $ */
  11. /* Memory management package                    */
  12.  
  13. #if defined(VAX11C)
  14. #define  __FILE__  "mem.c"
  15. #endif
  16.  
  17. #include    <stdio.h>
  18. #include    <stdlib.h>
  19. #include    <io.h>
  20.  
  21. #ifndef MEM_H
  22. #include    "mem.h"
  23. #endif
  24.  
  25. #ifndef assert
  26. #include    <assert.h>
  27. #endif
  28.  
  29. #if defined(_MSC_VER)
  30. #include    <dos.h>
  31. #endif
  32.  
  33. #if !defined(VAX11C)
  34. #ifdef BSDUNIX
  35. #include <strings.h>
  36. #else
  37. #include <string.h>
  38. #endif
  39. #else
  40. extern char *strcpy(),*memcpy();
  41. extern int strlen();
  42. #endif  /* VAX11C */
  43.  
  44. int mem_inited = 0;           /* != 0 if initialized              */
  45.  
  46. static int mem_behavior = MEM_ABORTMSG;
  47. static int (*fp)() = (int (*)())NULL;    /* out-of-memory handler      */
  48. static int mem_count;         /* # of allocs that haven't been free'd   */
  49. static int mem_scount;        /* # of sallocs that haven't been free'd */
  50. static int near mem_exception(); /* called when out of memory */
  51.  
  52. /* Determine where to send error messages */
  53. #if defined(MSDOS) || defined(__MSDOS__)
  54. #define ferr      stdout      /* stderr can't be redirected with MS-DOS */
  55. #else
  56. #define ferr      stderr
  57. #endif
  58.  
  59. /*******************************/
  60.  
  61. void mem_setexception(flag,handler_fp)
  62. #if defined(__cplusplus) && __cplusplus
  63. enum MEM_E flag;
  64. #else
  65. int flag;
  66. #endif
  67. int (*handler_fp)();
  68. {
  69.     mem_behavior = flag;
  70.     fp = (mem_behavior == MEM_CALLFP) ? handler_fp : 0;
  71. #if defined(MEM_DEBUG) && MEM_DEBUG
  72.     assert(0 <= flag && flag <= MEM_RETRY);
  73. #endif
  74. }
  75.  
  76. /*************************
  77.  * This is called when we're out of memory.
  78.  * Returns:
  79.  *    1:    try again to allocate the memory
  80.  *    0:    give up and return NULL
  81.  */
  82.  
  83. static int near mem_exception()
  84. {   int behavior;
  85.  
  86.     behavior = mem_behavior;
  87.     while (1)
  88.     {
  89.       switch (behavior)
  90.       {
  91.           case MEM_ABORTMSG:
  92. #if defined(MSDOS) || defined(__MSDOS__) || defined(__OS2__)
  93.             /* Avoid linking in buffered I/O */
  94.           { static char msg[] = "Fatal error: out of memory\r\n";
  95.  
  96.             write(1,msg,sizeof(msg) - 1);
  97.           }
  98. #else
  99.             fputs("Fatal error: out of memory\n",ferr);
  100. #endif
  101.             /* FALL-THROUGH */
  102.           case MEM_ABORT:
  103.             exit(EXIT_FAILURE);
  104.             /* NOTREACHED */
  105.           case MEM_CALLFP:
  106.             assert(fp);
  107.             behavior = (*fp)();
  108.             break;
  109.           case MEM_RETNULL:
  110.             return 0;
  111.           case MEM_RETRY:
  112.             return 1;
  113.           default:
  114.             assert(0);
  115.       }
  116.     }
  117. }
  118.  
  119. /****************************/
  120.  
  121. #if defined(MEM_DEBUG) && MEM_DEBUG
  122.  
  123. #undef mem_strdup
  124.  
  125. char *mem_strdup(s)
  126. const char *s;
  127. {
  128.       return mem_strdup_debug(s,__FILE__,__LINE__);
  129. }
  130.  
  131. char *mem_strdup_debug(s,file,line)
  132. char *file;
  133. const char *s;
  134. int line;
  135. {
  136.       char *p;
  137.  
  138.       p = s
  139.           ? (char *) mem_malloc_debug((unsigned) strlen(s) + 1,file,line)
  140.           : NULL;
  141.       return p ? strcpy(p,s) : p;
  142. }
  143. #else
  144. char *mem_strdup(s)
  145. const char *s;
  146. {
  147.       char *p;
  148.  
  149.       p = s ? (char *) mem_malloc((unsigned) strlen(s) + 1) : NULL;
  150.       return p ? strcpy(p,s) : p;
  151. }
  152.  
  153. #endif /* MEM_DEBUG */
  154.  
  155. #ifdef MEM_DEBUG
  156.  
  157. static long mem_maxalloc;     /* max # of bytes allocated         */
  158. static long mem_numalloc;     /* current # of bytes allocated           */
  159.  
  160. #define BEFOREVAL 0x12345678  /* value to detect underrun   */
  161. #define AFTERVAL  0x87654321  /* value to detect overrun    */
  162.  
  163. #if SUN || SUN386
  164. static long afterval = AFTERVAL;    /* so we can do &afterval     */
  165. #endif
  166.  
  167. /* The following should be selected to give maximum probability that    */
  168. /* pointers loaded with these values will cause an obvious crash. On    */
  169. /* Unix machines, a large value will cause a segment fault.       */
  170. /* MALLOCVAL is the value to set malloc'd data to.                */
  171.  
  172. #if MSDOS || __MSDOS__ || __OS2__
  173. #define BADVAL          0xFF
  174. #define MALLOCVAL 0xEE
  175. #else
  176. #define BADVAL          0x7A
  177. #define MALLOCVAL 0xEE
  178. #endif
  179.  
  180. /* Disable mapping macros     */
  181. #undef      mem_malloc
  182. #undef      mem_calloc
  183. #undef      mem_realloc
  184. #undef      mem_free
  185.  
  186. /* Create a list of all alloc'ed pointers, retaining info about where   */
  187. /* each alloc came from. This is a real memory and speed hog, but who   */
  188. /* cares when you've got obscure pointer bugs.                    */
  189.  
  190. static struct mem_debug
  191. {     struct mh
  192.       { struct mem_debug *Mnext;    /* next in list               */
  193.         struct mem_debug *Mprev;    /* previous value in list     */
  194.         char *Mfile;          /* filename of where allocated            */
  195.         int Mline;            /* line number of where allocated   */
  196.         unsigned Mnbytes;     /* size of the allocation           */
  197.         long Mbeforeval;      /* detect underrun of data          */
  198.       } m;
  199.       char data[1];           /* the data actually allocated            */
  200. } mem_alloclist =
  201. {
  202.    {  (struct mem_debug *) NULL,
  203.       (struct mem_debug *) NULL,
  204.       "noname",
  205.       11111,
  206.       0,
  207.       BEFOREVAL
  208.    },
  209.    AFTERVAL
  210. };
  211.  
  212. /* Convert from a void *to a mem_debug struct.  */
  213. #define mem_ptrtodl(p)  ((struct mem_debug *) ((char *)p - sizeof(struct mh)))
  214.  
  215. /* Convert from a mem_debug struct to a mem_ptr.      */
  216. #define mem_dltoptr(dl) ((void *) &((dl)->data[0]))
  217.  
  218. #define next            m.Mnext
  219. #define prev            m.Mprev
  220. #define file            m.Mfile
  221. #define line            m.Mline
  222. #define nbytes          m.Mnbytes
  223. #define beforeval m.Mbeforeval
  224.  
  225. /*****************************
  226.  * Set new value of file,line
  227.  */
  228.  
  229. void mem_setnewfileline(ptr,fil,lin)
  230. void *ptr;
  231. char *fil;
  232. int lin;
  233. {
  234.     struct mem_debug *dl;
  235.  
  236.     dl = mem_ptrtodl(ptr);
  237.     dl->file = fil;
  238.     dl->line = lin;
  239. }
  240.  
  241. /****************************
  242.  * Print out struct mem_debug.
  243.  */
  244.  
  245. static void near mem_printdl(dl)
  246. struct mem_debug *dl;
  247. {
  248. #if LPTR
  249.       fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%lx\n",
  250.             dl->file,dl->line,dl->nbytes,mem_dltoptr(dl));
  251. #else
  252.       fprintf(ferr,"alloc'd from file '%s' line %d nbytes %d ptr x%x\n",
  253.             dl->file,dl->line,dl->nbytes,mem_dltoptr(dl));
  254. #endif
  255. }
  256.  
  257. /****************************
  258.  * Print out file and line number.
  259.  */
  260.  
  261. static void near mem_fillin(fil,lin)
  262. char *fil;
  263. int lin;
  264. {
  265.       fprintf(ferr,"File '%s' line %d\n",fil,lin);
  266.       fflush(ferr);
  267. }
  268.  
  269. /****************************
  270.  * If MEM_DEBUG is not on for some modules, these routines will get
  271.  * called.
  272.  */
  273.  
  274. void *mem_calloc(u)
  275. unsigned u;
  276. {
  277.       return mem_calloc_debug(u,__FILE__,__LINE__);
  278. }
  279.  
  280. void *mem_malloc(u)
  281. unsigned u;
  282. {
  283.       return mem_malloc_debug(u,__FILE__,__LINE__);
  284. }
  285.  
  286. void *mem_realloc(p,u)
  287. void *p;
  288. unsigned u;
  289. {
  290.       return mem_realloc_debug(p,u,__FILE__,__LINE__);
  291. }
  292.  
  293. void mem_free(p)
  294. void *p;
  295. {
  296.       mem_free_debug(p,__FILE__,__LINE__);
  297. }    
  298.  
  299.  
  300. /**************************/
  301.  
  302. void mem_freefp(p)
  303. void *p;
  304. {
  305.       mem_free(p);
  306. }
  307.  
  308. /***********************
  309.  * Debug versions of mem_calloc(), mem_free() and mem_realloc().
  310.  */
  311.  
  312. void *mem_malloc_debug(n,fil,lin)
  313. unsigned n;
  314. char *fil;
  315. int lin;
  316. {   void *p;
  317.  
  318.     p = mem_calloc_debug(n,fil,lin);
  319.     if (p)
  320.       memset(p,MALLOCVAL,n);
  321.     return p;
  322. }
  323.  
  324. void *mem_calloc_debug(n,fil,lin)
  325. unsigned n;
  326. char *fil;
  327. int lin;
  328. {
  329.     struct mem_debug *dl;
  330.  
  331.     do
  332.       dl = (struct mem_debug *)
  333.           calloc(sizeof(*dl) + n + sizeof(AFTERVAL) - 1,1);
  334.     while (dl == NULL && mem_exception());
  335.     if (dl == NULL)
  336.     {
  337. #if 0
  338.       printf("Insufficient memory for alloc of %d at ",n);
  339.       mem_fillin(fil,lin);
  340.       printf("Max allocated was: %ld\n",mem_maxalloc);
  341. #endif
  342.       return NULL;
  343.     }
  344.     dl->file = fil;
  345.     dl->line = lin;
  346.     dl->nbytes = n;
  347.     dl->beforeval = BEFOREVAL;
  348. #if SUN || SUN386 /* bus error if we store a long at an odd address */
  349.     memcpy(&(dl->data[n]),&afterval,sizeof(AFTERVAL));
  350. #else
  351.     *(long *) &(dl->data[n]) = AFTERVAL;
  352. #endif
  353.  
  354.     /* Add dl to start of allocation list */
  355.     dl->next = mem_alloclist.next;
  356.     dl->prev = &mem_alloclist;
  357.     mem_alloclist.next = dl;
  358.     if (dl->next != NULL)
  359.       dl->next->prev = dl;
  360.  
  361.     mem_count++;
  362.     mem_numalloc += n;
  363.     if (mem_numalloc > mem_maxalloc)
  364.       mem_maxalloc = mem_numalloc;
  365.     return mem_dltoptr(dl);
  366. }
  367.  
  368. void mem_free_debug(ptr,fil,lin)
  369. void *ptr;
  370. char *fil;
  371. int lin;
  372. {
  373.       struct mem_debug *dl;
  374.  
  375.       if (ptr == NULL)
  376.             return;
  377. #if 0
  378.       {     fprintf(ferr,"Freeing NULL pointer at ");
  379.             goto err;
  380.       }
  381. #endif
  382.       if (mem_count <= 0)
  383.       {     fprintf(ferr,"More frees than allocs at ");
  384.             goto err;
  385.       }
  386.       dl = mem_ptrtodl(ptr);
  387.       if (dl->beforeval != BEFOREVAL)
  388.       {
  389. #if LPTR
  390.             fprintf(ferr,"Pointer x%lx underrun\n",ptr);
  391. #else
  392.             fprintf(ferr,"Pointer x%x underrun\n",ptr);
  393. #endif
  394.             goto err2;
  395.       }
  396. #if SUN || SUN386 /* Bus error if we read a long from an odd address    */
  397.       if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0)
  398. #else
  399.       if (*(long *) &dl->data[dl->nbytes] != AFTERVAL)
  400. #endif
  401.       {
  402. #if LPTR
  403.             fprintf(ferr,"Pointer x%lx overrun\n",ptr);
  404. #else
  405.             fprintf(ferr,"Pointer x%x overrun\n",ptr);
  406. #endif
  407.             goto err2;
  408.       }
  409.       mem_numalloc -= dl->nbytes;
  410.       if (mem_numalloc < 0)
  411.       {     fprintf(ferr,"error: mem_numalloc = %ld, dl->nbytes = %d\n",
  412.                   mem_numalloc,dl->nbytes);
  413.             goto err2;
  414.       }
  415.  
  416.       /* Remove dl from linked list */
  417.       if (dl->prev)
  418.             dl->prev->next = dl->next;
  419.       if (dl->next)
  420.             dl->next->prev = dl->prev;
  421.  
  422.       /* Stomp on the freed storage to help detect references     */
  423.       /* after the storage was freed.                       */
  424.       memset((void *) dl,BADVAL,sizeof(*dl) + dl->nbytes);
  425.       mem_count--;
  426.  
  427.       /* Some compilers can detect errors in the heap.      */
  428. #if defined(DLC)
  429.       {     int i;
  430.             i = free(dl);
  431.             assert(i == 0);
  432.       }
  433. #else
  434.       free((void *) dl);
  435. #endif
  436.       return;
  437.  
  438. err2:
  439.       mem_printdl(dl);
  440. err:
  441.       fprintf(ferr,"free'd from ");
  442.       mem_fillin(fil,lin);
  443.       assert(0);
  444.       /* NOTREACHED */
  445. }
  446.  
  447. /*******************
  448.  * Debug version of mem_realloc().
  449.  */
  450.  
  451. void *mem_realloc_debug(oldp,n,fil,lin)
  452. void *oldp;
  453. unsigned n;
  454. char *fil;
  455. int lin;
  456. {   void *p;
  457.     struct mem_debug *dl;
  458.  
  459.     if (n == 0)
  460.     { mem_free_debug(oldp,fil,lin);
  461.       p = NULL;
  462.     }
  463.     else if (oldp == NULL)
  464.       p = mem_malloc_debug(n,fil,lin);
  465.     else
  466.     {
  467.       p = mem_malloc_debug(n,fil,lin);
  468.       if (p != NULL)
  469.       {
  470.           dl = mem_ptrtodl(oldp);
  471.           if (dl->nbytes < n)
  472.             n = dl->nbytes;
  473.           memcpy(p,oldp,n);
  474.           mem_free_debug(oldp,fil,lin);
  475.       }
  476.     }
  477.     return p;
  478. }
  479.  
  480. /***************************/
  481.  
  482. void mem_check()
  483. {   register struct mem_debug *dl;
  484.  
  485.     for (dl = mem_alloclist.next; dl != NULL; dl = dl->next)
  486.       mem_checkptr(mem_dltoptr(dl));
  487. }
  488.  
  489. /***************************/
  490.  
  491. void mem_checkptr(p)
  492. register void *p;
  493. {   register struct mem_debug *dl;
  494.  
  495.     for (dl = mem_alloclist.next; dl != NULL; dl = dl->next)
  496.     {
  497.       if (p >= (void *) &(dl->data[0]) &&
  498.           p < (void *)((char *)dl + sizeof(struct mem_debug)-1 + dl->nbytes))
  499.           goto L1;
  500.     }
  501.     assert(0);
  502.  
  503. L1:
  504.     dl = mem_ptrtodl(p);
  505.     if (dl->beforeval != BEFOREVAL)
  506.     {
  507. #if LPTR
  508.           fprintf(ferr,"Pointer x%lx underrun\n",p);
  509. #else
  510.           fprintf(ferr,"Pointer x%x underrun\n",p);
  511. #endif
  512.           goto err2;
  513.     }
  514. #if SUN || SUN386 /* Bus error if we read a long from an odd address    */
  515.     if (memcmp(&dl->data[dl->nbytes],&afterval,sizeof(AFTERVAL)) != 0)
  516. #else
  517.     if (*(long *) &dl->data[dl->nbytes] != AFTERVAL)
  518. #endif
  519.     {
  520. #if LPTR
  521.           fprintf(ferr,"Pointer x%lx overrun\n",p);
  522. #else
  523.           fprintf(ferr,"Pointer x%x overrun\n",p);
  524. #endif
  525.           goto err2;
  526.     }
  527.     return;
  528.  
  529. err2:
  530.     mem_printdl(dl);
  531.     assert(0);
  532. }
  533.  
  534. #else
  535.  
  536. /***************************/
  537.  
  538. void *mem_malloc(numbytes)
  539. unsigned numbytes;
  540. {     void *p;
  541.  
  542.       if (numbytes == 0)
  543.             return NULL;
  544.       while (1)
  545.       {
  546.             p = malloc(numbytes);
  547.             if (p == NULL)
  548.             {     if (mem_exception())
  549.                         continue;
  550.             }
  551.             else
  552.                   mem_count++;
  553.             break;
  554.       }
  555.       /*printf("malloc(%d) = x%lx\n",numbytes,p);*/
  556.       return p;
  557. }
  558.  
  559. /***************************/
  560.  
  561. void *mem_calloc(numbytes)
  562. unsigned numbytes;
  563. {     void *p;
  564.  
  565.       if (numbytes == 0)
  566.             return NULL;
  567.       while (1)
  568.       {
  569.             p = calloc(numbytes,1);
  570.             if (p == NULL)
  571.             {     if (mem_exception())
  572.                         continue;
  573.             }
  574.             else
  575.                   mem_count++;
  576.             break;
  577.       }
  578.       /*printf("calloc(%d) = x%lx\n",numbytes,p);*/
  579.       return p;
  580. }
  581.  
  582. /***************************/
  583.  
  584. void *mem_realloc(oldmem_ptr,newnumbytes)
  585. void *oldmem_ptr;
  586. unsigned newnumbytes;
  587. {   void *p;
  588.  
  589.     if (oldmem_ptr == NULL)
  590.       p = mem_malloc(newnumbytes);
  591.     else if (newnumbytes == 0)
  592.     { mem_free(oldmem_ptr);
  593.       p = NULL;
  594.     }
  595.     else
  596.     {
  597.       do
  598.           p = realloc(oldmem_ptr,newnumbytes);
  599.       while (p == NULL && mem_exception());
  600.     }
  601.     /*printf("realloc(x%lx,%d) = x%lx\n",oldmem_ptr,newnumbytes,p);*/
  602.     return p;
  603. }
  604.  
  605. /***************************/
  606.  
  607. void mem_free(ptr)
  608. void *ptr;
  609. {
  610.     /*printf("free(x%lx)\n",ptr);*/
  611.     if (ptr != NULL)
  612.     { assert(mem_count > 0);
  613.       mem_count--;
  614. #if defined(DLC) && DLC
  615.       {     int i;
  616.  
  617.             i = free(ptr);
  618.             assert(i == 0);
  619.       }
  620. #else
  621.       free(ptr);
  622. #endif
  623.     }
  624. }
  625.  
  626. #endif /* MEM_DEBUG */
  627.  
  628. /***************************/
  629.  
  630. void mem_init()
  631. {
  632.       if (mem_inited == 0)
  633.       {     mem_count = 0;
  634. #if defined(MEM_DEBUG) && MEM_DEBUG
  635.             mem_numalloc = 0;
  636.             mem_maxalloc = 0;
  637.             mem_alloclist.next = NULL;
  638. #endif
  639. #if defined(__ZTC__) || defined(__SC__)
  640.             /* Necessary if mem_sfree() calls free() before any   */
  641.             /* calls to malloc().                           */
  642.             free(malloc(1));  /* initialize storage allocator     */
  643. #endif
  644.             mem_inited++;
  645.       }
  646. }
  647.  
  648. /***************************/
  649.  
  650. void mem_term()
  651. {
  652.  
  653.       if (mem_inited)
  654.       {
  655. #if defined(MEM_DEBUG) && MEM_DEBUG
  656.             register struct mem_debug *dl;
  657.  
  658.             for (dl = mem_alloclist.next; dl; dl = dl->next)
  659.             {     fprintf(ferr,"Unfreed pointer: ");
  660.                   mem_printdl(dl);
  661.             }
  662. #if 0
  663.             fprintf(ferr,"Max amount ever allocated == %ld bytes\n",
  664.                   mem_maxalloc);
  665. #endif
  666. #else
  667.             if (mem_count)
  668.                   fprintf(ferr,"%d unfreed items\n",mem_count);
  669.             if (mem_scount)
  670.                   fprintf(ferr,"%d unfreed s items\n",mem_scount);
  671. #endif /* MEM_DEBUG */
  672.             assert(mem_count == 0 && mem_scount == 0);
  673.             mem_inited = 0;
  674.       }
  675. }
  676.  
  677. #undef next
  678. #undef prev
  679. #undef file
  680. #undef line
  681. #undef nbytes
  682. #undef beforeval
  683.