1 /* $Id: mem.c,v 1.14 2004-08-01 16:28:33 schaffner Exp $ */
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.
17 * Files for debugging memory allocator
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 );
37 #if defined(__APPLE__) && defined(__MACH__)
38 #include <sys/malloc.h>
39 #elif defined(macintosh)
52 #include <Processes.h>
53 ubyte virtual_memory_on = 0;
55 #if defined(RELEASE) || defined(NDEBUG)
63 static int sMemStatsFileInitialized = false;
64 static FILE* sMemStatsFile = NULL;
65 static char sMemStatsFileName[32] = "memstats.txt";
66 #endif // end of if MEMSTATS
68 #else // no memstats on pc
72 #define FULL_MEM_CHECKING 1
74 #if defined(FULL_MEM_CHECKING) && !defined(NDEBUG)
77 #define CHECKBYTE 0xFC
79 #define MAX_INDEX 10000
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;
89 int show_mem_info = 1;
91 static int free_list[MAX_INDEX];
93 static int num_blocks = 0;
95 static int Initialized = 0;
97 static int LargestIndex = 0;
99 int out_of_memory = 0;
101 void mem_display_blocks();
109 for (i=0; i<MAX_INDEX; i++ )
123 atexit(mem_display_blocks);
127 // need to check for virtual memory since we will need to do some
128 // tricks based on whether it is on or not
132 THz theD2PartionPtr = nil;
133 unsigned long thePartitionSize = 0;
136 MoreMasters(); // allocate 240 more master pointers (mainly for snd handles)
140 virtual_memory_on = 0;
141 if(Gestalt(gestaltVMAttr, &mem_type) == noErr)
143 virtual_memory_on = 1;
146 sMemStatsFile = fopen(sMemStatsFileName, "wt");
148 if (sMemStatsFile != NULL)
150 sMemStatsFileInitialized = true;
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",
158 #endif // end of ifdef MEMSTATS
161 #endif // end of ifdef macintosh
165 void PrintInfo( int id )
167 fprintf( stderr, "\tBlock '%s' created in %s, line %d.\n", Varname[id], Filename[id], LineNum[id] );
171 void * mem_malloc( unsigned int size, char * var, char * filename, int line, int fill_zero )
180 // printf("malloc: %d %s %d\n", size, filename, line);
184 unsigned long theFreeMem = 0;
186 if (sMemStatsFileInitialized)
188 theFreeMem = FreeMem();
190 fprintf(sMemStatsFile,
191 "\n%9u bytes free before attempting: MALLOC %9u bytes.",
196 #endif // end of ifdef memstats
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" );
204 id = free_list[ num_blocks++ ];
206 if (id > LargestIndex ) LargestIndex = id;
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" );
216 ptr = malloc( size+CHECKSIZE );
218 ptr = (void *)NewPtrClear( size+CHECKSIZE );
222 for (j=0; j<=LargestIndex; j++ )
224 if (Present[j] && MallocBase[j] == (unsigned int)ptr )
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" );
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" );
242 MallocBase[id] = ptr;
243 MallocSize[id] = size;
245 Filename[id] = filename;
251 BytesMalloced += size;
253 for (i=0; i<CHECKSIZE; i++ )
254 pc[size+i] = CHECKBYTE;
257 memset( ptr, 0, size );
263 int mem_find_id( void * buffer )
267 for (i=0; i<=LargestIndex; i++ )
269 if (MallocBase[i] == buffer )
276 int mem_check_integrity( int block_number )
281 CheckData = (ubyte *)((char *)MallocBase[block_number] + MallocSize[block_number]);
285 for (i=0; i<CHECKSIZE; i++ )
286 if (CheckData[i] != CHECKBYTE ) {
288 fprintf( stderr, "OA: %p ", &CheckData[i] );
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 );
302 void mem_free( void * buffer )
311 unsigned long theFreeMem = 0;
313 if (sMemStatsFileInitialized)
315 theFreeMem = FreeMem();
317 fprintf(sMemStatsFile,
318 "\n%9u bytes free before attempting: FREE", theFreeMem);
321 #endif // end of ifdef memstats
323 if (buffer==NULL && (!out_of_memory))
325 fprintf( stderr, "\nMEM_FREE_NULL: An attempt was made to free the null pointer.\n" );
326 Warning( "MEM: Freeing the NULL pointer!" );
331 id = mem_find_id( buffer );
333 if (id==-1 && (!out_of_memory))
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!" );
341 mem_check_integrity( id );
343 BytesMalloced -= MallocSize[id];
348 DisposePtr( (Ptr)buffer );
356 free_list[ --num_blocks ] = id;
359 void *mem_realloc(void * buffer, unsigned int size, char * var, char * filename, int line)
370 } else if (buffer == NULL) {
371 newbuffer = mem_malloc(size, var, filename, line, 0);
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);
385 char *mem_strdup(char *str, char *var, char *filename, int line)
389 newstr = mem_malloc(strlen(str) + 1, var, filename, line, 0);
395 void mem_display_blocks()
399 if (Initialized==0) return;
403 if (sMemStatsFileInitialized)
405 unsigned long theFreeMem = 0;
407 theFreeMem = FreeMem();
409 fprintf(sMemStatsFile,
410 "\n%9u bytes free before closing MEMSTATS file.", theFreeMem);
411 fprintf(sMemStatsFile, "\nMemory Stats File Closed.");
412 fclose(sMemStatsFile);
415 #endif // end of ifdef memstats
418 for (i=0; i<=LargestIndex; i++ )
420 if (Present[i]==1 && (!out_of_memory))
424 fprintf( stderr, "\nMEM_LEAKAGE: Memory block has not been freed.\n" );
427 mem_free( (void *)MallocBase[i] );
431 if (numleft && (!out_of_memory))
433 Warning( "MEM: %d blocks were left allocated!\n", numleft );
438 void mem_validate_heap()
442 for (i=0; i<LargestIndex; i++ )
444 mem_check_integrity( i );
452 ef = fopen( "DESCENT.MEM", "wt" );
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] );
460 fprintf( ef, "%d bytes (%d Kbytes) allocated.\n", size, size/1024 );
466 static int Initialized = 0;
467 static unsigned int SmallestAddress = 0xFFFFFFF;
468 static unsigned int LargestAddress = 0x0;
469 static unsigned int BytesMalloced = 0;
471 void mem_display_blocks();
474 #define CHECKBYTE 0xFC
476 int show_mem_info = 0;
482 SmallestAddress = 0xFFFFFFF;
483 LargestAddress = 0x0;
485 atexit(mem_display_blocks);
489 // need to check for virtual memory since we will need to do some
490 // tricks based on whether it is on or not
494 THz theD2PartionPtr = nil;
495 unsigned long thePartitionSize = 0;
498 MoreMasters(); // allocate 240 more master pointers (mainly for snd handles)
502 virtual_memory_on = 0;
503 if(Gestalt(gestaltVMAttr, &mem_type) == noErr)
505 virtual_memory_on = 1;
508 sMemStatsFile = fopen(sMemStatsFileName, "wt");
510 if (sMemStatsFile != NULL)
512 sMemStatsFileInitialized = true;
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",
520 #endif // end of ifdef memstats
523 #endif // end of ifdef macintosh
527 void * mem_malloc( unsigned int size, char * var, char * filename, int line, int fill_zero )
538 unsigned long theFreeMem = 0;
540 if (sMemStatsFileInitialized)
542 theFreeMem = FreeMem();
544 fprintf(sMemStatsFile,
545 "\n%9u bytes free before attempting: MALLOC %9u bytes.",
550 #endif // end of ifdef memstats
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" );
560 ptr = malloc( size + CHECKSIZE );
562 ptr = (void *)NewPtrClear( size+CHECKSIZE );
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" );
572 base = (unsigned int)ptr;
573 if ( base < SmallestAddress ) SmallestAddress = base;
574 if ( (base+size) > LargestAddress ) LargestAddress = base+size;
579 BytesMalloced += *psize;
582 memset( ptr, 0, size );
587 void mem_free( void * buffer )
589 int * psize = (int *)buffer;
597 unsigned long theFreeMem = 0;
599 if (sMemStatsFileInitialized)
601 theFreeMem = FreeMem();
603 fprintf(sMemStatsFile,
604 "\n%9u bytes free before attempting: FREE", theFreeMem);
607 #endif // end of ifdef memstats
610 fprintf( stderr, "\nMEM_FREE_NULL: An attempt was made to free the null pointer.\n" );
611 Warning( "MEM: Freeing the NULL pointer!" );
616 BytesMalloced -= *psize;
621 DisposePtr( (Ptr)buffer );
625 void mem_display_blocks()
627 if (Initialized==0) return;
631 if (sMemStatsFileInitialized)
633 unsigned long theFreeMem = 0;
635 theFreeMem = FreeMem();
637 fprintf(sMemStatsFile,
638 "\n%9u bytes free before closing MEMSTATS file.", theFreeMem);
639 fprintf(sMemStatsFile, "\nMemory Stats File Closed.");
640 fclose(sMemStatsFile);
643 #endif // end of ifdef memstats
645 if (BytesMalloced != 0 ) {
646 fprintf( stderr, "\nMEM_LEAKAGE: %d bytes of memory have not been freed.\n", BytesMalloced );
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 );
658 void mem_validate_heap()
671 // routine to try and compact and purge the process manager zone to squeeze
672 // some temporary memory out of it for QT purposes.
678 THz appZone, processZone;
681 // compact the system zone to try and squeeze some temporary memory out of it
682 MaxMemSys(&heapSize);
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);
693 SetZone(processZone);
694 MaxMem(&heapSize); // purge and compact the Process Manager Zone.
696 DisposeHandle(tempHandle);