1 /****************************************************************************
2 * pov_mem.cpp
3 *
4 * This module contains the code for our own memory allocation/deallocation,
5 * providing memory tracing, statistics, and garbage collection options.
6 *
7 * from Persistence of Vision(tm) Ray Tracer version 3.6.
8 * Copyright 1991-2003 Persistence of Vision Team
9 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
10 *---------------------------------------------------------------------------
11 * NOTICE: This source code file is provided so that users may experiment
12 * with enhancements to POV-Ray and to port the software to platforms other
13 * than those supported by the POV-Ray developers. There are strict rules
14 * regarding how you are permitted to use this file. These rules are contained
15 * in the distribution and derivative versions licenses which should have been
16 * provided with this file.
17 *
18 * These licences may be found online, linked from the end-user license
19 * agreement that is located at http://www.povray.org/povlegal.html
20 *---------------------------------------------------------------------------
21 * This program is based on the popular DKB raytracer version 2.12.
22 * DKBTrace was originally written by David K. Buck.
23 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
24 *---------------------------------------------------------------------------
25 * $File: //depot/povray/3.6-release/source/pov_mem.cpp $
26 * $Revision: #3 $
27 * $Change: 3032 $
28 * $DateTime: 2004/08/02 18:43:41 $
29 * $Author: chrisc $
30 * $Log$
31 *****************************************************************************/
32
33 #include "frame.h"
34 #include "userio.h" /* Error() */
35
36 #include "pov_mem.h"
37 #include "parse.h" /* MAError() */
38 #include "povray.h" /* stats[] global var */
39 #include "optout.h"
40 #include "pov_util.h"
41 #include "povmsend.h"
42
43 BEGIN_POV_NAMESPACE
44
45 /************************************************************************
46 * AUTHOR
47 *
48 * Steve Anger:70714,3113
49 *
50 * DESCRIPTION
51 *
52 This module replaces the memory allocation calls malloc, calloc, realloc
53 and free with the macros POV_MALLOC, POV_CALLOC, POV_REALLOC, and POV_FREE.
54 These macros work the same as the standard C functions except that the
55 POV_xALLOC functions also take a message as the last parameter and
56 automatically call MAError(msg) if the allocation fails. That means that
57 instead of writing
58
59 if ((New = malloc(sizeof(*New))) == NULL)
60 {
61 MAError ("new object");
62 }
63
64 you'd just use
65
66 New = POV_MALLOC (sizeof(*New), "new object");
67
68 This also expands the function of the macros to include error checking and
69 memory tracking.
70
71 The following macros need to be defined in config.h, depending of what
72 features the compile needs:
73
74 #define MEM_TAG - Enables memory tag debugging
75 --------------------------------------------------
76 Memory tag debugging adds a 32-bit identifier to the beginning of each
77 allocated memory block and erases it after the block has been free'd. This
78 lets POV_FREE verify that the block it's freeing is valid and issue an
79 error message if it isn't. Makes it easy to find those nasty double free's
80 which usually corrupt the heap.
81
82 #define MEM_RECLAIM - Enables garbage collection
83 ------------------------------------------------
84 Garbage collection maintains a list of all currently allocated memory so
85 that it can be free'd when the program exits. Normally POV-Ray will free all
86 of its memory on its own, however abnormal exits such as parser errors or
87 user aborts bypass the destructors. There are four functions which control
88 the garbage collection:
89
90 mem_init()
91 Initializes global variables used by the garbage collection routines.
92 This function should be called once before any memory allocation functions
93 are called.
94
95 mem_mark()
96 Starts a new memory pool. The next call to mem_release() will only release
97 memory allocated after this call.
98
99 mem_release ()
100 Releases all unfree'd memory allocated since the last call to mem_mark().
101 The LogFile parameter determines if it dumps the list of unfree'd memory to
102 a file.
103
104 mem_release_all ()
105 Releases all unfree'd memory allocated since the program started running.
106
107 POV-Ray only uses the mem_release_all() function however mem_mark() and
108 mem_release() might be useful for implenting a leak-free animation loop.
109
110 #define MEM_TRACE - Enables garbage collection and memory tracing
111 -------------------------------------------------------------------
112 Memory tracing stores the file name and line number for ever POV_xALLOC
113 call and dumps a list of unfree'd blocks when POV-Ray terminates.
114
115 #define MEM_STATS 1 - enables tracking of memory statistics
116 -------------------------------------------------------------------
117 Memory statistics enables routines that will track overall memory usage.
118 After all memory allocation/deallocation has taken place, and before you
119 re-initialize everything with another mem_init() call, you can call some
120 accessor routines to determine how memory was used. Setting MEM_STATS
121 to 1 only tracks peak memory usage. Setting it to 2 additionally tracks
122 number of calls to malloc/free and some other statistics.
123 *
124 * CHANGES
125 *
126 * Aug 1995 : Steve Anger - Creation.
127 * Apr 1996 : Eduard Schwan - Added MEM_STATS code
128 * Jul 1996 : Andreas Dilger - Force mem_header to align on double boundary
129 **************************************************************************/
130
131
132 /****************************************************************************/
133 /* Allow user definable replacements for memory functions */
134 /****************************************************************************/
135 #ifndef MALLOC
136 #define MALLOC malloc
137 #endif
138
139 #ifndef CALLOC
140 #define CALLOC calloc
141 #endif
142
143 #ifndef REALLOC
144 #define REALLOC realloc
145 #endif
146
147 #ifndef FREE
148 #define FREE free
149 #endif
150
151
152 /****************************************************************************/
153 /* internal use */
154 /****************************************************************************/
155
156 // if PREFILL or GUARD on, STATS must also be on
157 #if (defined(MEM_PREFILL) || defined (MEM_GUARD)) && !defined (MEM_STATS)
158 #define MEM_STATS
159 #endif
160
161 // if TRACE is on, the RECLAIM must also be on
162 #if defined(MEM_TRACE) && !defined (MEM_RECLAIM)
163 #define MEM_RECLAIM
164 #endif
165
166 // This is the filename created for memory leakage information
167 #if defined(MEM_TRACE)
168 #define MEM_LOG_FNAME "Memory.Log"
169 #endif
170
171 // determine if we need to add a header to our memory records
172 #if defined(MEM_TAG) || defined(MEM_RECLAIM) || defined(MEM_TRACE) || defined(MEM_STATS)
173 #define MEM_HEADER
174 #endif
175
176 #ifdef MEM_HEADER
177
178 #define MEMNODE struct mem_node
179
180 #ifndef MEM_HEADER_ALIGNMENT
181 #define MEM_HEADER_ALIGNMENT sizeof(double)
182 #endif
183
184 struct mem_node
185 {
186 #ifdef MEM_TAG
187 int tag;
188 #endif
189
190 #ifdef MEM_STATS
191 size_t size;
192 #endif
193
194 #ifdef MEM_RECLAIM
195 MEMNODE *prev;
196 MEMNODE *next;
197 int poolno;
198
199 #ifdef MEM_TRACE
200 const char *file;
201 int line;
202 #endif
203
204 #endif
205
206 };
207
208 #endif /* MEM_HEADER */
209
210
211 #if defined(MEM_RECLAIM)
212 static int poolno = 0; // GLOBAL VARIABLE
213 static MEMNODE *memlist = NULL; // GLOBAL VARIABLE
214 #endif
215
216 #if !defined(MEM_PREFILL_STRING)
217 #define MEM_PREFILL_STRING "POVR"
218 #endif
219
220 #if !defined(MEM_CLEAR_STRING)
221 #define MEM_CLEAR_STRING "CLEA"
222 #endif
223
224 #if !defined(MEM_GUARD_STRING)
225 #define MEM_GUARD_STRING "GURD"
226 #endif
227
228 #if defined(MEM_GUARD)
229 static char *mem_guard_string = MEM_GUARD_STRING;
230 static size_t mem_guard_string_len = 0;
231 #if !defined(MEM_GUARD_SIZE)
232 #define MEM_GUARD_SIZE MEM_HEADER_ALIGNMENT
233 #endif
234 #else
235 #define MEM_GUARD_SIZE 0
236 #endif
237
238 #if defined(MEM_PREFILL)
239 static char *mem_prefill_string = MEM_PREFILL_STRING; // GLOBAL VARIABLE
240 static size_t mem_prefill_string_len = 0; // GLOBAL VARIABLE
241 static char *mem_clear_string = MEM_CLEAR_STRING; // GLOBAL VARIABLE
242 static size_t mem_clear_string_len = 0; // GLOBAL VARIABLE
243 #endif
244
245 static int leak_msg = false; // GLOBAL VARIABLE
246
247
248 #ifdef MEM_HEADER
249 const int NODESIZE = (((sizeof(MEMNODE) + (MEM_HEADER_ALIGNMENT - 1)) / MEM_HEADER_ALIGNMENT) * MEM_HEADER_ALIGNMENT);
250 #else
251 const int NODESIZE = 0;
252 #endif
253
254
255 #if defined(MEM_RECLAIM)
256 static void add_node(MEMNODE * node);
257 static void remove_node(MEMNODE * node);
258 #endif
259
260
261 #if defined(MEM_TAG)
262 // the tag value that marks our used memory
263 #define MEMTAG_VALUE 0x4D546167L
264
265 static int mem_check_tag(MEMNODE * node);
266 #endif
267
268
269 #if defined(MEM_RECLAIM)
270 static long num_nodes; /* keep track of valence of node list */ // GLOBAL VARIABLE
271 #endif /* MEM_RECLAIM */
272
273
274 #if defined(MEM_STATS)
275
276 typedef struct MemStats_Struct MEMSTATS;
277
278 struct MemStats_Struct
279 {
280 size_t smallest_alloc; /* smallest # of bytes in one malloc() */
281 size_t largest_alloc; /* largest # of bytes in one malloc() */
282 size_t current_mem_usage; /* current total # of bytes allocated */
283 size_t largest_mem_usage; /* peak total # of bytes allocated */
284 #if (MEM_STATS>=2)
285 /* could add a running average size too, someday */
286 long int total_allocs; /* total # of alloc calls */
287 long int total_frees; /* total # of free calls */
288 const char *smallest_file; /* file name of largest alloc */
289 int smallest_line; /* file line of largest alloc */
290 const char *largest_file; /* file name of largest alloc */
291 int largest_line; /* file line of largest alloc */
292 #endif
293 };
294
295 /* keep track of memory allocation statistics */
296 static MEMSTATS mem_stats; // GLOBAL VARIABLE
297
298 /* local prototypes */
299 static void mem_stats_init (void);
300 static void mem_stats_alloc (size_t nbytes, const char *file, int line);
301 static void mem_stats_free (size_t nbytes);
302
303 #endif
304
305
306 /****************************************************************************/
mem_init()307 void mem_init()
308 {
309 #if defined(MEM_RECLAIM)
310 num_nodes = 0;
311 poolno = 0;
312 memlist = NULL;
313 #endif
314 #if defined(MEM_GUARD)
315 mem_guard_string_len = strlen(mem_guard_string);
316 #endif
317 #if defined(MEM_PREFILL)
318 mem_prefill_string_len = strlen(mem_prefill_string);
319 mem_clear_string_len = strlen(mem_clear_string);
320 #endif
321 #if defined(MEM_STATS)
322 mem_stats_init();
323 #endif
324 leak_msg = false;
325 }
326
327
328 #if defined(MEM_TAG)
329 /****************************************************************************/
330 /* return true if pointer is non-null and has a valid tag */
mem_check_tag(MEMNODE * node)331 static int mem_check_tag(MEMNODE *node)
332 {
333 int isOK = false;
334
335 if (node != NULL)
336 if (node->tag == MEMTAG_VALUE)
337 isOK = true;
338 return isOK;
339 }
340
341 #endif /* MEM_TAG */
342
343
344 /****************************************************************************/
pov_malloc(size_t size,const char * file,int line,const char * msg)345 void *pov_malloc(size_t size, const char *file, int line, const char *msg)
346 {
347 void *block;
348 size_t totalsize;
349 #if defined(MEM_HEADER)
350 MEMNODE *node;
351 #endif
352
353 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
354 char *memptr;
355 size_t i;
356 #endif
357
358 #if defined(MEM_HEADER)
359 if (size == 0)
360 {
361 Error("Attempt to malloc zero size block (File: %s Line: %d).", file, line);
362 }
363 #endif
364
365 totalsize = size + NODESIZE + (MEM_GUARD_SIZE * 2); /* number of bytes allocated in OS */
366
367 block = (void *)MALLOC(totalsize);
368
369 if (block == NULL)
370 MAError(msg, size);
371
372 #if defined(MEM_HEADER)
373 node = (MEMNODE *) block;
374 #endif
375
376 #if defined(MEM_TAG)
377 node->tag = MEMTAG_VALUE;
378 #endif
379
380 #if defined(MEM_TRACE) || defined(MEM_STATS)
381 node->size = totalsize;
382 #endif
383 #if defined(MEM_TRACE)
384 node->file = file;
385 node->line = line;
386 #endif
387
388 #if defined(MEM_PREFILL)
389 memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
390 for(i = 0; i < size; i++)
391 memptr[i] = mem_prefill_string[i % mem_prefill_string_len];
392 #endif
393
394 #if defined(MEM_GUARD)
395 memptr = (char *)block + NODESIZE;
396 for(i = 0; i < MEM_GUARD_SIZE; i++)
397 memptr[i] = mem_guard_string[i % mem_guard_string_len];
398 memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
399 for(i = 0; i < MEM_GUARD_SIZE; i++)
400 memptr[i] = mem_guard_string[i % mem_guard_string_len];
401 #endif
402
403 #if defined(MEM_RECLAIM)
404 add_node(node);
405 #endif
406
407 #if defined(MEM_STATS)
408 mem_stats_alloc(totalsize, file, line);
409 #endif
410
411 return (void *)((char *)block + NODESIZE + MEM_GUARD_SIZE);
412 }
413
414
415 /****************************************************************************/
pov_calloc(size_t nitems,size_t size,const char * file,int line,const char * msg)416 void *pov_calloc(size_t nitems, size_t size, const char *file, int line, const char *msg)
417 {
418 void *block;
419 size_t actsize;
420
421 actsize = nitems * size;
422
423 #if defined(MEM_HEADER)
424 if (actsize == 0)
425 {
426 Error("Attempt to calloc zero size block (File: %s Line: %d).", file, line);
427 }
428 #endif
429
430 block = (void *)pov_malloc(actsize, file, line, msg);
431
432 if (block != NULL)
433 memset(block, 0, actsize);
434
435 return block;
436 }
437
438
439 /****************************************************************************/
pov_realloc(void * ptr,size_t size,const char * file,int line,const char * msg)440 void *pov_realloc(void *ptr, size_t size, const char *file, int line, const char *msg)
441 {
442 void *block;
443 #if defined(MEM_STATS)
444 size_t oldsize;
445 #endif
446
447 #if defined(MEM_HEADER)
448 MEMNODE *node;
449 #endif
450
451 #if defined(MEM_RECLAIM)
452 MEMNODE *prev;
453 MEMNODE *next;
454 #endif
455
456 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
457 char *memptr;
458 size_t i;
459 #endif
460
461 if (size == 0)
462 {
463 if (ptr)
464 pov_free(ptr, file, line);
465 return NULL;
466 }
467 else if (ptr == NULL)
468 return pov_malloc(size, file, line, msg);
469
470 block = (void *)((char *)ptr - NODESIZE - MEM_GUARD_SIZE);
471
472 #if defined(MEM_GUARD)
473 memptr = (char *)block + NODESIZE;
474 for(i = 0; i < MEM_GUARD_SIZE; i++)
475 {
476 if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
477 {
478 Warning(0, "Damaged start guard detected in resized block (File: %s Line: %d).", file, line);
479 break;
480 }
481 }
482 memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
483 for(i = 0; i < MEM_GUARD_SIZE; i++)
484 {
485 if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
486 {
487 Warning(0, "Damaged end guard detected in resized block (File: %s Line: %d).", file, line);
488 break;
489 }
490 }
491 #endif
492
493 #if defined(MEM_HEADER)
494 node = (MEMNODE *) block;
495 #endif
496
497 #if defined(MEM_TAG)
498 if (node->tag != MEMTAG_VALUE)
499 Error("Attempt to realloc invalid block (File: %s Line: %d).", file, line);
500
501 node->tag = ~node->tag;
502 #endif
503
504 #if defined(MEM_RECLAIM)
505 prev = node->prev;
506 next = node->next;
507 #endif
508
509 #if defined(MEM_STATS)
510 oldsize = ((MEMNODE *)block)->size;
511
512 #if defined(MEM_PREFILL)
513 memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
514 for(i = size; i < oldsize - NODESIZE - (MEM_GUARD_SIZE * 2); i++)
515 memptr[i] = mem_clear_string[i % mem_clear_string_len];
516 #endif
517 #endif
518
519 block = (void *)REALLOC(block, NODESIZE + (MEM_GUARD_SIZE * 2) + size);
520
521 if (block == NULL)
522 MAError(msg, size);
523
524 #if defined(MEM_STATS)
525 /* REALLOC does an implied FREE... */
526 mem_stats_free(oldsize);
527 /* ...and an implied MALLOC... */
528 mem_stats_alloc(NODESIZE + (MEM_GUARD_SIZE * 2) + size, file, line);
529
530 #if defined(MEM_PREFILL)
531 memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
532 for(i = oldsize - NODESIZE - (MEM_GUARD_SIZE * 2); i < size; i++)
533 memptr[i] = mem_prefill_string[i % mem_prefill_string_len];
534 #endif
535
536 #endif
537
538 #if defined(MEM_HEADER)
539 node = (MEMNODE *) block;
540 #endif
541
542 #if defined(MEM_TAG)
543 node->tag = MEMTAG_VALUE;
544 #endif
545
546 #if defined(MEM_TRACE) || defined(MEM_STATS)
547 node->size = size + NODESIZE + (MEM_GUARD_SIZE * 2);
548 #endif
549 #if defined(MEM_TRACE)
550 node->file = file;
551 node->line = line;
552 #endif
553
554 #if defined(MEM_GUARD)
555 memptr = (char *)block + NODESIZE;
556 for(i = 0; i < MEM_GUARD_SIZE; i++)
557 memptr[i] = mem_guard_string[i % mem_guard_string_len];
558 memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
559 for(i = 0; i < MEM_GUARD_SIZE; i++)
560 memptr[i] = mem_guard_string[i % mem_guard_string_len];
561 #endif
562
563 #if defined(MEM_RECLAIM)
564 if (prev == NULL)
565 memlist = node;
566 else
567 prev->next = node;
568 if (node->next != NULL)
569 node->next->prev = node;
570 if (next != NULL)
571 next->prev = node;
572 #endif
573
574 return (void *)((char *)block + NODESIZE + MEM_GUARD_SIZE);
575 }
576
577
578 /****************************************************************************/
pov_free(void * ptr,const char * file,int line)579 void pov_free(void *ptr, const char *file, int line)
580 {
581 void *block;
582
583 #if defined(MEM_HEADER)
584 MEMNODE *node;
585 #endif
586
587 #if defined(MEM_PREFILL) || defined(MEM_GUARD)
588 char *memptr;
589 size_t size;
590 size_t i;
591 #endif
592
593 if (ptr == NULL)
594 Error("Attempt to free NULL pointer (File: %s Line: %d).", file, line);
595
596 block = (void *)((char *)ptr - NODESIZE - MEM_GUARD_SIZE);
597
598 #if defined(MEM_HEADER)
599 node = (MEMNODE *) block;
600 #endif
601
602 #if defined(MEM_TAG)
603 #if defined(MEM_PREFILL)
604 size = ((MEMNODE *)block)->size;
605 memptr = (char *)block + NODESIZE + MEM_GUARD_SIZE;
606 for(i = 0; i < size - NODESIZE - (MEM_GUARD_SIZE * 2); i++)
607 memptr[i] = mem_clear_string[i % mem_clear_string_len];
608 #endif
609
610 if (node->tag == ~MEMTAG_VALUE)
611 {
612 Warning(0, "Attempt to free already free'd block (File: %s Line: %d).", file, line);
613 return;
614 }
615 else if (node->tag != MEMTAG_VALUE)
616 {
617 Warning(0, "Attempt to free invalid block (File: %s Line: %d).", file, line);
618 return;
619 }
620
621 #endif
622
623 #if defined(MEM_GUARD)
624 memptr = (char *)block + NODESIZE;
625 for(i = 0; i < MEM_GUARD_SIZE; i++)
626 {
627 if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
628 {
629 Warning(0, "Damaged start guard detected in free'd block (File: %s Line: %d).", file, line);
630 break;
631 }
632 }
633 memptr = (char *)block + ((MEMNODE *)block)->size - MEM_GUARD_SIZE;
634 for(i = 0; i < MEM_GUARD_SIZE; i++)
635 {
636 if(memptr[i] != mem_guard_string[i % mem_guard_string_len])
637 {
638 Warning(0, "Damaged end guard detected in free'd block (File: %s Line: %d).", file, line);
639 break;
640 }
641 }
642 #endif
643
644 #if defined(MEM_RECLAIM)
645 remove_node(node);
646 #endif
647
648 #if defined(MEM_TAG)
649 /* do this After remove_node, so remove_node can check validity of nodes */
650 node->tag = ~node->tag;
651 #endif
652
653 #if defined(MEM_STATS)
654 mem_stats_free(((MEMNODE*)block)->size);
655 #endif
656
657 FREE(block);
658 }
659
660
661 /****************************************************************************/
662 /* Starts a new memory pool. The next mem_release() call will
663 only release memory allocated after this call. */
mem_mark()664 void mem_mark()
665 {
666 #if defined(MEM_RECLAIM)
667 poolno++;
668 #endif
669 }
670
671
672 /****************************************************************************/
673 /* Releases all unfree'd memory from current memory pool */
mem_release()674 void mem_release()
675 {
676 #if defined(MEM_RECLAIM)
677 OStream *f = NULL;
678 MEMNODE *p, *tmp;
679 size_t totsize;
680
681 p = memlist;
682 totsize = 0;
683
684 #if defined(MEM_TRACE)
685 if (p != NULL && (p->poolno == poolno))
686 f = New_OStream(MEM_LOG_FNAME, POV_File_Data_LOG, true);
687 #endif /* MEM_TRACE */
688
689 while (p != NULL && (p->poolno == poolno))
690 {
691 #if defined(MEM_TRACE)
692
693 #if defined(MEM_TAG)
694 if (!mem_check_tag(p))
695 Debug_Info("mem_release(): Memory pointer corrupt!\n");
696 #endif /* MEM_TAG */
697
698 totsize += (p->size - NODESIZE - (MEM_GUARD_SIZE * 2));
699 if (!leak_msg)
700 {
701 Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME);
702 leak_msg = true;
703 }
704
705 if (f != NULL)
706 f->printf("File:%13s Line:%4d Size:%lu\n", p->file, p->line, (unsigned long)(p->size - NODESIZE - (MEM_GUARD_SIZE * 2)));
707 #endif /* MEM_TRACE */
708
709 #if defined(MEM_STATS)
710 mem_stats_free(p->size);
711 #endif
712
713 tmp = p;
714 p = p->next;
715 remove_node(tmp);
716 FREE(tmp);
717 }
718
719 if (f != NULL)
720 delete f;
721
722 if (totsize > 0)
723 Debug_Info("%lu bytes reclaimed (pool #%d)\n", totsize, poolno);
724
725 if (poolno > 0)
726 poolno--;
727
728 #if defined(MEM_STATS)
729 /* reinitialize the stats structure for next time through */
730 mem_stats_init();
731 #endif
732
733 #endif /* MEM_RECLAIM */
734 }
735
736
737 /****************************************************************************/
738 /* Released all unfree'd memory from all pools */
mem_release_all()739 void mem_release_all()
740 {
741 #if defined(MEM_RECLAIM)
742 OStream *f = NULL;
743 MEMNODE *p, *tmp;
744 size_t totsize;
745
746 Send_Progress("Reclaiming memory", PROGRESS_RECLAIMING_MEMORY);
747
748 p = memlist;
749 totsize = 0;
750
751 #if defined(MEM_TRACE)
752 if (p != NULL)
753 f = New_OStream(MEM_LOG_FNAME, POV_File_Data_LOG, true);
754 #endif
755
756 while (p != NULL)
757 {
758 #if defined(MEM_TRACE)
759
760 #if defined(MEM_TAG)
761 if (!mem_check_tag(p))
762 Debug_Info("mem_release_all(): Memory pointer corrupt!\n");
763 #endif /* MEM_TAG */
764
765 totsize += (p->size - NODESIZE - (MEM_GUARD_SIZE * 2));
766 if (!leak_msg)
767 {
768 Debug_Info("Memory leakage detected, see file '%s' for list\n",MEM_LOG_FNAME);
769 leak_msg = true;
770 }
771
772 if (f != NULL)
773 f->printf("File:%13s Line:%4d Size:%lu\n", p->file, p->line, (unsigned long)(p->size - NODESIZE - (MEM_GUARD_SIZE * 2)));
774 #endif
775
776 #if defined(MEM_STATS)
777 /* This is after we have printed stats, and this may slow us down a little, */
778 /* so we may want to simply re-initialize the mem-stats at the end of this loop. */
779 mem_stats_free(p->size);
780 #endif
781
782 tmp = p;
783 p = p->next;
784 remove_node(tmp);
785 FREE(tmp);
786 }
787
788 if (f != NULL)
789 delete f;
790
791 if (totsize > 0)
792 Debug_Info("\n%lu bytes reclaimed\n", totsize);
793
794 poolno = 0;
795 memlist = NULL;
796 #endif
797
798 #if defined(MEM_STATS)
799 /* reinitialize the stats structure for next time through */
800 mem_stats_init();
801 #endif
802
803 }
804
805
806 /****************************************************************************/
807 #if defined(MEM_RECLAIM)
808 /* Adds a new node to the 'allocated' list */
add_node(MEMNODE * node)809 static void add_node(MEMNODE *node)
810 {
811
812 #if defined(MEM_TAG)
813 if (!mem_check_tag(node))
814 Debug_Info("add_node(): Memory pointer corrupt!\n");
815 #endif /* MEM_TAG */
816
817 if (memlist == NULL)
818 {
819 memlist = node;
820 node->poolno = poolno;
821 node->prev = NULL;
822 node->next = NULL;
823 num_nodes = 0;
824 }
825 else
826 {
827 memlist->prev = node;
828 node->poolno = poolno;
829 node->prev = NULL;
830 node->next = memlist;
831 memlist = node;
832 }
833 num_nodes++;
834 }
835
836
837 /****************************************************************************/
838 /* Detatches a node from the 'allocated' list but doesn't free it */
remove_node(MEMNODE * node)839 static void remove_node(MEMNODE *node)
840 {
841
842 #if defined(MEM_TAG)
843 if (!mem_check_tag(node))
844 Debug_Info("remove_node(): Memory pointer corrupt!\n");
845 #endif /* MEM_TAG */
846
847 num_nodes--;
848 if (node->prev != NULL)
849 node->prev->next = node->next;
850
851 if (node->next != NULL)
852 node->next->prev = node->prev;
853
854 if (memlist == node)
855 {
856 #if defined(MEM_TAG)
857 /* check node->next if it is non-null, to insure it is safe to assign. */
858 /* if it is null, it is safe since it is the last in the list. */
859 if (node->next)
860 if (!mem_check_tag(node->next))
861 Debug_Info("remove_node(): memlist pointer corrupt!\n");
862 #endif /* MEM_TAG */
863
864 memlist = node->next;
865 }
866
867 }
868
869 #endif /* MEM_RECLAIM */
870
871
872 /****************************************************************************/
873 /* A strdup routine that uses POV_MALLOC */
874 /****************************************************************************/
pov_strdup(const char * s)875 char *pov_strdup(const char *s)
876 {
877 char *New;
878
879 New=(char *)POV_MALLOC(strlen(s)+1,s);
880 strcpy(New,s);
881 return (New);
882 }
883
884 /****************************************************************************/
885 /* A memmove routine for those systems that don't have one */
886 /****************************************************************************/
887
pov_memmove(void * dest,void * src,size_t length)888 void *pov_memmove (void *dest, void *src, size_t length)
889 {
890 char *csrc =(char *)src;
891 char *cdest=(char *)dest;
892
893 if (csrc < cdest && csrc + length >= cdest)
894 {
895 size_t size = cdest - csrc;
896
897 while (length > 0)
898 {
899 POV_MEMCPY(cdest + length - size, csrc + length - size, size);
900
901 length -= size;
902
903 if (length < size)
904 size = length;
905 }
906 }
907 /* I'm not sure if this is needed, but my docs on memcpy say the regions
908 * can't overlap, so theoretically we need to special case this. If you
909 * don't think it's necessary, you can just comment this part out.
910 */
911 else if (cdest < csrc && cdest + length >= csrc)
912 {
913 char *new_dest = cdest;
914 size_t size = csrc - cdest;
915
916 while (length > 0)
917 {
918 POV_MEMCPY(new_dest, csrc, length);
919
920 new_dest += size;
921 csrc += size;
922 length -= size;
923
924 if (length < size)
925 size = length;
926 }
927 }
928 else
929 {
930 POV_MEMCPY(cdest, csrc, length);
931 }
932
933 return cdest;
934 }
935
936
937 /****************************************************************************/
938 /* Memory Statistics gathering routines */
939 /****************************************************************************/
940
941 #if defined(MEM_STATS)
942
943 /****************************************************************************/
mem_stats_init()944 static void mem_stats_init()
945 {
946 mem_stats.smallest_alloc = 65535; /* Must be an unsigned number */
947 mem_stats.largest_alloc = 0;
948 mem_stats.current_mem_usage = 0;
949 mem_stats.largest_mem_usage = 0;
950 #if (MEM_STATS>=2)
951 mem_stats.total_allocs = 0;
952 mem_stats.total_frees = 0;
953 mem_stats.largest_file = "none";
954 mem_stats.largest_line = -1;
955 mem_stats.smallest_file = "none";
956 mem_stats.smallest_line = -1;
957 #endif
958 }
959
960 /****************************************************************************/
961 /* update appropriate fields when an allocation takes place */
mem_stats_alloc(size_t nbytes,const char * file,int line)962 static void mem_stats_alloc(size_t nbytes, const char *file, int line)
963 {
964 /* update the fields */
965 if (((int) mem_stats.smallest_alloc<0) || (nbytes<mem_stats.smallest_alloc))
966 {
967 mem_stats.smallest_alloc = nbytes;
968 #if (MEM_STATS>=2)
969 mem_stats.smallest_file = file;
970 mem_stats.smallest_line = line;
971 #endif
972 }
973
974 if (nbytes>mem_stats.largest_alloc)
975 {
976 mem_stats.largest_alloc = nbytes;
977 #if (MEM_STATS>=2)
978 mem_stats.largest_file = file;
979 mem_stats.largest_line = line;
980 #endif
981 }
982
983 #if (MEM_STATS>=2)
984 mem_stats.total_allocs++;
985 #endif
986
987 mem_stats.current_mem_usage += nbytes;
988
989 if (mem_stats.current_mem_usage>mem_stats.largest_mem_usage)
990 {
991 mem_stats.largest_mem_usage = mem_stats.current_mem_usage;
992 }
993
994 }
995
996 /****************************************************************************/
997 /* update appropriate fields when a free takes place */
mem_stats_free(size_t nbytes)998 static void mem_stats_free(size_t nbytes)
999 {
1000 /* update the fields */
1001 mem_stats.current_mem_usage -= nbytes;
1002 #if (MEM_STATS>=2)
1003 mem_stats.total_frees++;
1004 #endif
1005 }
1006
1007 /****************************************************************************/
1008 /* Level 1 */
1009
1010 /****************************************************************************/
mem_stats_smallest_alloc()1011 size_t mem_stats_smallest_alloc()
1012 {
1013 return mem_stats.smallest_alloc;
1014 }
1015 /****************************************************************************/
mem_stats_largest_alloc()1016 size_t mem_stats_largest_alloc()
1017 {
1018 return mem_stats.largest_alloc;
1019 }
1020 /****************************************************************************/
mem_stats_current_mem_usage()1021 size_t mem_stats_current_mem_usage()
1022 {
1023 return mem_stats.current_mem_usage;
1024 }
1025 /****************************************************************************/
mem_stats_largest_mem_usage()1026 size_t mem_stats_largest_mem_usage()
1027 {
1028 return mem_stats.largest_mem_usage;
1029 }
1030
1031 /****************************************************************************/
1032 /* Level 2 */
1033
1034 #if (MEM_STATS>=2)
1035
1036 /****************************************************************************/
mem_stats_smallest_file()1037 const char *mem_stats_smallest_file()
1038 {
1039 return mem_stats.smallest_file;
1040 }
1041 /****************************************************************************/
mem_stats_smallest_line()1042 int mem_stats_smallest_line()
1043 {
1044 return mem_stats.smallest_line;
1045 }
1046 /****************************************************************************/
mem_stats_largest_file()1047 const char *mem_stats_largest_file()
1048 {
1049 return mem_stats.largest_file;
1050 }
1051 /****************************************************************************/
mem_stats_largest_line()1052 int mem_stats_largest_line()
1053 {
1054 return mem_stats.largest_line;
1055 }
1056 /****************************************************************************/
mem_stats_total_allocs()1057 long int mem_stats_total_allocs()
1058 {
1059 return mem_stats.total_allocs;
1060 }
1061 /****************************************************************************/
mem_stats_total_frees()1062 long int mem_stats_total_frees()
1063 {
1064 return mem_stats.total_frees;
1065 }
1066
1067 #endif
1068
1069 #endif /* MEM_STATS */
1070
1071 END_POV_NAMESPACE
1072