1
/* Copyright (C) 2000-2003 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
* Memory sub-system, written by Bjorn Benson
18
Fixed to use my_sys scheme by Michael Widenius
20
[This posting refers to an article entitled "oops, corrupted memory
21
again!" in net.lang.c. I am posting it here because it is source.]
23
My tool for approaching this problem is to build another level of data
24
abstraction on top of malloc() and free() that implements some checking.
25
This does a number of things for you:
26
- Checks for overruns and underruns on allocated data
27
- Keeps track of where in the program the memory was malloc'ed
28
- Reports on pieces of memory that were not free'ed
29
- Records some statistics such as maximum memory used
30
- Marks newly malloc'ed and newly free'ed memory with special values
31
You can use this scheme to:
32
- Find bugs such as overrun, underrun, etc because you know where
33
a piece of data was malloc'ed and where it was free'ed
34
- Find bugs where memory was not free'ed
35
- Find bugs where newly malloc'ed memory is used without initializing
36
- Find bugs where newly free'ed memory is still used
37
- Determine how much memory your program really uses
40
To implement my scheme you must have a C compiler that has __LINE__ and
41
__FILE__ macros. If your compiler doesn't have these then (a) buy another:
42
compilers that do are available on UNIX 4.2bsd based systems and the PC,
43
and probably on other machines; or (b) change my scheme somehow. I have
44
recomendations on both these points if you would like them (e-mail please).
46
There are 4 functions in my package:
47
char *NEW( uSize ) Allocate memory of uSize bytes
48
(equivalent to malloc())
49
char *REA( pPtr, uSize) Allocate memory of uSize bytes, move data and
51
(equivalent to realloc())
52
FREE( pPtr ) Free memory allocated by NEW
53
(equivalent to free())
54
TERMINATE(file,flag) End system, report errors and stats on file
55
I personally use two more functions, but have not included them here:
56
char *STRSAVE( sPtr ) Save a copy of the string in dynamic memory
57
char *RENEW( pPtr, uSize )
58
(equivalent to realloc())
63
#define SAFEMALLOC /* Get protos from my_sys */
66
#include "mysys_priv.h"
68
#include "my_static.h"
69
#include "mysys_err.h"
71
ulonglong sf_malloc_mem_limit= ~(ulonglong)0;
73
#ifndef PEDANTIC_SAFEMALLOC
75
Set to 1 after TERMINATE() if we had to fiddle with sf_malloc_count and
76
the linked list of blocks so that _sanity() will not fuss when it
79
static int sf_malloc_tampered= 0;
83
/* Static functions prototypes */
85
static int check_ptr(const char *where, uchar *ptr, const char *sFile,
87
static int _checkchunk(struct st_irem *pRec, const char *sFile, uint uLine);
90
Note: We only fill up the allocated block. This do not include
91
malloc() roundoff or the extra space required by the irem
96
NEW'ed memory is filled with this value so that references to it will
97
end up being very strange.
99
#define ALLOC_VAL (uchar) 0xA5
101
FEEE'ed memory is filled with this value so that references to it will
102
end up being very strange.
104
#define FREE_VAL (uchar) 0x8F
105
#define MAGICKEY 0x14235296 /* A magic value for underrun key */
108
Warning: do not change the MAGICEND? values to something with the
109
high bit set. Various C compilers (like the 4.2bsd one) do not do
110
the sign extension right later on in this code and you will get
114
#define MAGICEND0 0x68 /* Magic values for overrun keys */
115
#define MAGICEND1 0x34 /* " */
116
#define MAGICEND2 0x7A /* " */
117
#define MAGICEND3 0x15 /* " */
120
/* Allocate some memory. */
122
void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags)
124
struct st_irem *irem;
126
DBUG_ENTER("_mymalloc");
127
DBUG_PRINT("enter",("Size: %lu", (ulong) size));
129
if (!sf_malloc_quick)
130
(void) _sanity (filename, lineno);
132
if (size + sf_malloc_cur_memory > sf_malloc_mem_limit)
136
/* Allocate the physical memory */
137
irem= (struct st_irem *) malloc (ALIGN_SIZE(sizeof(struct st_irem)) +
139
size + /* size requested */
140
4 + /* overrun mark */
143
/* Check if there isn't anymore memory avaiable */
146
if (MyFlags & MY_FAE)
147
error_handler_hook=fatal_error_handler_hook;
148
if (MyFlags & (MY_FAE+MY_WME))
152
sprintf(buff,"Out of memory at line %d, '%s'", lineno, filename);
153
my_message(EE_OUTOFMEMORY, buff, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH));
154
sprintf(buff,"needed %lu byte (%luk), memory in use: %lu bytes (%luk)",
155
(ulong) size, (ulong) (size + 1023L) / 1024L,
156
(ulong) sf_malloc_max_memory,
157
(ulong) (sf_malloc_max_memory + 1023L) / 1024L);
158
my_message(EE_OUTOFMEMORY, buff, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH));
160
DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'",
161
sf_malloc_max_memory,lineno, filename));
162
if (MyFlags & MY_FAE)
164
DBUG_RETURN ((void*) 0);
167
/* Fill up the structure */
168
data= (((uchar*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) +
170
*((uint32*) (data-sizeof(uint32)))= MAGICKEY;
171
data[size + 0]= MAGICEND0;
172
data[size + 1]= MAGICEND1;
173
data[size + 2]= MAGICEND2;
174
data[size + 3]= MAGICEND3;
175
irem->filename= (char *) filename;
176
irem->linenum= lineno;
177
irem->datasize= size;
180
/* Add this remember structure to the linked list */
181
pthread_mutex_lock(&THR_LOCK_malloc);
182
if ((irem->next= sf_malloc_root))
183
sf_malloc_root->prev= irem;
184
sf_malloc_root= irem;
186
/* Keep the statistics */
187
sf_malloc_cur_memory+= size;
188
if (sf_malloc_cur_memory > sf_malloc_max_memory)
189
sf_malloc_max_memory= sf_malloc_cur_memory;
191
pthread_mutex_unlock(&THR_LOCK_malloc);
193
/* Set the memory to the aribtrary wierd value */
194
if ((MyFlags & MY_ZEROFILL) || !sf_malloc_quick)
195
bfill(data, size, (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL));
196
/* Return a pointer to the real data */
197
DBUG_PRINT("exit",("ptr: %p", data));
198
if (sf_min_adress > data)
200
if (sf_max_adress < data)
202
DBUG_RETURN((void*) data);
207
Allocate some new memory and move old memoryblock there.
208
Free then old memoryblock
211
void *_myrealloc(register void *ptr, register size_t size,
212
const char *filename, uint lineno, myf MyFlags)
214
struct st_irem *irem;
216
DBUG_ENTER("_myrealloc");
218
if (!ptr && (MyFlags & MY_ALLOW_ZERO_PTR))
219
DBUG_RETURN(_mymalloc(size, filename, lineno, MyFlags));
221
if (!sf_malloc_quick)
222
(void) _sanity (filename, lineno);
224
if (check_ptr("Reallocating", (uchar*) ptr, filename, lineno))
225
DBUG_RETURN((uchar*) NULL);
227
irem= (struct st_irem *) (((char*) ptr) - ALIGN_SIZE(sizeof(struct st_irem))-
229
if (*((uint32*) (((char*) ptr)- sizeof(uint32))) != MAGICKEY)
231
fprintf(stderr, "Error: Reallocating unallocated data at line %d, '%s'\n",
233
DBUG_PRINT("safe",("Reallocating unallocated data at line %d, '%s'",
235
(void) fflush(stderr);
236
DBUG_RETURN((uchar*) NULL);
239
if ((data= _mymalloc(size,filename,lineno,MyFlags))) /* Allocate new area */
241
size=min(size, irem->datasize); /* Move as much as possibly */
242
memcpy((uchar*) data, ptr, (size_t) size); /* Copy old data */
243
_myfree(ptr, filename, lineno, 0); /* Free not needed area */
247
if (MyFlags & MY_HOLD_ON_ERROR)
249
if (MyFlags & MY_FREE_ON_ERROR)
250
_myfree(ptr, filename, lineno, 0);
256
/* Deallocate some memory. */
258
void _myfree(void *ptr, const char *filename, uint lineno, myf myflags)
260
struct st_irem *irem;
261
DBUG_ENTER("_myfree");
262
DBUG_PRINT("enter",("ptr: %p", ptr));
264
if (!sf_malloc_quick)
265
(void) _sanity (filename, lineno);
267
if ((!ptr && (myflags & MY_ALLOW_ZERO_PTR)) ||
268
check_ptr("Freeing",(uchar*) ptr,filename,lineno))
271
/* Calculate the address of the remember structure */
272
irem= (struct st_irem *) ((char*) ptr- ALIGN_SIZE(sizeof(struct st_irem))-
276
Check to make sure that we have a real remember structure.
277
Note: this test could fail for four reasons:
278
(1) The memory was already free'ed
279
(2) The memory was never new'ed
280
(3) There was an underrun
281
(4) A stray pointer hit this location
284
if (*((uint32*) ((char*) ptr- sizeof(uint32))) != MAGICKEY)
286
fprintf(stderr, "Error: Freeing unallocated data at line %d, '%s'\n",
288
DBUG_PRINT("safe",("Unallocated data at line %d, '%s'",lineno,filename));
289
(void) fflush(stderr);
293
/* Remove this structure from the linked list */
294
pthread_mutex_lock(&THR_LOCK_malloc);
296
irem->prev->next= irem->next;
298
sf_malloc_root= irem->next;
301
irem->next->prev= irem->prev;
302
/* Handle the statistics */
303
sf_malloc_cur_memory-= irem->datasize;
305
pthread_mutex_unlock(&THR_LOCK_malloc);
308
/* Mark this data as free'ed */
309
if (!sf_malloc_quick)
310
bfill(ptr, irem->datasize, (pchar) FREE_VAL);
312
*((uint32*) ((char*) ptr- sizeof(uint32)))= ~MAGICKEY;
313
/* Actually free the memory */
318
/* Check if we have a wrong pointer */
320
static int check_ptr(const char *where, uchar *ptr, const char *filename,
325
fprintf(stderr, "Error: %s NULL pointer at line %d, '%s'\n",
326
where,lineno, filename);
327
DBUG_PRINT("safe",("Null pointer at line %d '%s'", lineno, filename));
328
(void) fflush(stderr);
332
if ((long) ptr & (ALIGN_SIZE(1)-1))
334
fprintf(stderr, "Error: %s wrong aligned pointer at line %d, '%s'\n",
335
where,lineno, filename);
336
DBUG_PRINT("safe",("Wrong aligned pointer at line %d, '%s'",
338
(void) fflush(stderr);
342
if (ptr < sf_min_adress || ptr > sf_max_adress)
344
fprintf(stderr, "Error: %s pointer out of range at line %d, '%s'\n",
345
where,lineno, filename);
346
DBUG_PRINT("safe",("Pointer out of range at line %d '%s'",
348
(void) fflush(stderr);
356
Report on all the memory pieces that have not been free'ed
360
file Write output to this file
361
flag If <> 0, also write statistics
364
void TERMINATE(FILE *file, uint flag)
366
struct st_irem *irem;
367
DBUG_ENTER("TERMINATE");
368
pthread_mutex_lock(&THR_LOCK_malloc);
371
Report the difference between number of calls to
372
NEW and the number of calls to FREE. >0 means more
373
NEWs than FREEs. <0, etc.
380
fprintf(file, "Warning: Not freed memory segments: %u\n", sf_malloc_count);
383
DBUG_PRINT("safe",("sf_malloc_count: %u", sf_malloc_count));
387
Report on all the memory that was allocated with NEW
388
but not free'ed with FREE.
391
if ((irem= sf_malloc_root))
395
fprintf(file, "Warning: Memory that was not free'ed (%lu bytes):\n",
396
(ulong) sf_malloc_cur_memory);
399
DBUG_PRINT("safe",("Memory that was not free'ed (%lu bytes):",
400
(ulong) sf_malloc_cur_memory));
403
char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) +
408
"\t%6lu bytes at %p, allocated at line %4u in '%s'",
409
(ulong) irem->datasize, data, irem->linenum, irem->filename);
414
("%6lu bytes at %p, allocated at line %4d in '%s'",
415
(ulong) irem->datasize,
416
data, irem->linenum, irem->filename));
420
/* Report the memory usage statistics */
423
fprintf(file, "Maximum memory usage: %lu bytes (%luk)\n",
424
(ulong) sf_malloc_max_memory,
425
(ulong) (sf_malloc_max_memory + 1023L) / 1024L);
428
DBUG_PRINT("safe",("Maximum memory usage: %lu bytes (%luk)",
429
(ulong) sf_malloc_max_memory,
430
(ulong) (sf_malloc_max_memory + 1023L) /1024L));
431
pthread_mutex_unlock(&THR_LOCK_malloc);
437
Report where a piece of memory was allocated
439
This is usefull to call from withing a debugger
443
void sf_malloc_report_allocated(void *memory)
445
struct st_irem *irem;
446
for (irem= sf_malloc_root ; irem ; irem=irem->next)
448
char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) +
450
if (data <= (char*) memory && (char*) memory <= data + irem->datasize)
452
printf("%lu bytes at %p, allocated at line %u in '%s'\n",
453
(ulong) irem->datasize, data, irem->linenum, irem->filename);
459
/* Returns 0 if chunk is ok */
461
static int _checkchunk(register struct st_irem *irem, const char *filename,
467
data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) +
469
/* Check for a possible underrun */
470
if (*((uint32*) (data- sizeof(uint32))) != MAGICKEY)
472
fprintf(stderr, "Error: Memory allocated at %s:%d was underrun,",
473
irem->filename, irem->linenum);
474
fprintf(stderr, " discovered at %s:%d\n", filename, lineno);
475
(void) fflush(stderr);
476
DBUG_PRINT("safe",("Underrun at %p, allocated at %s:%d",
477
data, irem->filename, irem->linenum));
481
/* Check for a possible overrun */
482
magicp= data + irem->datasize;
483
if (*magicp++ != MAGICEND0 ||
484
*magicp++ != MAGICEND1 ||
485
*magicp++ != MAGICEND2 ||
486
*magicp++ != MAGICEND3)
488
fprintf(stderr, "Error: Memory allocated at %s:%d was overrun,",
489
irem->filename, irem->linenum);
490
fprintf(stderr, " discovered at '%s:%d'\n", filename, lineno);
491
(void) fflush(stderr);
492
DBUG_PRINT("safe",("Overrun at %p, allocated at %s:%d",
493
data, irem->filename, irem->linenum));
500
/* Returns how many wrong chunks */
502
int _sanity(const char *filename, uint lineno)
504
reg1 struct st_irem *irem;
508
pthread_mutex_lock(&THR_LOCK_malloc);
509
#ifndef PEDANTIC_SAFEMALLOC
510
if (sf_malloc_tampered && (int) sf_malloc_count < 0)
513
count=sf_malloc_count;
514
for (irem= sf_malloc_root; irem != NULL && count-- ; irem= irem->next)
515
flag+= _checkchunk (irem, filename, lineno);
516
pthread_mutex_unlock(&THR_LOCK_malloc);
519
const char *format="Error: Safemalloc link list destroyed, discovered at '%s:%d'";
520
fprintf(stderr, format, filename, lineno); fputc('\n',stderr);
521
fprintf(stderr, "root=%p,count=%d,irem=%p\n", sf_malloc_root,count,irem);
522
(void) fflush(stderr);
523
DBUG_PRINT("safe",(format, filename, lineno));
530
/* malloc and copy */
532
void *_my_memdup(const void *from, size_t length, const char *filename,
533
uint lineno, myf MyFlags)
536
if ((ptr= _mymalloc(length,filename,lineno,MyFlags)) != 0)
537
memcpy(ptr, from, length);
542
char *_my_strdup(const char *from, const char *filename, uint lineno,
546
size_t length= strlen(from)+1;
547
if ((ptr= (char*) _mymalloc(length,filename,lineno,MyFlags)) != 0)
548
memcpy((uchar*) ptr, (uchar*) from, (size_t) length);
553
char *_my_strndup(const char *from, size_t length,
554
const char *filename, uint lineno,
558
if ((ptr= (char*) _mymalloc(length+1,filename,lineno,MyFlags)) != 0)
560
memcpy((uchar*) ptr, (uchar*) from, (size_t) length);