home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / mysys / safemalloc.c < prev    next >
C/C++ Source or Header  |  2000-11-16  |  16KB  |  525 lines

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This library is free software; you can redistribute it and/or
  4.    modify it under the terms of the GNU Library General Public
  5.    License as published by the Free Software Foundation; either
  6.    version 2 of the License, or (at your option) any later version.
  7.    
  8.    This library is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.    Library General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU Library General Public
  14.    License along with this library; if not, write to the Free
  15.    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  16.    MA 02111-1307, USA */
  17.  
  18. /*
  19.  * [This posting refers to an article entitled "oops, corrupted memory
  20.  * again!" in net.lang.c.  I am posting it here because it is source.]
  21.  *
  22.  * My tool for approaching this problem is to build another level of data
  23.  * abstraction on top of malloc() and free() that implements some checking.
  24.  * This does a number of things for you:
  25.  *    - Checks for overruns and underruns on allocated data
  26.  *    - Keeps track of where in the program the memory was malloc'ed
  27.  *    - Reports on pieces of memory that were not free'ed
  28.  *    - Records some statistics such as maximum memory used
  29.  *    - Marks newly malloc'ed and newly free'ed memory with special values
  30.  * You can use this scheme to:
  31.  *    - Find bugs such as overrun, underrun, etc because you know where
  32.  *      a piece of data was malloc'ed and where it was free'ed
  33.  *    - Find bugs where memory was not free'ed
  34.  *    - Find bugs where newly malloc'ed memory is used without initializing
  35.  *    - Find bugs where newly free'ed memory is still used
  36.  *    - Determine how much memory your program really uses
  37.  *    - and other things
  38.  */
  39.  
  40. /*
  41.  * To implement my scheme you must have a C compiler that has __LINE__ and
  42.  * __FILE__ macros.  If your compiler doesn't have these then (a) buy another:
  43.  * compilers that do are available on UNIX 4.2bsd based systems and the PC,
  44.  * and probably on other machines; or (b) change my scheme somehow.  I have
  45.  * recomendations on both these points if you would like them (e-mail please).
  46.  *
  47.  * There are 4 functions in my package:
  48.  *    char *NEW( uSize )    Allocate memory of uSize bytes
  49.  *                (equivalent to malloc())
  50.  *    char *REA( pPtr, uSize) Allocate memory of uSize bytes, move data and
  51.  *                free pPtr.
  52.  *                (equivalent to realloc())
  53.  *    FREE( pPtr )        Free memory allocated by NEW
  54.  *                (equivalent to free())
  55.  *    TERMINATE(file)        End system, report errors and stats on file
  56.  * I personally use two more functions, but have not included them here:
  57.  *    char *STRSAVE( sPtr )    Save a copy of the string in dynamic memory
  58.  *    char *RENEW( pPtr, uSize )
  59.  *                (equivalent to realloc())
  60.  */
  61.  
  62. /*
  63.  * Memory sub-system, written by Bjorn Benson
  64.    Fixed to use my_sys scheme by Michael Widenius
  65.  */
  66.  
  67. #ifndef SAFEMALLOC
  68. #define SAFEMALLOC            /* Get protos from my_sys */
  69. #endif
  70.  
  71. #include "mysys_priv.h"
  72. #include <m_string.h>
  73. #include "my_static.h"
  74. #include "mysys_err.h"
  75.  
  76. ulonglong safemalloc_mem_limit = ~(ulonglong)0;
  77.  
  78. #define pNext        tInt._pNext
  79. #define pPrev        tInt._pPrev
  80. #define sFileName    tInt._sFileName
  81. #define uLineNum    tInt._uLineNum
  82. #define uDataSize    tInt._uDataSize
  83. #define lSpecialValue    tInt._lSpecialValue
  84.  
  85.     /* Static functions prototypes */
  86.  
  87. static int check_ptr(const char *where, byte *ptr, const char *sFile,
  88.              uint uLine);
  89. static int _checkchunk(struct remember *pRec, const char *sFile, uint uLine);
  90.  
  91. /*
  92.  *    Note: both these refer to the NEW'ed
  93.  *    data only.  They do not include
  94.  *    malloc() roundoff or the extra
  95.  *    space required by the remember
  96.  *    structures.
  97.  */
  98.  
  99. #define ALLOC_VAL    (uchar) 0xA5    /* NEW'ed memory is filled with this */
  100.                 /* value so that references to it will     */
  101.                 /* end up being very strange.         */
  102. #define FREE_VAL    (uchar) 0x8F    /* FREE'ed memory is filled with this */
  103.                 /* value so that references to it will     */
  104.                 /* also end up being strange.         */
  105.  
  106. #define MAGICKEY    0x14235296    /* A magic value for underrun key */
  107. #define MAGICEND0    0x68        /* Magic values for overrun keys  */
  108. #define MAGICEND1    0x34        /*        "          */
  109. #define MAGICEND2    0x7A        /*        "          */
  110. #define MAGICEND3    0x15        /*        "          */
  111.  
  112.  /* Warning: do not change the MAGICEND? values to */
  113.  /* something with the high bit set.  Various C    */
  114.  /* compilers (like the 4.2bsd one) do not do the  */
  115.  /* sign extension right later on in this code and */
  116.  /* you will get erroneous errors.          */
  117.  
  118.  
  119. /*
  120.  * gptr _mymalloc( uint uSize, my_string sFile, uint uLine, MyFlags )
  121.  *    Allocate some memory.
  122.  */
  123.  
  124. gptr _mymalloc (uint uSize, const char *sFile, uint uLine, myf MyFlags)
  125. {
  126.     struct remember *pTmp;
  127.     DBUG_ENTER("_mymalloc");
  128.     DBUG_PRINT("enter",("Size: %u",uSize));
  129.  
  130.  
  131.     if (!sf_malloc_quick)
  132.       (void) _sanity (sFile, uLine);
  133.  
  134.     if(uSize + lCurMemory > safemalloc_mem_limit)
  135.       pTmp = 0;
  136.     else
  137.        /* Allocate the physical memory */
  138.        pTmp = (struct remember *) malloc (
  139.         sizeof (struct irem)            /* remember data  */
  140.         + sf_malloc_prehunc
  141.         + uSize                    /* size requested */
  142.         + 4                    /* overrun mark   */
  143.         + sf_malloc_endhunc
  144.         );
  145.  
  146.     /* Check if there isn't anymore memory avaiable */
  147.     if (pTmp == NULL)
  148.     {
  149.       if (MyFlags & MY_FAE)
  150.     error_handler_hook=fatal_error_handler_hook;
  151.       if (MyFlags & (MY_FAE+MY_WME))
  152.       {
  153.     char buff[SC_MAXWIDTH];
  154.     my_errno=errno;
  155.     sprintf(buff,"Out of memory at line %d, '%s'", uLine, sFile);
  156.     my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG));
  157.     sprintf(buff,"needed %d byte (%ldk), memory in use: %ld bytes (%ldk)",
  158.         uSize, (uSize + 1023L) / 1024L,
  159.         lMaxMemory, (lMaxMemory + 1023L) / 1024L);
  160.     my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG));
  161.       }
  162.       DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'",
  163.               lMaxMemory,uLine, sFile));
  164.       if (MyFlags & MY_FAE)
  165.     exit(1);
  166.       DBUG_RETURN ((gptr) NULL);
  167.     }
  168.  
  169.     /* Fill up the structure */
  170.     *((long*) ((char*) &pTmp -> lSpecialValue+sf_malloc_prehunc)) = MAGICKEY;
  171.     pTmp -> aData[uSize + sf_malloc_prehunc+0] = MAGICEND0;
  172.     pTmp -> aData[uSize + sf_malloc_prehunc+1] = MAGICEND1;
  173.     pTmp -> aData[uSize + sf_malloc_prehunc+2] = MAGICEND2;
  174.     pTmp -> aData[uSize + sf_malloc_prehunc+3] = MAGICEND3;
  175.     pTmp -> sFileName = (my_string) sFile;
  176.     pTmp -> uLineNum = uLine;
  177.     pTmp -> uDataSize = uSize;
  178.     pTmp -> pPrev = NULL;
  179.  
  180.     /* Add this remember structure to the linked list */
  181.     pthread_mutex_lock(&THR_LOCK_malloc);
  182.     if ((pTmp->pNext=pRememberRoot))
  183.     {
  184.       pRememberRoot -> pPrev = pTmp;
  185.     }
  186.     pRememberRoot = pTmp;
  187.  
  188.     /* Keep the statistics */
  189.     lCurMemory += uSize;
  190.     if (lCurMemory > lMaxMemory) {
  191.     lMaxMemory = lCurMemory;
  192.     }
  193.     cNewCount++;
  194.     pthread_mutex_unlock(&THR_LOCK_malloc);
  195.  
  196.     /* Set the memory to the aribtrary wierd value */
  197. #ifdef HAVE_purify
  198.     if (MyFlags & MY_ZEROFILL)
  199. #endif
  200.       bfill(&pTmp -> aData[sf_malloc_prehunc],uSize,
  201.         (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL));
  202.     /* Return a pointer to the real data */
  203.     DBUG_PRINT("exit",("ptr: %lx",&(pTmp -> aData[sf_malloc_prehunc])));
  204.     if (sf_min_adress > &(pTmp -> aData[sf_malloc_prehunc]))
  205.       sf_min_adress = &(pTmp -> aData[sf_malloc_prehunc]);
  206.     if (sf_max_adress < &(pTmp -> aData[sf_malloc_prehunc]))
  207.       sf_max_adress = &(pTmp -> aData[sf_malloc_prehunc]);
  208.     DBUG_RETURN ((gptr) &(pTmp -> aData[sf_malloc_prehunc]));
  209. }
  210.  
  211. /*
  212.  *  Allocate some new memory and move old memoryblock there.
  213.  *  Free then old memoryblock
  214.  */
  215.  
  216. gptr _myrealloc (register gptr pPtr, register uint uSize,
  217.          const char *sFile, uint uLine, myf MyFlags)
  218. {
  219.   struct remember *pRec;
  220.   gptr ptr;
  221.   DBUG_ENTER("_myrealloc");
  222.  
  223.   if (!pPtr && (MyFlags & MY_ALLOW_ZERO_PTR))
  224.     DBUG_RETURN(_mymalloc(uSize,sFile,uLine,MyFlags));
  225.  
  226.   if (!sf_malloc_quick)
  227.     (void) _sanity (sFile, uLine);
  228.  
  229.   if (check_ptr("Reallocating",(byte*) pPtr,sFile,uLine))
  230.     DBUG_RETURN((gptr) NULL);
  231.  
  232.   pRec = (struct remember *) ((char*) pPtr - sizeof (struct irem)-
  233.                   sf_malloc_prehunc);
  234.   if (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc))
  235.       != MAGICKEY)
  236.   {
  237.     fprintf (stderr, "Reallocating unallocated data at line %d, '%s'\n",
  238.          uLine, sFile);
  239.     DBUG_PRINT("safe",("Reallocating unallocated data at line %d, '%s'",
  240.                uLine, sFile));
  241.     (void) fflush(stderr);
  242.     DBUG_RETURN((gptr) NULL);
  243.   }
  244.  
  245.   if ((ptr=_mymalloc(uSize,sFile,uLine,MyFlags)))    /* Allocate new area */
  246.   {
  247.     uSize=min(uSize,pRec-> uDataSize);        /* Move as much as possibly */
  248.     memcpy((byte*) ptr,pPtr,(size_t) uSize);    /* Copy old data */
  249.     _myfree(pPtr,sFile,uLine,0);        /* Free not needed area */
  250.   }
  251.   else
  252.   {
  253.     if (MyFlags & MY_HOLD_ON_ERROR)
  254.       DBUG_RETURN(pPtr);
  255.     if (MyFlags & MY_FREE_ON_ERROR)
  256.       _myfree(pPtr,sFile,uLine,0);
  257.   }
  258.   DBUG_RETURN(ptr);
  259. } /* _myrealloc */
  260.  
  261.  
  262. /*
  263.  * void _myfree( my_string pPtr, my_string sFile, uint uLine, myf myflags)
  264.  *    Deallocate some memory.
  265.  */
  266.  
  267. void _myfree (gptr pPtr, const char *sFile, uint uLine, myf myflags)
  268. {
  269.   struct remember *pRec;
  270.   DBUG_ENTER("_myfree");
  271.   DBUG_PRINT("enter",("ptr: %lx",pPtr));
  272.  
  273.   if (!sf_malloc_quick)
  274.     (void) _sanity (sFile, uLine);
  275.  
  276.   if ((!pPtr && (myflags & MY_ALLOW_ZERO_PTR)) ||
  277.       check_ptr("Freeing",(byte*) pPtr,sFile,uLine))
  278.     DBUG_VOID_RETURN;
  279.  
  280.   /* Calculate the address of the remember structure */
  281.   pRec = (struct remember *) ((byte*) pPtr-sizeof(struct irem)-
  282.                   sf_malloc_prehunc);
  283.  
  284.   /* Check to make sure that we have a real remember structure    */
  285.   /* Note: this test could fail for four reasons:        */
  286.   /*    (1) The memory was already free'ed            */
  287.   /*    (2) The memory was never new'ed                */
  288.   /*    (3) There was an underrun                */
  289.   /*    (4) A stray pointer hit this location            */
  290.  
  291.   if (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc))
  292.       != MAGICKEY)
  293.   {
  294.     fprintf (stderr, "Freeing unallocated data at line %d, '%s'\n",
  295.          uLine, sFile);
  296.     DBUG_PRINT("safe",("Unallocated data at line %d, '%s'",uLine,sFile));
  297.     (void) fflush(stderr);
  298.     DBUG_VOID_RETURN;
  299.   }
  300.  
  301.   /* Remove this structure from the linked list */
  302.   pthread_mutex_lock(&THR_LOCK_malloc);
  303.   if (pRec -> pPrev) {
  304.     pRec -> pPrev -> pNext = pRec -> pNext;
  305.   } else {
  306.     pRememberRoot = pRec -> pNext;
  307.   }
  308.   if (pRec -> pNext) {
  309.     pRec -> pNext -> pPrev = pRec -> pPrev;
  310.   }
  311.   /* Handle the statistics */
  312.   lCurMemory -= pRec -> uDataSize;
  313.   cNewCount--;
  314.   pthread_mutex_unlock(&THR_LOCK_malloc);
  315.  
  316. #ifndef HAVE_purify
  317.   /* Mark this data as free'ed */
  318.   bfill(&pRec->aData[sf_malloc_prehunc],pRec->uDataSize,(pchar) FREE_VAL);
  319. #endif
  320.   *((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc)) = ~MAGICKEY;
  321.  
  322.   /* Actually free the memory */
  323.   free ((my_string ) pRec);
  324.   DBUG_VOID_RETURN;
  325. }
  326.  
  327.     /* Check if we have a wrong  pointer */
  328.  
  329. static int check_ptr(const char *where, byte *ptr, const char *sFile,
  330.              uint uLine)
  331. {
  332.   if (!ptr)
  333.   {
  334.     fprintf (stderr, "%s NULL pointer at line %d, '%s'\n",
  335.          where,uLine, sFile);
  336.     DBUG_PRINT("safe",("Null pointer at line %d '%s'", uLine, sFile));
  337.     (void) fflush(stderr);
  338.     return 1;
  339.   }
  340. #ifndef _MSC_VER
  341.   if ((long) ptr & (MY_ALIGN(1,sizeof(char *))-1))
  342.   {
  343.     fprintf (stderr, "%s wrong aligned pointer at line %d, '%s'\n",
  344.          where,uLine, sFile);
  345.     DBUG_PRINT("safe",("Wrong aligned pointer at line %d, '%s'",
  346.                uLine,sFile));
  347.     (void) fflush(stderr);
  348.     return 1;
  349.   }
  350. #endif
  351.   if (ptr < sf_min_adress || ptr > sf_max_adress)
  352.   {
  353.     fprintf (stderr, "%s pointer out of range at line %d, '%s'\n",
  354.          where,uLine, sFile);
  355.     DBUG_PRINT("safe",("Pointer out of range at line %d '%s'",
  356.                uLine,sFile));
  357.     (void) fflush(stderr);
  358.     return 1;
  359.   }
  360.   return 0;
  361. }
  362.  
  363.  
  364. /*
  365.  * TERMINATE(FILE *file)
  366.  *    Report on all the memory pieces that have not been
  367.  *    free'ed as well as the statistics.
  368.  */
  369.  
  370. void TERMINATE (FILE *file)
  371. {
  372.   struct remember *pPtr;
  373.   DBUG_ENTER("TERMINATE");
  374.   pthread_mutex_lock(&THR_LOCK_malloc);
  375.  
  376.   /* Report the difference between number of calls to  */
  377.   /* NEW and the number of calls to FREE.  >0 means more     */
  378.   /* NEWs than FREEs.  <0, etc.                 */
  379.  
  380.   if (cNewCount)
  381.   {
  382.     if (file)
  383.     {
  384.       fprintf (file, "cNewCount: %d\n", cNewCount);
  385.       (void) fflush(file);
  386.     }
  387.     DBUG_PRINT("safe",("cNewCount: %d",cNewCount));
  388.   }
  389.  
  390.   /* Report on all the memory that was allocated with NEW     */
  391.   /* but not free'ed with FREE.                 */
  392.  
  393.   if ((pPtr=pRememberRoot))
  394.   {
  395.     if (file)
  396.     {
  397.       fprintf(file, "Memory that was not free'ed (%ld bytes):\n",lCurMemory);
  398.       (void) fflush(file);
  399.     }
  400.     DBUG_PRINT("safe",("Memory that was not free'ed (%ld bytes):",lCurMemory));
  401.     while (pPtr)
  402.     {
  403.       if (file)
  404.       {
  405.     fprintf (file,
  406.          "\t%6u bytes at 0x%09lx, allocated at line %4u in '%s'\n",
  407.          pPtr -> uDataSize,
  408.          (ulong) &(pPtr -> aData[sf_malloc_prehunc]),
  409.          pPtr -> uLineNum, pPtr -> sFileName);
  410.     (void) fflush(file);
  411.       }
  412.       DBUG_PRINT("safe",
  413.          ("%6u bytes at 0x%09lx, allocated at line %4d in '%s'",
  414.           pPtr -> uDataSize, &(pPtr -> aData[sf_malloc_prehunc]),
  415.           pPtr -> uLineNum, pPtr -> sFileName));
  416.       pPtr = pPtr -> pNext;
  417.     }
  418.   }
  419.   /* Report the memory usage statistics */
  420.   if (file)
  421.   {
  422.     fprintf (file, "Maximum memory usage: %ld bytes (%ldk)\n",
  423.          lMaxMemory, (lMaxMemory + 1023L) / 1024L);
  424.     (void) fflush(file);
  425.   }
  426.   DBUG_PRINT("safe",("Maximum memory usage: %ld bytes (%ldk)",
  427.              lMaxMemory, (lMaxMemory + 1023L) / 1024L));
  428.   pthread_mutex_unlock(&THR_LOCK_malloc);
  429.   DBUG_VOID_RETURN;
  430. }
  431.  
  432.  
  433.     /* Returns 0 if chunk is ok */
  434.  
  435. static int _checkchunk (register struct remember *pRec, const char *sFile,
  436.             uint uLine)
  437. {
  438.   reg1 uint uSize;
  439.   reg2 my_string magicp;
  440.   reg3 int flag=0;
  441.  
  442.   /* Check for a possible underrun */
  443.   if (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc))
  444.       != MAGICKEY)
  445.   {
  446.     fprintf (stderr, "Memory allocated at %s:%d was underrun,",
  447.          pRec -> sFileName, pRec -> uLineNum);
  448.     fprintf (stderr, " discovered at %s:%d\n", sFile, uLine);
  449.     (void) fflush(stderr);
  450.     DBUG_PRINT("safe",("Underrun at %lx, allocated at %s:%d",
  451.                &(pRec -> aData[sf_malloc_prehunc]),
  452.                pRec -> sFileName,
  453.                pRec -> uLineNum));
  454.     flag=1;
  455.   }
  456.  
  457.   /* Check for a possible overrun */
  458.   uSize = pRec -> uDataSize;
  459.   magicp = &(pRec -> aData[uSize+sf_malloc_prehunc]);
  460.   if (*magicp++ != MAGICEND0 ||
  461.       *magicp++ != MAGICEND1 ||
  462.       *magicp++ != MAGICEND2 ||
  463.       *magicp++ != MAGICEND3)
  464.   {
  465.     fprintf (stderr, "Memory allocated at %s:%d was overrun,",
  466.          pRec -> sFileName, pRec -> uLineNum);
  467.     fprintf (stderr, " discovered at '%s:%d'\n", sFile, uLine);
  468.     (void) fflush(stderr);
  469.     DBUG_PRINT("safe",("Overrun at %lx, allocated at %s:%d",
  470.                &(pRec -> aData[sf_malloc_prehunc]),
  471.                pRec -> sFileName,
  472.                pRec -> uLineNum));
  473.     flag=1;
  474.   }
  475.   return(flag);
  476. }
  477.  
  478.  
  479.     /* Returns how many wrong chunks */
  480.  
  481. int _sanity (const char *sFile, uint uLine)
  482. {
  483.   reg1 struct remember *pTmp;
  484.   reg2 int flag=0;
  485.   uint count=0;
  486.  
  487.   pthread_mutex_lock(&THR_LOCK_malloc);
  488.   count=cNewCount;
  489.   for (pTmp = pRememberRoot; pTmp != NULL && count-- ; pTmp = pTmp -> pNext)
  490.     flag+=_checkchunk (pTmp, sFile, uLine);
  491.   pthread_mutex_unlock(&THR_LOCK_malloc);
  492.   if (count || pTmp)
  493.   {
  494.     const char *format="Safemalloc link list destroyed, discovered at '%s:%d'";
  495.     fprintf (stderr, format, sFile, uLine); fputc('\n',stderr);
  496.     (void) fflush(stderr);
  497.     DBUG_PRINT("safe",(format, sFile, uLine));
  498.     flag=1;
  499.   }
  500.   return flag;
  501. } /* _sanity */
  502.  
  503.  
  504.     /* malloc and copy */
  505.  
  506. gptr _my_memdup(const byte *from, uint length, const char *sFile, uint uLine,
  507.         myf MyFlags)
  508. {
  509.   gptr ptr;
  510.   if ((ptr=_mymalloc(length,sFile,uLine,MyFlags)) != 0)
  511.     memcpy((byte*) ptr, (byte*) from,(size_t) length);
  512.   return(ptr);
  513. } /*_my_memdup */
  514.  
  515.  
  516. my_string _my_strdup(const char *from, const char *sFile, uint uLine,
  517.              myf MyFlags)
  518. {
  519.   gptr ptr;
  520.   uint length=(uint) strlen(from)+1;
  521.   if ((ptr=_mymalloc(length,sFile,uLine,MyFlags)) != 0)
  522.     memcpy((byte*) ptr, (byte*) from,(size_t) length);
  523.   return((my_string) ptr);
  524. } /* _my_strdup */
  525.