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)); }
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 <unknown>\r\n" ) ; }
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 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 }
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, "<unknown>" ); } 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 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, "<unknown>" ); } 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( " <unknown>" ) ; if (Dump_to_log) { mprintf((" <unknown>" )) ; } } }
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 }
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 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 LuaError(struct lua_State* L, char* format, ...) { int val; Messagebox_active = true; gr_activate(0); /* va_start(args, format); vsprintf(AssertText1,format,args); va_end(args); */ //filename = strrchr(filename, '\\')+1; //sprintf(AssertText2,"LuaError: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line ); 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; va_start(args, format); vsprintf(AssertText1, format, args); dumpBuffer.Printf(AssertText1); va_end(args); } 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); 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(Separator); ade_stackdump(L, AssertText2); dumpBuffer.Printf(AssertText2); dumpBuffer.Printf("\r\n"); dumpBuffer.Printf(Separator); dump_text_to_clipboard(dumpBuffer.buffer); 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 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)); if (Cmdline_nowarn) return; 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 */ std::string assertString( AssertText1 ); assertString += "\n"; assertString += callStack.DumpToString( ); /* Copy to the clipboard */ dump_text_to_clipboard( assertString.c_str( ) ); 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); 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); #ifndef INTERPLAYQA Int3(); #else AsmInt3(); #endif gr_activate(1); Messagebox_active = false; }