This commit was manufactured by cvs2svn to create tag 'd2x-0_1_2'.
[btb/d2x.git] / mem / mem.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/mem/mem.c,v $
16  * $Revision: 1.7 $
17  * $Author: bradleyb $
18  * $Date: 2001-11-09 06:56:41 $
19  *
20  * Files for debugging memory allocator
21  *
22  * $Log: not supported by cvs2svn $
23  * Revision 1.6  2001/11/08 10:17:40  bradleyb
24  * added d_realloc/mem_realloc functions
25  *
26  * Revision 1.5  2001/10/19 08:06:20  bradleyb
27  * Partial application of linux/alpha patch.  Courtesy of Falk Hueffner <falk.hueffner@student.uni-tuebingen.de>
28  *
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <conf.h>
33 #endif
34
35 // Warning( "MEM: Too many malloc's!" );
36 // Warning( "MEM: Malloc returnd an already alloced block!" );
37 // Warning( "MEM: Malloc Failed!" );
38 // Warning( "MEM: Freeing the NULL pointer!" );
39 // Warning( "MEM: Freeing a non-malloced pointer!" );
40 // Warning( "MEM: %d/%d check bytes were overwritten at the end of %8x", ec, CHECKSIZE, buffer  );
41 // Warning( "MEM: %d blocks were left allocated!", numleft );
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <malloc.h>
47
48 #include "pstypes.h"
49 #include "mono.h"
50 #include "error.h"
51
52 #ifdef MACINTOSH
53
54         #include <Gestalt.h>
55         #include <Memory.h>
56         #include <Processes.h>
57         ubyte virtual_memory_on = 0;
58
59         #if defined(RELEASE) || defined(NDEBUG)
60                 #define MEMSTATS        0
61         #else
62                 #define MEMSTATS        1
63         #endif
64
65         
66         #if MEMSTATS
67                 static  int             sMemStatsFileInitialized        = false;
68                 static  FILE*   sMemStatsFile                           = NULL;
69                 static  char    sMemStatsFileName[32]           = "memstats.txt";
70         #endif  // end of if MEMSTATS
71         
72 #else   // no memstats on pc
73         #define MEMSTATS 0
74 #endif
75
76 #define FULL_MEM_CHECKING 1
77
78 #if defined(FULL_MEM_CHECKING) && !defined(NDEBUG)
79
80 #define CHECKSIZE 16
81 #define CHECKBYTE 0xFC
82
83 #define MAX_INDEX 10000
84
85 static void *MallocBase[MAX_INDEX];
86 static unsigned int MallocSize[MAX_INDEX];
87 static unsigned char Present[MAX_INDEX];
88 static char * Filename[MAX_INDEX];
89 static char * Varname[MAX_INDEX];
90 static int LineNum[MAX_INDEX];
91 static int BytesMalloced = 0;
92
93 int show_mem_info = 1;
94
95 static int free_list[MAX_INDEX];
96
97 static int num_blocks = 0;
98
99 static int Initialized = 0;
100
101 static int LargestIndex = 0;
102
103 int out_of_memory = 0;
104
105 void mem_display_blocks();
106
107 void mem_init()
108 {
109         int i;
110         
111         Initialized = 1;
112
113         for (i=0; i<MAX_INDEX; i++ )
114         {
115                 free_list[i] = i;
116                 MallocBase[i] = 0;
117                 MallocSize[i] = 0;
118                 Present[i] = 0;
119                 Filename[i] = NULL;
120                 Varname[i] = NULL;
121                 LineNum[i] = 0;
122         }
123
124         num_blocks = 0;
125         LargestIndex = 0;
126
127         atexit(mem_display_blocks);
128         
129 #ifdef MACINTOSH
130
131 // need to check for virtual memory since we will need to do some
132 // tricks based on whether it is on or not
133
134         {
135                 int                     mem_type;
136                 THz                             theD2PartionPtr = nil;
137                 unsigned long   thePartitionSize = 0;
138                 
139                 MaxApplZone();
140                 MoreMasters();                  // allocate 240 more master pointers (mainly for snd handles)
141                 MoreMasters();
142                 MoreMasters();
143                 MoreMasters();
144                 virtual_memory_on = 0;
145                 if(Gestalt(gestaltVMAttr, &mem_type) == noErr)
146                         if (mem_type & 0x1)
147                                 virtual_memory_on = 1;
148                                 
149                 #if MEMSTATS
150                         sMemStatsFile = fopen(sMemStatsFileName, "wt");
151         
152                         if (sMemStatsFile != NULL)
153                         {
154                                 sMemStatsFileInitialized = true;
155         
156                                 theD2PartionPtr = ApplicationZone();
157                                 thePartitionSize =  ((unsigned long) theD2PartionPtr->bkLim) - ((unsigned long) theD2PartionPtr);
158                                 fprintf(sMemStatsFile, "\nMemory Stats File Initialized.");
159                                 fprintf(sMemStatsFile, "\nDescent 2 launched in partition of %u bytes.\n",
160                                                 thePartitionSize);
161                         }
162                 #endif  // end of ifdef MEMSTATS
163         }
164
165 #endif  // end of ifdef macintosh
166
167 }
168
169 void PrintInfo( int id )
170 {
171         fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", Varname[id], Filename[id], LineNum[id] );
172 }
173
174
175 void * mem_malloc( unsigned int size, char * var, char * filename, int line, int fill_zero )
176 {
177         int i, id;
178         void *ptr;
179         char * pc;
180
181         if (Initialized==0)
182                 mem_init();
183
184 //      printf("malloc: %d %s %d\n", size, filename, line);
185
186 #if MEMSTATS
187         {
188                 unsigned long   theFreeMem = 0;
189         
190                 if (sMemStatsFileInitialized)
191                 {
192                         theFreeMem = FreeMem();
193                 
194                         fprintf(sMemStatsFile,
195                                         "\n%9u bytes free before attempting: MALLOC %9u bytes.",
196                                         theFreeMem,
197                                         size);
198                 }
199         }
200 #endif  // end of ifdef memstats
201
202         if ( num_blocks >= MAX_INDEX )  {
203                 fprintf( stderr,"\nMEM_OUT_OF_SLOTS: Not enough space in mem.c to hold all the mallocs.\n" );           
204                 fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", var, filename, line );
205                 Error( "MEM_OUT_OF_SLOTS" );
206         }
207
208         id = free_list[ num_blocks++ ];
209
210         if (id > LargestIndex ) LargestIndex = id;
211
212         if (id==-1)
213         {
214                 fprintf( stderr,"\nMEM_OUT_OF_SLOTS: Not enough space in mem.c to hold all the mallocs.\n" );           
215                 fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", Varname[id], Filename[id], LineNum[id] );
216                 Error( "MEM_OUT_OF_SLOTS" );
217         }
218
219 #ifndef MACINTOSH
220         ptr = malloc( size+CHECKSIZE );
221 #else
222         ptr = (void *)NewPtrClear( size+CHECKSIZE );
223 #endif
224
225         /*
226         for (j=0; j<=LargestIndex; j++ )
227         {
228                 if (Present[j] && MallocBase[j] == (unsigned int)ptr )
229                 {
230                         fprintf( stderr,"\nMEM_SPACE_USED: Malloc returned a block that is already marked as preset.\n" );
231                         fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", Varname[id], Filename[id], Line[id] );
232                         Warning( "MEM_SPACE_USED" );
233                         Int3();
234                         }
235         }
236         */
237
238         if (ptr==NULL)
239         {
240                 out_of_memory = 1;
241                 fprintf( stderr, "\nMEM_OUT_OF_MEMORY: Malloc returned NULL\n" );
242                 fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", Varname[id], Filename[id], LineNum[id] );
243                 Error( "MEM_OUT_OF_MEMORY" );
244         }
245
246         MallocBase[id] = ptr;
247         MallocSize[id] = size;
248         Varname[id] = var;
249         Filename[id] = filename;
250         LineNum[id] = line;
251         Present[id]    = 1;
252
253         pc = (char *)ptr;
254
255         BytesMalloced += size;
256
257         for (i=0; i<CHECKSIZE; i++ )
258                 pc[size+i] = CHECKBYTE;
259
260         if (fill_zero)
261                 memset( ptr, 0, size );
262
263         return ptr;
264
265 }
266
267 int mem_find_id( void * buffer )
268 {
269         int i;
270
271         for (i=0; i<=LargestIndex; i++ )
272           if (Present[i]==1)
273             if (MallocBase[i] == buffer )
274               return i;
275
276         // Didn't find id.
277         return -1;
278 }
279
280 int mem_check_integrity( int block_number )
281 {
282         int i, ErrorCount;
283         ubyte * CheckData;
284
285         CheckData = (char *)(MallocBase[block_number] + MallocSize[block_number]);
286
287         ErrorCount = 0;
288                         
289         for (i=0; i<CHECKSIZE; i++ )
290                 if (CheckData[i] != CHECKBYTE ) {
291                         ErrorCount++;
292                         fprintf( stderr, "OA: %p ", &CheckData[i] );
293                 }
294
295         if (ErrorCount &&  (!out_of_memory))    {
296                 fprintf( stderr, "\nMEM_OVERWRITE: Memory after the end of allocated block overwritten.\n" );
297                 PrintInfo( block_number );
298                 fprintf( stderr, "\t%d/%d check bytes were overwritten.\n", ErrorCount, CHECKSIZE );
299                 Int3();
300         }
301
302         return ErrorCount;
303
304 }
305
306 void mem_free( void * buffer )
307 {
308         int id;
309
310         if (Initialized==0)
311                 mem_init();
312
313 #if MEMSTATS
314         {
315                 unsigned long   theFreeMem = 0;
316         
317                 if (sMemStatsFileInitialized)
318                 {
319                         theFreeMem = FreeMem();
320                 
321                         fprintf(sMemStatsFile,
322                                         "\n%9u bytes free before attempting: FREE", theFreeMem);
323                 }
324         }
325 #endif  // end of ifdef memstats
326
327         if (buffer==NULL  &&  (!out_of_memory))
328         {
329                 fprintf( stderr, "\nMEM_FREE_NULL: An attempt was made to free the null pointer.\n" );
330                 Warning( "MEM: Freeing the NULL pointer!" );
331                 Int3();
332                 return;
333         }
334
335         id = mem_find_id( buffer );
336
337         if (id==-1 &&  (!out_of_memory))
338         {
339                 fprintf( stderr, "\nMEM_FREE_NOMALLOC: An attempt was made to free a ptr that wasn't\nallocated with mem.h included.\n" );
340                 Warning( "MEM: Freeing a non-malloced pointer!" );
341                 Int3();
342                 return;
343         }
344         
345         mem_check_integrity( id );
346         
347         BytesMalloced -= MallocSize[id];
348
349 #ifndef MACINTOSH
350         free( buffer );
351 #else
352         DisposePtr( (Ptr)buffer );
353 #endif
354         
355         
356         Present[id] = 0;
357         MallocBase[id] = 0;
358         MallocSize[id] = 0;
359
360         free_list[ --num_blocks ] = id;
361 }
362
363 void *mem_realloc(void * buffer, unsigned int size, char * var, char * filename, int line)
364 {
365         void *newbuffer;
366         int id;
367
368         if (Initialized==0)
369                 mem_init();
370
371         if (size == 0) {
372                 mem_free(buffer);
373                 newbuffer = NULL;
374         } else if (buffer == NULL) {
375                 newbuffer = mem_malloc(size, var, filename, line, 0);
376         } else {
377                 newbuffer = mem_malloc(size, var, filename, line, 0);
378                 if (newbuffer != NULL) {
379                         id = mem_find_id(buffer);
380                         if (MallocSize[id] < size)
381                                 size = MallocSize[id];
382                         memcpy(newbuffer, buffer, size);
383                         mem_free(buffer);
384                 }
385         }
386         return newbuffer;
387 }
388
389 void mem_display_blocks()
390 {
391         int i, numleft;
392
393         if (Initialized==0) return;
394         
395 #if MEMSTATS
396         {       
397                 if (sMemStatsFileInitialized)
398                 {
399                         unsigned long   theFreeMem = 0;
400
401                         theFreeMem = FreeMem();
402                 
403                         fprintf(sMemStatsFile,
404                                         "\n%9u bytes free before closing MEMSTATS file.", theFreeMem);
405                         fprintf(sMemStatsFile, "\nMemory Stats File Closed.");
406                         fclose(sMemStatsFile);
407                 }
408         }
409 #endif  // end of ifdef memstats
410
411         numleft = 0;
412         for (i=0; i<=LargestIndex; i++ )
413         {
414                 if (Present[i]==1 &&  (!out_of_memory))
415                 {
416                         numleft++;
417                         if (show_mem_info)      {
418                                 fprintf( stderr, "\nMEM_LEAKAGE: Memory block has not been freed.\n" );
419                                 PrintInfo( i );
420                         }
421                         mem_free( (void *)MallocBase[i] );
422                 }
423         }
424
425         if (numleft &&  (!out_of_memory))
426         {
427                 Warning( "MEM: %d blocks were left allocated!", numleft );
428         }
429
430 }
431
432 void mem_validate_heap()
433 {
434         int i;
435         
436         for (i=0; i<LargestIndex; i++  )
437                 if (Present[i]==1 )
438                         mem_check_integrity( i );
439 }
440
441 void mem_print_all()
442 {
443         FILE * ef;
444         int i, size = 0;
445
446         ef = fopen( "DESCENT.MEM", "wt" );
447         
448         for (i=0; i<LargestIndex; i++  )
449                 if (Present[i]==1 )     {
450                         size += MallocSize[i];
451                         //fprintf( ef, "Var:%s\t File:%s\t Line:%d\t Size:%d Base:%x\n", Varname[i], Filename[i], Line[i], MallocSize[i], MallocBase[i] );
452                         fprintf( ef, "%12d bytes in %s declared in %s, line %d\n", MallocSize[i], Varname[i], Filename[i], LineNum[i]  );
453                 }
454         fprintf( ef, "%d bytes (%d Kbytes) allocated.\n", size, size/1024 ); 
455         fclose(ef);
456 }
457
458 #else
459
460 static int Initialized = 0;
461 static unsigned int SmallestAddress = 0xFFFFFFF;
462 static unsigned int LargestAddress = 0x0;
463 static unsigned int BytesMalloced = 0;
464
465 void mem_display_blocks();
466
467 #define CHECKSIZE 16
468 #define CHECKBYTE 0xFC
469
470 int show_mem_info = 0;
471
472 void mem_init()
473 {
474         Initialized = 1;
475
476         SmallestAddress = 0xFFFFFFF;
477         LargestAddress = 0x0;
478
479         atexit(mem_display_blocks);
480
481 #ifdef MACINTOSH
482
483         // need to check for virtual memory since we will need to do some
484         // tricks based on whether it is on or not
485         
486         {
487                 int                     mem_type;
488                 THz                             theD2PartionPtr = nil;
489                 unsigned long   thePartitionSize = 0;
490                 
491                 MaxApplZone();
492                 MoreMasters();          // allocate 240 more master pointers (mainly for snd handles)
493                 MoreMasters();
494                 MoreMasters();
495                 MoreMasters();
496                 virtual_memory_on = 0;
497                 if(Gestalt(gestaltVMAttr, &mem_type) == noErr)
498                         if (mem_type & 0x1)
499                                 virtual_memory_on = 1;
500                                 
501                 #if MEMSTATS
502                         sMemStatsFile = fopen(sMemStatsFileName, "wt");
503
504                         if (sMemStatsFile != NULL)
505                         {
506                                 sMemStatsFileInitialized = true;
507
508                                 theD2PartionPtr = ApplicationZone();
509                                 thePartitionSize =  ((unsigned long) theD2PartionPtr->bkLim) - ((unsigned long) theD2PartionPtr);
510                                 fprintf(sMemStatsFile, "\nMemory Stats File Initialized.");
511                                 fprintf(sMemStatsFile, "\nDescent 2 launched in partition of %u bytes.\n",
512                                                 thePartitionSize);
513                         }
514                 #endif  // end of ifdef memstats
515         }
516         
517 #endif  // end of ifdef macintosh
518
519 }
520
521 void * mem_malloc( unsigned int size, char * var, char * filename, int line, int fill_zero )
522 {
523         unsigned int base;
524         void *ptr;
525         int * psize;
526
527         if (Initialized==0)
528                 mem_init();
529
530 #if MEMSTATS
531         {
532                 unsigned long   theFreeMem = 0;
533         
534                 if (sMemStatsFileInitialized)
535                 {
536                         theFreeMem = FreeMem();
537                 
538                         fprintf(sMemStatsFile,
539                                         "\n%9u bytes free before attempting: MALLOC %9u bytes.",
540                                         theFreeMem,
541                                         size);
542                 }
543         }
544 #endif  // end of ifdef memstats
545
546         if (size==0)    {
547                 fprintf( stderr, "\nMEM_MALLOC_ZERO: Attempting to malloc 0 bytes.\n" );
548                 fprintf( stderr, "\tVar %s, file %s, line %d.\n", var, filename, line );
549                 Error( "MEM_MALLOC_ZERO" );
550                 Int3();
551         }
552
553 #ifndef MACINTOSH
554         ptr = malloc( size + CHECKSIZE );
555 #else
556         ptr = (void *)NewPtrClear( size+CHECKSIZE );
557 #endif
558
559         if (ptr==NULL)  {
560                 fprintf( stderr, "\nMEM_OUT_OF_MEMORY: Malloc returned NULL\n" );
561                 fprintf( stderr, "\tVar %s, file %s, line %d.\n", var, filename, line );
562                 Error( "MEM_OUT_OF_MEMORY" );
563                 Int3();
564         }
565
566         base = (unsigned int)ptr;
567         if ( base < SmallestAddress ) SmallestAddress = base;
568         if ( (base+size) > LargestAddress ) LargestAddress = base+size;
569
570
571         psize = (int *)ptr;
572         psize--;
573         BytesMalloced += *psize;
574
575         if (fill_zero)
576                 memset( ptr, 0, size );
577
578         return ptr;
579 }
580
581 void mem_free( void * buffer )
582 {
583         int * psize = (int *)buffer;
584         psize--;
585
586         if (Initialized==0)
587                 mem_init();
588
589 #if MEMSTATS
590         {
591                 unsigned long   theFreeMem = 0;
592         
593                 if (sMemStatsFileInitialized)
594                 {
595                         theFreeMem = FreeMem();
596                 
597                         fprintf(sMemStatsFile,
598                                         "\n%9u bytes free before attempting: FREE", theFreeMem);
599                 }
600         }
601 #endif  // end of ifdef memstats
602
603         if (buffer==NULL)       {
604                 fprintf( stderr, "\nMEM_FREE_NULL: An attempt was made to free the null pointer.\n" );
605                 Warning( "MEM: Freeing the NULL pointer!" );
606                 Int3();
607                 return;
608         }
609
610         BytesMalloced -= *psize;
611
612 #ifndef MACINTOSH
613         free( buffer );
614 #else
615         DisposePtr( (Ptr)buffer );
616 #endif
617 }
618
619 void mem_display_blocks()
620 {
621         if (Initialized==0) return;
622
623 #if MEMSTATS
624         {       
625                 if (sMemStatsFileInitialized)
626                 {
627                         unsigned long   theFreeMem = 0;
628
629                         theFreeMem = FreeMem();
630                 
631                         fprintf(sMemStatsFile,
632                                         "\n%9u bytes free before closing MEMSTATS file.", theFreeMem);
633                         fprintf(sMemStatsFile, "\nMemory Stats File Closed.");
634                         fclose(sMemStatsFile);
635                 }
636         }
637 #endif  // end of ifdef memstats
638
639         if (BytesMalloced != 0 )        {
640                 fprintf( stderr, "\nMEM_LEAKAGE: %d bytes of memory have not been freed.\n", BytesMalloced );
641         }
642
643         if (show_mem_info)      {
644                 fprintf( stderr, "\n\nMEMORY USAGE:\n" );
645                 fprintf( stderr, "  %u Kbytes dynamic data\n", (LargestAddress-SmallestAddress+512)/1024 );
646                 fprintf( stderr, "  %u Kbytes code/static data.\n", (SmallestAddress-(4*1024*1024)+512)/1024 );
647                 fprintf( stderr, "  ---------------------------\n" );
648                 fprintf( stderr, "  %u Kbytes required.\n",     (LargestAddress-(4*1024*1024)+512)/1024 );
649         }
650 }
651
652 void mem_validate_heap()
653 {
654 }
655
656 void mem_print_all()
657 {
658 }
659
660 #endif
661
662
663 #ifdef MACINTOSH
664
665 // routine to try and compact and purge the process manager zone to squeeze
666 // some temporary memory out of it for QT purposes.
667
668 void PurgeTempMem()
669 {
670         OSErr err;
671         Handle tempHandle;
672         THz appZone, processZone;
673         Size heapSize;
674         
675         // compact the system zone to try and squeeze some temporary memory out of it
676         MaxMemSys(&heapSize);
677         
678         // compact the Process Manager zone to get more temporary memory
679         appZone = ApplicationZone();
680         tempHandle = TempNewHandle(10, &err);           // temporary allocation may fail
681         if (!err && (tempHandle != NULL) ) {
682                 processZone = HandleZone(tempHandle);
683                 if ( MemError() || (processZone == NULL) ) {
684                         DisposeHandle(tempHandle);
685                         return;
686                 }
687                 SetZone(processZone);
688                 MaxMem(&heapSize);                              // purge and compact the Process Manager Zone.
689                 SetZone(appZone);
690                 DisposeHandle(tempHandle);
691         }
692 }
693
694 #endif