home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / text / hyper / hsc_source.lha / hsc / source / ugly / umemory.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-26  |  16.3 KB  |  723 lines

  1. /*
  2.  * ugly/umemory.c
  3.  *
  4.  * additional memory manegment functions;
  5.  * implements some parts of Amiga-developer-tool
  6.  * "MungWall" at source-level
  7.  *
  8.  * Copyright (C) 1994,95,96  Thomas Aglassinger
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  *
  24.  * updated: 26-Jan-1997
  25.  * created: 29-Mar-1994
  26.  *
  27.  */
  28.  
  29. /*
  30.  *
  31.  * Memory munging:
  32.  *
  33.  *   Except for ucalloc(), memory is pre-munged on allocation with
  34.  *   $DEADFOOD. When this is used in an Enforcer report, the caller is
  35.  *   allocating memory and doesn't initialize it before using it.
  36.  *
  37.  *   Memory is filled with $DEADBEEF before it is freed, encouraging
  38.  *   programs reusing free'ed memory to crash.
  39.  *
  40.  * Memory watching:
  41.  *
  42.  *   Null sized malloc()'s are reported. The integrity of the walls will
  43.  *   be tested according to the size specified when free'ed.
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50.  
  51. #include "utypes.h"
  52.  
  53. #define NOEXTERN_UGLY_UMEMORY_H
  54. #include "umemory.h"
  55.  
  56. /*
  57.  * size of wall build around every memory block
  58.  */
  59. #define UMEM_WALLSIZE 16
  60.  
  61. /*
  62.  * blocksize memory allocations are rounded up by OS
  63.  * (this one's only needed to compute the amount of
  64.  * slack-memory and won't cause any problems if wrong)
  65.  */
  66. #if defined(AMIGA)
  67. #define UMEM_BLOCKSIZE 8        /* AmigaOS */
  68. #else
  69. #define UMEM_BLOCKSIZE 8
  70. #endif
  71.  
  72. #ifndef modfit
  73. #define modfit(x,by) ((by)*(((x)+(by-1))/(by)))
  74. #endif
  75.  
  76. static UGLYMEM *first = NULL;
  77.  
  78. static UBYTE deadbeef[4] =
  79. {0xDE, 0xAD, 0xBE, 0xEF};       /* used to fill mem after free() */
  80. static UBYTE deadfood[4] =
  81. {0xDE, 0xAD, 0xF0, 0x0D};       /* used to fill mem after malloc() */
  82.  
  83. static UBYTE ugly_fillchar = 0x81;
  84.  
  85. static ULONG ugly_umalloc_count = 0;    /* num. of calls to umalloc()/ucalloc() */
  86. static ULONG ugly_ufree_count = 0;      /* num. of calls to ufree() */
  87. static ULONG ugly_umalloc_count_fail = 0;       /* num. of failed calls to umalloc() */
  88. static ULONG ugly_ufree_count_fail = 0;         /* num. of failed calls to ufree() */
  89. static ULONG ugly_maxmem_usage = 0;     /* maximum memmory used */
  90. static ULONG ugly_curmem_usage = 0;     /* current memory used */
  91. static ULONG ugly_real_maxmem_usage = 0;        /* maximum memmory used */
  92. static ULONG ugly_real_curmem_usage = 0;        /* current memory used */
  93. static ULONG ugly_maxnod_usage = 0;     /* maximum num. of memmory nodes used */
  94. static ULONG ugly_curnod_usage = 0;     /* current num. of memory nodes used */
  95.  
  96. /* forward reference */
  97. void *ugly_malloc_notracking(size_t size);
  98. static BOOL ugly_walldamaged(UGLYMEM * umem);
  99.  
  100. /* function pointer for nomem-handler */
  101. BOOL(*ugly_nomem_handler) (size_t size) = NULL;
  102.  
  103. /*
  104.  * find_umem
  105.  */
  106. static UGLYMEM *find_umem(void *mem)
  107. {
  108.     UGLYMEM *nxtum = first;
  109.     UGLYMEM *found = NULL;
  110.  
  111.     while (nxtum && (!found))
  112.     {
  113.  
  114.         if (nxtum->ptr == mem)
  115.             found = nxtum;
  116.         nxtum = nxtum->next;
  117.  
  118.     }
  119.  
  120. #if DEBUG_UGLY_MEMORY==2
  121.     if (!found)
  122.     {
  123.         fprintf(stderr, "*memory* FIND_UMEM: couln't find %p\n", mem);
  124.     }
  125. #endif
  126.  
  127.     return (found);
  128. }
  129.  
  130. /*
  131.  * find_prev
  132.  */
  133. static UGLYMEM *find_prev(UGLYMEM * umem)
  134. {
  135.     UGLYMEM *prev = first;
  136.     UGLYMEM *pprev = NULL;
  137.     BOOL found = FALSE;
  138.  
  139.     while (prev && (!found))
  140.     {
  141.         found = (prev == umem);
  142.         if (!found)
  143.         {
  144.             pprev = prev;
  145.             prev = prev->next;
  146.         }
  147.     }
  148.  
  149.     return (pprev);
  150. }
  151.  
  152. /*
  153.  * fill_mem4, fill_mem
  154.  *
  155.  * fill memory with value specified
  156.  */
  157. static void fill_mem4(void *mem, size_t size, UBYTE value[4])
  158. {
  159.     size_t i;
  160.  
  161.     for (i = 0; i < size; i++)
  162.         (((UBYTE *) mem)[i]) = value[i % 4];
  163. }
  164.  
  165. static void fill_mem(void *mem, size_t size, UBYTE value)
  166. {
  167.     size_t i;
  168.  
  169.     for (i = 0; i < size; i++)
  170.         (((UBYTE *) mem)[i]) = value;
  171. }
  172.  
  173. /*
  174.  * del_uglymem
  175.  *
  176.  * free an uglymem-entry
  177.  */
  178. static void del_uglymem(UGLYMEM * umem)
  179. {
  180.     UGLYMEM *prev = find_prev(umem);
  181.  
  182.     /* unlink from list */
  183.     if (prev)
  184.     {
  185.         prev->next = umem->next;
  186.     }
  187.     else
  188.     {
  189.         first = umem->next;
  190.     }
  191.  
  192.     /* check for damaged wall */
  193.     if (!ugly_walldamaged(umem))
  194.     {
  195.  
  196.         /* wall ok:
  197.          *
  198.          * fill memory with $DEADBEEF,
  199.          * free memory */
  200.         fill_mem4(umem->lower, umem->size + 2 * UMEM_WALLSIZE, deadbeef);
  201.         free(umem->lower);
  202.  
  203.     }
  204.     /* free memory structure */
  205.     umem->lower = NULL;
  206.     umem->upper = NULL;
  207.     umem->size = 0;
  208.     umem->file = NULL;
  209.     umem->line = 0;
  210.     free(umem);
  211. }
  212.  
  213. /*
  214.  * new uglymem
  215.  *
  216.  * alloc & init a new entry of ugly mem
  217.  */
  218. static UGLYMEM *new_uglymem(size_t memsize, STRPTR memfile, ULONG memline)
  219. {
  220.     UGLYMEM *newmem = (UGLYMEM *) malloc(sizeof(UGLYMEM));
  221.  
  222.     if (newmem)
  223.     {
  224.  
  225.         newmem->lower = (STRPTR) ugly_malloc_notracking(memsize
  226.                                                         + 2 * UMEM_WALLSIZE);
  227.         if (newmem->lower)
  228.         {
  229.  
  230.             /* compute location of main mem/upper wall */
  231.             newmem->ptr = (void *) (newmem->lower + UMEM_WALLSIZE);
  232.             newmem->upper = (newmem->lower + UMEM_WALLSIZE + memsize);
  233.  
  234.             /* link to list */
  235.             newmem->next = first;
  236.             first = newmem;
  237.  
  238.             /* init data */
  239.             newmem->size = memsize;
  240.             newmem->file = memfile;
  241.             newmem->line = memline;
  242.             newmem->fillchar = ugly_fillchar;
  243.  
  244.             /* fill new mem area with $DEADF00D */
  245.             fill_mem4(newmem->ptr, memsize, deadfood);
  246.  
  247.             /* fill lower/upper wall */
  248.             fill_mem(newmem->lower, UMEM_WALLSIZE, ugly_fillchar);
  249.             fill_mem(newmem->upper, UMEM_WALLSIZE, ugly_fillchar);
  250.  
  251.             /* update fillchar */
  252.             if (ugly_fillchar == 0xff)
  253.                 ugly_fillchar = 0x81;
  254.             else
  255.                 ugly_fillchar++;
  256.  
  257.         }
  258.         else
  259.             free(newmem);
  260.     }
  261.     return (newmem);
  262. }
  263.  
  264. static void uglymem_message(STRPTR msg)
  265. {
  266.     fprintf(stderr, "%s\n", msg);
  267. }
  268.  
  269. static void ugly_memdump(void *ptr, size_t size)
  270. {
  271.     STRPTR data = (STRPTR) ptr;
  272.  
  273.     /* limit size */
  274.     if (size > 16)
  275.         size = 16;
  276.  
  277.     fprintf(stderr, "  %p:", ptr);
  278.     if (data)
  279.     {
  280.  
  281.         size_t i;
  282.  
  283.         /* hex dump */
  284.         for (i = 0; i < size; i++)
  285.         {
  286.  
  287.             if (!(i % 4))
  288.                 fprintf(stderr, " ");
  289.             fprintf(stderr, "%02x", data[i]);
  290.  
  291.         }
  292.  
  293.         /* fill with blanks */
  294.         while (i < 16)
  295.         {
  296.  
  297.             if (!(i % 4))
  298.                 fprintf(stderr, " ");
  299.             fprintf(stderr, "  ");
  300.             i++;
  301.         }
  302.  
  303.         fprintf(stderr, "  \"");
  304.         /* ascii dump */
  305.         for (i = 0; i < size; i++)
  306.             if (data[i] < ' ')
  307.                 fprintf(stderr, ".");
  308.             else
  309.                 fprintf(stderr, "%c", data[i]);
  310.         fprintf(stderr, "\"\n");
  311.  
  312.     }
  313.     else
  314.         fprintf(stderr, "NULL\n");
  315.  
  316. }
  317.  
  318. static void uglymem_meminfo(void *ptr, STRPTR file, ULONG line)
  319. {
  320.     fprintf(stderr, "  %p: from \"%s\" (%lu)\n", ptr, file, line);
  321. }
  322.  
  323. static void umem_info(UGLYMEM * umem)
  324. {
  325.     fprintf(stderr, "  %p: %lu (0x%lx) bytes from \"%s\" (%lu)\n",
  326.             umem->ptr, (ULONG) umem->size, (ULONG) umem->size,
  327.             umem->file, umem->line);
  328. }
  329.  
  330. /*
  331.  *-------------------------------------
  332.  * wall check functions
  333.  *-------------------------------------
  334.  */
  335.  
  336. /*
  337.  * str_ubyte
  338.  *
  339.  * convert a UBYTE-value to hex/dez/char and return
  340.  * results as a displayable string
  341.  */
  342. static STRPTR str_ubyte(UBYTE val)
  343. {
  344.     static STRARR strbuf[30];
  345.     UBYTE ch = val;
  346.  
  347.     if (ch < 32)
  348.         ch = '.';
  349.  
  350.     sprintf(strbuf, "(0x%02x/#%d/`%c')", val, val, ch);
  351.  
  352.     return (strbuf);
  353. }
  354.  
  355. /*
  356.  * ugly_walldamaged
  357.  *
  358.  * check memory walls a specifc entry in memory-list,
  359.  * output message if wall is damaged
  360.  */
  361. static BOOL ugly_walldamaged(UGLYMEM * umem)
  362. {
  363.     size_t i = 0;
  364.     BOOL damaged = FALSE;
  365.  
  366.     while (!damaged && (i < UMEM_WALLSIZE))
  367.     {
  368.  
  369.         BOOL lower_damaged = (umem->lower[i] != umem->fillchar);
  370.         BOOL upper_damaged = (umem->upper[i] != umem->fillchar);
  371.  
  372.         damaged = lower_damaged || upper_damaged;
  373.         if (damaged)
  374.         {
  375.  
  376.             STRPTR wall;
  377.             UBYTE value;
  378.  
  379.             if (lower_damaged)
  380.             {
  381.                 wall = "LOWER";
  382.                 value = umem->lower[i];
  383.             }
  384.             else
  385.             {
  386.                 wall = "UPPER";
  387.                 value = umem->upper[i];
  388.             }
  389.  
  390.             fprintf(stderr, "*** MEMORY WALL DAMAGED!!!\n");
  391.             fprintf(stderr, "*** %s wall, byte#%lu is %s instead of 0x%02x\n",
  392.                     wall, (ULONG) i, str_ubyte(value), umem->fillchar);
  393.             umem_info(umem);
  394.             ugly_memdump(umem->ptr, umem->size);
  395.             fprintf(stderr, "  * lower wall:\n");
  396.             ugly_memdump(umem->lower, UMEM_WALLSIZE);
  397.             fprintf(stderr, "  * upper wall:\n");
  398.             ugly_memdump(umem->upper, UMEM_WALLSIZE);
  399.  
  400.         }
  401.         else
  402.             i++;
  403.  
  404.     }
  405.  
  406.     return (damaged);
  407. }
  408.  
  409. /*
  410.  * uglymem_wallcheck
  411.  *
  412.  * display a header message and check all walls for consistency
  413.  */
  414. void uglymem_wallcheck(STRPTR msg, STRPTR file, ULONG line)
  415. {
  416.     UGLYMEM *umem = first;
  417.  
  418.     if (umem)
  419.     {
  420.  
  421.         /* report header */
  422.         fprintf(stderr, "MEMORY WALL-CHECK (%s)", msg);
  423.         if (file)
  424.             fprintf(stderr, " from `%s' (%lu)", file, line);
  425.         fprintf(stderr, "\n");
  426.  
  427.         /* check all elements */
  428.         while (umem)
  429.         {
  430.  
  431.             if (umem->ptr)
  432.             {
  433.                 ugly_walldamaged(umem);
  434.                 umem = umem->next;
  435.             }
  436.             else
  437.             {
  438.                 umem = NULL;
  439.                 fprintf(stderr, "\n** PANIC: memory list trashed\n");
  440.             }
  441.         }
  442.     }
  443. }
  444.  
  445. /*
  446.  *-------------------------------------
  447.  * memory statistics functions
  448.  *-------------------------------------
  449.  */
  450.  
  451. /*
  452.  * ugly_mem_report
  453.  *
  454.  * displaly all memory nodes currently allocated
  455.  */
  456. void uglymem_report(STRPTR msg, STRPTR file, ULONG line, STRPTR date, STRPTR time)
  457. {
  458.     UGLYMEM *umem = first;
  459.  
  460.     if (umem)
  461.     {
  462.  
  463.         /* report header */
  464.         fprintf(stderr, "MEMORY REPORT (%s)\n", msg);
  465.         if (file)
  466.             fprintf(stderr, "(\"%s\" (%lu), at %s, %s)\n",
  467.                     file, line, date, time);
  468.  
  469.         /* print all elements */
  470.         while (umem)
  471.         {
  472.  
  473.             if (umem->ptr)
  474.             {
  475.                 umem_info(umem);
  476.                 ugly_memdump(umem->ptr, umem->size);
  477.                 umem = umem->next;
  478.             }
  479.             else
  480.             {
  481.                 umem = NULL;
  482.                 fprintf(stderr, "##\n## panic: memory list trashed\n##\n");
  483.             }
  484.         }
  485.     }
  486. }
  487.  
  488. /*
  489.  * ugly_mem_stats
  490.  *
  491.  * display memory statistics (nodes & size allocated)
  492.  */
  493. void uglymem_stats(STRPTR msg, STRPTR file, ULONG line, STRPTR date, STRPTR time)
  494. {
  495.     /* statistics header */
  496.     fprintf(stderr, "MEMORY STATISTICS (%s)\n", msg);
  497.     if (file)
  498.         fprintf(stderr, "(\"%s\" (%lu), at %s, %s)\n",
  499.                 file, line, date, time);
  500.  
  501.     /* memory statistics */
  502.     fprintf(stderr, "  bytes used: %lu max: %lu/%lu  ",
  503.             ugly_curmem_usage, ugly_real_maxmem_usage,
  504.             ugly_maxmem_usage);
  505.     if (ugly_maxmem_usage)
  506.         fprintf(stderr, "slack: %lu%%\n",
  507.                 (100 * (ugly_real_maxmem_usage - ugly_maxmem_usage))
  508.                 / ugly_maxmem_usage);
  509.     else
  510.         fprintf(stderr, "no slack\n");
  511.     fprintf(stderr, "  nodes used: %lu (max: %lu)\n",
  512.             ugly_curnod_usage, ugly_maxnod_usage);
  513.     fprintf(stderr, "  calls to: umalloc(%lu)   ufree(%lu)\n",
  514.             ugly_umalloc_count, ugly_ufree_count);
  515.  
  516. }
  517.  
  518. /*
  519.  *-------------------------------------
  520.  * atexit functions
  521.  *-------------------------------------
  522.  */
  523.  
  524. /*
  525.  * atexit_uglymemory_real
  526.  */
  527. void atexit_uglymemory_real(void)
  528. {
  529.     ULONG mem_lost = ugly_curmem_usage;
  530.     uglymem_report("at exit:  MEMORY LEAK detected!",
  531.                    NULL, 0, NULL, NULL);
  532.     uglymem_stats("[exit]", NULL, 0, NULL, NULL);
  533.  
  534.     /* release all lost mem */
  535.     while (first)
  536.         del_uglymem(first);
  537.  
  538.     if (mem_lost)
  539.         fprintf(stderr, "\n%lu bytes of memory lost!\n", mem_lost);
  540. }
  541.  
  542. /*
  543.  * atexit_uglymemory_dummy
  544.  */
  545. void atexit_uglymemory_dummy(void)
  546. {
  547.     /* do nufin */
  548. }
  549.  
  550. /*
  551.  *-------------------------------------
  552.  * memory handling functions
  553.  *-------------------------------------
  554.  */
  555.  
  556. /*
  557.  * ugly_malloc_notracking
  558.  */
  559. void *ugly_malloc_notracking(size_t size)
  560. {
  561.     void *mem;
  562.     BOOL retry;
  563.  
  564.     do
  565.     {
  566.  
  567.         mem = malloc(size);
  568.         if (!mem && ugly_nomem_handler)
  569.         {
  570.  
  571.             /* call nomem-handler */
  572.             retry = (*ugly_nomem_handler) (size);
  573.             if (!retry)
  574.                 exit(EXIT_FAILURE);     /* abort programm */
  575.  
  576.         }
  577.         else
  578.             retry = FALSE;
  579.  
  580.     }
  581.     while (retry);
  582.  
  583.     return (mem);
  584. }
  585.  
  586. /*
  587.  * ugly_malloc_tracking
  588.  */
  589. void *ugly_malloc_tracking(size_t size, STRPTR file, ULONG line)
  590. {
  591.     void *mem = NULL;
  592.     UGLYMEM *umem = NULL;
  593.  
  594. #if DEBUG_UGLY_MEMORY==2
  595.     fprintf(stderr, "*memory* UMALLOC() from `%s' (%lu)\n", file, line);
  596. #endif
  597.     if (size)
  598.     {
  599.  
  600.         /* update num. of calls to umalloc() */
  601.         ugly_umalloc_count++;
  602.  
  603.         /* alloc new uglymem */
  604.         umem = new_uglymem(size, file, line);
  605.         if (umem)
  606.         {
  607.  
  608.             mem = umem->ptr;
  609.  
  610.             /* update memory usage and num of nodes */
  611.             ugly_curmem_usage += size;
  612.             ugly_real_curmem_usage += modfit(size, UMEM_BLOCKSIZE);
  613.             if (ugly_curmem_usage > ugly_maxmem_usage)
  614.                 ugly_maxmem_usage = ugly_curmem_usage;
  615.             if (ugly_real_curmem_usage > ugly_real_maxmem_usage)
  616.                 ugly_real_maxmem_usage = ugly_real_curmem_usage;
  617.             ugly_curnod_usage++;
  618.             if (ugly_curnod_usage > ugly_maxnod_usage)
  619.                 ugly_maxnod_usage = ugly_curnod_usage;
  620.  
  621.         }
  622.     }
  623.     else
  624.     {
  625.  
  626.         /* zero-alloc */
  627.  
  628.         /* update num. of failed calls to umalloc() */
  629.         ugly_umalloc_count_fail++;
  630.  
  631.         uglymem_message("MALLOC: zero-sized allocation");
  632.         uglymem_meminfo(NULL, file, line);
  633.  
  634.     }
  635.  
  636.     return (mem);
  637. }
  638.  
  639. /*
  640.  * ugly_free
  641.  */
  642. void ugly_free(void *ptr, STRPTR file, ULONG line)
  643. {
  644. #if DEBUG_UGLY_MEMORY==2
  645.     fprintf(stderr, "*memory* UFREE() from `%s' (%lu)\n", file, line);
  646. #elif 0
  647.     fputc('.', stderr);         /* still alive? */
  648.     fflush(stderr);
  649. #endif
  650.     if (ptr)
  651.     {
  652.  
  653.         UGLYMEM *umem = find_umem(ptr);
  654.  
  655.         if (umem)
  656.         {
  657.  
  658.             /* update num. of calls to ufree() */
  659.             ugly_ufree_count++;
  660.  
  661.             /* update memory usage */
  662.             ugly_curmem_usage -= umem->size;
  663.             ugly_real_curmem_usage -= modfit(umem->size, UMEM_BLOCKSIZE);
  664.  
  665.             /* remove node from mem-list */
  666.             del_uglymem(umem);
  667.             ugly_curnod_usage--;
  668.  
  669.         }
  670.         else
  671.         {
  672.  
  673.             /* ptr has never been allocated */
  674.  
  675.             /* update num. of calls to ufree() */
  676.             ugly_ufree_count_fail++;
  677.  
  678.             /* -> error message */
  679.             uglymem_message("*** FREE: memory never allocated "
  680.                             " or released twice");
  681.             uglymem_meminfo(ptr, file, line);
  682.         }
  683.     }
  684. }
  685.  
  686. /*
  687.  * ugly_realloc
  688.  *
  689.  * replacement of realloc()
  690.  */
  691. void *ugly_realloc(void *ptr, size_t size, STRPTR file, ULONG line)
  692. {
  693.     void *newptr = ugly_malloc_tracking(size, file, line);
  694.     UGLYMEM *umem = find_umem(ptr);
  695.  
  696.     if (newptr && umem)
  697.     {
  698.         /* copy old data */
  699.         memcpy(newptr, umem->ptr, umem->size);
  700.         /* free old memory */
  701.         ugly_free(ptr, file, line);
  702.     }
  703.     return (newptr);
  704. }
  705.  
  706. /*
  707.  * ugly_calloc
  708.  *
  709.  * replacement of calloc()
  710.  */
  711. void *ugly_calloc_(size_t count, size_t size, STRPTR file, ULONG line)
  712. {
  713.     /* alloc new mem */
  714.     void *mem = ugly_malloc_tracking(count * size, file, line);
  715.  
  716.     /* fill mem with zero */
  717.     if (mem)
  718.     {
  719.         memset(mem, 0, size * count);
  720.     }
  721.     return (mem);
  722. }
  723.