00001 /* Copyright (C) 2000-2003 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 * Memory sub-system, written by Bjorn Benson 00019 Fixed to use my_sys scheme by Michael Widenius 00020 00021 [This posting refers to an article entitled "oops, corrupted memory 00022 again!" in net.lang.c. I am posting it here because it is source.] 00023 00024 My tool for approaching this problem is to build another level of data 00025 abstraction on top of malloc() and free() that implements some checking. 00026 This does a number of things for you: 00027 - Checks for overruns and underruns on allocated data 00028 - Keeps track of where in the program the memory was malloc'ed 00029 - Reports on pieces of memory that were not free'ed 00030 - Records some statistics such as maximum memory used 00031 - Marks newly malloc'ed and newly free'ed memory with special values 00032 You can use this scheme to: 00033 - Find bugs such as overrun, underrun, etc because you know where 00034 a piece of data was malloc'ed and where it was free'ed 00035 - Find bugs where memory was not free'ed 00036 - Find bugs where newly malloc'ed memory is used without initializing 00037 - Find bugs where newly free'ed memory is still used 00038 - Determine how much memory your program really uses 00039 - and other things 00040 00041 To implement my scheme you must have a C compiler that has __LINE__ and 00042 __FILE__ macros. If your compiler doesn't have these then (a) buy another: 00043 compilers that do are available on UNIX 4.2bsd based systems and the PC, 00044 and probably on other machines; or (b) change my scheme somehow. I have 00045 recomendations on both these points if you would like them (e-mail please). 00046 00047 There are 4 functions in my package: 00048 char *NEW( uSize ) Allocate memory of uSize bytes 00049 (equivalent to malloc()) 00050 char *REA( pPtr, uSize) Allocate memory of uSize bytes, move data and 00051 free pPtr. 00052 (equivalent to realloc()) 00053 FREE( pPtr ) Free memory allocated by NEW 00054 (equivalent to free()) 00055 TERMINATE(file) End system, report errors and stats on file 00056 I personally use two more functions, but have not included them here: 00057 char *STRSAVE( sPtr ) Save a copy of the string in dynamic memory 00058 char *RENEW( pPtr, uSize ) 00059 (equivalent to realloc()) 00060 00061 */ 00062 00063 #ifndef SAFEMALLOC 00064 #define SAFEMALLOC /* Get protos from my_sys */ 00065 #endif 00066 00067 #include "mysys_priv.h" 00068 #include <m_string.h> 00069 #include "my_static.h" 00070 #include "mysys_err.h" 00071 00072 ulonglong sf_malloc_mem_limit= ~(ulonglong)0; 00073 00074 #ifndef PEDANTIC_SAFEMALLOC 00075 /* 00076 Set to 1 after TERMINATE() if we had to fiddle with sf_malloc_count and 00077 the linked list of blocks so that _sanity() will not fuss when it 00078 is not supposed to 00079 */ 00080 static int sf_malloc_tampered= 0; 00081 #endif 00082 00083 00084 /* Static functions prototypes */ 00085 00086 static int check_ptr(const char *where, byte *ptr, const char *sFile, 00087 uint uLine); 00088 static int _checkchunk(struct st_irem *pRec, const char *sFile, uint uLine); 00089 00090 /* 00091 Note: We only fill up the allocated block. This do not include 00092 malloc() roundoff or the extra space required by the irem 00093 structures. 00094 */ 00095 00096 /* 00097 NEW'ed memory is filled with this value so that references to it will 00098 end up being very strange. 00099 */ 00100 #define ALLOC_VAL (uchar) 0xA5 00101 /* 00102 FEEE'ed memory is filled with this value so that references to it will 00103 end up being very strange. 00104 */ 00105 #define FREE_VAL (uchar) 0x8F 00106 #define MAGICKEY 0x14235296 /* A magic value for underrun key */ 00107 00108 /* 00109 Warning: do not change the MAGICEND? values to something with the 00110 high bit set. Various C compilers (like the 4.2bsd one) do not do 00111 the sign extension right later on in this code and you will get 00112 erroneous errors. 00113 */ 00114 00115 #define MAGICEND0 0x68 /* Magic values for overrun keys */ 00116 #define MAGICEND1 0x34 /* " */ 00117 #define MAGICEND2 0x7A /* " */ 00118 #define MAGICEND3 0x15 /* " */ 00119 00120 00121 /* Allocate some memory. */ 00122 00123 gptr _mymalloc(uint size, const char *filename, uint lineno, myf MyFlags) 00124 { 00125 struct st_irem *irem; 00126 char *data; 00127 DBUG_ENTER("_mymalloc"); 00128 DBUG_PRINT("enter",("Size: %u",size)); 00129 00130 if (!sf_malloc_quick) 00131 (void) _sanity (filename, lineno); 00132 00133 if (size + sf_malloc_cur_memory > sf_malloc_mem_limit) 00134 irem= 0; 00135 else 00136 { 00137 /* Allocate the physical memory */ 00138 irem= (struct st_irem *) malloc (ALIGN_SIZE(sizeof(struct st_irem)) + 00139 sf_malloc_prehunc + 00140 size + /* size requested */ 00141 4 + /* overrun mark */ 00142 sf_malloc_endhunc); 00143 } 00144 /* Check if there isn't anymore memory avaiable */ 00145 if (!irem) 00146 { 00147 if (MyFlags & MY_FAE) 00148 error_handler_hook=fatal_error_handler_hook; 00149 if (MyFlags & (MY_FAE+MY_WME)) 00150 { 00151 char buff[SC_MAXWIDTH]; 00152 my_errno=errno; 00153 sprintf(buff,"Out of memory at line %d, '%s'", lineno, filename); 00154 my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG)); 00155 sprintf(buff,"needed %d byte (%ldk), memory in use: %ld bytes (%ldk)", 00156 size, (size + 1023L) / 1024L, 00157 sf_malloc_max_memory, (sf_malloc_max_memory + 1023L) / 1024L); 00158 my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG)); 00159 } 00160 DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'", 00161 sf_malloc_max_memory,lineno, filename)); 00162 if (MyFlags & MY_FAE) 00163 exit(1); 00164 DBUG_RETURN ((gptr) 0); 00165 } 00166 00167 /* Fill up the structure */ 00168 data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + 00169 sf_malloc_prehunc); 00170 *((uint32*) (data-sizeof(uint32)))= MAGICKEY; 00171 data[size + 0]= MAGICEND0; 00172 data[size + 1]= MAGICEND1; 00173 data[size + 2]= MAGICEND2; 00174 data[size + 3]= MAGICEND3; 00175 irem->filename= (my_string) filename; 00176 irem->linenum= lineno; 00177 irem->datasize= size; 00178 irem->prev= NULL; 00179 00180 /* Add this remember structure to the linked list */ 00181 pthread_mutex_lock(&THR_LOCK_malloc); 00182 if ((irem->next= sf_malloc_root)) 00183 sf_malloc_root->prev= irem; 00184 sf_malloc_root= irem; 00185 00186 /* Keep the statistics */ 00187 sf_malloc_cur_memory+= size; 00188 if (sf_malloc_cur_memory > sf_malloc_max_memory) 00189 sf_malloc_max_memory= sf_malloc_cur_memory; 00190 sf_malloc_count++; 00191 pthread_mutex_unlock(&THR_LOCK_malloc); 00192 00193 /* Set the memory to the aribtrary wierd value */ 00194 if ((MyFlags & MY_ZEROFILL) || !sf_malloc_quick) 00195 bfill(data, size, (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL)); 00196 /* Return a pointer to the real data */ 00197 DBUG_PRINT("exit",("ptr: 0x%lx", data)); 00198 if (sf_min_adress > data) 00199 sf_min_adress= data; 00200 if (sf_max_adress < data) 00201 sf_max_adress= data; 00202 DBUG_RETURN ((gptr) data); 00203 } 00204 00205 00206 /* 00207 Allocate some new memory and move old memoryblock there. 00208 Free then old memoryblock 00209 */ 00210 00211 gptr _myrealloc(register gptr ptr, register uint size, 00212 const char *filename, uint lineno, myf MyFlags) 00213 { 00214 struct st_irem *irem; 00215 char *data; 00216 DBUG_ENTER("_myrealloc"); 00217 00218 if (!ptr && (MyFlags & MY_ALLOW_ZERO_PTR)) 00219 DBUG_RETURN(_mymalloc(size, filename, lineno, MyFlags)); 00220 00221 if (!sf_malloc_quick) 00222 (void) _sanity (filename, lineno); 00223 00224 if (check_ptr("Reallocating", (byte*) ptr, filename, lineno)) 00225 DBUG_RETURN((gptr) NULL); 00226 00227 irem= (struct st_irem *) (((char*) ptr) - ALIGN_SIZE(sizeof(struct st_irem))- 00228 sf_malloc_prehunc); 00229 if (*((uint32*) (((char*) ptr)- sizeof(uint32))) != MAGICKEY) 00230 { 00231 fprintf(stderr, "Error: Reallocating unallocated data at line %d, '%s'\n", 00232 lineno, filename); 00233 DBUG_PRINT("safe",("Reallocating unallocated data at line %d, '%s'", 00234 lineno, filename)); 00235 (void) fflush(stderr); 00236 DBUG_RETURN((gptr) NULL); 00237 } 00238 00239 if ((data= _mymalloc(size,filename,lineno,MyFlags))) /* Allocate new area */ 00240 { 00241 size=min(size, irem->datasize); /* Move as much as possibly */ 00242 memcpy((byte*) data, ptr, (size_t) size); /* Copy old data */ 00243 _myfree(ptr, filename, lineno, 0); /* Free not needed area */ 00244 } 00245 else 00246 { 00247 if (MyFlags & MY_HOLD_ON_ERROR) 00248 DBUG_RETURN(ptr); 00249 if (MyFlags & MY_FREE_ON_ERROR) 00250 _myfree(ptr, filename, lineno, 0); 00251 } 00252 DBUG_RETURN(data); 00253 } /* _myrealloc */ 00254 00255 00256 /* Deallocate some memory. */ 00257 00258 void _myfree(gptr ptr, const char *filename, uint lineno, myf myflags) 00259 { 00260 struct st_irem *irem; 00261 DBUG_ENTER("_myfree"); 00262 DBUG_PRINT("enter",("ptr: 0x%lx", ptr)); 00263 00264 if (!sf_malloc_quick) 00265 (void) _sanity (filename, lineno); 00266 00267 if ((!ptr && (myflags & MY_ALLOW_ZERO_PTR)) || 00268 check_ptr("Freeing",(byte*) ptr,filename,lineno)) 00269 DBUG_VOID_RETURN; 00270 00271 /* Calculate the address of the remember structure */ 00272 irem= (struct st_irem *) ((char*) ptr- ALIGN_SIZE(sizeof(struct st_irem))- 00273 sf_malloc_prehunc); 00274 00275 /* 00276 Check to make sure that we have a real remember structure. 00277 Note: this test could fail for four reasons: 00278 (1) The memory was already free'ed 00279 (2) The memory was never new'ed 00280 (3) There was an underrun 00281 (4) A stray pointer hit this location 00282 */ 00283 00284 if (*((uint32*) ((char*) ptr- sizeof(uint32))) != MAGICKEY) 00285 { 00286 fprintf(stderr, "Error: Freeing unallocated data at line %d, '%s'\n", 00287 lineno, filename); 00288 DBUG_PRINT("safe",("Unallocated data at line %d, '%s'",lineno,filename)); 00289 (void) fflush(stderr); 00290 DBUG_VOID_RETURN; 00291 } 00292 00293 /* Remove this structure from the linked list */ 00294 pthread_mutex_lock(&THR_LOCK_malloc); 00295 if (irem->prev) 00296 irem->prev->next= irem->next; 00297 else 00298 sf_malloc_root= irem->next; 00299 00300 if (irem->next) 00301 irem->next->prev= irem->prev; 00302 /* Handle the statistics */ 00303 sf_malloc_cur_memory-= irem->datasize; 00304 sf_malloc_count--; 00305 pthread_mutex_unlock(&THR_LOCK_malloc); 00306 00307 #ifndef HAVE_purify 00308 /* Mark this data as free'ed */ 00309 if (!sf_malloc_quick) 00310 bfill(ptr, irem->datasize, (pchar) FREE_VAL); 00311 #endif 00312 *((uint32*) ((char*) ptr- sizeof(uint32)))= ~MAGICKEY; 00313 /* Actually free the memory */ 00314 free((char*) irem); 00315 DBUG_VOID_RETURN; 00316 } 00317 00318 /* Check if we have a wrong pointer */ 00319 00320 static int check_ptr(const char *where, byte *ptr, const char *filename, 00321 uint lineno) 00322 { 00323 if (!ptr) 00324 { 00325 fprintf(stderr, "Error: %s NULL pointer at line %d, '%s'\n", 00326 where,lineno, filename); 00327 DBUG_PRINT("safe",("Null pointer at line %d '%s'", lineno, filename)); 00328 (void) fflush(stderr); 00329 return 1; 00330 } 00331 #ifndef _MSC_VER 00332 if ((long) ptr & (ALIGN_SIZE(1)-1)) 00333 { 00334 fprintf(stderr, "Error: %s wrong aligned pointer at line %d, '%s'\n", 00335 where,lineno, filename); 00336 DBUG_PRINT("safe",("Wrong aligned pointer at line %d, '%s'", 00337 lineno,filename)); 00338 (void) fflush(stderr); 00339 return 1; 00340 } 00341 #endif 00342 if (ptr < sf_min_adress || ptr > sf_max_adress) 00343 { 00344 fprintf(stderr, "Error: %s pointer out of range at line %d, '%s'\n", 00345 where,lineno, filename); 00346 DBUG_PRINT("safe",("Pointer out of range at line %d '%s'", 00347 lineno,filename)); 00348 (void) fflush(stderr); 00349 return 1; 00350 } 00351 return 0; 00352 } 00353 00354 00355 /* 00356 TERMINATE(FILE *file) 00357 Report on all the memory pieces that have not been 00358 free'ed as well as the statistics. 00359 */ 00360 00361 void TERMINATE(FILE *file) 00362 { 00363 struct st_irem *irem; 00364 DBUG_ENTER("TERMINATE"); 00365 pthread_mutex_lock(&THR_LOCK_malloc); 00366 00367 /* 00368 Report the difference between number of calls to 00369 NEW and the number of calls to FREE. >0 means more 00370 NEWs than FREEs. <0, etc. 00371 */ 00372 00373 if (sf_malloc_count) 00374 { 00375 if (file) 00376 { 00377 fprintf(file, "Warning: Not freed memory segments: %u\n", 00378 sf_malloc_count); 00379 (void) fflush(file); 00380 } 00381 DBUG_PRINT("safe",("sf_malloc_count: %u", sf_malloc_count)); 00382 } 00383 00384 /* 00385 Report on all the memory that was allocated with NEW 00386 but not free'ed with FREE. 00387 */ 00388 00389 if ((irem= sf_malloc_root)) 00390 { 00391 if (file) 00392 { 00393 fprintf(file, "Warning: Memory that was not free'ed (%ld bytes):\n", 00394 sf_malloc_cur_memory); 00395 (void) fflush(file); 00396 } 00397 DBUG_PRINT("safe",("Memory that was not free'ed (%ld bytes):", 00398 sf_malloc_cur_memory)); 00399 while (irem) 00400 { 00401 char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + 00402 sf_malloc_prehunc); 00403 if (file) 00404 { 00405 fprintf(file, 00406 "\t%6u bytes at 0x%09lx, allocated at line %4u in '%s'", 00407 irem->datasize, (long) data, irem->linenum, irem->filename); 00408 fprintf(file, "\n"); 00409 (void) fflush(file); 00410 } 00411 DBUG_PRINT("safe", 00412 ("%6u bytes at 0x%09lx, allocated at line %4d in '%s'", 00413 irem->datasize, data, irem->linenum, irem->filename)); 00414 irem= irem->next; 00415 } 00416 } 00417 /* Report the memory usage statistics */ 00418 if (file) 00419 { 00420 fprintf(file, "Maximum memory usage: %ld bytes (%ldk)\n", 00421 sf_malloc_max_memory, (sf_malloc_max_memory + 1023L) / 1024L); 00422 (void) fflush(file); 00423 } 00424 DBUG_PRINT("safe",("Maximum memory usage: %ld bytes (%ldk)", 00425 sf_malloc_max_memory, (sf_malloc_max_memory + 1023L) / 00426 1024L)); 00427 pthread_mutex_unlock(&THR_LOCK_malloc); 00428 DBUG_VOID_RETURN; 00429 } 00430 00431 00432 /* Returns 0 if chunk is ok */ 00433 00434 static int _checkchunk(register struct st_irem *irem, const char *filename, 00435 uint lineno) 00436 { 00437 int flag=0; 00438 char *magicp, *data; 00439 00440 data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + 00441 sf_malloc_prehunc); 00442 /* Check for a possible underrun */ 00443 if (*((uint32*) (data- sizeof(uint32))) != MAGICKEY) 00444 { 00445 fprintf(stderr, "Error: Memory allocated at %s:%d was underrun,", 00446 irem->filename, irem->linenum); 00447 fprintf(stderr, " discovered at %s:%d\n", filename, lineno); 00448 (void) fflush(stderr); 00449 DBUG_PRINT("safe",("Underrun at 0x%lx, allocated at %s:%d", 00450 data, irem->filename, irem->linenum)); 00451 flag=1; 00452 } 00453 00454 /* Check for a possible overrun */ 00455 magicp= data + irem->datasize; 00456 if (*magicp++ != MAGICEND0 || 00457 *magicp++ != MAGICEND1 || 00458 *magicp++ != MAGICEND2 || 00459 *magicp++ != MAGICEND3) 00460 { 00461 fprintf(stderr, "Error: Memory allocated at %s:%d was overrun,", 00462 irem->filename, irem->linenum); 00463 fprintf(stderr, " discovered at '%s:%d'\n", filename, lineno); 00464 (void) fflush(stderr); 00465 DBUG_PRINT("safe",("Overrun at 0x%lx, allocated at %s:%d", 00466 data, 00467 irem->filename, 00468 irem->linenum)); 00469 flag=1; 00470 } 00471 return(flag); 00472 } 00473 00474 00475 /* Returns how many wrong chunks */ 00476 00477 int _sanity(const char *filename, uint lineno) 00478 { 00479 reg1 struct st_irem *irem; 00480 reg2 int flag=0; 00481 uint count=0; 00482 00483 pthread_mutex_lock(&THR_LOCK_malloc); 00484 #ifndef PEDANTIC_SAFEMALLOC 00485 if (sf_malloc_tampered && (int) sf_malloc_count < 0) 00486 sf_malloc_count=0; 00487 #endif 00488 count=sf_malloc_count; 00489 for (irem= sf_malloc_root; irem != NULL && count-- ; irem= irem->next) 00490 flag+= _checkchunk (irem, filename, lineno); 00491 pthread_mutex_unlock(&THR_LOCK_malloc); 00492 if (count || irem) 00493 { 00494 const char *format="Error: Safemalloc link list destroyed, discovered at '%s:%d'"; 00495 fprintf(stderr, format, filename, lineno); fputc('\n',stderr); 00496 fprintf(stderr, "root=%p,count=%d,irem=%p\n", sf_malloc_root,count,irem); 00497 (void) fflush(stderr); 00498 DBUG_PRINT("safe",(format, filename, lineno)); 00499 flag=1; 00500 } 00501 return flag; 00502 } /* _sanity */ 00503 00504 00505 /* malloc and copy */ 00506 00507 gptr _my_memdup(const byte *from, uint length, const char *filename, 00508 uint lineno, myf MyFlags) 00509 { 00510 gptr ptr; 00511 if ((ptr=_mymalloc(length,filename,lineno,MyFlags)) != 0) 00512 memcpy((byte*) ptr, (byte*) from,(size_t) length); 00513 return(ptr); 00514 } /*_my_memdup */ 00515 00516 00517 char *_my_strdup(const char *from, const char *filename, uint lineno, 00518 myf MyFlags) 00519 { 00520 gptr ptr; 00521 uint length=(uint) strlen(from)+1; 00522 if ((ptr=_mymalloc(length,filename,lineno,MyFlags)) != 0) 00523 memcpy((byte*) ptr, (byte*) from,(size_t) length); 00524 return((char*) ptr); 00525 } /* _my_strdup */ 00526 00527 00528 char *_my_strndup(const char *from, uint length, 00529 const char *filename, uint lineno, 00530 myf MyFlags) 00531 { 00532 gptr ptr; 00533 if ((ptr=_mymalloc(length+1,filename,lineno,MyFlags)) != 0) 00534 { 00535 memcpy((byte*) ptr, (byte*) from,(size_t) length); 00536 ptr[length]=0; 00537 } 00538 return((char *) ptr); 00539 }
1.4.7

