//************************************************************************** // // Filename : MemMan.cpp // // Purpose : function definitions for the memory manager // // Modification history : // // 11sep96:HJH - Creation // 29may97:ARM - Fix & improve MemDebugCounter handling, logging of // MemAlloc/MemFree, and reporting of any errors // //************************************************************************** //************************************************************************** // // Includes // //************************************************************************** //#ifdef JA2_PRECOMPILED_HEADERS // #include "JA2 SGP ALL.H" //#elif defined( WIZ8_PRECOMPILED_HEADERS ) // #include "WIZ8 SGP ALL.H" //#else #include "types.h" #include #include #include #include #include "MemMan.h" #include "Debug.h" #include #ifdef _DEBUG #include #endif //#endif #ifdef _DEBUG //#define DEBUG_MEM_LEAKS // turns on tracking of every MemAlloc and MemFree! #endif //************************************************************************** // // Variables // //************************************************************************** #ifdef JA2 #include "mousesystem.h" #include "MessageBoxScreen.h" STR16 gzJA2ScreenNames[] = { L"EDIT_SCREEN", L"SAVING_SCREEN", L"LOADING_SCREEN", L"ERROR_SCREEN", L"INIT_SCREEN", L"GAME_SCREEN", L"ANIEDIT_SCREEN", L"PALEDIT_SCREEN", L"DEBUG_SCREEN", L"MAP_SCREEN", L"LAPTOP_SCREEN", L"LOADSAVE_SCREEN", L"MAPUTILITY_SCREEN", L"FADE_SCREEN", L"MSG_BOX_SCREEN", L"MAINMENU_SCREEN", L"AUTORESOLVE_SCREEN", L"SAVE_LOAD_SCREEN", L"OPTIONS_SCREEN", L"SHOPKEEPER_SCREEN", L"SEX_SCREEN", L"GAME_INIT_OPTIONS_SCREEN", L"DEMO_EXIT_SCREEN", L"INTRO_SCREEN", L"CREDIT_SCREEN", #ifdef JA2BETAVERSION L"AIVIEWER_SCREEN", L"QUEST_DEBUG_SCREEN", #endif }; #endif #ifdef EXTREME_MEMORY_DEBUGGING typedef struct MEMORY_NODE { PTR pBlock; struct MEMORY_NODE *next, *prev; UINT8 *pCode; UINT32 uiSize; }MEMORY_NODE; MEMORY_NODE *gpMemoryHead = NULL; MEMORY_NODE *gpMemoryTail = NULL; UINT32 guiMemoryNodes = 0; UINT32 guiTotalMemoryNodes = 0; #endif static BOOLEAN gfMemDebug = TRUE; // debug variable for total memory currently allocated UINT32 guiMemTotal = 0; UINT32 guiMemAlloced = 0; UINT32 guiMemFreed = 0; UINT32 MemDebugCounter = 0; BOOLEAN fMemManagerInit = FALSE; //************************************************************************** // // Function Prototypes // //************************************************************************** void DebugPrint( void ); //************************************************************************** // // Functions // //************************************************************************** //************************************************************************** // // MemInit // // // // Parameter List : // Return Value : // Modification history : // // 12sep96:HJH -> modified for use by Wizardry // //************************************************************************** BOOLEAN InitializeMemoryManager( void ) { // Register the memory manager with the debugger RegisterDebugTopic(TOPIC_MEMORY_MANAGER, "Memory Manager"); MemDebugCounter = 0; guiMemTotal = 0; guiMemAlloced = 0; guiMemFreed = 0; fMemManagerInit = TRUE; #ifdef EXTREME_MEMORY_DEBUGGING gpMemoryHead = NULL; gpMemoryTail = NULL; guiMemoryNodes = 0; guiTotalMemoryNodes = 0; #endif return(TRUE); } //************************************************************************** // // MemDebug // // To set whether or not we should print debug info. // // Parameter List : // Return Value : // Modification history : // // 12sep96:HJH -> modified for use by Wizardry // //************************************************************************** void MemDebug( BOOLEAN f ) { gfMemDebug = f; } //************************************************************************** // // MemShutdown // // Shuts down the memory manager. // // Parameter List : // Return Value : // Modification history : // // 12sep96:HJH -> modified for use by Wizardry // //************************************************************************** void ShutdownMemoryManager( void ) { if ( MemDebugCounter != 0 ) { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" ")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" ")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" >>>>> MEMORY LEAK DETECTED!!! <<<<< ")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d memory blocks still allocated", MemDebugCounter )); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total STILL allocated", guiMemTotal )); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total was allocated", guiMemAlloced)); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total was freed", guiMemFreed)); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" ")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****")); DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" ")); #ifndef EXTREME_MEMORY_DEBUGGING #ifdef JA2BETAVERSION { FILE *fp; fp = fopen( "MemLeakInfo.txt", "a" ); if( fp ) { fprintf( fp, "\n\n" ); fprintf( fp, ">>>>> MEMORY LEAK DETECTED!!! <<<<<\n" ); fprintf( fp, " %d bytes memory total was allocated\n", guiMemAlloced ); fprintf( fp, "- %d bytes memory total was freed\n", guiMemFreed ); fprintf( fp, "_______________________________________________\n" ); fprintf( fp, "%d bytes memory total STILL allocated\n", guiMemTotal ); fprintf( fp, "%d memory blocks still allocated\n", MemDebugCounter ); fprintf( fp, "guiScreenExitedFrom = %S\n", gzJA2ScreenNames[ gMsgBox.uiExitScreen ] ); fprintf( fp, "\n\n" ); } fclose( fp ); } #endif #endif } UnRegisterDebugTopic( TOPIC_MEMORY_MANAGER, "Memory Manager Un-initialized" ); fMemManagerInit = FALSE; } #ifdef _DEBUG PTR MemAllocReal( UINT32 uiSize, const char *pcFile, INT32 iLine ) { PTR ptr; if( !uiSize ) { return NULL; } if ( !fMemManagerInit ) DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAlloc: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) ); ptr = _malloc_dbg( uiSize, _NORMAL_BLOCK, pcFile, iLine ); if (ptr != NULL) { guiMemTotal += uiSize; guiMemAlloced += uiSize; MemDebugCounter++; } else { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAlloc failed: %d bytes (line %d file %s)", uiSize, iLine, pcFile) ); } #ifdef DEBUG_MEM_LEAKS DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemAlloc %p: %d bytes (line %d file %s)", ptr, uiSize, iLine, pcFile) ); #endif return( ptr ); } void MemFreeReal( PTR ptr, const char *pcFile, INT32 iLine ) { UINT32 uiSize; if ( !fMemManagerInit ) DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFree: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) ); if (ptr != NULL) { uiSize = _msize(ptr); guiMemTotal -= uiSize; guiMemFreed += uiSize; _free_dbg( ptr, _NORMAL_BLOCK ); #ifdef DEBUG_MEM_LEAKS DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemFree %p: %d bytes (line %d file %s)", ptr, uiSize, iLine, pcFile) ); #endif } else { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFree ERROR: NULL ptr received (line %d file %s)", iLine, pcFile) ); } // count even a NULL ptr as a MemFree, not because it's really a memory leak, but because it is still an error of some // sort (nobody should ever be freeing NULL pointers), and this will help in tracking it down if the above DbgMessage // is not noticed. MemDebugCounter--; } PTR MemReallocReal( PTR ptr, UINT32 uiSize, const char *pcFile, INT32 iLine ) { PTR ptrNew; UINT32 uiOldSize; if ( !fMemManagerInit ) DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemRealloc: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) ); if(ptr != NULL) { uiOldSize = _msize(ptr); guiMemTotal -= uiOldSize; guiMemFreed += uiOldSize; MemDebugCounter--; } // Note that the ptr changes to ptrNew... ptrNew = _realloc_dbg( ptr, uiSize, _NORMAL_BLOCK, pcFile, iLine ); if (ptrNew == NULL) { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemReAlloc failed: ptr %d, %d -> %d bytes (line %d file %s)", ptr, uiOldSize, uiSize, iLine, pcFile) ); if ( uiSize != 0 ) { // ptr is left untouched, so undo the math above guiMemTotal += uiOldSize; guiMemFreed -= uiOldSize; MemDebugCounter++; } } else { #ifdef DEBUG_MEM_LEAKS DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemRealloc %p: Resizing %d bytes to %d bytes (line %d file %s) - New ptr %p", ptr, uiOldSize, uiSize, iLine, pcFile, ptrNew ) ); #endif guiMemTotal += uiSize; guiMemAlloced += uiSize; MemDebugCounter++; } return( ptrNew ); } #endif PTR *MemAllocLocked( UINT32 uiSize ) { PTR ptr; if ( !fMemManagerInit ) DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAllocLocked: Warning -- Memory manager not initialized!!! ") ); ptr = VirtualAlloc( NULL, uiSize, MEM_COMMIT, PAGE_READWRITE ); if ( ptr ) { VirtualLock( ptr, uiSize ); guiMemTotal += uiSize; guiMemAlloced += uiSize; MemDebugCounter++; } else { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAllocLocked failed: %d bytes", uiSize) ); } #ifdef DEBUG_MEM_LEAKS DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemAllocLocked %p: %d bytes", ptr, uiSize) ); #endif return( ptr ); } void MemFreeLocked( PTR ptr, UINT32 uiSize ) { if ( !fMemManagerInit ) DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFreeLocked: Warning -- Memory manager not initialized!!! ") ); if (ptr != NULL) { VirtualUnlock( ptr, uiSize ); VirtualFree( ptr, uiSize, MEM_RELEASE ); guiMemTotal -= uiSize; guiMemFreed += uiSize; } else { DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFreeLocked ERROR: NULL ptr received, size %d", uiSize) ); } // count even a NULL ptr as a MemFree, not because it's really a memory leak, but because it is still an error of some // sort (nobody should ever be freeing NULL pointers), and this will help in tracking it down if the above DbgMessage // is not noticed. MemDebugCounter--; #ifdef DEBUG_MEM_LEAKS DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemFreeLocked %p", ptr) ); #endif } //************************************************************************** // // MemGetFree // // // // Parameter List : // Return Value : // Modification history : // // ??sep96:HJH -> modified for use by Wizardry // //************************************************************************** UINT32 MemGetFree( void ) { MEMORYSTATUS ms; ms.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus( &ms ); return( ms.dwAvailPhys ); } //************************************************************************** // // MemGetTotalSystem // // // // Parameter List : // Return Value : // Modification history : // // May98:HJH -> Carter // //************************************************************************** UINT32 MemGetTotalSystem( void ) { MEMORYSTATUS ms; ms.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus( &ms ); return( ms.dwTotalPhys ); } //************************************************************************** // // MemCheckPool // // // // Parameter List : // Return Value : // Modification history : // // 23sep96:HJH -> modified for use by Wizardry // //************************************************************************** BOOLEAN MemCheckPool( void ) { BOOLEAN fRet = TRUE; #ifdef _DEBUG fRet = _CrtCheckMemory(); Assert( fRet ); #endif return(fRet); } #ifdef EXTREME_MEMORY_DEBUGGING PTR MemAllocXDebug( UINT32 size, const char *szCodeString, INT32 iLineNum, void *pSpecial ) { PTR ptr; UINT16 usLength; UINT8 str[70]; UINT8 *pStr; if( !size ) { return NULL; } if( !pSpecial ) { ptr = malloc( size ); } else { ptr = pSpecial; } if( ptr ) { // Set into video object list if( gpMemoryHead ) { //Add node after tail gpMemoryTail->next = (MEMORY_NODE*)malloc( sizeof( MEMORY_NODE ) ); Assert( gpMemoryTail->next ); //out of memory? gpMemoryTail->next->prev = gpMemoryTail; gpMemoryTail->next->next = NULL; gpMemoryTail = gpMemoryTail->next; } else { //new list gpMemoryHead = (MEMORY_NODE*)malloc( sizeof( MEMORY_NODE ) ); Assert( gpMemoryHead ); //out of memory? gpMemoryHead->prev = gpMemoryHead->next = NULL; gpMemoryTail = gpMemoryHead; } //record the code location of the calling creating function. pStr = strrchr( szCodeString, '\\' ); pStr++; sprintf( str, "%s -- line(%d)", pStr, iLineNum ); usLength = strlen( str ) + 1; gpMemoryTail->pCode = (UINT8*)malloc( usLength ); memset( gpMemoryTail->pCode, 0, usLength ); strcpy( gpMemoryTail->pCode, str ); //record the size gpMemoryTail->uiSize = size; //Set the hVObject into the node. gpMemoryTail->pBlock = ptr; guiMemoryNodes++; guiTotalMemoryNodes++; } return( ptr ); } void MemFreeXDebug( PTR ptr, const char *szCodeString, INT32 iLineNum, void *pSpecial ) { MEMORY_NODE *curr; if( ptr ) { curr = gpMemoryHead; while( curr ) { if( curr->pBlock == ptr ) { //Found the node, so detach it and delete it. if( !pSpecial ) { free( ptr ); } if( curr == gpMemoryHead ) { //Advance the head, because we are going to remove the head node. gpMemoryHead = gpMemoryHead->next; } if( curr == gpMemoryTail ) { //Back up the tail, because we are going to remove the tail node. gpMemoryTail = gpMemoryTail->prev; } //Detach the node from the vobject list if( curr->next ) { //Make the prev node point to the next curr->next->prev = curr->prev; } if( curr->prev ) { //Make the next node point to the prev curr->prev->next = curr->next; } //The node is now detached. Now deallocate it. free( curr ); curr = NULL; guiMemoryNodes--; return; } curr = curr->next; } } } PTR MemReallocXDebug( PTR ptr, UINT32 size, const char *szCodeString, INT32 iLineNum, void *pSpecial ) { MEMORY_NODE *curr; PTR ptrNew; UINT16 usLength; UINT8 str[70]; UINT8 *pStr; if( !ptr && size ) { return MemAllocXDebug( size, szCodeString, iLineNum, pSpecial ); } curr = gpMemoryHead; while( curr ) { if( curr->pBlock == ptr ) { // Note that the ptr changes to ptrNew... if( !pSpecial ) { ptrNew = realloc( ptr, size ); } else { ptrNew = pSpecial; } if( ptrNew ) { curr->pBlock = ptrNew; curr->uiSize = size; free( curr->pCode ); //record the code location of the calling creating function. pStr = strrchr( szCodeString, '\\' ); pStr++; sprintf( str, "%s -- line(%d)", pStr, iLineNum ); usLength = strlen( str ) + 1; curr->pCode = (UINT8*)malloc( usLength ); memset( curr->pCode, 0, usLength ); strcpy( curr->pCode, str ); } else { ptr = ptr; } return ptrNew; } curr = curr->next; } return 0; } typedef struct DUMPFILENAME { UINT8 str[70]; }DUMPFILENAME; void DumpMemoryInfoIntoFile( UINT8 *filename, BOOLEAN fAppend ) { MEMORY_NODE *curr; FILE *fp; DUMPFILENAME *pCode; UINT32 *puiCounter, *puiSize; UINT8 tempCode[ 70 ]; UINT32 i, uiUniqueID, uiTotalKbWasted = 0, uiBytesRemainder = 0; BOOLEAN fFound; if( fAppend ) { fp = fopen( filename, "a" ); } else { fp = fopen( filename, "w" ); } Assert( fp ); if( !guiMemoryNodes ) { fprintf( fp, "NO MEMORY LEAKS DETECTED! CONGRATULATIONS!\n" ); fclose( fp ); return; } //Allocate enough strings and counters for each node. pCode = (DUMPFILENAME*)malloc( sizeof( DUMPFILENAME ) * guiMemoryNodes ); memset( pCode, 0, sizeof( DUMPFILENAME ) * guiMemoryNodes ); puiSize = (UINT32*)malloc( 4 * guiMemoryNodes ); memset( puiSize, 0, 4 * guiMemoryNodes ); puiCounter = (UINT32*)malloc( 4 * guiMemoryNodes ); memset( puiCounter, 0, 4 * guiMemoryNodes ); //Loop through the list and record every unique filename and count them uiUniqueID = 0; curr = gpMemoryHead; while( curr ) { strcpy( tempCode, curr->pCode ); fFound = FALSE; for( i = 0; i < uiUniqueID; i++ ) { if( !_stricmp( tempCode, pCode[i].str ) ) { //same string fFound = TRUE; (puiCounter[ i ])++; (puiSize[ i ]) += curr->uiSize; break; } } if( !fFound ) { strcpy( pCode[i].str, tempCode ); (puiSize[ i ]) += curr->uiSize; (puiCounter[ i ])++; uiUniqueID++; } curr = curr->next; } //Now dump the info. fprintf( fp, "--------------------------------------------------------------------------------\n" ); fprintf( fp, "%d unique memory allocation locations exist in %d memory nodes\n", uiUniqueID, guiMemoryNodes ); fprintf( fp, "--------------------------------------------------------------------------------\n" ); for( i = 0; i < uiUniqueID; i++ ) { fprintf( fp, "%d occurrences of %s (total size %d bytes)\n", puiCounter[i], pCode[i].str, puiSize[i] ); uiBytesRemainder += puiSize[i]; if( uiBytesRemainder >= 1024 ) { uiTotalKbWasted += uiBytesRemainder/1024; uiBytesRemainder %= 1024; } } fprintf( fp, "--------------------------------------------------------------------------------\n" ); fprintf( fp, "%dKB of memory total wasn't cleaned up!\n", uiTotalKbWasted ); fprintf( fp, "--------------------------------------------------------------------------------\n" ); fclose( fp ); //Free all memory associated with this operation. free( pCode ); free( puiCounter ); free( puiSize ); } BOOLEAN _AddAndRecordMemAlloc( UINT32 size, UINT32 uiLineNum, UINT8 *pSourceFile ) { return 0; } #endif