/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ // Nothing in this module should be externalized!!! //XSTR:OFF //#define DUMPRAM // This dumps all symbol sizes. See John for more info /* Windows Headers */ #include #include #include #include #ifdef _MSC_VER # include /* Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors */ # define SHOW_CALL_STACK #endif // _MSC_VER /* STL Headers */ #include /* SCP Headers */ #include "osapi/osapi.h" #include "globalincs/pstypes.h" #include "globalincs/systemvars.h" #include "cmdline/cmdline.h" #include "parse/lua.h" #include "parse/parselo.h" #include "debugconsole/console.h" #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) # include "globalincs/mspdb_callstack.h" #endif extern void gr_activate(int active); #ifndef _ASSERT #ifndef _DEBUG #define _ASSERT(expr) ((void)0) #else #define _ASSERT(expr) (assert(expr)) // #error _ASSERT is not defined yet for debug mode with non-MSVC compilers #endif #endif const char *clean_filename( const char *name) { const char *p = name+strlen(name)-1; // Move p to point to first letter of EXE filename while( (*p!='\\') && (*p!='/') && (*p!=':') && (p>= name) ) p--; p++; return p; } /* MSVC2005+ callstack support */ #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) class SCP_DebugCallStack : public SCP_IDumpHandler { public: virtual bool ResolveSymbols( ) { return true; } virtual void OnBegin( ) { } virtual void OnEnd( ) { } virtual void OnEntry( void* address, const char* module, const char* symbol ) { UNREFERENCED_PARAMETER( address ); StackEntry entry; entry.module = clean_filename( module ); entry.symbol = symbol; m_stackFrames.push_back( entry ); } virtual void OnError( const char* error ) { /* No error handling here! */ UNREFERENCED_PARAMETER( error ); } SCP_string DumpToString( ) { SCP_string callstack; for ( size_t i = 0; i < m_stackFrames.size( ); i++ ) { callstack += m_stackFrames[ i ].module + "! " + m_stackFrames[ i ].symbol + "\n"; } return callstack; /* Inefficient, but we don't need efficient here */ } private: struct StackEntry { SCP_string module; SCP_string symbol; }; SCP_vector< StackEntry > m_stackFrames; }; #elif defined( SHOW_CALL_STACK ) class PE_Debug { public : PE_Debug() ; ~PE_Debug() ; void ClearReport() ; int DumpDebugInfo( std::ostream& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ; private : // Report data enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ; char latestModule[ MAX_MODULENAME_LEN ] ; char latestFile[ VA_MAX_FILENAME_LEN ] ; // File mapping data HANDLE hFile ; HANDLE hFileMapping ; PIMAGE_DOS_HEADER fileBase ; // Pointers to debug information PIMAGE_NT_HEADERS NT_Header ; PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo ; PIMAGE_SYMBOL COFFSymbolTable ; int COFFSymbolCount ; const char* stringTable ; void ClearFileCache() ; void ClearDebugPtrs() ; void MapFileInMemory( const char* module ) ; void FindDebugInfo() ; void DumpSymbolInfo( std::ostream& dumpBuffer, DWORD relativeAddress ) ; void DumpLineNumber(std::ostream& dumpBuffer, DWORD relativeAddress ) ; PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader() ; PIMAGE_SECTION_HEADER SectionHeaderFromName( const char* name ) ; const char* GetSymbolName( PIMAGE_SYMBOL sym ) ; } ; // Add an offset to a pointer and cast to a given type; may be // implemented as a template function but Visual C++ has some problems. #define BasedPtr( type, ptr, ofs ) (type)( (DWORD)(ptr) + (DWORD)(ofs) ) PE_Debug :: PE_Debug() { // Init file mapping cache hFileMapping = 0 ; hFile = INVALID_HANDLE_VALUE ; fileBase = 0 ; ClearDebugPtrs() ; ClearReport() ; } PE_Debug :: ~PE_Debug() { ClearFileCache() ; } void PE_Debug :: ClearReport() { latestModule[ 0 ] = 0 ; latestFile[ 0 ] = 0 ; } void PE_Debug :: ClearDebugPtrs() { NT_Header = NULL ; COFFDebugInfo = NULL ; COFFSymbolTable = NULL ; COFFSymbolCount = 0 ; stringTable = NULL ; } void PE_Debug :: ClearFileCache() { if( fileBase ) { UnmapViewOfFile( fileBase ) ; fileBase = 0 ; } if( hFileMapping != 0 ) { CloseHandle( hFileMapping ) ; hFileMapping = 0 ; } if( hFile != INVALID_HANDLE_VALUE ) { CloseHandle( hFile ) ; hFile = INVALID_HANDLE_VALUE ; } } void PE_Debug :: DumpLineNumber(std::ostream& dumpBuffer, DWORD relativeAddress ) { PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo, COFFDebugInfo->LvaToFirstLinenumber ) ; DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ; const DWORD none = (DWORD)-1 ; DWORD maxAddr = 0 ; DWORD lineNum = none ; for( DWORD i=0; i < lineCount; i++ ) { if( line->Linenumber != 0 ) // A regular line number { // look for line with bigger address <= relativeAddress if( line->Type.VirtualAddress <= relativeAddress && line->Type.VirtualAddress > maxAddr ) { maxAddr = line->Type.VirtualAddress ; lineNum = line->Linenumber ; } } line++ ; } if( lineNum != none ) { dumpBuffer << " line " << lineNum << "\n"; } } const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym ) { const int NAME_MAX_LEN = 64 ; static char buf[ NAME_MAX_LEN ] ; if( sym->N.Name.Short != 0 ) { strncpy( buf, (const char*)sym->N.ShortName, 8 ) ; buf[ 8 ] = 0 ; } else { strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ; buf[ NAME_MAX_LEN - 1 ] = 0 ; } return( buf ) ; } void unmangle(char *dst, const char *src) { //strcpy_s( dst, src ); //return; src++; while( (*src) && (*src!=' ') && (*src!='@') ) { *dst++ = *src++; } *dst++ = 0; } #ifdef DUMPRAM typedef struct MemSymbol { int section; int offset; int size; char name[132]; } MemSymbol; int Num_symbols = 0; int Max_symbols = 0; MemSymbol *Symbols; void InitSymbols() { Num_symbols = 0; Max_symbols = 5000; Symbols = (MemSymbol *)vm_malloc(Max_symbols*sizeof(MemSymbol)); if ( !Symbols ) { Max_symbols = 0; } } void Add_Symbol( int section, int offset, const char *name, char *module ) { if ( Num_symbols >= Max_symbols ) { return; } MemSymbol * sym = &Symbols[Num_symbols++]; sym->section = section; sym->offset = offset; sym->size = -1; strcpy_s( sym->name, name ); strcat_s( sym->name, "(" ); strcat_s( sym->name, module ); strcat_s( sym->name, ")" ); } int Sym_compare( const void *arg1, const void *arg2 ) { MemSymbol * sym1 = (MemSymbol *)arg1; MemSymbol * sym2 = (MemSymbol *)arg2; if ( sym1->section < sym2->section ) { return -1; } else if ( sym1->section > sym2->section ) { return 1; } else { if ( sym1->offset > sym2->offset ) { return 1; } else { return -1; } } } int Sym_compare1( const void *arg1, const void *arg2 ) { MemSymbol * sym1 = (MemSymbol *)arg1; MemSymbol * sym2 = (MemSymbol *)arg2; if ( sym1->size < sym2->size ) { return 1; } else if ( sym1->size > sym2->size ) { return -1; } else { return 0; } } void DumpSymbols() { int i; insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare ); for (i=0;isection == sym2->section) ) { sym1->size = sym2->offset-sym1->offset; } else { sym1->size = -1; } } insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 ); FILE *fp = fopen( "dump", "wt" ); fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" ); int total_size = 0; for (i=0;isize > 0 ) total_size += sym->size; fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size ); } fclose(fp); vm_free( Symbols ); Symbols = NULL; _asm int 3 } #endif void PE_Debug::DumpSymbolInfo(std::ostream& dumpBuffer, DWORD relativeAddress ) { // Variables to keep track of function symbols PIMAGE_SYMBOL currentSym = COFFSymbolTable ; PIMAGE_SYMBOL fnSymbol = NULL ; DWORD maxFnAddress = 0 ; #ifdef DUMPRAM InitSymbols(); #endif // Variables to keep track of file symbols PIMAGE_SYMBOL fileSymbol = NULL ; PIMAGE_SYMBOL latestFileSymbol = NULL ; for ( int i = 0; i < COFFSymbolCount; i++ ) { // Look for .text section where relativeAddress belongs to. // Keep track of the filename the .text section belongs to. if ( currentSym->StorageClass == IMAGE_SYM_CLASS_FILE ) { latestFileSymbol = currentSym; } // Borland uses "CODE" instead of the standard ".text" entry // Microsoft uses sections that only _begin_ with .text const char* symName = GetSymbolName( currentSym ) ; if ( strnicmp( symName, ".text", 5 ) == 0 || strcmpi( symName, "CODE" ) == 0 ) { if ( currentSym->Value <= relativeAddress ) { PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1) ; if ( currentSym->Value + auxSym->Section.Length >= relativeAddress ) { fileSymbol = latestFileSymbol ; } } } // Look for the function with biggest address <= relativeAddress BOOL isFunction = ISFCN( currentSym->Type ); // Type == 0x20, See WINNT.H if ( isFunction && ( currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC ) ) { if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) { maxFnAddress = currentSym->Value ; fnSymbol = currentSym ; } } #ifdef DUMPRAM if ( !isFunction && (currentSym->SectionNumber >= 0) ) { if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) { char pretty_module[1024]; if ( fileSymbol ) { const char* auxSym = (const char*)(latestFileSymbol + 1) ; char tmpFile[ VA_MAX_FILENAME_LEN ] ; strcpy_s( tmpFile, auxSym ) ; strcpy_s( pretty_module, tmpFile ); char *p = pretty_module+strlen(pretty_module)-1; // Move p to point to first letter of EXE filename while( (*p!='\\') && (*p!='/') && (*p!=':') ) p--; p++; if ( strlen(p) < 1 ) { strcpy_s( pretty_module, "" ); } else { memmove( pretty_module, p, strlen(p)+1 ); } } else { strcpy_s( pretty_module, "" ); } Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module ); } } #endif // Advance counters, skip aux symbols i += currentSym->NumberOfAuxSymbols ; currentSym += currentSym->NumberOfAuxSymbols ; currentSym++ ; } #ifdef DUMPRAM DumpSymbols(); #endif // dump symbolic info if found if ( fileSymbol ) { const char* auxSym = (const char*)(fileSymbol + 1) ; if( strcmpi( latestFile, auxSym ) ) { strcpy_s( latestFile, auxSym ) ; //JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ; } } else { latestFile[ 0 ] = 0 ; //JAS dumpBuffer.Printf( " file: unknown\r\n" ) ; } if ( fnSymbol ) { char tmp_name[1024]; unmangle(tmp_name, GetSymbolName( fnSymbol ) ); dumpBuffer << " " << tmp_name << "()"; } else { dumpBuffer << " "; } } PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name ) { PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ; for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ ) { if( strnicmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 ) return( section ) ; else section++ ; } return 0; } PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader() { // Some files have a wrong entry in the COFF header, so // first check if the debug info exists at all if( NT_Header->FileHeader.PointerToSymbolTable == 0 ) return( 0 ) ; DWORD debugDirRVA = NT_Header->OptionalHeader. DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ]. VirtualAddress; if( debugDirRVA == 0 ) return( 0 ) ; // The following values must be calculated differently for MS/Borland files PIMAGE_DEBUG_DIRECTORY debugDir ; DWORD size ; // Borland files have the debug directory at the beginning of a .debug section PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName( ".debug" ) ; if( debugHeader && debugHeader->VirtualAddress == debugDirRVA ) { debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase) ; size = NT_Header->OptionalHeader. DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size * sizeof( IMAGE_DEBUG_DIRECTORY ) ; } else // Microsoft debug directory is in the .rdata section { debugHeader = SectionHeaderFromName( ".rdata" ) ; if( debugHeader == 0 ) return( 0 ) ; size = NT_Header->OptionalHeader. DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size ; DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress ; debugDir = BasedPtr( PIMAGE_DEBUG_DIRECTORY, fileBase, debugHeader->PointerToRawData + offsetInto_rdata ) ; } // look for COFF debug info DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ; for( DWORD i = 0; i < debugFormats; i++ ) { if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF ) return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ; else debugDir++ ; } return( NULL ) ; } void PE_Debug :: FindDebugInfo() { ClearDebugPtrs() ; // Put everything into a try/catch in case the file has wrong fields try { // Verify that fileBase is a valid pointer to a DOS header if( fileBase->e_magic == IMAGE_DOS_SIGNATURE ) { // Get a pointer to the PE header NT_Header = BasedPtr( PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew ) ; // Verify that NT_Header is a valid pointer to a NT header if( NT_Header->Signature == IMAGE_NT_SIGNATURE ) { // Get a pointer to the debug header if any COFFDebugInfo = GetDebugHeader() ; // Get a pointer to the symbol table and retrieve the number of symbols if( NT_Header->FileHeader.PointerToSymbolTable ) COFFSymbolTable = BasedPtr( PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable ) ; COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols ; // The string table starts right after the symbol table stringTable = (const char*)(COFFSymbolTable + COFFSymbolCount) ; } } } catch( ... ) { // Header wrong, do nothing } } void PE_Debug :: MapFileInMemory( const char* module ) { ClearFileCache() ; hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ; if( hFile != INVALID_HANDLE_VALUE ) { hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ; if( hFileMapping != 0 ) fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ) ; } // NB: open files/mapping are closed later in ClearFileCache } int PE_Debug::DumpDebugInfo(std::ostream& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) { // Avoid to open, map and looking for debug header/symbol table // by caching the latest and comparing the actual module with // the latest one. static char module[ MAX_MODULENAME_LEN ] ; GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ; // New module if( strcmpi( latestModule, module ) ) { strcpy_s( latestModule, module ); //JAS dumpBuffer.Printf( "Module: %s\r\n", module ); MapFileInMemory( module ); FindDebugInfo(); } char pretty_module[1024]; strcpy_s( pretty_module, module ); char *p = pretty_module+strlen(pretty_module)-1; // Move p to point to first letter of EXE filename while( (*p!='\\') && (*p!='/') && (*p!=':') ) p--; p++; if ( strlen(p) < 1 ) { strcpy_s( pretty_module, "" ); } else { memmove( pretty_module, p, strlen(p)+1 ); } if ( fileBase ) { // Put everything into a try/catch in case the file has wrong fields try { DWORD relativeAddress = caller - (BYTE*)hInstance ; // Dump symbolic information and line number if available if( COFFSymbolCount != 0 && COFFSymbolTable != NULL ) { DumpSymbolInfo( dumpBuffer, relativeAddress ) ; if( COFFDebugInfo ) DumpLineNumber( dumpBuffer, relativeAddress ) ; return 1; } else { //dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this module.\r\n" ) ; //JAS dumpBuffer.Printf( " no debug information\r\n" ) ; dumpBuffer << " " << pretty_module << " " << std::setprecision(8) << std::hex << (void*)caller << std::resetiosflags(std::ios::showbase) << "()\n"; return 0; } } catch( ... ) { // Header wrong, do nothing return 0; } } else { dumpBuffer << " " << pretty_module << " " << std::setprecision(8) << std::hex << (void*)caller << std::resetiosflags(std::ios::showbase) << "()\n"; //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ; //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ; return 0; } Int3(); } void DumpCallsStack( std::ostream& dumpBuffer ) { static PE_Debug PE_debug ; // The structure of the stack frames is the following: // EBP -> parent stack frame EBP // return address for this call ( = caller ) // The chain can be navigated iteratively, after the // initial value of EBP is loaded from the register DWORD parentEBP, retval; MEMORY_BASIC_INFORMATION mbi ; HINSTANCE hInstance; int depth = 0; __asm MOV parentEBP, EBP do { depth++; if ( depth > 16 ) break; if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) ) { break; } parentEBP = *(DWORD*)parentEBP ; BYTE **NextCaller = ((BYTE**)parentEBP + 1); if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) { break; } BYTE* caller = *NextCaller; // Error sometimes!!! // Skip the first EBP as it points to AssertionFailed, which is // uninteresting for the user if ( depth > 1 ) { // Get the instance handle of the module where caller belongs to retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ; // The instance handle is equal to the allocation base in Win32 hInstance = (HINSTANCE)mbi.AllocationBase ; if( ( retval == sizeof( mbi ) ) && hInstance ) { if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) { //break; } } else { break ; // End of the call chain } } } while( TRUE ) ; PE_debug.ClearReport() ; // Prepare for future calls } #endif //SHOW_CALL_STACK SCP_string dump_stacktrace() { #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) /* Dump the callstack */ SCP_DebugCallStack callStack; SCP_DumpStack(dynamic_cast< SCP_IDumpHandler* >(&callStack)); return callStack.DumpToString(); #elif defined( SHOW_CALL_STACK ) SCP_stringstream stream; DumpCallsStack(stream); return stream.str(); #else return "No stacktrace available!"; #endif } //================= memory stuff /* char *format_mem( DWORD num ) { if ( num < 1024 ) { sprintf( tmp_mem, "%d bytes", num ); } else if ( num < 1024*1024 ) { sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f ); } else { sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f) ); } return tmp_mem; } */ /* void getmem() { DWORD retval; MEMORY_BASIC_INFORMATION mbi ; HINSTANCE hInstance; MEMORYSTATUS ms; ms.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus(&ms); printf( "Percent of memory in use: %d%%\n", ms.dwMemoryLoad ); printf( "Bytes of physical memory: %s\n", format_mem(ms.dwTotalPhys) ); printf( "Free physical memory bytes: %s\n", format_mem(ms.dwAvailPhys) ); // printf( "Bytes of paging file: %s\n", format_mem(ms.dwTotalPageFile) ); // bytes of paging file printf( "Free bytes of paging file: %s\n", format_mem(ms.dwAvailPageFile) ); // free bytes of paging file printf( "User bytes of address space: %s\n", format_mem(ms.dwTotalVirtual) ); // printf( "Free user bytes: %s\n", format_mem(ms.dwAvailVirtual) ); // free user bytes // Get the instance handle of the module where caller belongs to retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ; // The instance handle is equal to the allocation base in Win32 hInstance = (HINSTANCE)mbi.AllocationBase ; if( ( retval == sizeof( mbi ) ) && hInstance ) { printf( "Virtual Query succeeded...\n" ); } else { printf( "Virtual Query failed...\n" ); } } */ #ifndef NDEBUG int TotalRam = 0; #define nNoMansLandSize 4 typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char * szFileName; int nLine; size_t nDataSize; int nBlockUse; long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) // this block of code is never referenced... #if 0 int __cdecl MyAllocHook( int nAllocType, void * pvData, size_t nSize, int nBlockUse, long lRequest, const char * szFileName, int nLine ) { // char *operation[] = { "", "allocating", "re-allocating", "freeing" }; // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" }; if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations return( TRUE ); _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) ); _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) ); if ( nAllocType == 3 ) { _CrtMemBlockHeader *phd = pHdr(pvData); nSize = phd->nDataSize; } // mprintf(( "Total RAM = %d\n", TotalRam )); mprintf(( "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n", szFileName, nLine, operation[nAllocType], nSize, blockType[nBlockUse], lRequest )); if ( pvData != NULL ) mprintf(( " at %X", pvData )); mprintf(("\n" )); return( TRUE ); // Allow the memory operation to proceed } #endif // #if 0 void windebug_memwatch_init() { //_CrtSetAllocHook(MyAllocHook); TotalRam = 0; } #endif int Watch_malloc = 0; DCF_BOOL(watch_malloc, Watch_malloc ); // Returns 0 if not enough RAM. int vm_init(int min_heap_size) { #ifndef NDEBUG TotalRam = 0; #endif return 1; } #ifdef _DEBUG #ifdef _REPORT_MEM_LEAKS const int MAX_MEM_POINTERS = 50000; typedef struct { char filename[33]; int size; int line; void *ptr; } MemPtrInfo; MemPtrInfo mem_ptr_list[MAX_MEM_POINTERS]; #endif const int MAX_MEM_MODULES = 600; typedef struct { char filename[33]; int size; int magic_num1; int magic_num2; bool in_use; } MemBlockInfo; MemBlockInfo mem_block_list[MAX_MEM_MODULES]; int memblockinfo_sort_compare( const void *arg1, const void *arg2 ) { MemBlockInfo *mbi1 = (MemBlockInfo *) arg1; MemBlockInfo *mbi2 = (MemBlockInfo *) arg2; if (mbi1->size > mbi2->size) return -1; if (mbi1->size < mbi2->size) return 1; return 0; } void memblockinfo_sort() { insertion_sort(mem_block_list, MAX_MEM_MODULES, sizeof(MemBlockInfo), memblockinfo_sort_compare ); } void memblockinfo_sort_get_entry(int index, char *filename, int *size) { Assert(index < MAX_MEM_MODULES); strcpy(filename, mem_block_list[index].filename); *size = mem_block_list[index].size; } static bool first_time = true; void register_malloc( int size, char *filename, int line, void *ptr) { if(first_time == true) { ZeroMemory(mem_block_list, MAX_MEM_MODULES * sizeof(MemBlockInfo) ); first_time = false; // Get current flag int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); // Turn on leak-checking bit tmpFlag |= _CRTDBG_LEAK_CHECK_DF; // Set flag to the new value _CrtSetDbgFlag( tmpFlag ); #ifdef _REPORT_MEM_LEAKS ZeroMemory(mem_ptr_list, MAX_MEM_POINTERS * sizeof(MemPtrInfo)); #endif } char *temp = strrchr(filename, '\\'); if(temp) filename = temp + 1; // calculate magic numbers int magic1, magic2, len = strlen(filename); magic1 = magic2 = 0; for(int c = 0; c < len; c++) { magic1 += filename[c]; if(c % 2) magic2 += filename[c]; else magic2 -= filename[c]; } for(int i = 0; i < MAX_MEM_MODULES; i++) { // Found the first empty entry, fill it if(mem_block_list[i].in_use == false) { strcpy_s(mem_block_list[i].filename, filename); mem_block_list[i].size = size; mem_block_list[i].magic_num1 = magic1; mem_block_list[i].magic_num2 = magic2; mem_block_list[i].in_use = true; break; } // Found a matching entry, update it if( mem_block_list[i].magic_num1 == magic1 && mem_block_list[i].magic_num2 == magic2 && stricmp(mem_block_list[i].filename, filename) == 0) { mem_block_list[i].size += size; break; } } // Now if system is compiled register it with the fuller system #ifdef _REPORT_MEM_LEAKS // Find empty slot int count = 0; while(mem_ptr_list[count].ptr != NULL) { count++; // If you hit this just increase MAX_MEM_POINTERS Assert(count < MAX_MEM_POINTERS); } mem_ptr_list[count].ptr = ptr; mem_ptr_list[count].line = line; mem_ptr_list[count].size = size; strcpy_s(mem_ptr_list[count].filename, filename); #endif } void memblockinfo_output_memleak() { if(!Cmdline_show_mem_usage) return; if(TotalRam == 0) return; if(TotalRam < 0) { _RPT1(_CRT_WARN, "TotalRam bad value!",TotalRam); return; } _RPT1(_CRT_WARN, "malloc memory leak of %d\n",TotalRam); // Now if system is compiled register it with the fuller system #ifdef _REPORT_MEM_LEAKS int total = 0; // Find empty slot for(int f = 0; f < MAX_MEM_POINTERS; f++) { if(mem_ptr_list[f].ptr) { _RPT3(_CRT_WARN, "Memory leaks: (%s line %d) of %d bytes\n", mem_ptr_list[f].filename, mem_ptr_list[f].line, mem_ptr_list[f].size); total += mem_ptr_list[f].size; } } Assert(TotalRam == total); #else for(int i = 0; i < MAX_MEM_MODULES; i++) { // Found the first empty entry, fill it if(mem_block_list[i].size > 0) { // Oh... bad code... making assumsions... _RPT2(_CRT_WARN, "Possible memory leaks: %s %d\n", mem_block_list[i].filename, mem_block_list[i].size); } } #endif } void unregister_malloc(char *filename, int size, void *ptr) { // calculate magic numbers int magic1, magic2, len; char *temp = strrchr(filename, '\\'); if(temp) filename = temp + 1; len = strlen(filename); magic1 = magic2 = 0; for(int c = 0; c < len; c++) { magic1 += filename[c]; if(c % 2) magic2 += filename[c]; else magic2 -= filename[c]; } // Now if system is compiled register it with the fuller system #ifdef _REPORT_MEM_LEAKS // Find empty slot for(int f = 0; f < MAX_MEM_POINTERS; f++) { if(mem_ptr_list[f].ptr == ptr) { mem_ptr_list[f].ptr = NULL; break; } } #endif for(int i = 0; i < MAX_MEM_MODULES; i++) { // Found a matching entry, update it if( mem_block_list[i].magic_num1 == magic1 && mem_block_list[i].magic_num2 == magic2 && stricmp(mem_block_list[i].filename, filename) == 0) { mem_block_list[i].size -= size; return; } } } #endif #ifndef NDEBUG void *_vm_malloc( int size, char *filename, int line, int quiet ) #else void *_vm_malloc( int size, int quiet ) #endif { void *ptr = NULL; ptr = _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ ); if (ptr == NULL) { mprintf(( "Malloc failed!!!!!!!!!!!!!!!!!!!\n" )); if (quiet) { return NULL; } Error(LOCATION, "Malloc Failed!\n"); } #ifndef NDEBUG TotalRam += size; if(Cmdline_show_mem_usage) register_malloc(size, filename, line, ptr); #endif return ptr; } #ifndef NDEBUG char *_vm_strdup( const char *ptr, char *filename, int line ) #else char *_vm_strdup( const char *ptr ) #endif { char *dst; int len = strlen(ptr); dst = (char *)vm_malloc( len+1 ); if (!dst) return NULL; strcpy_s( dst, len + 1, ptr ); return dst; } #ifndef NDEBUG char *_vm_strndup( const char *ptr, int size, char *filename, int line ) #else char *_vm_strndup( const char *ptr, int size ) #endif { char *dst; dst = (char *)vm_malloc( size+1 ); if (!dst) return NULL; strncpy( dst, ptr, size ); // make sure it has a NULL terminiator dst[size] = '\0'; return dst; } #ifndef NDEBUG void _vm_free( void *ptr, char *filename, int line ) #else void _vm_free( void *ptr ) #endif { if ( !ptr ) { return; } #ifndef NDEBUG _CrtMemBlockHeader *phd = pHdr(ptr); int nSize = phd->nDataSize; TotalRam -= nSize; if(Cmdline_show_mem_usage) unregister_malloc(filename, nSize, ptr); #endif _free_dbg(ptr,_NORMAL_BLOCK); } void vm_free_all() { } #ifndef NDEBUG void *_vm_realloc( void *ptr, int size, char *filename, int line, int quiet ) #else void *_vm_realloc( void *ptr, int size, int quiet ) #endif { // if this is the first time it's used then we need to malloc it first if ( ptr == NULL ) return vm_malloc(size); void *ret_ptr = NULL; #ifndef NDEBUG // Unregistered the previous allocation _CrtMemBlockHeader *phd = pHdr(ptr); int nSize = phd->nDataSize; TotalRam -= nSize; if(Cmdline_show_mem_usage) unregister_malloc(filename, nSize, ptr); #endif ret_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, __FILE__, __LINE__ ); if (ret_ptr == NULL) { mprintf(( "realloc failed!!!!!!!!!!!!!!!!!!!\n" )); if (quiet && (size > 0) && (ptr != NULL)) { // realloc doesn't touch the original ptr in the case of failure so we could still use it return NULL; } Error(LOCATION, "Out of memory. Try closing down other applications, increasing your\n" "virtual memory size, or installing more physical RAM.\n"); } #ifndef NDEBUG TotalRam += size; // register this allocation if(Cmdline_show_mem_usage) register_malloc(size, filename, line, ret_ptr); #endif return ret_ptr; }