1 /*
2 An interface to malloc() and free(). The code allows for
3 logging of memory usage to check for memory leaks and some
4 error checking. Only really used on the SUNs for memory
5 error checking. Borrowed from ptools and PETSc.
6 */
7 #include "BSprivate.h"
8 #if defined(HAVE_STDLIB_H)
9 #include <stdlib.h>
10 #endif
11 #if defined(HAVE_MALLOC_H) && !defined(__cplusplus)
12 #include <malloc.h>
13 #endif
14
15 void *TrMalloc(unsigned int, int, char *);
16 int TrFree( void *, int, char * );
17
18 /*
19 Code for checking if a pointer is out of the range of the
20 previously malloced memory. This approach will not always
21 work, it's just a check.
22 */
23 void *Low_address = (void *) 0x0 , *High_address = (void *) 0xEEEEEEEE;
24 int TrMallocUsed = 0;
25
26 /*
27 TrSpace - Routines for tracing space usage.
28
29 Description:
30 TrMalloc replaces malloc and TrFree replaces free. These routines
31 have the same syntax and semantics as the routines that they replace,
32 In addition, there are routines to report statistics on the memory
33 usage, and to report the currently allocated space. These routines
34 are built on top of malloc and free, and can be used together with
35 them as long as any space allocated with TrMalloc is only freed with
36 TrFree.
37 */
38
39 /* HEADER_DOUBLES is the number of doubles in a TrSpace header */
40 /* We have to be careful about alignment rules here */
41 #if defined(HAVE_64BITS)
42 #define TR_ALIGN_BYTES 8
43 #define TR_ALIGN_MASK 0x7
44 #define TR_FNAME_LEN 16
45 #define HEADER_DOUBLES 8
46 #else
47 #define TR_ALIGN_BYTES 4
48 #define TR_ALIGN_MASK 0x3
49 #define TR_FNAME_LEN 12
50 #define HEADER_DOUBLES 5
51 #endif
52
53 #define COOKIE_VALUE 0xf0e0d0c9
54 #define ALREADY_FREED 0x0f0e0d9c
55 #define MAX_TR_STACK 20
56 #define TR_MALLOC 0x1
57 #define TR_FREE 0x2
58
59 typedef struct _trSPACE {
60 unsigned long size;
61 int id;
62 int lineno;
63 char fname[TR_FNAME_LEN];
64 unsigned long cookie;
65 struct _trSPACE *next, *prev;
66 } TRSPACE;
67 /* This union is used to insure that the block passed to the user is
68 aligned on a double boundary */
69 typedef union {
70 TRSPACE sp;
71 double v[HEADER_DOUBLES];
72 } TrSPACE;
73
74 static long allocated = 0, frags = 0;
75 static TRSPACE *TRhead = 0;
76 static int TRid = 0;
77 static int TRdebugLevel = 0;
78 static long TRMaxMem = 0;
79 static long TRMaxMemId = 0;
80
81 #if defined(PARCH_sun4) && defined(__cplusplus)
82 extern "C" {
83 extern int malloc_verify();
84 }
85 #elif defined(PARCH_sun4)
86 extern int malloc_verify();
87 #endif
88
89 /*
90 TrValid - Test the allocated blocks for validity. This can be used to
91 check for memory overwrites.
92
93 Input Parameter:
94 . line, file - line number and filename where call originated.
95
96 Return value:
97 The number of errors detected.
98
99 Output Effect:
100 Error messages are written to stdout. These have the form of either
101
102 $ Block [id=%d(%d)] at address %lx is corrupted (probably write past end)
103 $ Block allocated in <filename>[<linenumber>]
104
105 if the sentinal at the end of the block has been corrupted, and
106
107 $ Block at address %lx is corrupted
108
109 if the sentinal at the begining of the block has been corrupted.
110
111 The address is the actual address of the block. The id is the
112 value of TRID.
113
114 No output is generated if there are no problems detected.
115 */
TrValid(int line,char * file)116 int TrValid(int line,char *file )
117 {
118 TRSPACE *head;
119 char *a;
120 unsigned long *nend;
121
122 head = TRhead;
123 while (head) {
124 if (head->cookie != COOKIE_VALUE) {
125 fprintf( stderr, "called from %s line %d \n",file,line );
126 fprintf( stderr, "Block at address %p is corrupted\n", head );
127 MY_SETERRC(1,"TrValid");
128 }
129 a = (char *)(((TrSPACE*)head) + 1);
130 nend = (unsigned long *)(a + head->size);
131 if (nend[0] != COOKIE_VALUE) {
132 fprintf( stderr, "called from %s line %d\n",file,line );
133 head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
134 if (nend[0] == ALREADY_FREED) {
135 fprintf(stderr,"Block [id=%d(%lx)] at address %p already freed\n",
136 head->id, head->size, a );
137 MY_SETERRC(1,"TrValid:Freed block in memory list, corrupted memory");
138 } else {
139 fprintf( stderr,
140 "Block [id=%d(%lx)] at address %p is corrupted (probably write past end)\n",
141 head->id, head->size, a );
142 fprintf(stderr,"Block allocated in %s[%d]\n",head->fname,head->lineno);
143 MY_SETERRC(1,"TrValid:Corrupted memory");
144 }
145 }
146 head = head->next;
147 }
148 #if defined(PARCH_sun4)
149 malloc_verify();
150 #endif
151
152 return 0;
153 }
154
155 /*
156 TrMalloc - Malloc with tracing.
157
158 Input Parameters:
159 . a - number of bytes to allocate
160 . lineno - line number where used. Use __LINE__ for this
161 . fname - file name where used. Use __FILE__ for this
162
163 Returns:
164 double aligned pointer to requested storage, or null if not
165 available.
166 */
TrMalloc(unsigned int a,int lineno,char * fname)167 void *TrMalloc(unsigned int a, int lineno, char *fname )
168 {
169 TRSPACE *head;
170 char *inew;
171 unsigned long *nend;
172 unsigned int nsize;
173 int l,ierr;
174
175 if (TRdebugLevel > 0) {
176 ierr = TrValid(lineno,fname); if (ierr) return 0;
177 }
178
179 if (a == 0) {
180 fprintf(stderr,"TrMalloc ERROR: malloc zero length, this is illegal!\n");
181 return 0;
182 }
183 nsize = a;
184 if (nsize & TR_ALIGN_MASK) nsize += (TR_ALIGN_BYTES - (nsize & TR_ALIGN_MASK));
185 inew = (char *) malloc( (unsigned)(nsize+sizeof(TrSPACE)+sizeof(unsigned long)));
186 if (!inew) return 0;
187
188 /*
189 Keep track of range of memory locations we have malloced in
190 */
191 #if !defined(PETSC_INSIGHT)
192 if (Low_address > (void *) inew) Low_address = (void *) inew;
193 if (High_address < (void *) (inew+nsize+sizeof(TrSPACE)+sizeof(unsigned long)))
194 High_address = (void *) (inew+nsize+sizeof(TrSPACE)+sizeof(unsigned long));
195 #endif
196
197
198 head = (TRSPACE *)inew;
199 inew += sizeof(TrSPACE);
200
201 if (TRhead) TRhead->prev = head;
202 head->next = TRhead;
203 TRhead = head;
204 head->prev = 0;
205 head->size = nsize;
206 head->id = TRid;
207 head->lineno = lineno;
208 if ((l = strlen(fname)) > TR_FNAME_LEN-1) fname += (l - (TR_FNAME_LEN-1));
209 strncpy( head->fname, fname, (TR_FNAME_LEN-1) );
210 head->fname[TR_FNAME_LEN-1] = 0;
211 head->cookie = COOKIE_VALUE;
212 nend = (unsigned long *)(inew + nsize);
213 nend[0] = COOKIE_VALUE;
214
215 allocated += nsize;
216 if (allocated > TRMaxMem) {
217 TRMaxMem = allocated;
218 TRMaxMemId = TRid;
219 }
220 frags ++;
221 return (void *)inew;
222 }
223
224
225 /*
226 TrFree - Free with tracing.
227
228 Input Parameters:
229 . a - pointer to a block allocated with TrMalloc
230 . line - line in file where called
231 . file - Name of file where called
232 */
TrFree(void * aa,int line,char * file)233 int TrFree( void *aa, int line, char *file )
234 {
235 char *a = (char *) aa;
236 TRSPACE *head;
237 char *ahead;
238 unsigned long *nend;
239 int ierr;
240
241 /* Don't try to handle empty blocks */
242 if (!a) {
243 fprintf(stderr,"TrFree called from line %d in %s\n",line,file);
244 MY_SETERRC(1,"TrFree:Trying to free null block");
245 }
246
247 if (TRdebugLevel > 0) {
248 ierr = TrValid(line,file); CHKERR(ierr);
249 }
250
251 #if !defined(PETSC_INSIGHT)
252 if (Low_address > aa || High_address < aa){
253 fprintf(stderr,"TrFree called with address not allocated by TrMalloc\n");
254 MY_SETERRC(1,"TrFree:Invalid Address");
255 }
256 #endif
257
258 ahead = a;
259 a = a - sizeof(TrSPACE);
260 head = (TRSPACE *)a;
261 if (head->cookie != COOKIE_VALUE) {
262 /* Damaged header */
263 fprintf( stderr, "Block at address %p is corrupted; cannot free;\n\
264 may be block not allocated with TrMalloc or Malloc\n", a );
265 MY_SETERRC(1,"TrFree:Bad location or corrupted memory");
266 }
267 nend = (unsigned long *)(ahead + head->size);
268 if (*nend != COOKIE_VALUE) {
269 if (*nend == ALREADY_FREED) {
270 fprintf(stderr,"Block [id=%d(%lx)] at address %p was already freed\n",
271 head->id, head->size, a + sizeof(TrSPACE) );
272 head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
273 if (head->lineno > 0)
274 fprintf( stderr, "Block freed in %s[%d]\n", head->fname, head->lineno );
275 else
276 fprintf( stderr, "Block allocated at %s[%d]\n",head->fname,-head->lineno);
277 MY_SETERRC(1,"TrFree:Memory already freed");
278 }
279 else {
280 /* Damaged tail */
281 fprintf( stderr,
282 "Block [id=%d(%lx)] at address %p is corrupted (probably write past end)\n",
283 head->id, head->size, a );
284 head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
285 fprintf( stderr, "Block allocated in %s[%d]\n", head->fname, head->lineno );
286 MY_SETERRC(1,"TrFree:Corrupted memory");
287 }
288 }
289 /* Mark the location freed */
290 *nend = ALREADY_FREED;
291 /* Save location where freed. If we suspect the line number, mark as
292 allocated location */
293 if (line > 0 && line < 5000) {
294 head->lineno = line;
295 strncpy( head->fname, file, (TR_FNAME_LEN-1) );
296 }
297 else {
298 head->lineno = - head->lineno;
299 }
300
301 allocated -= head->size;
302 frags --;
303 if (head->prev) head->prev->next = head->next;
304 else TRhead = head->next;
305
306 if (head->next) head->next->prev = head->prev;
307 free( a );
308 return 0;
309 }
310
311 /*
312 TrSpace - Returns space statistics.
313
314 Output Parameters:
315 . space - number of bytes currently allocated
316 . frags - number of blocks currently allocated
317 . maxs - maximum number of bytes ever allocated
318
319 .keywords: memory, allocation, tracing, space, statistics
320
321 .seealso: TrDump()
322 */
TrSpace(double * space,double * fr,double * maxs)323 int TrSpace( double *space, double *fr, double *maxs )
324 {
325 if (space) *space = (double) allocated;
326 if (fr) *fr = (double) frags;
327 if (maxs) *maxs = (double) TRMaxMem;
328 return 0;
329 }
330
331 /*
332 TrDump - Dumps the allocated memory blocks to a file. The information
333 printed is: size of space (in bytes), address of space, id of space,
334 file in which space was allocated, and line number at which it was
335 allocated.
336
337 Input Parameter:
338 . fp - file pointer. If fp is NULL, stderr is assumed.
339
340 Options Database Key:
341 $ -trdump : dumps unfreed memory during call to BSfinalize()
342
343 .keywords: memory, allocation, tracing, space, statistics
344
345 .seealso: TrSpace()
346 */
TrDump(FILE * fp)347 int TrDump( FILE *fp )
348 {
349 TRSPACE *head;
350 int rank;
351
352 MPI_Comm_rank(MPI_COMM_WORLD,&rank);
353 if (fp == 0) fp = stderr;
354 if (allocated > 0) {
355 fprintf(fp,"[%d]Total space allocated %d\n",rank,(int)allocated);
356 }
357 head = TRhead;
358 while (head) {
359 fprintf( fp, "[%d]%d bytes at address [%p], id = ",rank,
360 (int) head->size, head + sizeof(TrSPACE) );
361 head->fname[TR_FNAME_LEN-1] = 0;
362 fprintf(fp, "%d, %s line number %d\n",head->id,head->fname,head->lineno);
363 head = head->next;
364 }
365 return 0;
366 }
367
368 /*
369 TrDebugLevel - Set the level of debugging for the space management
370 routines.
371
372 Input Parameter:
373 . level - level of debugging. Currently, either 0 (no checking) or 1
374 (use TrValid at each TrMalloc or TrFree).
375 */
TrDebugLevel(int level)376 int TrDebugLevel(int level )
377 {
378 TRdebugLevel = level;
379 return 0;
380 }
381
382
383 #define TR_MAX_DUMP 100
384 /*
385 The following routine attempts to give useful information about the
386 memory usage when an "out-of-memory" error is encountered. The rules are:
387 If there are less than TR_MAX_DUMP blocks, output those.
388 Otherwise, try to find multiple instances of the same routine/line #, and
389 print a summary by number:
390 file line number-of-blocks total-number-of-blocks
391
392 We have to do a sort-in-place for this
393 */
394
395 /*
396 Sort by file/line number. Do this without calling a system routine or
397 allocating ANY space (space is being optimized here).
398
399 We do this by first recursively sorting halves of the list and then
400 merging them.
401 */
402
403 /* Merge two lists, returning the head of the merged list */
TrImerge(TRSPACE * l1,TRSPACE * l2)404 TRSPACE *TrImerge(TRSPACE * l1,TRSPACE * l2 )
405 {
406 TRSPACE *head = 0, *tail = 0;
407 int sign;
408
409 while (l1 && l2) {
410 sign = strcmp(l1->fname, l2->fname);
411 if (sign > 0 || (sign == 0 && l1->lineno >= l2->lineno)) {
412 if (head) tail->next = l1;
413 else head = tail = l1;
414 tail = l1;
415 l1 = l1->next;
416 }
417 else {
418 if (head) tail->next = l2;
419 else head = tail = l2;
420 tail = l2;
421 l2 = l2->next;
422 }
423 }
424 /* Add the remaining elements to the end */
425 if (l1) tail->next = l1;
426 if (l2) tail->next = l2;
427 return head;
428 }
429
430 /* Sort head with n elements, returning the head */
TrIsort(TRSPACE * head,int n)431 TRSPACE *TrIsort( TRSPACE * head,int n )
432 {
433 TRSPACE *p, *l1, *l2;
434 int m, i;
435
436 if (n <= 1) return head;
437
438 /* This guarentees that m, n are both > 0 */
439 m = n / 2;
440 p = head;
441 for (i=0; i<m-1; i++) p = p->next;
442 /* p now points to the END of the first list */
443 l2 = p->next;
444 p->next = 0;
445 l1 = TrIsort( head, m );
446 l2 = TrIsort( l2, n - m );
447 return TrImerge( l1, l2 );
448 }
449
TrSortBlocks()450 int TrSortBlocks()
451 {
452 TRSPACE *head;
453 int cnt;
454
455 head = TRhead;
456 cnt = 0;
457 while (head) {
458 cnt ++;
459 head = head->next;
460 }
461 TRhead = TrIsort( TRhead, cnt );
462 return 0;
463 }
464
465 /* Takes sorted input and dumps as an aggregate */
TrDumpGrouped(FILE * fp)466 int TrDumpGrouped(FILE *fp )
467 {
468 TRSPACE *head, *cur;
469 int nblocks;
470 unsigned long nbytes;
471
472 if (fp == 0) fp = stderr;
473
474 TrSortBlocks();
475 head = TRhead;
476 cur = 0;
477 while (head) {
478 cur = head->next;
479 nblocks = 1;
480 nbytes = head->size;
481 while (cur && !strcmp(cur->fname,head->fname) && cur->lineno == head->lineno){
482 nblocks++;
483 nbytes += cur->size;
484 cur = cur->next;
485 }
486 fprintf( fp, "File %13s line %5d: %ld bytes in %d allocation%c\n",
487 head->fname, head->lineno, nbytes, nblocks,(nblocks > 1) ? 's' : ' ');
488 head = cur;
489 }
490 fflush( fp );
491 return 0;
492 }
493
494
495
496
497
498