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