2 * $Logfile: /Freespace2/code/GlobalIncs/WinDebug.cpp $
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 4 12/01/98 4:46p Dave
15 * Put in targa bitmap support (16 bit).
17 * 3 11/30/98 5:31p Dave
18 * Fixed up Fred support for software mode.
20 * 2 10/07/98 10:52a Dave
23 * 1 10/07/98 10:48a Dave
25 * 32 5/25/98 1:39p John
26 * added code to give error and abort if malloc fails.
28 * 31 5/06/98 8:03p Allender
29 * AL: Do early out if trying to free NULL pointer in vm_free(). Print
30 * out warning message.
32 * 30 4/30/98 12:02a Lawrance
33 * compile out Warnings() on NDEBUG builds
35 * 29 4/22/98 5:15p Lawrance
38 * 28 4/21/98 1:02p John
39 * fixed bug where the clipboard text dumped wasn't null terminated.
41 * 27 4/20/98 3:22p John
42 * fixed bug that displayed wrong amount of allocated ram.
44 * 26 4/17/98 3:27p Allender
46 * 25 4/17/98 1:41p Allender
47 * made to compile with NDEBUG defined
49 * 24 4/17/98 7:00a John
50 * Added in hooks for new memory allocator. I haven't tried compiling
51 * under NDEBUG, but I tried to put in code for it.
53 * 23 4/01/98 9:21p John
54 * Made NDEBUG, optimized build with no warnings or errors.
56 * 22 3/31/98 8:17p John
57 * Added code to dump memory contents
59 * 21 3/31/98 11:17a Allender
60 * fix bug with INTERPLAYQA Int3's and Warning dialogs
62 * 20 3/30/98 4:02p John
63 * Made machines with < 32 MB of RAM use every other frame of certain
64 * bitmaps. Put in code to keep track of how much RAM we've malloc'd.
66 * 19 3/14/98 3:35p John
67 * cleaned up call stack code. Exited out on not properly aligned EBP's.
69 * 18 3/14/98 3:25p John
70 * Added code to check the parentEBP pointer for validity before
73 * 17 3/11/98 5:34p Lawrance
74 * Fix typo in error dialog box
76 * 16 3/06/98 2:21p John
77 * Correct some ok/cancel messages. Made call stack info prettier for
78 * modules with no debugging info
80 * 15 3/05/98 3:04p John
81 * Made Errors, Assert, Warning info paste to clipboard.
83 * 14 3/05/98 9:17a John
84 * Limited stack dump depth to 16
86 * 13 3/05/98 9:14a John
87 * Added in a simple function name unmangler
89 * 12 3/04/98 7:08p John
90 * Made Freespace generate COFF files. Made Assert,Error, and Warning
91 * display the call stack.
93 * 11 2/22/98 2:48p John
94 * More String Externalization Classification
99 // Nothing in this module should be externalized!!!
102 //#define DUMPRAM // This dumps all symbol sizes. See John for more info
105 #include <windowsx.h>
111 //Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors
112 #define SHOW_CALL_STACK
114 #ifdef SHOW_CALL_STACK
119 enum { BUFFER_SIZE = 32000 } ;
122 void Printf( const char* format, ... ) ;
123 void SetWindowText( HWND hWnd ) const ;
124 char buffer[ BUFFER_SIZE ] ;
131 DumpBuffer :: DumpBuffer()
137 void DumpBuffer :: Clear()
143 void DumpBuffer :: Printf( const char* format, ... )
145 // protect against obvious buffer overflow
146 if( current - buffer < BUFFER_SIZE )
149 va_start( argPtr, format ) ;
150 int count = vsprintf( current, format, argPtr ) ;
157 void DumpBuffer :: SetWindowText( HWND hWnd ) const
159 SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ;
172 int DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ;
176 enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ;
177 char latestModule[ MAX_MODULENAME_LEN ] ;
178 char latestFile[ VA_MAX_FILENAME_LEN ] ;
181 HANDLE hFileMapping ;
182 PIMAGE_DOS_HEADER fileBase ;
183 // Pointers to debug information
184 PIMAGE_NT_HEADERS NT_Header ;
185 PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo ;
186 PIMAGE_SYMBOL COFFSymbolTable ;
187 int COFFSymbolCount ;
188 const char* stringTable ;
190 void ClearFileCache() ;
191 void ClearDebugPtrs() ;
192 void MapFileInMemory( const char* module ) ;
193 void FindDebugInfo() ;
194 void DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
195 void DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
196 PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader() ;
197 PIMAGE_SECTION_HEADER SectionHeaderFromName( const char* name ) ;
198 const char* GetSymbolName( PIMAGE_SYMBOL sym ) ;
202 // Add an offset to a pointer and cast to a given type; may be
203 // implemented as a template function but Visual C++ has some problems.
204 #define BasedPtr( type, ptr, ofs ) (type)( (DWORD)(ptr) + (DWORD)(ofs) )
207 PE_Debug :: PE_Debug()
209 // Init file mapping cache
211 hFile = INVALID_HANDLE_VALUE ;
218 PE_Debug :: ~PE_Debug()
224 void PE_Debug :: ClearReport()
226 latestModule[ 0 ] = 0 ;
227 latestFile[ 0 ] = 0 ;
231 void PE_Debug :: ClearDebugPtrs()
234 COFFDebugInfo = NULL ;
235 COFFSymbolTable = NULL ;
236 COFFSymbolCount = 0 ;
241 void PE_Debug :: ClearFileCache()
245 UnmapViewOfFile( fileBase ) ;
248 if( hFileMapping != 0 )
250 CloseHandle( hFileMapping ) ;
253 if( hFile != INVALID_HANDLE_VALUE )
255 CloseHandle( hFile ) ;
256 hFile = INVALID_HANDLE_VALUE ;
261 void PE_Debug :: DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress )
263 PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo,
264 COFFDebugInfo->LvaToFirstLinenumber ) ;
265 DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ;
266 const DWORD none = (DWORD)-1 ;
268 DWORD lineNum = none ;
269 for( DWORD i=0; i < lineCount; i++ )
271 if( line->Linenumber != 0 ) // A regular line number
273 // look for line with bigger address <= relativeAddress
274 if( line->Type.VirtualAddress <= relativeAddress &&
275 line->Type.VirtualAddress > maxAddr )
277 maxAddr = line->Type.VirtualAddress ;
278 lineNum = line->Linenumber ;
283 if( lineNum != none )
284 dumpBuffer.Printf( " line %d\r\n", lineNum ) ;
286 // dumpBuffer.Printf( " line <unknown>\r\n" ) ;
290 const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym )
292 const int NAME_MAX_LEN = 64 ;
293 static char buf[ NAME_MAX_LEN ] ;
294 if( sym->N.Name.Short != 0 )
296 strncpy( buf, (const char*)sym->N.ShortName, 8 ) ;
301 strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ;
302 buf[ NAME_MAX_LEN - 1 ] = 0 ;
307 void unmangle(char *dst, const char *src)
309 //strcpy( dst, src );
313 while( (*src) && (*src!=' ') && (*src!='@') ) {
321 typedef struct MemSymbol {
336 Symbols = (MemSymbol *)malloc(Max_symbols*sizeof(MemSymbol));
342 void Add_Symbol( int section, int offset, const char *name, char *module )
344 if ( Num_symbols >= Max_symbols ) {
348 MemSymbol * sym = &Symbols[Num_symbols++];
350 sym->section = section;
351 sym->offset = offset;
354 strcpy( sym->name, name );
355 strcat( sym->name, "(" );
356 strcat( sym->name, module );
357 strcat( sym->name, ")" );
361 int Sym_compare( const void *arg1, const void *arg2 )
363 MemSymbol * sym1 = (MemSymbol *)arg1;
364 MemSymbol * sym2 = (MemSymbol *)arg2;
366 if ( sym1->section < sym2->section ) {
368 } else if ( sym1->section > sym2->section ) {
371 if ( sym1->offset > sym2->offset ) {
379 int Sym_compare1( const void *arg1, const void *arg2 )
381 MemSymbol * sym1 = (MemSymbol *)arg1;
382 MemSymbol * sym2 = (MemSymbol *)arg2;
384 if ( sym1->size < sym2->size ) {
386 } else if ( sym1->size > sym2->size ) {
397 qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare );
399 for (i=0;i<Num_symbols; i++ ) {
400 MemSymbol * sym1 = &Symbols[i];
401 MemSymbol * sym2 = &Symbols[i+1];
402 if ( (i<Num_symbols-1) && (sym1->section == sym2->section) ) {
403 sym1->size = sym2->offset-sym1->offset;
409 qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 );
412 FILE *fp = fopen( "dump", "wt" );
414 fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" );
417 for (i=0;i<Num_symbols; i++ ) {
418 MemSymbol * sym = &Symbols[i];
420 total_size += sym->size;
421 fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size );
432 void PE_Debug::DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress )
434 // Variables to keep track of function symbols
435 PIMAGE_SYMBOL currentSym = COFFSymbolTable ;
436 PIMAGE_SYMBOL fnSymbol = NULL ;
437 DWORD maxFnAddress = 0 ;
443 // Variables to keep track of file symbols
444 PIMAGE_SYMBOL fileSymbol = NULL ;
445 PIMAGE_SYMBOL latestFileSymbol = NULL ;
446 for ( int i = 0; i < COFFSymbolCount; i++ ) {
448 // Look for .text section where relativeAddress belongs to.
449 // Keep track of the filename the .text section belongs to.
450 if ( currentSym->StorageClass == IMAGE_SYM_CLASS_FILE ) {
451 latestFileSymbol = currentSym;
454 // Borland uses "CODE" instead of the standard ".text" entry
455 // Microsoft uses sections that only _begin_ with .text
456 const char* symName = GetSymbolName( currentSym ) ;
458 if ( strnicmp( symName, ".text", 5 ) == 0 || strcmpi( symName, "CODE" ) == 0 ) {
459 if ( currentSym->Value <= relativeAddress ) {
460 PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1) ;
461 if ( currentSym->Value + auxSym->Section.Length >= relativeAddress ) {
462 fileSymbol = latestFileSymbol ;
468 // Look for the function with biggest address <= relativeAddress
469 BOOL isFunction = ISFCN( currentSym->Type ); // Type == 0x20, See WINNT.H
470 if ( isFunction && ( currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC ) ) {
472 if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) {
473 maxFnAddress = currentSym->Value ;
474 fnSymbol = currentSym ;
479 if ( !isFunction && (currentSym->SectionNumber>-1) ) {
480 if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) {
482 char pretty_module[1024];
485 const char* auxSym = (const char*)(latestFileSymbol + 1) ;
486 char tmpFile[ VA_MAX_FILENAME_LEN ] ;
487 strcpy( tmpFile, auxSym ) ;
488 strcpy( pretty_module, tmpFile );
489 char *p = pretty_module+strlen(pretty_module)-1;
490 // Move p to point to first letter of EXE filename
491 while( (*p!='\\') && (*p!='/') && (*p!=':') )
494 if ( strlen(p) < 1 ) {
495 strcpy( pretty_module, "<unknown>" );
497 memmove( pretty_module, p, strlen(p)+1 );
500 strcpy( pretty_module, "" );
503 Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module );
508 // Advance counters, skip aux symbols
509 i += currentSym->NumberOfAuxSymbols ;
510 currentSym += currentSym->NumberOfAuxSymbols ;
518 // dump symbolic info if found
520 const char* auxSym = (const char*)(fileSymbol + 1) ;
522 if( strcmpi( latestFile, auxSym ) ) {
523 strcpy( latestFile, auxSym ) ;
524 //JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ;
527 latestFile[ 0 ] = 0 ;
528 //JAS dumpBuffer.Printf( " file: unknown\r\n" ) ;
533 unmangle(tmp_name, GetSymbolName( fnSymbol ) );
534 dumpBuffer.Printf( " %s()", tmp_name ) ;
536 dumpBuffer.Printf( " <unknown>" ) ;
541 PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name )
543 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ;
544 for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ )
546 if( strnicmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 )
555 PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader()
557 // Some files have a wrong entry in the COFF header, so
558 // first check if the debug info exists at all
559 if( NT_Header->FileHeader.PointerToSymbolTable == 0 )
561 DWORD debugDirRVA = NT_Header->OptionalHeader.
562 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].
564 if( debugDirRVA == 0 )
567 // The following values must be calculated differently for MS/Borland files
568 PIMAGE_DEBUG_DIRECTORY debugDir ;
571 // Borland files have the debug directory at the beginning of a .debug section
572 PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName( ".debug" ) ;
573 if( debugHeader && debugHeader->VirtualAddress == debugDirRVA )
575 debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase) ;
576 size = NT_Header->OptionalHeader.
577 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size *
578 sizeof( IMAGE_DEBUG_DIRECTORY ) ;
581 // Microsoft debug directory is in the .rdata section
583 debugHeader = SectionHeaderFromName( ".rdata" ) ;
584 if( debugHeader == 0 )
586 size = NT_Header->OptionalHeader.
587 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size ;
588 DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress ;
589 debugDir = BasedPtr( PIMAGE_DEBUG_DIRECTORY, fileBase,
590 debugHeader->PointerToRawData + offsetInto_rdata ) ;
593 // look for COFF debug info
594 DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ;
595 for( DWORD i = 0; i < debugFormats; i++ )
597 if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF )
598 return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ;
606 void PE_Debug :: FindDebugInfo()
609 // Put everything into a try/catch in case the file has wrong fields
612 // Verify that fileBase is a valid pointer to a DOS header
613 if( fileBase->e_magic == IMAGE_DOS_SIGNATURE )
615 // Get a pointer to the PE header
616 NT_Header = BasedPtr( PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew ) ;
617 // Verify that NT_Header is a valid pointer to a NT header
618 if( NT_Header->Signature == IMAGE_NT_SIGNATURE )
620 // Get a pointer to the debug header if any
621 COFFDebugInfo = GetDebugHeader() ;
622 // Get a pointer to the symbol table and retrieve the number of symbols
623 if( NT_Header->FileHeader.PointerToSymbolTable )
625 BasedPtr( PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable ) ;
626 COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols ;
627 // The string table starts right after the symbol table
628 stringTable = (const char*)(COFFSymbolTable + COFFSymbolCount) ;
634 // Header wrong, do nothing
639 void PE_Debug :: MapFileInMemory( const char* module )
642 hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL,
643 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
644 if( hFile != INVALID_HANDLE_VALUE )
646 hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ;
647 if( hFileMapping != 0 )
648 fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ) ;
650 // NB: open files/mapping are closed later in ClearFileCache
654 int PE_Debug::DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance )
656 // Avoid to open, map and looking for debug header/symbol table
657 // by caching the latest and comparing the actual module with
659 static char module[ MAX_MODULENAME_LEN ] ;
660 GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ;
663 if( strcmpi( latestModule, module ) ) {
664 strcpy( latestModule, module );
665 //JAS dumpBuffer.Printf( "Module: %s\r\n", module );
666 MapFileInMemory( module );
670 char pretty_module[1024];
672 strcpy( pretty_module, module );
673 char *p = pretty_module+strlen(pretty_module)-1;
674 // Move p to point to first letter of EXE filename
675 while( (*p!='\\') && (*p!='/') && (*p!=':') )
678 if ( strlen(p) < 1 ) {
679 strcpy( pretty_module, "<unknown>" );
681 memmove( pretty_module, p, strlen(p)+1 );
685 // Put everything into a try/catch in case the file has wrong fields
687 DWORD relativeAddress = caller - (BYTE*)hInstance ;
688 // Dump symbolic information and line number if available
689 if( COFFSymbolCount != 0 && COFFSymbolTable != NULL ) {
690 DumpSymbolInfo( dumpBuffer, relativeAddress ) ;
692 DumpLineNumber( dumpBuffer, relativeAddress ) ;
695 //dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this module.\r\n" ) ;
696 //JAS dumpBuffer.Printf( " no debug information\r\n" ) ;
697 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
701 // Header wrong, do nothing
705 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
706 //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ;
707 //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ;
716 void DumpCallsStack( DumpBuffer& dumpBuffer )
718 const char* separator = "------------------------------------------------------------------\r\n" ;
719 static PE_Debug PE_debug ;
721 dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ;
722 dumpBuffer.Printf( separator ) ;
724 // The structure of the stack frames is the following:
725 // EBP -> parent stack frame EBP
726 // return address for this call ( = caller )
727 // The chain can be navigated iteratively, after the
728 // initial value of EBP is loaded from the register
729 DWORD parentEBP, retval;
730 MEMORY_BASIC_INFORMATION mbi ;
735 __asm MOV parentEBP, EBP
742 if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) ) {
745 parentEBP = *(DWORD*)parentEBP ;
747 BYTE **NextCaller = ((BYTE**)parentEBP + 1);
749 if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) {
753 BYTE* caller = *NextCaller; // Error sometimes!!!
755 // Skip the first EBP as it points to AssertionFailed, which is
756 // uninteresting for the user
760 // Get the instance handle of the module where caller belongs to
761 retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ;
763 // The instance handle is equal to the allocation base in Win32
764 hInstance = (HINSTANCE)mbi.AllocationBase ;
766 if( ( retval == sizeof( mbi ) ) && hInstance ) {
767 if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) {
771 break ; // End of the call chain
777 dumpBuffer.Printf( separator ) ;
778 PE_debug.ClearReport() ; // Prepare for future calls
782 // This ought to be local to VerboseAssert, but it
783 // causes problems in Visual C++ (in the CRTL init phase)
784 static DumpBuffer dumpBuffer ;
786 #endif //SHOW_CALL_STACK
789 char AssertText1[1024];
790 char AssertText2[1024];
792 uint flags = MB_TASKMODAL|MB_SETFOREGROUND;
794 extern void gr_force_windowed();
796 void dump_text_to_clipboard(char *text)
798 int len = strlen(text)+1024;
800 HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len );
801 if ( !h_text ) return;
802 char *ptr = (char *)GlobalLock(h_text);
805 // copy then, if you find any \n's without \r's, then add in the \r.
808 if ( (*text == '\n') && (last_char != '\r') ) {
816 GlobalUnlock(h_text);
819 SetClipboardData(CF_TEXT, h_text);
824 void _cdecl WinAssert(char * text, char * filename, int linenum )
831 sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d", text, filename, linenum );
833 #ifdef SHOW_CALL_STACK
835 dumpBuffer.Printf( AssertText1 );
836 dumpBuffer.Printf( "\r\n" );
837 DumpCallsStack( dumpBuffer ) ;
838 dump_text_to_clipboard(dumpBuffer.buffer);
840 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
841 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n");
843 val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags );
845 val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags );
860 void _cdecl Error( char * filename, int line, char * format, ... )
867 va_start(args, format);
868 vsprintf(AssertText1,format,args);
870 sprintf(AssertText2,"Error: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
872 #ifdef SHOW_CALL_STACK
874 dumpBuffer.Printf( AssertText2 );
875 dumpBuffer.Printf( "\r\n" );
876 DumpCallsStack( dumpBuffer ) ;
877 dump_text_to_clipboard(dumpBuffer.buffer);
879 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
880 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
882 val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_OKCANCEL );
884 strcat(AssertText2,"\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
885 val = MessageBox(NULL, AssertText2, "Error!", flags|MB_OKCANCEL );
888 if (val == IDCANCEL ) {
899 void _cdecl Warning( char * filename, int line, char * format, ... )
909 va_start(args, format);
910 vsprintf(AssertText1,format,args);
912 sprintf(AssertText2,"Warning: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
915 #ifdef SHOW_CALL_STACK
917 dumpBuffer.Printf( AssertText2 );
918 dumpBuffer.Printf( "\r\n" );
919 DumpCallsStack( dumpBuffer ) ;
920 dump_text_to_clipboard(dumpBuffer.buffer);
922 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
923 dumpBuffer.Printf("\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
925 id = MessageBox(NULL, dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL|flags );
927 strcat(AssertText2,"\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
928 id = MessageBox(NULL, AssertText2, "Warning!", MB_YESNOCANCEL|flags );
930 if ( id == IDCANCEL )
932 else if ( id == IDYES ) {
944 //================= memory stuff
946 char *format_mem( DWORD num )
949 sprintf( tmp_mem, "%d bytes", num );
950 } else if ( num < 1024*1024 ) {
951 sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f );
953 sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f) );
963 MEMORY_BASIC_INFORMATION mbi ;
968 ms.dwLength = sizeof(MEMORYSTATUS);
969 GlobalMemoryStatus(&ms);
971 printf( "Percent of memory in use: %d%%\n", ms.dwMemoryLoad );
972 printf( "Bytes of physical memory: %s\n", format_mem(ms.dwTotalPhys) );
973 printf( "Free physical memory bytes: %s\n", format_mem(ms.dwAvailPhys) ); //
974 printf( "Bytes of paging file: %s\n", format_mem(ms.dwTotalPageFile) ); // bytes of paging file
975 printf( "Free bytes of paging file: %s\n", format_mem(ms.dwAvailPageFile) ); // free bytes of paging file
976 printf( "User bytes of address space: %s\n", format_mem(ms.dwTotalVirtual) ); //
977 printf( "Free user bytes: %s\n", format_mem(ms.dwAvailVirtual) ); // free user bytes
980 // Get the instance handle of the module where caller belongs to
981 retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ;
983 // The instance handle is equal to the allocation base in Win32
984 hInstance = (HINSTANCE)mbi.AllocationBase ;
986 if( ( retval == sizeof( mbi ) ) && hInstance ) {
987 printf( "Virtual Query succeeded...\n" );
989 printf( "Virtual Query failed...\n" );
1001 #define nNoMansLandSize 4
1003 typedef struct _CrtMemBlockHeader
1005 struct _CrtMemBlockHeader * pBlockHeaderNext;
1006 struct _CrtMemBlockHeader * pBlockHeaderPrev;
1012 unsigned char gap[nNoMansLandSize];
1014 * unsigned char data[nDataSize];
1015 * unsigned char anotherGap[nNoMansLandSize];
1017 } _CrtMemBlockHeader;
1019 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1021 int __cdecl MyAllocHook(
1027 const char * szFileName,
1031 // char *operation[] = { "", "allocating", "re-allocating", "freeing" };
1032 // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };
1034 if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations
1037 _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
1038 _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );
1040 if ( nAllocType == 3 ) {
1041 _CrtMemBlockHeader *phd = pHdr(pvData);
1042 nSize = phd->nDataSize;
1045 // mprintf(( "Total RAM = %d\n", TotalRam ));
1047 /* mprintf(( "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n",
1048 szFileName, nLine, operation[nAllocType], nSize,
1049 blockType[nBlockUse], lRequest ));
1050 if ( pvData != NULL )
1051 mprintf(( " at %X", pvData ));
1056 return( TRUE ); // Allow the memory operation to proceed
1059 void windebug_memwatch_init()
1061 //_CrtSetAllocHook(MyAllocHook);
1070 int Watch_malloc = 0;
1071 DCF_BOOL(watch_malloc, Watch_malloc );
1075 #define HEAP_FLAG HEAP_NO_SERIALIZE
1076 // #define HEAP_FLAG HEAP_GENERATE_EXCEPTIONS
1078 // Returns 0 if not enough RAM.
1079 int vm_init(int min_heap_size)
1086 Heap = HeapCreate( HEAP_FLAG, min_heap_size, 0 );
1087 if ( Heap == NULL ) {
1094 char *clean_filename(char *name)
1096 char *p = name+strlen(name)-1;
1097 // Move p to point to first letter of EXE filename
1098 while( (*p!='\\') && (*p!='/') && (*p!=':') )
1106 void *vm_malloc( int size, char *filename, int line )
1108 void *vm_malloc( int size )
1115 return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ );
1119 void *ptr = HeapAlloc(Heap, HEAP_FLAG, size );
1121 if ( ptr == NULL ) {
1122 mprintf(( "HeapAlloc failed!!!!!!!!!!!!!!!!!!!\n" ));
1124 Error(LOCATION, "Out of memory. Try closing down other applications, increasing your\n"
1125 "virtual memory size, or installing more physical RAM.\n");
1129 int actual_size = HeapSize(Heap, HEAP_FLAG, ptr);
1130 if ( Watch_malloc ) {
1131 mprintf(( "Malloc %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line ));
1133 TotalRam += actual_size;
1139 char *vm_strdup( const char *ptr, char *filename, int line )
1141 char *vm_strdup( const char *ptr )
1145 int len = strlen(ptr);
1147 dst = (char *)vm_malloc( len+1, filename, line );
1149 dst = (char *)vm_malloc( len+1 );
1156 void vm_free( void *ptr, char *filename, int line )
1158 void vm_free( void *ptr )
1163 mprintf(("Why are you trying to free a NULL pointer? [%s(%d)]\n", clean_filename(filename), line));
1165 mprintf(("Why are you trying to free a NULL pointer?\n"));
1173 _CrtMemBlockHeader *phd = pHdr(ptr);
1174 int nSize = phd->nDataSize;
1179 _free_dbg(ptr,_NORMAL_BLOCK);
1185 int actual_size = HeapSize(Heap, HEAP_FLAG, ptr);
1186 if ( Watch_malloc ) {
1187 mprintf(( "Free %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line ));
1189 TotalRam -= actual_size;
1191 HeapFree( Heap, HEAP_FLAG, ptr );
1192 HeapCompact(Heap, HEAP_FLAG);