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