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