1 /****************************************************************************
4 * This module contains the code for our own memory allocation/deallocation,
5 * providing memory tracing, statistics, and garbage collection options.
7 * from Persistence of Vision(tm) Ray Tracer
8 * Copyright 1996,1999 Persistence of Vision Team
9 *---------------------------------------------------------------------------
10 * NOTICE: This source code file is provided so that users may experiment
11 * with enhancements to POV-Ray and to port the software to platforms other
12 * than those supported by the POV-Ray Team. There are strict rules under
13 * which you are permitted to use this file. The rules are in the file
14 * named POVLEGAL.DOC which should be distributed with this file.
15 * If POVLEGAL.DOC is not available or for more info please contact the POV-Ray
16 * Team Coordinator by email to team-coord@povray.org or visit us on the web at
17 * http://www.povray.org. The latest version of POV-Ray may be found at this site.
19 * This program is based on the popular DKB raytracer version 2.12.
20 * DKBTrace was originally written by David K. Buck.
21 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
23 *****************************************************************************/
26 #include "povproto.h" /* Error() */
29 #include "parse.h" /* MAError() */
30 #include "povray.h" /* stats[] global var */
31 #include <stdlib.h> /* for calloc */
33 /************************************************************************
36 * Steve Anger:70714,3113
40 This module replaces the memory allocation calls malloc, calloc, realloc
41 and free with the macros POV_MALLOC, POV_CALLOC, POV_REALLOC, and POV_FREE.
42 These macros work the same as the standard C functions except that the
43 POV_xALLOC functions also take a message as the last parameter and
44 automatically call MAError(msg) if the allocation fails. That means that
47 if ((New = malloc(sizeof(*New))) == NULL)
49 MAError ("new object");
54 New = POV_MALLOC (sizeof(*New), "new object");
56 This also expands the function of the macros to include error checking and
59 The following macros need to be defined in config.h, depending of what
60 features the compile needs:
62 #define MEM_TAG - Enables memory tag debugging
63 --------------------------------------------------
64 Memory tag debugging adds a 32-bit identifier to the beginning of each
65 allocated memory block and erases it after the block has been free'd. This
66 lets POV_FREE verify that the block it's freeing is valid and issue an
67 error message if it isn't. Makes it easy to find those nasty double free's
68 which usually corrupt the heap.
70 #define MEM_RECLAIM - Enables garbage collection
71 ------------------------------------------------
72 Garbage collection maintains a list of all currently allocated memory so
73 that it can be free'd when the program exits. Normally POV-Ray will free all
74 of its memory on its own, however abnormal exits such as parser errors or
75 user aborts bypass the destructors. There are four functions which control
76 the garbage collection:
79 Initializes global variables used by the garbage collection routines.
80 This function should be called once before any memory allocation functions
84 Starts a new memory pool. The next call to mem_release() will only release
85 memory allocated after this call.
87 mem_release (int LogFile)
88 Releases all unfree'd memory allocated since the last call to mem_mark().
89 The LogFile parameter determines if it dumps the list of unfree'd memory to
92 mem_release_all (int LogFile)
93 Releases all unfree'd memory allocated since the program started running.
95 POV-Ray only uses the mem_release_all() function however mem_mark() and
96 mem_release() might be useful for implenting a leak-free animation loop.
98 #define MEM_TRACE - Enables garbage collection and memory tracing
99 -------------------------------------------------------------------
100 Memory tracing stores the file name and line number for ever POV_xALLOC
101 call and dumps a list of unfree'd blocks when POV-Ray terminates.
103 #define MEM_STATS 1 - enables tracking of memory statistics
104 -------------------------------------------------------------------
105 Memory statistics enables routines that will track overall memory usage.
106 After all memory allocation/deallocation has taken place, and before you
107 re-initialize everything with another mem_init() call, you can call some
108 accessor routines to determine how memory was used. Setting MEM_STATS
109 to 1 only tracks peak memory usage. Setting it to 2 additionally tracks
110 number of calls to malloc/free and some other statistics.
114 * Aug 1995 : Steve Anger - Creation.
115 * Apr 1996 : Eduard Schwan - Added MEM_STATS code
116 * Jul 1996 : Andreas Dilger - Force mem_header to align on double boundary
117 **************************************************************************/
120 /****************************************************************************/
121 /* Allow user definable replacements for memory functions */
122 /****************************************************************************/
124 #define MALLOC malloc
128 #define CALLOC calloc
132 #define REALLOC realloc
140 /****************************************************************************/
142 /****************************************************************************/
144 /* if TRACE is on, the RECLAIM must also be on */
145 #if defined(MEM_TRACE) && !defined (MEM_RECLAIM)
149 /* This is the filename created for memory leakage information */
150 #if defined(MEM_TRACE)
151 #define MEM_LOG_FNAME "Memory.Log"
154 /* determine if we need to add a header to our memory records */
155 #if defined(MEM_TAG) || defined(MEM_RECLAIM) || defined(MEM_TRACE) || defined(MEM_STATS)
159 #define MEMNODE struct mem_node
161 #if defined(MEM_HEADER)
166 /* We have to do lots of testing here to make sure that the size of the
167 * mem_node struct is an even multiple of the sizeof(double) (usually 8
168 * bytes, or we royally screw up some architectures. Yuck!!! To make
169 * things easier, we have smaller groups of variables, and make them
170 * work out to multiples of 8 bytes, rather than trying to do it for the
171 * whole structure. In most cases, only 4 bytes are wasted, as this is
172 * mostly for debugging anyways.
177 #if !defined(MEM_STATS) || defined(MEM_TRACE)
179 #endif /* !MEM_STATS */
182 #if defined(MEM_STATS) && !defined(MEM_TRACE)
184 #if !defined(MEM_TAG)
186 #endif /* !MEM_TAG */
187 #endif /* MEM_STATS */
189 #if defined(MEM_RECLAIM)
193 #if defined(MEM_TRACE)
199 #endif /* MEM_TRACE */
200 #endif /* MEM_RECLAIM */
203 #endif /* MEM_HEADER */
206 #if defined(MEM_RECLAIM)
207 static int poolno
= 0;
208 static MEMNODE
*memlist
= NULL
;
212 static int leak_msg
= FALSE
;
215 #if defined(MEM_HEADER)
216 #define NODESIZE ((sizeof(MEMNODE)+3)/4)*4 /* Align memory on 4 byte boundary */
222 #if defined(MEM_RECLAIM)
223 static void add_node(MEMNODE
* node
);
224 static void remove_node(MEMNODE
* node
);
229 /* the tag value that marks our used memory */
230 #define MEMTAG_VALUE 0x4D546167L
232 static int mem_check_tag(MEMNODE
* node
);
237 #if defined(MEM_RECLAIM)
238 static long num_nodes
; /* keep track of valence of node list */
239 #endif /* MEM_RECLAIM */
242 #if defined(MEM_STATS)
244 typedef struct MemStats_Struct MEMSTATS
;
246 struct MemStats_Struct
248 size_t smallest_alloc
; /* smallest # of bytes in one malloc() */
249 size_t largest_alloc
; /* largest # of bytes in one malloc() */
250 size_t current_mem_usage
; /* current total # of bytes allocated */
251 size_t largest_mem_usage
; /* peak total # of bytes allocated */
253 /* could add a running average size too, someday */
254 long int total_allocs
; /* total # of alloc calls */
255 long int total_frees
; /* total # of free calls */
256 char *smallest_file
; /* file name of largest alloc */
257 int smallest_line
; /* file line of largest alloc */
258 char *largest_file
; /* file name of largest alloc */
259 int largest_line
; /* file line of largest alloc */
263 /* keep track of memory allocation statistics */
264 static MEMSTATS mem_stats
;
266 /* local prototypes */
267 static void mem_stats_init (void);
268 static void mem_stats_alloc (size_t nbytes
, char *file
, int line
);
269 static void mem_stats_free (size_t nbytes
);
274 /****************************************************************************/
277 #if defined(MEM_RECLAIM)
282 #if defined(MEM_STATS)
290 /****************************************************************************/
291 /* return TRUE if pointer is non-null and has a valid tag */
292 static int mem_check_tag(MEMNODE
*node
)
297 if (node
->tag
== MEMTAG_VALUE
)
305 /****************************************************************************/
306 void *pov_malloc(size_t size
, char *file
, int line
, char *msg
)
310 #if defined(MEM_HEADER)
314 #if defined(MEM_HEADER)
317 Error("Attempt to malloc zero size block (File: %s Line: %d).\n", file
, line
);
321 totalsize
=size
+NODESIZE
; /* number of bytes allocated in OS */
323 block
= (void *)MALLOC(totalsize
);
328 #if defined(MEM_HEADER)
329 node
= (MEMNODE
*) block
;
333 node
->tag
= MEMTAG_VALUE
;
336 #if defined(MEM_TRACE) || defined(MEM_STATS)
337 node
->size
= totalsize
;
339 #if defined(MEM_TRACE)
344 #if defined(MEM_RECLAIM)
348 #if defined(MEM_STATS)
349 mem_stats_alloc(totalsize
, file
, line
);
352 return (void *)((char *)block
+ NODESIZE
);
356 /****************************************************************************/
357 void *pov_calloc(size_t nitems
, size_t size
, char *file
, int line
, char *msg
)
361 size_t totalsize
; /* number of bytes allocated in OS */
362 #if defined(MEM_HEADER)
367 totalsize
=actsize
+NODESIZE
;
369 #if defined(MEM_HEADER)
372 Error("Attempt to calloc zero size block (File: %s Line: %d).\n", file
, line
);
376 block
= (void *)MALLOC(totalsize
);
379 MAError(msg
, actsize
);
381 memset(block
, 0, totalsize
);
383 #if defined(MEM_HEADER)
384 node
= (MEMNODE
*) block
;
388 node
->tag
= MEMTAG_VALUE
;
391 #if defined(MEM_TRACE) || defined(MEM_STATS)
392 node
->size
= totalsize
;
394 #if defined(MEM_TRACE)
399 #if defined(MEM_RECLAIM)
403 #if defined(MEM_STATS)
404 mem_stats_alloc(totalsize
, file
, line
);
407 return (void *)((char *)block
+ NODESIZE
);
411 /****************************************************************************/
412 void *pov_realloc(void *ptr
, size_t size
, char *file
, int line
, char *msg
)
415 #if defined(MEM_STATS)
419 #if defined(MEM_HEADER)
423 #if defined(MEM_RECLAIM)
432 pov_free(ptr
, file
, line
);
435 else if (ptr
== NULL
)
436 return pov_malloc(size
, file
, line
, msg
);
438 block
= (void *)((char *)ptr
- NODESIZE
);
440 #if defined(MEM_HEADER)
441 node
= (MEMNODE
*) block
;
445 if (node
->tag
!= MEMTAG_VALUE
)
446 Error("Attempt to realloc invalid block (File: %s Line: %d).\n", file
, line
);
448 node
->tag
= ~node
->tag
;
451 #if defined(MEM_RECLAIM)
456 block
= (void *)REALLOC(block
, NODESIZE
+ size
);
461 #if defined(MEM_STATS)
462 /* REALLOC does an implied FREE... */
463 oldsize
= ((MEMNODE
*)block
)->size
;
464 mem_stats_free(oldsize
);
465 /* ...and an implied MALLOC... */
466 mem_stats_alloc(NODESIZE
+ size
, file
, line
);
469 #if defined(MEM_HEADER)
470 node
= (MEMNODE
*) block
;
474 node
->tag
= MEMTAG_VALUE
;
477 #if defined(MEM_TRACE) || defined(MEM_STATS)
478 node
->size
= size
+ NODESIZE
;
480 #if defined(MEM_TRACE)
485 #if defined(MEM_RECLAIM)
490 if (node
->next
!= NULL
)
491 node
->next
->prev
= node
;
496 return (void *)((char *)block
+ NODESIZE
);
500 /****************************************************************************/
501 void pov_free(void *ptr
, char *file
, int line
)
505 #if defined(MEM_HEADER)
510 Error("Attempt to free NULL pointer (File: %s Line: %d).\n", file
, line
);
512 block
= (void *)((char *)ptr
- NODESIZE
);
514 #if defined(MEM_HEADER)
515 node
= (MEMNODE
*) block
;
519 if (node
->tag
== ~MEMTAG_VALUE
)
521 Warning(0.0, "Attempt to free already free'd block (File: %s Line: %d).\n", file
, line
);
524 else if (node
->tag
!= MEMTAG_VALUE
)
526 Warning(0.0, "Attempt to free invalid block (File: %s Line: %d).\n", file
, line
);
532 #if defined(MEM_RECLAIM)
537 /* do this After remove_node, so remove_node can check validity of nodes */
538 node
->tag
= ~node
->tag
;
541 #if defined(MEM_STATS)
542 mem_stats_free(((MEMNODE
*)block
)->size
);
549 /****************************************************************************/
550 /* Starts a new memory pool. The next mem_release() call will
551 only release memory allocated after this call. */
554 #if defined(MEM_RECLAIM)
560 /****************************************************************************/
561 /* Releases all unfree'd memory from current memory pool */
562 void mem_release(int LogFile
)
564 #if defined(MEM_RECLAIM)
572 #if defined(MEM_TRACE)
575 if (p
!= NULL
&& (p
->poolno
== poolno
))
576 f
= fopen(MEM_LOG_FNAME
, APPEND_TXTFILE_STRING
);
578 #endif /* MEM_TRACE */
580 while (p
!= NULL
&& (p
->poolno
== poolno
))
582 #if defined(MEM_TRACE)
585 if (!mem_check_tag(p
))
586 Debug_Info("mem_release(): Memory pointer corrupt!\n");
589 totsize
+= (p
->size
-NODESIZE
);
594 Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME
);
599 fprintf(f
, "File:%13s Line:%4d Size:%lu\n", p
->file
, p
->line
, (unsigned long)(p
->size
-NODESIZE
));
601 #endif /* MEM_TRACE */
603 #if defined(MEM_STATS)
604 mem_stats_free(p
->size
);
617 Debug_Info("%lu bytes reclaimed (pool #%d)\n", totsize
, poolno
);
622 #if defined(MEM_STATS)
623 /* reinitialize the stats structure for next time through */
627 #endif /* MEM_RECLAIM */
631 /****************************************************************************/
632 /* Released all unfree'd memory from all pools */
633 void mem_release_all(int LogFile
)
635 #if defined(MEM_RECLAIM)
640 Status_Info("Reclaiming memory\n");
645 #if defined(MEM_TRACE)
649 f
= fopen(MEM_LOG_FNAME
, APPEND_TXTFILE_STRING
);
655 #if defined(MEM_TRACE)
658 if (!mem_check_tag(p
))
659 Debug_Info("mem_release_all(): Memory pointer corrupt!\n");
662 totsize
+= (p
->size
-NODESIZE
);
667 Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME
);
672 fprintf(f
, "File:%13s Line:%4d Size:%lu\n", p
->file
, p
->line
, (unsigned long)(p
->size
-NODESIZE
));
676 #if defined(MEM_STATS)
677 /* This is after we have printed stats, and this may slow us down a little, */
678 /* so we may want to simply re-initialize the mem-stats at the end of this loop. */
679 mem_stats_free(p
->size
);
692 Debug_Info("\n%lu bytes reclaimed\n", totsize
);
697 #if defined(MEM_STATS)
698 /* reinitialize the stats structure for next time through */
705 /****************************************************************************/
706 #if defined(MEM_RECLAIM)
707 /* Adds a new node to the 'allocated' list */
708 static void add_node(MEMNODE
*node
)
712 if (!mem_check_tag(node
))
713 Debug_Info("add_node(): Memory pointer corrupt!\n");
719 node
->poolno
= poolno
;
726 memlist
->prev
= node
;
727 node
->poolno
= poolno
;
729 node
->next
= memlist
;
736 /****************************************************************************/
737 /* Detatches a node from the 'allocated' list but doesn't free it */
738 static void remove_node(MEMNODE
*node
)
742 if (!mem_check_tag(node
))
743 Debug_Info("remove_node(): Memory pointer corrupt!\n");
747 if (node
->prev
!= NULL
)
748 node
->prev
->next
= node
->next
;
750 if (node
->next
!= NULL
)
751 node
->next
->prev
= node
->prev
;
756 /* check node->next if it is non-null, to insure it is safe to assign. */
757 /* if it is null, it is safe since it is the last in the list. */
759 if (!mem_check_tag(node
->next
))
760 Debug_Info("remove_node(): memlist pointer corrupt!\n");
763 memlist
= node
->next
;
768 #endif /* MEM_RECLAIM */
771 /****************************************************************************/
772 /* A strdup routine that uses POV_MALLOC */
773 /****************************************************************************/
774 char *pov_strdup(char *s
)
778 New
=(char *)POV_MALLOC(strlen(s
)+1,s
);
783 /****************************************************************************/
784 /* A memmove routine for those systems that don't have one */
785 /****************************************************************************/
787 void *pov_memmove (void *dest
, void *src
, size_t length
)
789 char *csrc
=(char *)src
;
790 char *cdest
=(char *)dest
;
792 if (csrc
< cdest
&& csrc
+ length
>= cdest
)
794 size_t size
= cdest
- csrc
;
798 memcpy(cdest
+ length
- size
, csrc
+ length
- size
, size
);
806 /* I'm not sure if this is needed, but my docs on memcpy say the regions
807 * can't overlap, so theoretically we need to special case this. If you
808 * don't think it's necessary, you can just comment this part out.
810 else if (cdest
< csrc
&& cdest
+ length
>= csrc
)
812 char *new_dest
= cdest
;
813 size_t size
= csrc
- cdest
;
817 memcpy(new_dest
, csrc
, length
);
829 memcpy(cdest
, csrc
, length
);
836 /****************************************************************************/
837 /* Memory Statistics gathering routines */
838 /****************************************************************************/
840 #if defined(MEM_STATS)
842 /****************************************************************************/
843 static void mem_stats_init()
845 mem_stats
.smallest_alloc
= 65535; /* Must be an unsigned number */
846 mem_stats
.largest_alloc
= 0;
847 mem_stats
.current_mem_usage
= 0;
848 mem_stats
.largest_mem_usage
= 0;
850 mem_stats
.total_allocs
= 0;
851 mem_stats
.total_frees
= 0;
852 mem_stats
.largest_file
= "none";
853 mem_stats
.largest_line
= -1;
854 mem_stats
.smallest_file
= "none";
855 mem_stats
.smallest_line
= -1;
859 /****************************************************************************/
860 /* update appropriate fields when an allocation takes place */
861 static void mem_stats_alloc(size_t nbytes
, char *file
, int line
)
863 /* update the fields */
864 if ((mem_stats
.smallest_alloc
<0) || (nbytes
<mem_stats
.smallest_alloc
))
866 mem_stats
.smallest_alloc
= nbytes
;
868 mem_stats
.smallest_file
= file
;
869 mem_stats
.smallest_line
= line
;
873 if (nbytes
>mem_stats
.largest_alloc
)
875 mem_stats
.largest_alloc
= nbytes
;
877 mem_stats
.largest_file
= file
;
878 mem_stats
.largest_line
= line
;
883 mem_stats
.total_allocs
++;
886 mem_stats
.current_mem_usage
+= nbytes
;
888 if (mem_stats
.current_mem_usage
>mem_stats
.largest_mem_usage
)
890 mem_stats
.largest_mem_usage
= mem_stats
.current_mem_usage
;
895 /****************************************************************************/
896 /* update appropriate fields when a free takes place */
897 static void mem_stats_free(size_t nbytes
)
899 /* update the fields */
900 mem_stats
.current_mem_usage
-= nbytes
;
902 mem_stats
.total_frees
++;
906 /****************************************************************************/
909 /****************************************************************************/
910 size_t mem_stats_smallest_alloc()
912 return mem_stats
.smallest_alloc
;
914 /****************************************************************************/
915 size_t mem_stats_largest_alloc()
917 return mem_stats
.largest_alloc
;
919 /****************************************************************************/
920 size_t mem_stats_current_mem_usage()
922 return mem_stats
.current_mem_usage
;
924 /****************************************************************************/
925 size_t mem_stats_largest_mem_usage()
927 return mem_stats
.largest_mem_usage
;
930 /****************************************************************************/
935 /****************************************************************************/
936 char *mem_stats_smallest_file()
938 return mem_stats
.smallest_file
;
940 /****************************************************************************/
941 int mem_stats_smallest_line()
943 return mem_stats
.smallest_line
;
945 /****************************************************************************/
946 char *mem_stats_largest_file()
948 return mem_stats
.largest_file
;
950 /****************************************************************************/
951 int mem_stats_largest_line()
953 return mem_stats
.largest_line
;
955 /****************************************************************************/
956 long int mem_stats_total_allocs()
958 return mem_stats
.total_allocs
;
960 /****************************************************************************/
961 long int mem_stats_total_frees()
963 return mem_stats
.total_frees
;
968 #endif /* MEM_STATS */