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