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