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