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