1 #define TRSPACE_SOURCE
2 #include "sowing.h"
3 #ifndef MEMORY_TRACING
4 #define MEMORY_TRACING 1
5 #endif
6 #include <stdio.h>
7 #include <string.h>
8 #define _TR_SOURCE
9 
10 #if HAVE_STDLIB_H
11 #include <stdlib.h>
12 #else
13 #ifdef __STDC__
14 extern void 	*calloc(/*size_t, size_t*/);
15 extern void	free(/*void * */);
16 extern void	*malloc(/*size_t*/);
17 #else
18 extern char *malloc();
19 extern char *calloc();
20 extern int free();
21 #endif
22 #endif
23 
24 #undef TRSPACE
25 
26 /*D
27     trspace - Routines for tracing space usage
28 
29     Description:
30     trmalloc replaces malloc and trfree replaces free.
31     These routines
32     have the same syntax and semantics as the routines that they replace,
33     In addition, there are routines to report statistics on the memory
34     usage, and to report the currently allocated space.  These routines
35     are built on top of malloc and free, and can be used together with
36     them as long as any space allocated with trmalloc is only freed with
37     trfree.
38 
39     Note that the malloced data is scrubbed each time; you don't get
40     random trash (or fortuitous zeros).  What you get is fc (bytes);
41     this will usually create a "bad" value.
42 
43     As an aid in developing codes, a maximum memory threshold can
44     be set with TrSetMaxMem.
45  D*/
46 
47 /* HEADER_DOUBLES is the number of doubles in a trSPACE header */
48 /* We have to be careful about alignment rules here */
49 #if defined(POINTER_64_BITS)
50 #define TR_ALIGN_BYTES 8
51 #define TR_ALIGN_MASK  0x7
52 #define TR_FNAME_LEN   16
53 #define HEADER_DOUBLES 12
54 #else
55 #define TR_ALIGN_BYTES 4
56 #define TR_ALIGN_MASK  0x3
57 #define TR_FNAME_LEN   12
58 #define HEADER_DOUBLES 8
59 #endif
60 
61 #define COOKIE_VALUE   0xf0e0d0c9
62 #define ALREADY_FREED  0x0f0e0d9c
63 
64 typedef struct _trSPACE {
65     unsigned long   size;
66     int             id;
67     int             lineno;
68     char            fname[TR_FNAME_LEN];
69     int             freed_lineno;
70     char            freed_fname[TR_FNAME_LEN];
71     unsigned long   cookie;
72     struct _trSPACE *next, *prev;
73     } TRSPACE;
74 /* This union is used to insure that the block passed to the user is
75    aligned on a double boundary */
76 typedef union {
77     TRSPACE sp;
78     double  v[HEADER_DOUBLES];
79     } TrSPACE;
80 
81 /*
82  * This package maintains some state about itself.  These globals hold
83  * this information.
84  */
85 static int     world_rank = -1;
86 static long    allocated = 0, frags = 0;
87 static TRSPACE *TRhead = 0;
88 static int     TRid = 0;
89 static int     TRlevel = 0;
90 #define MAX_TR_STACK 20
91 static int     TRstack[MAX_TR_STACK];
92 static int     TRstackp = 0;
93 static int     TRdebugLevel = 0;
94 #define TR_MALLOC 0x1
95 #define TR_FREE   0x2
96 
97 /* Used to keep track of allocations */
98 static long    TRMaxMem = 0;
99 static long    TRMaxMemId = 0;
100 /* Used to limit allocation */
101 static long    TRMaxMemAllow = 0;
102 
103 /*+C
104    trinit - Setup the space package.  Only needed for
105    error messages and flags.
106 +*/
trinit(int rank)107 void trinit( int rank )
108 {
109     world_rank = rank;
110 }
111 
112 /*+C
113     trmalloc - Malloc with tracing
114 
115     Input Parameters:
116 .   a   - number of bytes to allocate
117 .   lineno - line number where used.  Use __LINE__ for this
118 .   fname  - file name where used.  Use __FILE__ for this
119 
120     Returns:
121     double aligned pointer to requested storage, or null if not
122     available.
123  +*/
trmalloc(unsigned int a,int lineno,char * fname)124 void *trmalloc( unsigned int a, int lineno, char *fname )
125 {
126     TRSPACE          *head;
127     char             *new;
128     unsigned long    *nend;
129     unsigned int     nsize;
130     int              l;
131 
132     if (TRdebugLevel > 0) {
133 	char buf[256];
134 	sprintf( buf, "Invalid MALLOC arena detected at line %d in %s\n",
135 		 lineno, fname );
136 	if (trvalid( buf )) return 0;
137     }
138 
139     nsize = a;
140     if (nsize & TR_ALIGN_MASK)
141 	nsize += (TR_ALIGN_BYTES - (nsize & TR_ALIGN_MASK));
142     if ((long)(allocated + nsize) > TRMaxMemAllow && TRMaxMemAllow) {
143 	/* Return a null when memory would be exhausted */
144 	fprintf( stderr, "Exceeded allowed memory! \n" );
145 	return 0;
146     }
147 
148     new = malloc( (unsigned)( nsize + sizeof(TrSPACE) + sizeof(unsigned long) ) );
149     if (!new) return 0;
150 
151     memset( new, 0xfc, nsize + sizeof(TrSPACE) + sizeof(unsigned long) );
152     head = (TRSPACE *)new;
153     new  += sizeof(TrSPACE);
154 
155     if (TRhead)
156 	TRhead->prev = head;
157     head->next     = TRhead;
158     TRhead         = head;
159     head->prev     = 0;
160     head->size     = nsize;
161     head->id       = TRid;
162     head->lineno   = lineno;
163     if ((l = strlen( fname )) > TR_FNAME_LEN-1 ) fname += (l - (TR_FNAME_LEN-1));
164     strncpy( head->fname, fname, (TR_FNAME_LEN-1) );
165     head->fname[TR_FNAME_LEN-1]= 0;
166     head->cookie   = COOKIE_VALUE;
167     nend           = (unsigned long *)(new + nsize);
168     nend[0]        = COOKIE_VALUE;
169 
170     allocated += nsize;
171     if (allocated > TRMaxMem) {
172 	TRMaxMem   = allocated;
173 	TRMaxMemId = TRid;
174     }
175     frags     ++;
176 
177     if (TRlevel & TR_MALLOC)
178 	fprintf( stderr, "[%d] Allocating %d bytes at %lx in %s:%d\n",
179 		 world_rank, a, (long)new, fname, lineno );
180     return (void *)new;
181 }
182 
183 /*+C
184    trfree - Free with tracing
185 
186    Input Parameters:
187 .  a    - pointer to a block allocated with trmalloc
188 .  line - line in file where called
189 .  file - Name of file where called
190  +*/
trfree(void * a_ptr,int line,char * file)191 void trfree( void *a_ptr, int line, char *file )
192 {
193     TRSPACE  *head;
194     char     *ahead;
195     char     *a = (char *)a_ptr;
196     unsigned long *nend;
197     int      l, nset;
198 
199 /* Don't try to handle empty blocks */
200     if (!a) return;
201 
202     if (TRdebugLevel > 0) {
203 	if (trvalid( "Invalid MALLOC arena detected by FREE" )) return;
204     }
205 
206     ahead = a;
207     a     = a - sizeof(TrSPACE);
208     head  = (TRSPACE *)a;
209     if (head->cookie != COOKIE_VALUE) {
210 	/* Damaged header */
211 	fprintf( stderr, "[%d] Block at address %lx is corrupted; cannot free;\n\
212 may be block not allocated with trmalloc or MALLOC\n\
213 called in %s at line %d\n", world_rank, (long)a, file, line );
214 	return;
215     }
216     nend = (unsigned long *)(ahead + head->size);
217 /* Check that nend is properly aligned */
218     if ((sizeof(long) == 4 && ((long)nend & 0x3) != 0) ||
219 	(sizeof(long) == 8 && ((long)nend & 0x7) != 0)) {
220 	fprintf( stderr,
221  "[%d] Block at address %lx is corrupted (invalid address or header)\n\
222 called in %s at line %d\n", world_rank, (long)a + sizeof(TrSPACE),
223 		 file, line );
224 	return;
225     }
226     if (*nend != COOKIE_VALUE) {
227 	if (*nend == ALREADY_FREED) {
228 	    fprintf( stderr,
229 		     "[%d] Block [id=%d(%lu)] at address %lx was already freed\n",
230 		     world_rank, head->id, head->size, (long)a + sizeof(TrSPACE) );
231 	    head->fname[TR_FNAME_LEN-1]	  = 0;  /* Just in case */
232 	    head->freed_fname[TR_FNAME_LEN-1] = 0;  /* Just in case */
233 	    fprintf( stderr,
234 		     "[%d] Block freed in %s[%d]\n", world_rank, head->freed_fname,
235 		     head->freed_lineno );
236 	    fprintf( stderr,
237 		     "[%d] Block allocated at %s[%d]\n",
238 		     world_rank, head->fname, head->lineno );
239 	    return;
240 	}
241 	else {
242 	    /* Damaged tail */
243 	    fprintf( stderr,
244 		     "[%d] Block [id=%d(%lu)] at address %lx is corrupted (probably write past end)\n",
245 		     world_rank, head->id, head->size, (long)a );
246 	    head->fname[TR_FNAME_LEN-1]= 0;  /* Just in case */
247 	    fprintf( stderr,
248 		     "[%d] Block allocated in %s[%d]\n", world_rank,
249 		     head->fname, head->lineno );
250 	}
251     }
252 /* Mark the location freed */
253     *nend		   = ALREADY_FREED;
254     head->freed_lineno = line;
255     if ((l = strlen( file )) > TR_FNAME_LEN-1 ) file += (l - (TR_FNAME_LEN-1));
256     strncpy( head->freed_fname, file, (TR_FNAME_LEN-1) );
257 
258     allocated -= head->size;
259     frags     --;
260     if (head->prev)
261 	head->prev->next = head->next;
262     else
263 	TRhead = head->next;
264 
265     if (head->next)
266 	head->next->prev = head->prev;
267     if (TRlevel & TR_FREE)
268 	fprintf( stderr, "[%d] Freeing %lu bytes at %lx in %s:%d\n",
269 		 world_rank, head->size, (long)a + sizeof(TrSPACE),
270 		 file, line );
271 
272     /*
273        Now, scrub the data (except possibly the first few ints) to
274        help catch access to already freed data
275      */
276     nset = head->size -  2 * sizeof(int);
277     if (nset > 0)
278 	memset( ahead + 2 * sizeof(int), 0xda, nset );
279     free( a );
280 }
281 
282 /*+C
283    trvalid - test the allocated blocks for validity.  This can be used to
284    check for memory overwrites.
285 
286    Input Parameter:
287 .  str - string to write out only if an error is detected.
288 
289    Return value:
290    The number of errors detected.
291 
292    Output Effect:
293    Error messages are written to stdout.  These have the form of either
294 
295 $   Block [id=%d(%d)] at address %lx is corrupted (probably write past end)
296 $   Block allocated in <filename>[<linenumber>]
297 
298    if the sentinal at the end of the block has been corrupted, and
299 
300 $   Block at address %lx is corrupted
301 
302    if the sentinal at the begining of the block has been corrupted.
303 
304    The address is the actual address of the block.  The id is the
305    value of TRID.
306 
307    No output is generated if there are no problems detected.
308 +*/
trvalid(char * str)309 int trvalid( char *str )
310 {
311     TRSPACE *head;
312     char    *a;
313     unsigned long *nend;
314     int     errs = 0;
315 
316     head = TRhead;
317     while (head) {
318 	if (head->cookie != COOKIE_VALUE) {
319 	    if (!errs) fprintf( stderr, "%s\n", str );
320 	    errs++;
321 	    fprintf( stderr, "[%d] Block at address %lx is corrupted\n",
322 		     world_rank, (long)head );
323 	    /* Must stop because if head is invalid, then the data in the
324 	       head is probably also invalid, and using could lead to SEGV
325 	       or BUS
326 	    */
327 	    return errs;
328 	}
329 	a    = (char *)(((TrSPACE*)head) + 1);
330 	nend = (unsigned long *)(a + head->size);
331 	if (nend[0] != COOKIE_VALUE) {
332 	    if (!errs) fprintf( stderr, "%s\n", str );
333 	    errs++;
334 	    head->fname[TR_FNAME_LEN-1]= 0;  /* Just in case */
335 	    fprintf( stderr,
336 		     "[%d] Block [id=%d(%lu)] at address %lx is corrupted (probably write past end)\n",
337 		     world_rank, head->id, head->size, (long)a );
338 	    fprintf( stderr,
339 		     "[%d] Block allocated in %s[%d]\n",
340 		     world_rank, head->fname, head->lineno );
341 	    fprintf( stderr,
342 		     "[%d] Expected %x, read %lx\n",
343 		     world_rank, COOKIE_VALUE, nend[0] );
344 	}
345 	head = head->next;
346     }
347     return errs;
348 }
349 
350 /*+C
351    trspace - Return space statistics
352 
353    Output parameters:
354 .   space - number of bytes currently allocated
355 .   frags - number of blocks currently allocated
356  +*/
trspace(int * space,int * fr)357 void trspace( int *space, int *fr )
358 {
359     /* We use ints because systems without prototypes will usually
360        allow calls with ints instead of longs, leading to unexpected
361        behavior */
362     *space = (int)allocated;
363     *fr    = (int)frags;
364 }
365 
366 /*+C
367   trdump - Dump the allocated memory blocks to a file
368 
369   Input Parameter:
370 .  fp  - file pointer.  If fp is NULL, stderr is assumed.
371  +*/
trdump(FILE * fp)372 void trdump( FILE *fp )
373 {
374     TRSPACE *head;
375     int     id;
376 
377     if (fp == 0) fp = stderr;
378     head = TRhead;
379     while (head) {
380 	fprintf( fp, "[%d] %lu at [%lx], id = ",
381 		 world_rank, head->size, (long)head + sizeof(TrSPACE) );
382 	if (head->id >= 0) {
383 	    head->fname[TR_FNAME_LEN-1] = 0;
384 	    fprintf( fp, "%d %s[%d]\n",
385 		     head->id, head->fname, head->lineno );
386 	}
387 	else {
388 	    /* Decode the package values */
389 	    head->fname[TR_FNAME_LEN-1] = 0;
390 	    id = head->id;
391 	    fprintf( fp, "%d %s[%d]\n",
392 		     id, head->fname, head->lineno );
393 	}
394 	head = head->next;
395     }
396 /*
397     fprintf( fp, "# [%d] The maximum space allocated was %ld bytes [%ld]\n",
398 	     world_rank, TRMaxMem, TRMaxMemId );
399  */
400 }
401 
402 /* Configure will set HAVE_SEARCH for these systems.  We assume that
403    the system does NOT have search.h unless otherwise noted.
404    The otherwise noted lets the non-configure approach work on our
405    two major systems */
406 #if defined(HAVE_SEARCH_H)
407 
408 /*
409    Old test ...
410    (!defined(__MSDOS__) && !defined(fx2800) && !defined(tc2000) && \
411     !defined(NeXT) && !defined(c2mp) && !defined(intelnx) && !defined(BSD386))
412  */
413 /* The following routine uses the tsearch routines to summarize the
414    memory heap by id */
415 /* rs6000 and paragon needs _XOPEN_SOURCE to use tsearch */
416 #if (defined(rs6000) || defined(paragon)) && !defined(_XOPEN_SOURCE)
417 #define _NO_PROTO
418 #define _XOPEN_SOURCE
419 #endif
420 #if defined(HPUX) && !defined(_INCLUDE_XOPEN_SOURCE)
421 #define _INCLUDE_XOPEN_SOURCE
422 #endif
423 #include <search.h>
424 typedef struct { int id, size, lineno; char *fname; } TRINFO;
IntCompare(TRINFO * a,TRINFO * b)425 static int IntCompare( TRINFO *a, TRINFO *b )
426 {
427     return a->id - b->id;
428 }
429 static FILE *TRFP;
430 /*ARGSUSED*/
PrintSum(TRINFO ** a,VISIT order,int level)431 static void PrintSum( TRINFO **a, VISIT order, int level )
432 {
433     if (order == postorder || order == leaf)
434 	fprintf( TRFP, "[%d]%s[%d] has %d\n",
435 		 (*a)->id, (*a)->fname, (*a)->lineno, (*a)->size );
436 }
437 
438 /*+C
439   trSummary - Summarize the allocate memory blocks by id
440 
441   Input Parameter:
442 .  fp  - file pointer
443 
444   Note:
445   This routine is the same as trDump on those systems that do not include
446   /usr/include/search.h .
447  +*/
trSummary(FILE * fp)448 void trSummary( FILE *fp )
449 {
450 TRSPACE *head;
451 TRINFO  *root, *key, **fnd;
452 TRINFO  nspace[1000];
453 
454 root = 0;
455 head = TRhead;
456 key  = nspace;
457 while (head) {
458     key->id     = head->id;
459     key->size   = 0;
460     key->lineno = head->lineno;
461     key->fname  = head->fname;
462 #if !defined(IRIX) && !defined(solaris) && !defined(HPUX) && !defined(rs6000)
463     fnd    = (TRINFO **)tsearch( (char *) key, (char **) &root, IntCompare );
464 #else
465     fnd    = (TRINFO **)tsearch( (void *) key, (void **) &root,
466 				 (int (*)())IntCompare );
467 #endif
468     if (*fnd == key) {
469 	key->size = 0;
470 	key++;
471 	}
472     (*fnd)->size += head->size;
473     head = head->next;
474     }
475 
476 /* Print the data */
477 TRFP = fp;
478 twalk( (char *)root, (void (*)())PrintSum );
479 /*
480 fprintf( fp, "# [%d] The maximum space allocated was %d bytes [%d]\n",
481 	 world_rank, TRMaxMem, TRMaxMemId );
482 	 */
483 }
484 #else
trSummary(FILE * fp)485 void trSummary( FILE *fp )
486 {
487 fprintf( fp, "# [%d] The maximum space allocated was %ld bytes [%ld]\n",
488 	 world_rank, TRMaxMem, TRMaxMemId );
489 }
490 #endif
491 
492 /*+
493   trid - set an "id" field to be used with each fragment
494  +*/
trid(int id)495 void trid( int id )
496 {
497     TRid = id;
498 }
499 
500 /*+C
501   trlevel - Set the level of output to be used by the tracing routines
502 
503   Input Parameters:
504 . level = 0 - notracing
505 . level = 1 - trace mallocs
506 . level = 2 - trace frees
507 
508   Note:
509   You can add levels together to get combined tracing.
510  +*/
trlevel(int level)511 void trlevel( int level )
512 {
513     TRlevel = level;
514 }
515 
516 /*+C
517    trpush - Push an "id" value for the tracing space routines
518 
519    Input Parameters:
520 .  a      - value to push
521 +*/
trpush(int a)522 void trpush( int a )
523 {
524 if (TRstackp < MAX_TR_STACK - 1)
525     TRstack[++TRstackp] = a;
526 TRid = a;
527 }
528 
529 /*+C
530   trpop - Pop an "id" value for the tracing space routines
531 +*/
trpop(void)532 void trpop( void )
533 {
534 if (TRstackp > 1) {
535     TRstackp--;
536     TRid = TRstack[TRstackp];
537     }
538 else
539     TRid = 0;
540 }
541 
542 /*+C
543     trDebugLevel - set the level of debugging for the space management routines
544 
545     Input Parameter:
546 .   level - level of debugging.  Currently, either 0 (no checking) or 1
547     (use trvalid at each trmalloc or trfree).
548 +*/
trDebugLevel(int level)549 void trDebugLevel( int level )
550 {
551     TRdebugLevel = level;
552 }
553 
554 /*+C
555     trcalloc - Calloc with tracing
556 
557     Input Parameters:
558 .   nelem  - number of elements to allocate
559 .   elsize - size of each element
560 .   lineno - line number where used.  Use __LINE__ for this
561 .   fname  - file name where used.  Use __FILE__ for this
562 
563     Returns:
564     Double aligned pointer to requested storage, or null if not
565     available.
566  +*/
trcalloc(unsigned nelem,unsigned elsize,int lineno,char * fname)567 void *trcalloc( unsigned nelem, unsigned elsize, int lineno, char *fname )
568 {
569 void *p;
570 
571 p = trmalloc( (unsigned)(nelem*elsize), lineno, fname );
572 if (p) {
573     memset(p,0,nelem*elsize);
574     }
575 return p;
576 }
577 
578 /*+C
579     trrealloc - Realloc with tracing
580 
581     Input Parameters:
582 .   p      - pointer to old storage
583 .   size   - number of bytes to allocate
584 .   lineno - line number where used.  Use __LINE__ for this
585 .   fname  - file name where used.  Use __FILE__ for this
586 
587     Returns:
588     Double aligned pointer to requested storage, or null if not
589     available.  This implementation ALWAYS allocates new space and copies
590     the contents into the new space.
591  +*/
trrealloc(void * p,int size,int lineno,char * fname)592 void *trrealloc( void *p, int size, int lineno, char *fname )
593 {
594     void    *pnew;
595     char    *pa;
596     int     nsize;
597     TRSPACE *head;
598 
599 /* We should really use the size of the old block... */
600     pa   = (char *)p;
601     head = (TRSPACE *)(pa - sizeof(TrSPACE));
602     if (head->cookie != COOKIE_VALUE) {
603 	/* Damaged header */
604 	fprintf( stderr, "[%d] Block at address %lx is corrupted; cannot realloc;\n\
605 may be block not allocated with trmalloc or MALLOC\n",
606 		 world_rank, (long)pa );
607 	return 0;
608     }
609 
610     pnew = trmalloc( (unsigned)size, lineno, fname );
611     if (!pnew) return p;
612 
613     nsize = size;
614     if (head->size < (unsigned long)nsize) nsize = (int)(head->size);
615     memcpy( pnew, p, nsize );
616     trfree( p, lineno, fname );
617     return pnew;
618 }
619 
620 /*+C
621     trstrdup - Strdup with tracing
622 
623     Input Parameters:
624 .   str    - string to duplicate
625 .   lineno - line number where used.  Use __LINE__ for this
626 .   fname  - file name where used.  Use __FILE__ for this
627 
628     Returns:
629     Pointer to copy of the input string.
630  +*/
trstrdup(const char * str,int lineno,const char * fname)631 char *trstrdup( const char *str, int lineno, const char *fname )
632 {
633     char *p;
634     unsigned len = strlen( str ) + 1;
635 
636     p = (char *)trmalloc( len, lineno, (char *)fname );
637     if (p) {
638 	memcpy( p, str, len );
639 	trvalid( "memcpy broke string in strdup!" );
640     }
641     return p;
642 }
643 
644 
645 #define TR_MAX_DUMP 100
646 /*
647    The following routine attempts to give useful information about the
648    memory usage when an "out-of-memory" error is encountered.  The rules are:
649    If there are less than TR_MAX_DUMP blocks, output those.
650    Otherwise, try to find multiple instances of the same routine/line #, and
651    print a summary by number:
652    file line number-of-blocks total-number-of-blocks
653 
654    We have to do a sort-in-place for this
655  */
656 
657 /*
658   Sort by file/line number.  Do this without calling a system routine or
659   allocating ANY space (space is being optimized here).
660 
661   We do this by first recursively sorting halves of the list and then
662   merging them.
663  */
664 /* Forward refs for these local routines */
665 TRSPACE *trImerge ANSI_ARGS(( TRSPACE *, TRSPACE * ));
666 TRSPACE *trIsort  ANSI_ARGS(( TRSPACE *, int ));
667 void trSortBlocks ANSI_ARGS(( void ));
668 
669 /* Merge two lists, returning the head of the merged list */
trImerge(TRSPACE * l1,TRSPACE * l2)670 TRSPACE *trImerge( TRSPACE *l1, TRSPACE *l2 )
671 {
672 TRSPACE *head = 0, *tail = 0;
673 int     sign;
674 while (l1 && l2) {
675     sign = strcmp(l1->fname, l2->fname);
676     if (sign > 0 || (sign == 0 && l1->lineno >= l2->lineno)) {
677 	if (head) tail->next = l1;
678 	else      head = tail = l1;
679 	tail = l1;
680 	l1   = l1->next;
681 	}
682     else {
683 	if (head) tail->next = l2;
684 	else      head = tail = l2;
685 	tail = l2;
686 	l2   = l2->next;
687 	}
688     }
689 /* Add the remaining elements to the end */
690 if (l1) tail->next = l1;
691 if (l2) tail->next = l2;
692 
693 return head;
694 }
695 /* Sort head with n elements, returning the head */
trIsort(TRSPACE * head,int n)696 TRSPACE *trIsort( TRSPACE *head, int n )
697 {
698 TRSPACE *p, *l1, *l2;
699 int     m, i;
700 
701 if (n <= 1) return head;
702 
703 /* This guarentees that m, n are both > 0 */
704 m = n / 2;
705 p = head;
706 for (i=0; i<m-1; i++) p = p->next;
707 /* p now points to the END of the first list */
708 l2 = p->next;
709 p->next = 0;
710 l1 = trIsort( head, m );
711 l2 = trIsort( l2,   n - m );
712 return trImerge( l1, l2 );
713 }
714 
trSortBlocks(void)715 void trSortBlocks( void )
716 {
717 TRSPACE *head;
718 int     cnt;
719 
720 head = TRhead;
721 cnt  = 0;
722 while (head) {
723     cnt ++;
724     head = head->next;
725     }
726 TRhead = trIsort( TRhead, cnt );
727 }
728 
729 /* Takes sorted input and dumps as an aggregate */
trdumpGrouped(FILE * fp)730 void trdumpGrouped( FILE *fp )
731 {
732 TRSPACE *head, *cur;
733 int     nblocks, nbytes;
734 
735 if (fp == 0) fp = stderr;
736 
737 trSortBlocks();
738 head = TRhead;
739 cur  = 0;
740 while (head) {
741     cur     = head->next;
742     nblocks = 1;
743     nbytes  = (int)head->size;
744     while (cur && strcmp(cur->fname,head->fname) == 0 &&
745 	   cur->lineno == head->lineno ) {
746 	nblocks++;
747 	nbytes += (int)cur->size;
748 	cur    = cur->next;
749 	}
750     fprintf( fp, "[%d] File %13s line %5d: %d bytes in %d allocation%c\n",
751 	     world_rank, head->fname, head->lineno, nbytes, nblocks,
752 	     (nblocks > 1) ? 's' : ' ' );
753     head = cur;
754     }
755 fflush( fp );
756 }
757 
TrSetMaxMem(int size)758 void TrSetMaxMem( int size )
759 {
760     TRMaxMemAllow = size;
761 }
762 
TrInit(void)763 void TrInit( void )
764 {
765     char *p;
766     char *v;
767 
768     /* This is a special entry point that checks an environment variable
769        for the debug level.  This routine does not *need* to be called,
770        but it can help */
771 
772     v = getenv("TR_VERBOSE");
773     if (getenv("TR_DEBUG")) {
774 	trDebugLevel( 1 );
775 	if (v && strcmp(v,"yes")) printf( "Setting trDebugLevel to 1\n" );
776     }
777     p = getenv( "TR_LEVEL" );
778     if (p && *p) {
779 	int level = atoi(p);
780 	if (level >= 0 && level <= 3) {
781 	    trlevel( level );
782 	    if (v && strcmp(v,"yes")) printf( "Setting trlevel to 1\n" );
783 	}
784     }
785 }
786