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