1 /* ----------------------------------------------------------------------------
2 @COPYRIGHT  :
3               Copyright 1993,1994,1995 David MacDonald,
4               McConnell Brain Imaging Centre,
5               Montreal Neurological Institute, McGill University.
6               Permission to use, copy, modify, and distribute this
7               software and its documentation for any purpose and without
8               fee is hereby granted, provided that the above copyright
9               notice appear in all copies.  The author and McGill University
10               make no representations about the suitability of this
11               software for any purpose.  It is provided "as is" without
12               express or implied warranty.
13 ---------------------------------------------------------------------------- */
14 
15 #include  <internal_volume_io.h>
16 
17 /* ----------------------------- MNI Header -----------------------------------
18 @NAME       : alloc_check.c
19 @INPUT      :
20 @OUTPUT     :
21 @RETURNS    :
22 @DESCRIPTION: Maintains a skiplist structure to list all memory allocated,
23             : and check for errors such as freeing a pointer twice or
24             : overlapping allocations.
25 @METHOD     :
26 @GLOBALS    :
27 @CALLS      :
28 @CREATED    :                      David MacDonald
29 @MODIFIED   :
30 ---------------------------------------------------------------------------- */
31 
32 #define  MAX_SKIP_LEVELS   50
33 #define  SKIP_P            0.5
34 
35 #define  MEMORY_DIFFERENCE  1000000
36 
37 typedef  struct skip_entry
38 {
39     void                    *ptr;
40     size_t                  n_bytes;
41     STRING                  source_file;
42     int                     line_number;
43     int                     sequence_number;
44     struct  skip_entry      *forward[1];
45 } skip_entry;
46 
47 typedef  struct
48 {
49     size_t         next_memory_threshold;
50     size_t         total_memory_allocated;
51     skip_entry     *header;
52     int            level;
53 } alloc_struct;
54 
55 typedef  struct
56 {
57     skip_entry   *update[MAX_SKIP_LEVELS];
58 } update_struct;
59 
60 static  void     update_total_memory( alloc_struct *, size_t );
61 static  int      get_random_level( void );
62 static  void     output_entry( FILE *, skip_entry * );
63 static  BOOLEAN  size_display_enabled( void );
64 static  size_t   skip_alloc_size = 0;
65 
66 typedef  void      *alloc_ptr;
67 
68 #define  ALLOC_SKIP_STRUCT( ptr, n_level )                                    \
69      (ptr) = (skip_entry *) malloc(                                          \
70           (sizeof(skip_entry)+((size_t)(n_level)-1) * sizeof(skip_entry *)) );
71 
72 /* ----------------------------- MNI Header -----------------------------------
73 @NAME       : initialize_alloc_list
74 @INPUT      : alloc_list
75 @OUTPUT     :
76 @RETURNS    :
77 @DESCRIPTION: Initializes the allocation list to empty.
78 @METHOD     :
79 @GLOBALS    :
80 @CALLS      :
81 @CREATED    :                      David MacDonald
82 @MODIFIED   :
83 ---------------------------------------------------------------------------- */
84 
initialize_alloc_list(alloc_struct * alloc_list)85 static   void  initialize_alloc_list(
86     alloc_struct  *alloc_list )
87 {
88     int   i;
89 
90     alloc_list->next_memory_threshold = MEMORY_DIFFERENCE;
91     alloc_list->total_memory_allocated = 0;
92 
93     ALLOC_SKIP_STRUCT( alloc_list->header, MAX_SKIP_LEVELS );
94     skip_alloc_size += sizeof(skip_entry)+(MAX_SKIP_LEVELS-1) *
95                        sizeof(skip_entry *);
96     alloc_list->level = 1;
97 
98     for_less( i, 0, MAX_SKIP_LEVELS )
99         alloc_list->header->forward[i] = (skip_entry *) 0;
100 }
101 
102 /* ----------------------------- MNI Header -----------------------------------
103 @NAME       : check_initialized_alloc_list
104 @INPUT      : alloc_list
105 @OUTPUT     :
106 @RETURNS    :
107 @DESCRIPTION: Checks to make sure the allocation list is initialized.
108 @METHOD     :
109 @GLOBALS    :
110 @CALLS      :
111 @CREATED    : 1993            David MacDonald
112 @MODIFIED   :
113 ---------------------------------------------------------------------------- */
114 
check_initialized_alloc_list(alloc_struct * alloc_list)115 static  void  check_initialized_alloc_list(
116     alloc_struct  *alloc_list )
117 {
118     static   BOOLEAN  first = TRUE;
119 
120     if( first )
121     {
122         first = FALSE;
123         initialize_alloc_list( alloc_list );
124     }
125 }
126 
127 /* ----------------------------- MNI Header -----------------------------------
128 @NAME       : find_pointer_position
129 @INPUT      : alloc_list
130             : ptr
131 @OUTPUT     : update
132 @RETURNS    : TRUE if found
133 @DESCRIPTION: Searches the alloc_list for the given ptr, and sets the update
134             : struct so that it can provide an insert.
135 @METHOD     :
136 @GLOBALS    :
137 @CALLS      :
138 @CREATED    :                      David MacDonald
139 @MODIFIED   :
140 ---------------------------------------------------------------------------- */
141 
find_pointer_position(alloc_struct * alloc_list,void * ptr,update_struct * update)142 static  BOOLEAN  find_pointer_position(
143     alloc_struct    *alloc_list,
144     void            *ptr,
145     update_struct   *update )
146 {
147     int           i;
148     skip_entry    *x;
149     BOOLEAN       found;
150 
151     x = alloc_list->header;
152 
153     for( i = alloc_list->level-1;  i >= 0;  --i )
154     {
155         while( x->forward[i] != NULL && (void *) x->forward[i]->ptr < ptr )
156         {
157             x = x->forward[i];
158         }
159         update->update[i] = x;
160     }
161 
162     x = update->update[0]->forward[0];
163 
164     found = (x != NULL) && (x->ptr == ptr);
165 
166     return( found );
167 }
168 
169 /* ----------------------------- MNI Header -----------------------------------
170 @NAME       : insert_ptr_in_alloc_list
171 @INPUT      : alloc_list
172             : update           - the set of pointers indicating where to insert
173             : ptr              }
174             : n_bytes          }}
175             : source_file      }}} these are recorded in the list
176             : line_number      }}
177             : sequence_number    }
178 @OUTPUT     :
179 @RETURNS    :
180 @DESCRIPTION: Records the allocated pointer in the allocation list.
181 @METHOD     :
182 @GLOBALS    :
183 @CALLS      :
184 @CREATED    :                      David MacDonald
185 @MODIFIED   :
186 ---------------------------------------------------------------------------- */
187 
insert_ptr_in_alloc_list(alloc_struct * alloc_list,update_struct * update,void * ptr,size_t n_bytes,STRING source_file,int line_number,int sequence_number)188 static   void  insert_ptr_in_alloc_list(
189     alloc_struct   *alloc_list,
190     update_struct  *update,
191     void           *ptr,
192     size_t         n_bytes,
193     STRING         source_file,
194     int            line_number,
195     int            sequence_number )
196 {
197     int           i, new_level;
198     skip_entry    *x;
199 
200     new_level = get_random_level();
201 
202     if( new_level > alloc_list->level )
203     {
204         for( i = alloc_list->level;  i < new_level;  ++i )
205             update->update[i] = alloc_list->header;
206 
207         alloc_list->level = new_level;
208     }
209 
210     ALLOC_SKIP_STRUCT( x, new_level );
211     skip_alloc_size += sizeof(skip_entry)+((size_t)new_level-1) *
212                        sizeof(skip_entry *);
213 
214     x->ptr = ptr;
215     x->n_bytes = n_bytes;
216     x->source_file = source_file;
217     x->line_number = line_number;
218     x->sequence_number = sequence_number;
219     update_total_memory( alloc_list, n_bytes );
220 
221     for( i = 0;  i < new_level;  ++i )
222     {
223         x->forward[i] = update->update[i]->forward[i];
224         update->update[i]->forward[i] = x;
225     }
226 }
227 
228 /* ----------------------------- MNI Header -----------------------------------
229 @NAME       : check_overlap
230 @INPUT      : update
231             : ptr
232             : n_bytes
233 @OUTPUT     : entry
234 @RETURNS    : TRUE if an overlap
235 @DESCRIPTION: Checks the new ptr to see if it overlaps with the previous and
236             : following memory allocations in the list, and returns the result.
237 @METHOD     :
238 @GLOBALS    :
239 @CALLS      :
240 @CREATED    :                      David MacDonald
241 @MODIFIED   :
242 ---------------------------------------------------------------------------- */
243 
check_overlap(alloc_struct * alloc_list,update_struct * update,void * ptr,size_t n_bytes,skip_entry ** entry)244 static  BOOLEAN  check_overlap(
245     alloc_struct       *alloc_list,
246     update_struct      *update,
247     void               *ptr,
248     size_t             n_bytes,
249     skip_entry         **entry )
250 {
251     BOOLEAN      overlap;
252 
253     overlap = FALSE;
254 
255     *entry = update->update[0];
256 
257     if( *entry != alloc_list->header && *entry != (skip_entry *) 0 )
258     {
259         if( (void *) ((char *) (*entry)->ptr + (*entry)->n_bytes) > ptr )
260              overlap = TRUE;
261         else
262         {
263             (*entry) = (*entry)->forward[0];
264             if( *entry != (skip_entry *) 0 &&
265                 (void *) ((char*)ptr + n_bytes) > (*entry)->ptr )
266                 overlap = TRUE;
267         }
268     }
269 
270     return( overlap );
271 }
272 
273 /* ----------------------------- MNI Header -----------------------------------
274 @NAME       : remove_ptr_from_alloc_list
275 @INPUT      : alloc_list
276             : ptr
277 @OUTPUT     : source_file
278             : line_number
279             : sequence_number
280 @RETURNS    : TRUE if it existed
281 @DESCRIPTION: Finds and deletes the entry in the skip list associated with
282             : ptr, and returns the information associated with the entry
283             : (source_file, line_number, sequence_number).
284 @METHOD     :
285 @GLOBALS    :
286 @CALLS      :
287 @CREATED    :                      David MacDonald
288 @MODIFIED   :
289 ---------------------------------------------------------------------------- */
290 
remove_ptr_from_alloc_list(alloc_struct * alloc_list,void * ptr,STRING * source_file,int * line_number,int * sequence_number)291 static   BOOLEAN  remove_ptr_from_alloc_list(
292     alloc_struct   *alloc_list,
293     void           *ptr,
294     STRING         *source_file,
295     int            *line_number,
296     int            *sequence_number )
297 {
298     int           i;
299     BOOLEAN       found;
300     skip_entry    *x;
301     update_struct update;
302 
303     found = find_pointer_position( alloc_list, ptr, &update );
304 
305     if( found )
306     {
307         x = update.update[0]->forward[0];
308 
309         *source_file = x->source_file;
310         *line_number = x->line_number;
311         *sequence_number = x->sequence_number;
312 
313         update_total_memory( alloc_list, -x->n_bytes );
314 
315         for( i = 0;  i < alloc_list->level;  ++i )
316         {
317             if( update.update[i]->forward[i] != x )
318                 break;
319             update.update[i]->forward[i] = x->forward[i];
320         }
321 
322         skip_alloc_size -= sizeof(skip_entry) +
323                            (size_t) (i-1) * sizeof(skip_entry *);
324 
325         free( (alloc_ptr) x );
326 
327         while( alloc_list->level > 1 &&
328                alloc_list->header->forward[alloc_list->level-1] ==
329                     (skip_entry *) 0 )
330         {
331             --alloc_list->level;
332         }
333     }
334 
335     return( found );
336 }
337 
338 /* ----------------------------- MNI Header -----------------------------------
339 @NAME       : get_random_0_to_1
340 @INPUT      :
341 @OUTPUT     :
342 @RETURNS    : random number
343 @DESCRIPTION: Returns a random number >= 0 and < 1.
344 @METHOD     :
345 @GLOBALS    :
346 @CALLS      :
347 @CREATED    : 1993            David MacDonald
348 @MODIFIED   :
349 ---------------------------------------------------------------------------- */
350 
get_random_0_to_1(void)351 static  Real  get_random_0_to_1( void )
352 {
353     return( (Real) rand() );
354 }
355 
356 /* ----------------------------- MNI Header -----------------------------------
357 @NAME       : get_random_level
358 @INPUT      :
359 @OUTPUT     :
360 @RETURNS    : a random level between 1 and MAX_LEVELS
361 @DESCRIPTION: Determines a random level with exponential probability of higher
362             : levels.
363 @METHOD     :
364 @GLOBALS    :
365 @CALLS      :
366 @CREATED    :                      David MacDonald
367 @MODIFIED   :
368 ---------------------------------------------------------------------------- */
369 
get_random_level(void)370 static  int  get_random_level( void )
371 {
372     int    level;
373 
374     level = 1;
375 
376     while( get_random_0_to_1() < SKIP_P && level < MAX_SKIP_LEVELS )
377         ++level;
378 
379     return( level );
380 }
381 
382 /* ----------------------------- MNI Header -----------------------------------
383 @NAME       : memory_still_alloced
384 @INPUT      : alloc_list
385 @OUTPUT     :
386 @RETURNS    : TRUE or FALSE
387 @DESCRIPTION: Decides if any memory is still alloced, thus checking for
388               memory leaks.
389 @METHOD     :
390 @GLOBALS    :
391 @CALLS      :
392 @CREATED    :                      David MacDonald
393 @MODIFIED   :
394 ---------------------------------------------------------------------------- */
395 
memory_still_alloced(alloc_struct * alloc_list)396 static  BOOLEAN  memory_still_alloced(
397     alloc_struct  *alloc_list )
398 {
399     return( alloc_list->header->forward[0] != (skip_entry *) NULL );
400 }
401 
402 /* ----------------------------- MNI Header -----------------------------------
403 @NAME       : output_alloc_list
404 @INPUT      : file
405             : alloc_list
406 @OUTPUT     :
407 @RETURNS    :
408 @DESCRIPTION: Outputs the list of allocated memory to the file.
409 @METHOD     :
410 @GLOBALS    :
411 @CALLS      :
412 @CREATED    :                      David MacDonald
413 @MODIFIED   :
414 ---------------------------------------------------------------------------- */
415 
output_alloc_list(FILE * file,alloc_struct * alloc_list)416 static  void  output_alloc_list(
417     FILE          *file,
418     alloc_struct  *alloc_list )
419 {
420     skip_entry  *ptr;
421 
422     ptr = alloc_list->header->forward[0];
423 
424     while( ptr != (skip_entry *) 0 )
425     {
426         output_entry( file, ptr );
427         ptr = ptr->forward[0];
428     }
429 }
430 
431 /* ----------------------------- MNI Header -----------------------------------
432 @NAME       : update_total_memory
433 @INPUT      : alloc_list
434             : n_bytes
435 @OUTPUT     :
436 @RETURNS    :
437 @DESCRIPTION: Adds n_bytes to the size of memory recorded.
438 @METHOD     :
439 @GLOBALS    :
440 @CALLS      :
441 @CREATED    :                      David MacDonald
442 @MODIFIED   :
443 ---------------------------------------------------------------------------- */
444 
update_total_memory(alloc_struct * alloc_list,size_t n_bytes)445 static  void  update_total_memory(
446     alloc_struct  *alloc_list,
447     size_t        n_bytes )
448 {
449     alloc_list->total_memory_allocated += n_bytes;
450 
451     if( size_display_enabled() &&
452         alloc_list->total_memory_allocated >
453         alloc_list->next_memory_threshold )
454     {
455         alloc_list->next_memory_threshold = MEMORY_DIFFERENCE *
456                 (alloc_list->total_memory_allocated / MEMORY_DIFFERENCE + 1);
457         print( "Memory allocated =%5.1f Megabytes  (Overhead = %5.1f Mb)\n",
458                 (Real) alloc_list->total_memory_allocated / 1000000.0,
459                 (Real) skip_alloc_size / 1000000.0 );
460     }
461 }
462 
463 /* ----------------------------- MNI Header -----------------------------------
464 @NAME       : print_source_location
465 @INPUT      : source_file
466             : line_number
467             : sequence_number
468 @OUTPUT     :
469 @RETURNS    :
470 @DESCRIPTION: Prints the information about a particular allocation.
471 @METHOD     :
472 @GLOBALS    :
473 @CALLS      :
474 @CREATED    :                      David MacDonald
475 @MODIFIED   :
476 ---------------------------------------------------------------------------- */
477 
print_source_location(STRING source_file,int line_number,int sequence_number)478 static  void  print_source_location(
479     STRING   source_file,
480     int      line_number,
481     int      sequence_number )
482 {
483     print_error( "%s:%d\t%d'th alloc",
484                  source_file, line_number, sequence_number );
485 }
486 
487 /* ----------------------------- MNI Header -----------------------------------
488 @NAME       : output_entry
489 @INPUT      : file
490             : entry
491 @OUTPUT     :
492 @RETURNS    :
493 @DESCRIPTION: Outputs the information about an allocation entry to the file.
494 @METHOD     :
495 @GLOBALS    :
496 @CALLS      :
497 @CREATED    :                      David MacDonald
498 @MODIFIED   :
499 ---------------------------------------------------------------------------- */
500 
output_entry(FILE * file,skip_entry * entry)501 static  void  output_entry(
502     FILE          *file,
503     skip_entry    *entry )
504 {
505     (void) fprintf( file, "%s:%d\t%d'th alloc\n",
506                     entry->source_file,
507                     entry->line_number,
508                     entry->sequence_number );
509 }
510 
511 /*
512 --------------------------------------------------------------------------
513     Routines that are to be called from outside this file
514 --------------------------------------------------------------------------
515 */
516 
517 static   alloc_struct   alloc_list;
518 
519 /* ----------------------------- MNI Header -----------------------------------
520 @NAME       : get_total_memory_alloced
521 @INPUT      :
522 @OUTPUT     :
523 @RETURNS    : size_t  - the number of bytes allocated
524 @DESCRIPTION: Returns the total amount of memory allocated by the program,
525             : not counting that used by the skip list.
526 @METHOD     :
527 @GLOBALS    :
528 @CALLS      :
529 @CREATED    :                      David MacDonald
530 @MODIFIED   :
531 ---------------------------------------------------------------------------- */
532 
get_total_memory_alloced(void)533 VIOAPI  size_t  get_total_memory_alloced( void )
534 {
535     return( alloc_list.total_memory_allocated );
536 }
537 
538 static  BOOLEAN  checking_enabled;
539 static  BOOLEAN  enabled_initialized = FALSE;
540 
541 /* ----------------------------- MNI Header -----------------------------------
542 @NAME       : alloc_checking_enabled
543 @INPUT      :
544 @OUTPUT     :
545 @RETURNS    : TRUE if alloc checking is turned on
546 @DESCRIPTION: Checks an environment variable to see if alloc checking is
547             : not disabled.
548 @METHOD     :
549 @GLOBALS    :
550 @CALLS      :
551 @CREATED    :                      David MacDonald
552 @MODIFIED   :
553 ---------------------------------------------------------------------------- */
554 
alloc_checking_enabled(void)555 VIOAPI  BOOLEAN  alloc_checking_enabled( void )
556 {
557 #ifdef NO_DEBUG_ALLOC
558     return( FALSE );
559 #else
560     if( !enabled_initialized )
561     {
562         set_alloc_checking( ENV_EXISTS( "DEBUG_ALLOC" ) );
563     }
564 
565     return( checking_enabled );
566 #endif
567 }
568 
set_alloc_checking(BOOLEAN state)569 VIOAPI  void  set_alloc_checking( BOOLEAN state )
570 {
571     enabled_initialized = TRUE;
572     checking_enabled = state;
573 }
574 
575 /* ----------------------------- MNI Header -----------------------------------
576 @NAME       : size_display_enabled
577 @INPUT      :
578 @OUTPUT     :
579 @RETURNS    : TRUE if size displaying is turned on
580 @DESCRIPTION: Checks an environment variable to see if memory size display
581             : is disabled.
582 @METHOD     :
583 @GLOBALS    :
584 @CALLS      :
585 @CREATED    :                      David MacDonald
586 @MODIFIED   :
587 ---------------------------------------------------------------------------- */
588 
size_display_enabled(void)589 static  BOOLEAN  size_display_enabled( void )
590 {
591 #ifdef NO_DEBUG_ALLOC
592     return( FALSE );
593 #else
594     static  BOOLEAN  first = TRUE;
595     static  BOOLEAN  enabled;
596 
597     if( first )
598     {
599         enabled = ENV_EXISTS( "ALLOC_SIZE" );
600         first = FALSE;
601     }
602 
603     return( enabled );
604 #endif
605 }
606 
607 /* ----------------------------- MNI Header -----------------------------------
608 @NAME       : get_stop_sequence_number
609 @INPUT      :
610 @OUTPUT     :
611 @RETURNS    : which allocation number
612 @DESCRIPTION: Returns the number at which allocation should stop.  This is
613               used for debugging.  For instance, if an error message indicates
614               a problem with the 100'th alloc of the program, then do a
615               SETENV STOP_ALLOC_AT 100 and run the program from the debugger.
616               It will stop at the requested allocation.
617 @METHOD     :
618 @GLOBALS    :
619 @CALLS      :
620 @CREATED    : 1993            David MacDonald
621 @MODIFIED   :
622 ---------------------------------------------------------------------------- */
623 
get_stop_sequence_number(void)624 static  int  get_stop_sequence_number( void )
625 {
626     static   int   first = TRUE;
627     static   int   stop_sequence_number = -1;
628     STRING         str;
629 
630     if( first )
631     {
632         first = FALSE;
633         str = getenv( "STOP_ALLOC_AT" );
634         if( str == NULL ||
635             sscanf( str, "%d", &stop_sequence_number ) != 1 )
636             stop_sequence_number = -1;
637     }
638 
639     return( stop_sequence_number );
640 }
641 
642 /* ----------------------------- MNI Header -----------------------------------
643 @NAME       : get_current_sequence_number
644 @INPUT      :
645 @OUTPUT     :
646 @RETURNS    : the index of this alloc
647 @DESCRIPTION: Returns the count of how many allocations have been done, so that
648               each allocation can be assigned a value equal to its cardinality
649               in the set of allocations over the life of the program.
650 @METHOD     :
651 @GLOBALS    :
652 @CALLS      :
653 @CREATED    : 1993            David MacDonald
654 @MODIFIED   :
655 ---------------------------------------------------------------------------- */
656 
get_current_sequence_number(void)657 static  int  get_current_sequence_number( void )
658 {
659     static   int  current_sequence_number = 0;
660 
661     ++current_sequence_number;
662 
663     if( current_sequence_number == get_stop_sequence_number() )
664         handle_internal_error( "get_current_sequence_number" );
665 
666     return( current_sequence_number );
667 }
668 
669 /* ----------------------------- MNI Header -----------------------------------
670 @NAME       : record_ptr
671 @INPUT      : ptr
672             : n_bytes
673             : source_file
674             : line_number
675 @OUTPUT     :
676 @RETURNS    :
677 @DESCRIPTION: Records the information about a single allocation in the list.
678 @METHOD     :
679 @GLOBALS    :
680 @CALLS      :
681 @CREATED    :                      David MacDonald
682 @MODIFIED   :
683 ---------------------------------------------------------------------------- */
684 
record_ptr_alloc_check(void * ptr,size_t n_bytes,STRING source_file,int line_number)685 VIOAPI  void  record_ptr_alloc_check(
686     void      *ptr,
687     size_t    n_bytes,
688     STRING    source_file,
689     int       line_number )
690 {
691     update_struct  update_ptrs;
692     skip_entry     *entry;
693 
694     if( alloc_checking_enabled() )
695     {
696         check_initialized_alloc_list( &alloc_list );
697 
698         if( n_bytes == 0 )
699         {
700             print_source_location( source_file, line_number, -1 );
701             print_error( ": Alloc called with zero size.\n" );
702             abort_if_allowed();
703         }
704         else if( ptr == (void *) 0 )
705         {
706             print_source_location( source_file, line_number, -1 );
707             print_error( ": Alloc returned a NIL pointer.\n" );
708             abort_if_allowed();
709         }
710         else
711         {
712             (void) find_pointer_position( &alloc_list, ptr, &update_ptrs );
713 
714             if( check_overlap( &alloc_list, &update_ptrs, ptr, n_bytes, &entry))
715             {
716                 print_source_location( source_file, line_number, -1 );
717                 print_error(
718                  ": Alloc returned a pointer overlapping an existing block:\n"
719                  );
720                 print_source_location( entry->source_file, entry->line_number,
721                                        entry->sequence_number );
722                 print_error( "\n" );
723                 abort_if_allowed();
724             }
725             else
726                 insert_ptr_in_alloc_list( &alloc_list,
727                            &update_ptrs, ptr, n_bytes,
728                            source_file, line_number,
729                            get_current_sequence_number() );
730         }
731     }
732 }
733 
734 /* ----------------------------- MNI Header -----------------------------------
735 @NAME       : change_ptr
736 @INPUT      : old_ptr
737             : new_ptr
738             : n_bytes
739             : source_file
740             : line_number
741 @OUTPUT     :
742 @RETURNS    :
743 @DESCRIPTION: Changes the information (mainly the n_bytes) associated with a
744             : given pointer.  This function is called from the def_alloc
745             : macros after a realloc().
746 @METHOD     :
747 @GLOBALS    :
748 @CALLS      :
749 @CREATED    :                      David MacDonald
750 @MODIFIED   :
751 ---------------------------------------------------------------------------- */
752 
change_ptr_alloc_check(void * old_ptr,void * new_ptr,size_t n_bytes,STRING source_file,int line_number)753 VIOAPI  void  change_ptr_alloc_check(
754     void      *old_ptr,
755     void      *new_ptr,
756     size_t    n_bytes,
757     STRING    source_file,
758     int       line_number )
759 {
760     STRING         orig_source;
761     int            orig_line;
762     int            sequence_number;
763     skip_entry     *entry;
764     update_struct  update_ptrs;
765 
766     if( alloc_checking_enabled() )
767     {
768         check_initialized_alloc_list( &alloc_list );
769 
770         if( n_bytes == 0 )
771         {
772             print_source_location( source_file, line_number, -1 );
773             print_error( ": Realloc called with zero size.\n" );
774             abort_if_allowed();
775         }
776         else if( !remove_ptr_from_alloc_list( &alloc_list, old_ptr,
777                       &orig_source, &orig_line, &sequence_number ) )
778         {
779             print_source_location( source_file, line_number, -1 );
780             print_error( ": Tried to realloc a pointer not already alloced.\n");
781             abort_if_allowed();
782         }
783         else
784         {
785             (void) find_pointer_position( &alloc_list, new_ptr, &update_ptrs );
786 
787             if( check_overlap( &alloc_list, &update_ptrs, new_ptr, n_bytes,
788                                &entry ) )
789             {
790                 print_source_location( source_file, line_number, -1 );
791                 print_error(
792                ": Realloc returned a pointer overlapping an existing block:\n");
793                 print_source_location( entry->source_file, entry->line_number,
794                                        entry->sequence_number );
795                 print_error( "\n" );
796                 abort_if_allowed();
797             }
798             else
799                 insert_ptr_in_alloc_list( &alloc_list,
800                        &update_ptrs, new_ptr, n_bytes,
801                        orig_source, orig_line, sequence_number );
802         }
803     }
804 }
805 
806 /* ----------------------------- MNI Header -----------------------------------
807 @NAME       : unrecord_ptr
808 @INPUT      : ptr
809             : source_file
810             : line_number
811 @OUTPUT     :
812 @RETURNS    : TRUE if ptr was in list
813 @DESCRIPTION: Removes the entry for the given ptr from the list.  Called by
814             : the macros during a FREE.  Returns TRUE if the pointer was
815             : in the list.
816 @METHOD     :
817 @GLOBALS    :
818 @CALLS      :
819 @CREATED    :                      David MacDonald
820 @MODIFIED   :
821 ---------------------------------------------------------------------------- */
822 
unrecord_ptr_alloc_check(void * ptr,STRING source_file,int line_number)823 VIOAPI  BOOLEAN  unrecord_ptr_alloc_check(
824     void     *ptr,
825     STRING   source_file,
826     int      line_number )
827 {
828     BOOLEAN  was_previously_alloced;
829     STRING   orig_source;
830     int      orig_line;
831     int      sequence_number;
832 
833     was_previously_alloced = TRUE;
834 
835     if( alloc_checking_enabled() )
836     {
837         check_initialized_alloc_list( &alloc_list );
838 
839         if( ptr == (void *) 0 )
840         {
841             print_source_location( source_file, line_number, -1 );
842             print_error( ": Tried to free a NIL pointer.\n" );
843             abort_if_allowed();
844             was_previously_alloced = FALSE;
845         }
846         else if( !remove_ptr_from_alloc_list( &alloc_list, ptr, &orig_source,
847                                               &orig_line, &sequence_number ) )
848         {
849             print_source_location( source_file, line_number, -1 );
850             print_error( ": Tried to free a pointer not alloced.\n" );
851             abort_if_allowed();
852             was_previously_alloced = FALSE;
853         }
854     }
855 
856     return( was_previously_alloced );
857 }
858 
859 /* ----------------------------- MNI Header -----------------------------------
860 @NAME       : output_alloc_to_file
861 @INPUT      : filename
862 @OUTPUT     :
863 @RETURNS    :
864 @DESCRIPTION: Outputs a list of all memory allocated to the given file.  Usually
865             : done at the end of the program to see if there is any memory that
866             : was orphaned.
867 @METHOD     :
868 @GLOBALS    :
869 @CALLS      :
870 @CREATED    :                      David MacDonald
871 @MODIFIED   :
872 ---------------------------------------------------------------------------- */
873 
output_alloc_to_file(STRING filename)874 VIOAPI  void  output_alloc_to_file(
875     STRING   filename )
876 {
877     FILE     *file;
878     STRING   date_str;
879 
880     if( alloc_checking_enabled() )
881     {
882         check_initialized_alloc_list( &alloc_list );
883 
884         if( memory_still_alloced( &alloc_list ) )
885         {
886             print_error( "\n" );
887             print_error( "\n" );
888             print_error( "A memory leak was found in this program.\n" );
889             if( filename != NULL )
890                 print_error(
891                     "A description has been recorded in the file %s.\n",
892                        filename );
893             print_error(
894                "Please report this file to the author of the program.\n" );
895             print_error( "\n" );
896 
897             if( filename != NULL && filename[0] != (char) 0 )
898                 file = fopen( filename, "w" );
899             else
900                 file = stdout;
901 
902             if( file != (FILE *) 0 )
903             {
904                 date_str = get_date();
905 
906                 (void) fprintf( file, "Alloc table at %s\n", date_str );
907 
908                 delete_string( date_str );
909 
910                 output_alloc_list( file, &alloc_list );
911 
912                 if( file != stdout )
913                     (void) fclose( file );
914             }
915         }
916     }
917 }
918 
919 #ifndef  NO_DEBUG_ALLOC
920 
print_alloc_source_line(STRING filename,int line_number)921 VIOAPI  void  print_alloc_source_line(
922     STRING  filename,
923     int     line_number )
924 {
925     print_error( "    Source position: %s:%d\n", filename, line_number );
926 }
927 
928 #endif
929