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