2 * Orion's debugging malloc(), olawlor@acm.org, 1/11/2001
4 * This is a special version of malloc() and company for debugging software
5 * that is suspected of overrunning or underrunning the boundaries of a
6 * malloc buffer, or touching free memory.
8 * Detects writes before allocated region, writes after allocated region,
9 * double-deletes, uninitialized reads; i.e., most heap-related crashing errors.
10 * Includes a "CmiMemoryCheck()" routine which can check the entire heap's
11 * consistency on command.
13 * This version of malloc() should not be linked into production software,
14 * since it increases the time and memory overhead of malloc().
17 static void memAbort(const char *err
, void *ptr
)
21 CmiPrintf("[%d] memory-paranoid> FATAL HEAP ERROR! %s (block %p)\n",
23 CmiAbort("memory-paranoid> FATAL HEAP ERROR");
25 /*Simple printf version*/
26 fprintf(stderr
,"memory-paranoid> FATAL HEAP ERROR! %s (block %p)\n",
28 fflush(stdout
);fflush(stderr
);
33 /*This is the largest block we reasonably expect anyone to allocate*/
34 #define MAX_BLOCKSIZE (1024*1024*512) /* replaced by max_allocated */
35 static size_t max_allocated
= 0;
38 * Struct Slot contains all of the information about a malloc buffer except
39 * for the contents of its memory.
42 /*Doubly-linked allocated block list*/
46 /*The number of bytes of user data*/
49 /*A magic number field, to verify this is an actual malloc'd buffer*/
50 #define SLOTMAGIC 0x8402a5f5
51 #define SLOTMAGIC_VALLOC 0x7402a5f5
52 #define SLOTMAGIC_FREED 0xDEADBEEF
55 /* Controls the number of stack frames to print out */
57 void *from
[STACK_LEN
];
59 /*Padding to detect writes before and after buffer*/
60 #define PADLEN 32 /*Bytes of padding at start and end of buffer*/
63 typedef struct _Slot Slot
;
66 #define PADFN(i) (char)(217+(i))
67 /*Write these padding bytes*/
68 static void setPad(char *pad
) {
70 for (i
=0;i
<PADLEN
;i
++)
74 /*The given area is uninitialized-- fill it as such. */
75 static int memory_fill
=-1; /*-1 (alternate), 0 (zeros), or 1 (DE)*/
76 static int memory_fillphase
=0;
77 static void fill_uninit(char *loc
,int len
)
81 if (fill
==-1) /*Alternate zero and DE fill*/
82 fill
=(memory_fillphase
++)%2;
83 if (fill
!=0) fillChar
=0xDE;
85 memset(loc
,fillChar
,len
);
89 /*Convert a slot to a user address*/
90 static char *Slot_toUser(Slot
*s
) {
91 return ((char *)s
)+sizeof(Slot
);
94 /*Head of the circular slot list*/
95 static Slot slot_first
={&slot_first
,&slot_first
};
98 /********* Heap Checking ***********/
100 static int memory_checkfreq
=100; /*Check entire heap every this many malloc/frees*/
101 static int memory_checkphase
=0; /*malloc/free counter*/
102 static int memory_verbose
=0; /*Print out every time we check the heap*/
104 static void slotAbort(const char *why
,Slot
*s
) {
105 memory_checkfreq
=100000;
106 CmiPrintf("[%d] Error in block of %d bytes at %p, allocated from:\n",
107 CmiMyPe(), s
->userSize
, Slot_toUser(s
));
108 CmiBacktracePrint(s
->from
,STACK_LEN
);
109 memAbort(why
,Slot_toUser(s
));
112 /*Check these padding bytes*/
113 static void checkPad(char *pad
,char *errMsg
,void *ptr
,Slot
*s
) {
115 for (i
=0;i
<PADLEN
;i
++)
116 if (pad
[i
]!=PADFN(i
)) {
117 memory_checkfreq
=100000;
118 fprintf(stderr
,"Corrupted data:");
119 for (i
=0;i
<PADLEN
;i
++)
120 if (pad
[i
]!=PADFN(i
))
121 fprintf(stderr
," %02x",(unsigned
122 int)(unsigned char)pad
[i
]);
123 else fprintf(stderr
," -");
124 fprintf(stderr
,"\n");
129 /*Check if this pointer is "bad"-- not in the heap.*/
130 static int badPointer(Slot
*p
) {
132 if ((c
<(char *)0x1000) || (c
+0x1000)<(char *)0x1000)
137 /*Check this slot for consistency*/
138 static void checkSlot(Slot
*s
) {
139 char *user
=Slot_toUser(s
);
141 slotAbort("Non-heap pointer passed to checkSlot",s
);
142 if (s
->magic
!=SLOTMAGIC
&& s
->magic
!=SLOTMAGIC_VALLOC
)
143 slotAbort("Corrupted slot magic number",s
);
145 slotAbort("Corrupted (negative) user size field",s
);
146 if (s
->userSize
>max_allocated
)
147 slotAbort("Corrupted (huge) user size field",s
);
148 if (badPointer(s
->prev
) || (s
->prev
->next
!=s
))
149 slotAbort("Corrupted back link",s
);
150 if (badPointer(s
->next
) || (s
->next
->prev
!=s
))
151 slotAbort("Corrupted forward link",s
);
153 checkPad(s
->pad
,"Corruption before start of block",user
,s
);
154 checkPad(user
+s
->userSize
,"Corruption after block",user
,s
);
157 /*Check the entire heap for consistency*/
158 void memory_check(void)
160 Slot
*cur
=slot_first
.next
;
161 int nBlocks
=0, nBytes
=0;
163 while (cur
!=&slot_first
) {
166 nBytes
+=cur
->userSize
;
171 int nMegs
=nBytes
/(1024*1024);
172 int nKb
=(nBytes
-(nMegs
*1024*1024))/1024;
173 CmiPrintf("[%d] Heap checked-- clean. %d blocks / %d.%03d megs\n",
174 CmiMyPe(),nBlocks
,nMegs
,(int)(nKb
*1000.0/1024.0));
178 #define CMI_MEMORY_ROUTINES 1
180 /* Mark all allocated memory as being OK */
181 void CmiMemoryMark(void) { }
182 void CmiMemoryMarkBlock(void *blk
) { }
183 void CmiMemorySweep(const char *where
) { }
185 void CmiMemoryCheck(void)
190 /********** Allocation/Free ***********/
192 static int memoryTraceDisabled
= 0;
194 /*Write a valid slot to this field*/
195 static void *setSlot(Slot
*s
,int userSize
) {
196 char *user
=Slot_toUser(s
);
198 /*Determine if it's time for a heap check*/
199 if ((++memory_checkphase
)>=memory_checkfreq
) memory_check();
201 /*Splice into the slot list just past the head*/
202 s
->next
=slot_first
.next
;
210 if (memoryTraceDisabled
==0) {
211 memoryTraceDisabled
= 1;
212 CmiBacktraceRecord(s
->from
,3,&n
);
213 memoryTraceDisabled
= 0;
215 s
->from
[0] = (void*)10;
216 s
->from
[1] = (void*)9;
217 s
->from
[2] = (void*)8;
220 s
->userSize
=userSize
;
221 if (userSize
> max_allocated
) max_allocated
= userSize
;
222 setPad(s
->pad
); /*Padding before block*/
223 fill_uninit(user
,s
->userSize
); /*Block*/
224 setPad(user
+s
->userSize
); /*Padding after block*/
228 /*Delete this slot structure*/
229 static void freeSlot(Slot
*s
) {
232 /*Splice out of the slot list*/
233 s
->next
->prev
=s
->prev
;
234 s
->prev
->next
=s
->next
;
235 s
->prev
=s
->next
=(Slot
*)0x0F0;
237 s
->magic
=SLOTMAGIC_FREED
;
238 fill_uninit(Slot_toUser(s
),s
->userSize
);
241 /*Determine if it's time for a heap check*/
242 if ((++memory_checkphase
)>=memory_checkfreq
) memory_check();
245 /*Convert a user address to a slot*/
246 static Slot
*Slot_fmUser(void *user
) {
247 char *cu
=(char *)user
;
248 Slot
*s
=(Slot
*)(cu
-sizeof(Slot
));
254 /********** meta_ routines ***********/
256 #if ! CMK_MEMORY_BUILD_OS
257 /* Use Gnumalloc as meta-meta malloc fallbacks (mm_*) */
258 #include "memory-gnu.c"
261 /*Return the system page size*/
262 static int meta_getpagesize(void)
265 #if defined(CMK_GETPAGESIZE_AVAILABLE)
266 if (cache
==0) cache
=getpagesize();
268 if (cache
==0) cache
=8192;
273 /*Only display startup status messages from processor 0*/
274 static void status(char *msg
) {
275 if (CmiMyPe()==0 && !CmiArgGivingUsage()) {
279 static void meta_init(char **argv
)
281 CmiMemoryIs_flag
|=CMI_MEMORY_IS_PARANOID
;
282 CmiArgGroup("Converse","memory-paranoid");
283 status("Converse -memory mode: paranoid");
284 /*Parse uninitialized-memory-fill options:*/
285 if (CmiGetArgIntDesc(argv
,"+memory_fill",&memory_fill
, "Overwrite new and deleted memory")) {
288 if (CmiGetArgFlagDesc(argv
,"+memory_fillphase", "Invert memory overwrite pattern")) {
289 status(" phaseflip");
292 /*Parse heap-check options*/
293 if (CmiGetArgIntDesc(argv
,"+memory_checkfreq",&memory_checkfreq
, "Check heap this many mallocs")) {
294 status(" checkfreq");
296 if (CmiGetArgFlagDesc(argv
,"+memory_verbose", "Give a printout at each heap check")) {
303 static void *meta_malloc(size_t size
)
305 Slot
*s
=(Slot
*)mm_malloc(sizeof(Slot
)+size
+PADLEN
);
306 if (s
==NULL
) return s
;
307 return setSlot(s
,size
);
310 static void meta_free(void *mem
)
313 if (mem
==NULL
) return; /*Legal, but misleading*/
314 if (badPointer((Slot
*)mem
))
315 memAbort("Free'd near-NULL block",mem
);
318 if (s
->magic
==SLOTMAGIC_VALLOC
)
319 { /*Allocated with special alignment*/
321 mm_free(((char *)mem
)-meta_getpagesize());
323 else if (s
->magic
==SLOTMAGIC
)
324 { /*Ordinary allocated block */
328 else if (s
->magic
==SLOTMAGIC_FREED
)
329 memAbort("Free'd block twice",mem
);
330 else /*Unknown magic number*/
331 memAbort("Free'd non-malloc'd block",mem
);
334 static void *meta_calloc(size_t nelem
, size_t size
)
336 void *area
=meta_malloc(nelem
*size
);
337 if (area
!= NULL
) memset(area
,0,nelem
*size
);
341 static void meta_cfree(void *mem
)
346 static void *meta_realloc(void *oldBuffer
, size_t newSize
)
348 void *newBuffer
= meta_malloc(newSize
);
349 if ( newBuffer
&& oldBuffer
) {
350 /*Preserve old buffer contents*/
351 Slot
*o
=Slot_fmUser(oldBuffer
);
352 size_t size
=o
->userSize
;
353 if (size
>newSize
) size
=newSize
;
355 memcpy(newBuffer
, oldBuffer
, size
);
358 meta_free(oldBuffer
);
362 static void *meta_memalign(size_t align
, size_t size
)
364 /*Allocate a whole extra page for our slot structure*/
365 char *alloc
=(char *)mm_memalign(align
,meta_getpagesize()+size
+PADLEN
);
366 Slot
*s
=(Slot
*)(alloc
+meta_getpagesize()-sizeof(Slot
));
367 void *user
=setSlot(s
,size
);
368 s
->magic
=SLOTMAGIC_VALLOC
;
371 static void *meta_valloc(size_t size
)
373 return meta_memalign(meta_getpagesize(),size
);