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