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