/* * 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 #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); bool Messagebox_active = false; int Global_warning_count = 0; int Global_error_count = 0; const int Messagebox_lines = 30; #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; } #if defined( SHOW_CALL_STACK ) static bool Dump_to_log = true; class DumpBuffer { public : enum { BUFFER_SIZE = 32000 } ; DumpBuffer() ; void Clear() ; void Printf( const char* format, ... ) ; void SetWindowText( HWND hWnd ) const ; char buffer[ BUFFER_SIZE ] ; void Append(const char* text); void Truncate(size_t size); void TruncateLines(int num_allowed_lines); size_t Size() const; private : char* current ; } ; DumpBuffer :: DumpBuffer() { Clear() ; } void DumpBuffer :: Clear() { current = buffer ; } void DumpBuffer :: Append(const char* text) { strcat_s(buffer, text); } void DumpBuffer :: Truncate(size_t size) { if (size >= strlen(buffer)) return; buffer[size] = 0; } // adapted from parselo void DumpBuffer :: TruncateLines(int num_allowed_lines) { Assert(num_allowed_lines > 0); char *find_from = buffer; char *lastch = find_from + strlen(buffer) - 6; while (find_from < lastch) { if (num_allowed_lines <= 0) { *find_from = 0; strcat_s(buffer, "[...]"); break; } char *p = strchr(find_from, '\n'); if (p == NULL) break; num_allowed_lines--; find_from = p + 1; } } size_t DumpBuffer :: Size() const { return strlen(buffer); } void DumpBuffer :: Printf( const char* format, ... ) { va_list argPtr ; int pos = current - buffer; int max = BUFFER_SIZE - pos; // protect against obvious buffer overflow if (pos >= BUFFER_SIZE) return; va_start( argPtr, format ) ; vsnprintf( current, max-1, format, argPtr ) ; va_end( argPtr ) ; current[max-1] = '\0'; current += strlen(current); } void DumpBuffer :: SetWindowText( HWND hWnd ) const { SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ; } /* Needed by LUA printf */ // This ought to be local to VerboseAssert, but it // causes problems in Visual C++ (in the CRTL init phase) static DumpBuffer dumpBuffer; const char* Separator = "------------------------------------------------------------------\n" ; #endif /* 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( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ; void Display() ; 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( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ; void DumpLineNumber( DumpBuffer& 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( DumpBuffer& 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.Printf( " line %d\r\n", lineNum ) ; if (Dump_to_log) { mprintf(( " line %d\r\n", lineNum )) ; } } // else // dumpBuffer.Printf( " line \r\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( DumpBuffer& 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.Printf( " %s()", tmp_name ) ; if (Dump_to_log) { mprintf((" %s()", tmp_name )) ; } } else { dumpBuffer.Printf( " " ) ; if (Dump_to_log) { mprintf((" " )) ; } } } 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( DumpBuffer& 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.Printf( " %s %08x()\r\n", pretty_module, caller ) ; if (Dump_to_log) { mprintf((" %s %08x()\r\n", pretty_module, caller )) ; } return 0; } } catch( ... ) { // Header wrong, do nothing return 0; } } else { dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ; if (Dump_to_log) { mprintf(( " %s %08x()\r\n", pretty_module, caller )) ; } //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ; //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ; return 0; } Int3(); } void DumpCallsStack( DumpBuffer& dumpBuffer ) { static PE_Debug PE_debug ; dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ; dumpBuffer.Printf( Separator ) ; // 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 ) ; dumpBuffer.Printf( Separator ) ; PE_debug.ClearReport() ; // Prepare for future calls } #endif //SHOW_CALL_STACK char AssertText1[2048]; char AssertText2[1024]; uint flags = MB_SYSTEMMODAL|MB_SETFOREGROUND; //uint flags = MB_SYSTEMMODAL; extern void gr_force_windowed(); void dump_text_to_clipboard( const char *text ) { int len = strlen(text)+1024; HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len ); if ( !h_text ) return; char *ptr = (char *)GlobalLock(h_text); if ( !ptr ) return; // copy then, if you find any \n's without \r's, then add in the \r. char last_char = 0; while( *text ) { if ( (*text == '\n') && (last_char != '\r') ) { *ptr++ = '\r'; } last_char = *text; *ptr++ = last_char; text++; } *ptr++ = 0; GlobalUnlock(h_text); OpenClipboard(NULL); EmptyClipboard(); SetClipboardData(CF_TEXT, h_text); CloseClipboard(); } void _cdecl WinAssert(char * text, char * filename, int linenum ) { int val; // this stuff migt be really useful for solving bug reports and user errors. We should output it! mprintf(("ASSERTION: \"%s\" at %s:%d\n", text, strrchr(filename, '\\')+1, linenum )); #ifdef Allow_NoWarn if (Cmdline_nowarn) { return; } #endif Messagebox_active = true; gr_activate(0); filename = strrchr(filename, '\\')+1; sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d\r\n", text, filename, linenum ); #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) /* Dump the callstack */ SCP_DebugCallStack callStack; SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) ); /* Format the string */ SCP_string assertString( AssertText1 ); assertString += "\n"; assertString += callStack.DumpToString( ); /* Copy to the clipboard */ dump_text_to_clipboard( assertString.c_str( ) ); // truncate text truncate_message_lines(assertString, Messagebox_lines); assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n"; assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n"; val = MessageBox( NULL, assertString.c_str( ), "Assertion Failed!", MB_OKCANCEL | flags ); #elif defined( SHOW_CALL_STACK ) dumpBuffer.Clear(); dumpBuffer.Printf( AssertText1 ); dumpBuffer.Printf( "\r\n" ); DumpCallsStack( dumpBuffer ) ; dump_text_to_clipboard(dumpBuffer.buffer); // truncate text dumpBuffer.TruncateLines(Messagebox_lines); dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n"); val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags ); #else val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags ); #endif if (val == IDCANCEL) exit(1); Int3(); gr_activate(1); Messagebox_active = false; } void _cdecl WinAssert(char * text, char * filename, int linenum, const char * format, ... ) { int val; va_list args; memset( AssertText1, 0, sizeof(AssertText1) ); memset( AssertText2, 0, sizeof(AssertText2) ); va_start(args, format); vsnprintf(AssertText2, sizeof(AssertText2)-1, format, args); va_end(args); // this stuff migt be really useful for solving bug reports and user errors. We should output it! mprintf(("ASSERTION: \"%s\" at %s:%d\n %s\n", text, strrchr(filename, '\\')+1, linenum, AssertText2 )); #ifdef Allow_NoWarn if (Cmdline_nowarn) { return; } #endif Messagebox_active = true; gr_activate(0); filename = strrchr(filename, '\\')+1; snprintf( AssertText1, sizeof(AssertText1)-1, "Assert: %s\r\nFile: %s\r\nLine: %d\r\n%s\r\n", text, filename, linenum, AssertText2 ); #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) /* Dump the callstack */ SCP_DebugCallStack callStack; SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) ); /* Format the string */ SCP_string assertString( AssertText1 ); assertString += "\n"; assertString += callStack.DumpToString( ); /* Copy to the clipboard */ dump_text_to_clipboard( assertString.c_str( ) ); // truncate text truncate_message_lines(assertString, Messagebox_lines); assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n"; assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n"; val = MessageBox( NULL, assertString.c_str( ), "Assertion Failed!", MB_OKCANCEL | flags ); #elif defined ( SHOW_CALL_STACK ) dumpBuffer.Clear(); dumpBuffer.Printf( AssertText1 ); dumpBuffer.Printf( "\r\n" ); DumpCallsStack( dumpBuffer ) ; dump_text_to_clipboard(dumpBuffer.buffer); // truncate text dumpBuffer.TruncateLines(Messagebox_lines); dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n"); val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags ); #else val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags ); #endif if (val == IDCANCEL) exit(1); Int3(); gr_activate(1); Messagebox_active = false; } void LuaDebugPrint(lua_Debug &ar) { dumpBuffer.Printf( "Name:\t\t%s\r\n", ar.name); dumpBuffer.Printf( "Name of:\t%s\r\n", ar.namewhat); dumpBuffer.Printf( "Function type:\t%s\r\n", ar.what); dumpBuffer.Printf( "Defined on:\t%d\r\n", ar.linedefined); dumpBuffer.Printf( "Upvalues:\t%d\r\n", ar.nups); dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf( "Source:\t\t%s\r\n", ar.source); dumpBuffer.Printf( "Short source:\t%s\r\n", ar.short_src); dumpBuffer.Printf( "Current line:\t%d\r\n", ar.currentline); dumpBuffer.Printf( "- Function line:\t%d\r\n", (ar.linedefined ? (1 + ar.currentline - ar.linedefined) : 0)); } extern lua_Debug Ade_debug_info; extern char debug_stack[4][32]; void LuaError(struct lua_State *L, char *format, ...) { int val; Messagebox_active = true; gr_activate(0); dumpBuffer.Clear(); //WMC - if format is set to NULL, assume this is acting as an //error handler for Lua. if(format == NULL) { dumpBuffer.Printf("LUA ERROR: %s", lua_tostring(L, -1)); lua_pop(L, -1); } else { va_list args; memset(AssertText1, 0, sizeof(AssertText1)); memset(AssertText2, 0, sizeof(AssertText2)); va_start(args, format); vsnprintf(AssertText1, sizeof(AssertText1)-1, format,args); va_end(args); dumpBuffer.Printf(AssertText1); } dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf( "\r\n" ); //WMC - This is virtually worthless. /* dumpBuffer.Printf(Separator); dumpBuffer.Printf( "LUA Debug:" ); dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf(Separator); lua_Debug ar; if(lua_getstack(L, 0, &ar)) { lua_getinfo(L, "nSlu", &ar); LuaDebugPrint(ar); } else { dumpBuffer.Printf("(No stack debug info)\r\n"); } */ // TEST CODE dumpBuffer.Printf(Separator); dumpBuffer.Printf( "ADE Debug:" ); dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf(Separator); LuaDebugPrint(Ade_debug_info); dumpBuffer.Printf(Separator); dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf( "\r\n" ); AssertText2[0] = '\0'; dumpBuffer.Printf(Separator); // Get the stack via the debug.traceback() function lua_getglobal(L, LUA_DBLIBNAME); if (!lua_isnil(L, -1)) { dumpBuffer.Printf( "\r\n" ); lua_getfield(L, -1, "traceback"); lua_remove(L, -2); if (lua_pcall(L, 0, 1, 0) != 0) dumpBuffer.Printf("Error while retrieving stack: %s", lua_tostring(L, -1)); else dumpBuffer.Printf(lua_tostring(L, -1)); lua_pop(L, 1); } else { // If the debug library is nil then fall back to the default debug stack dumpBuffer.Printf("LUA Stack:\r\n"); int i; for (i = 0; i < 4; i++) { if (debug_stack[i][0] != '\0') dumpBuffer.Printf("\t%s\r\n", debug_stack[i]); } } dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf(Separator); ade_stackdump(L, AssertText2); dumpBuffer.Printf( AssertText2 ); dumpBuffer.Printf( "\r\n" ); dumpBuffer.Printf(Separator); dump_text_to_clipboard(dumpBuffer.buffer); // truncate text dumpBuffer.TruncateLines(Messagebox_lines); dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); dumpBuffer.Printf( "\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit"); val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_YESNOCANCEL ); if (val == IDCANCEL ) { exit(1); } else if(val == IDYES) { Int3(); } gr_activate(1); Messagebox_active = false; } void _cdecl Error( const char * filename, int line, const char * format, ... ) { Global_error_count++; int val; va_list args; memset( AssertText1, 0, sizeof(AssertText1) ); memset( AssertText2, 0, sizeof(AssertText2) ); va_start(args, format); vsnprintf(AssertText1, sizeof(AssertText1)-1, format, args); va_end(args); filename = strrchr(filename, '\\')+1; snprintf(AssertText2, sizeof(AssertText2)-1, "Error: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line); mprintf(("ERROR: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line)); Messagebox_active = true; gr_activate(0); #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) /* Dump the callstack */ SCP_DebugCallStack callStack; SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) ); /* Format the string */ SCP_string assertString( AssertText1 ); assertString += "\n"; assertString += callStack.DumpToString( ); /* Copy to the clipboard */ dump_text_to_clipboard( assertString.c_str( ) ); // truncate text truncate_message_lines(assertString, Messagebox_lines); assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n"; assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n"; val = MessageBox( NULL, assertString.c_str( ), "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL ); #elif defined( SHOW_CALL_STACK ) dumpBuffer.Clear(); dumpBuffer.Printf( AssertText2 ); dumpBuffer.Printf( "\r\n" ); DumpCallsStack( dumpBuffer ) ; dump_text_to_clipboard(dumpBuffer.buffer); // truncate text dumpBuffer.TruncateLines(Messagebox_lines); dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n"); val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL ); #else strcat_s(AssertText2,"\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n"); val = MessageBox(NULL, AssertText2, "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL ); #endif switch (val) { case IDCANCEL: exit(1); default: Int3(); break; } gr_activate(1); Messagebox_active = false; } void _cdecl WarningEx( char *filename, int line, const char *format, ... ) { #ifndef NDEBUG if (Cmdline_extra_warn) { char msg[sizeof(AssertText1)]; va_list args; memset(msg, 0, sizeof(msg)); va_start(args, format); vsnprintf(msg, sizeof(msg)-1, format, args); va_end(args); Warning(filename, line, msg); } #endif } void _cdecl Warning( char *filename, int line, const char *format, ... ) { Global_warning_count++; #ifndef NDEBUG va_list args; int result; int i; int slen = 0; // output to the debug log before anything else (so that we have a complete record) memset( AssertText1, 0, sizeof(AssertText1) ); memset( AssertText2, 0, sizeof(AssertText2) ); va_start(args, format); vsnprintf(AssertText1, sizeof(AssertText1) - 1, format, args); va_end(args); slen = strlen(AssertText1); // strip out the newline char so the output looks better for (i = 0; i < slen; i++){ if (AssertText1[i] == (char)0x0a) { AssertText2[i] = ' '; } else { AssertText2[i] = AssertText1[i]; } } // kill off extra white space at end if (AssertText2[slen-1] == (char)0x20) { AssertText2[slen-1] = '\0'; } else { // just being careful AssertText2[slen] = '\0'; } mprintf(("WARNING: \"%s\" at %s:%d\n", AssertText2, strrchr(filename, '\\')+1, line)); // now go for the additional popup window, if we want it ... #ifdef Allow_NoWarn if (Cmdline_nowarn) { return; } #endif filename = strrchr(filename, '\\')+1; sprintf(AssertText2, "Warning: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line ); Messagebox_active = true; gr_activate(0); #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING ) /* Dump the callstack */ SCP_DebugCallStack callStack; SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) ); /* Format the string */ SCP_string assertString( AssertText1 ); assertString += "\n"; assertString += callStack.DumpToString( ); /* Copy to the clipboard */ dump_text_to_clipboard( assertString.c_str( ) ); // truncate text truncate_message_lines(assertString, Messagebox_lines); assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n"; assertString += "\n\nUse Yes to break into Debugger, No to continue.\nand Cancel to Quit\n"; result = MessageBox( NULL, assertString.c_str( ), "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags ); #elif defined ( SHOW_CALL_STACK ) //we don't want to dump the call stack for every single warning Dump_to_log = false; dumpBuffer.Clear(); dumpBuffer.Printf( AssertText2 ); dumpBuffer.Printf( "\r\n" ); DumpCallsStack( dumpBuffer ) ; dump_text_to_clipboard(dumpBuffer.buffer); // truncate text dumpBuffer.TruncateLines(Messagebox_lines); dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); dumpBuffer.Printf("\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit"); result = MessageBox((HWND)os_get_window(), dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags ); Dump_to_log = true; #else strcat_s(AssertText2,"\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit"); result = MessageBox((HWND)os_get_window(), AssertText2, "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags ); #endif switch (result) { case IDYES: Int3(); break; case IDNO: break; case IDCANCEL: exit(1); } gr_activate(1); Messagebox_active = false; #endif // !NDEBUG } //================= 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; }