2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/GlobalIncs/WinDebug.cpp $
18 * Revision 1.2 2002/06/09 04:41:17 relnev
19 * added copyright header
21 * Revision 1.1.1.1 2002/05/03 03:28:09 root
25 * 4 12/01/98 4:46p Dave
26 * Put in targa bitmap support (16 bit).
28 * 3 11/30/98 5:31p Dave
29 * Fixed up Fred support for software mode.
31 * 2 10/07/98 10:52a Dave
34 * 1 10/07/98 10:48a Dave
36 * 32 5/25/98 1:39p John
37 * added code to give error and abort if malloc fails.
39 * 31 5/06/98 8:03p Allender
40 * AL: Do early out if trying to free NULL pointer in vm_free(). Print
41 * out warning message.
43 * 30 4/30/98 12:02a Lawrance
44 * compile out Warnings() on NDEBUG builds
46 * 29 4/22/98 5:15p Lawrance
49 * 28 4/21/98 1:02p John
50 * fixed bug where the clipboard text dumped wasn't null terminated.
52 * 27 4/20/98 3:22p John
53 * fixed bug that displayed wrong amount of allocated ram.
55 * 26 4/17/98 3:27p Allender
57 * 25 4/17/98 1:41p Allender
58 * made to compile with NDEBUG defined
60 * 24 4/17/98 7:00a John
61 * Added in hooks for new memory allocator. I haven't tried compiling
62 * under NDEBUG, but I tried to put in code for it.
64 * 23 4/01/98 9:21p John
65 * Made NDEBUG, optimized build with no warnings or errors.
67 * 22 3/31/98 8:17p John
68 * Added code to dump memory contents
70 * 21 3/31/98 11:17a Allender
71 * fix bug with INTERPLAYQA Int3's and Warning dialogs
73 * 20 3/30/98 4:02p John
74 * Made machines with < 32 MB of RAM use every other frame of certain
75 * bitmaps. Put in code to keep track of how much RAM we've malloc'd.
77 * 19 3/14/98 3:35p John
78 * cleaned up call stack code. Exited out on not properly aligned EBP's.
80 * 18 3/14/98 3:25p John
81 * Added code to check the parentEBP pointer for validity before
84 * 17 3/11/98 5:34p Lawrance
85 * Fix typo in error dialog box
87 * 16 3/06/98 2:21p John
88 * Correct some ok/cancel messages. Made call stack info prettier for
89 * modules with no debugging info
91 * 15 3/05/98 3:04p John
92 * Made Errors, SDL_assert, Warning info paste to clipboard.
94 * 14 3/05/98 9:17a John
95 * Limited stack dump depth to 16
97 * 13 3/05/98 9:14a John
98 * Added in a simple function name unmangler
100 * 12 3/04/98 7:08p John
101 * Made Freespace generate COFF files. Made SDL_assert,Error, and Warning
102 * display the call stack.
104 * 11 2/22/98 2:48p John
105 * More String Externalization Classification
110 // Nothing in this module should be externalized!!!
113 //#define DUMPRAM // This dumps all symbol sizes. See John for more info
116 #include <windowsx.h>
122 //Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors
123 #define SHOW_CALL_STACK
125 #ifdef SHOW_CALL_STACK
130 enum { BUFFER_SIZE = 32000 } ;
133 void Printf( const char* format, ... ) ;
134 void SetWindowText( HWND hWnd ) const ;
135 char buffer[ BUFFER_SIZE ] ;
142 DumpBuffer :: DumpBuffer()
148 void DumpBuffer :: Clear()
154 void DumpBuffer :: Printf( const char* format, ... )
156 // protect against obvious buffer overflow
157 if( current - buffer < BUFFER_SIZE )
160 va_start( argPtr, format ) ;
161 int count = vsprintf( current, format, argPtr ) ;
168 void DumpBuffer :: SetWindowText( HWND hWnd ) const
170 SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ;
183 int DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ;
187 enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ;
188 char latestModule[ MAX_MODULENAME_LEN ] ;
189 char latestFile[ VA_MAX_FILENAME_LEN ] ;
192 HANDLE hFileMapping ;
193 PIMAGE_DOS_HEADER fileBase ;
194 // Pointers to debug information
195 PIMAGE_NT_HEADERS NT_Header ;
196 PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo ;
197 PIMAGE_SYMBOL COFFSymbolTable ;
198 int COFFSymbolCount ;
199 const char* stringTable ;
201 void ClearFileCache() ;
202 void ClearDebugPtrs() ;
203 void MapFileInMemory( const char* module ) ;
204 void FindDebugInfo() ;
205 void DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
206 void DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
207 PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader() ;
208 PIMAGE_SECTION_HEADER SectionHeaderFromName( const char* name ) ;
209 const char* GetSymbolName( PIMAGE_SYMBOL sym ) ;
213 // Add an offset to a pointer and cast to a given type; may be
214 // implemented as a template function but Visual C++ has some problems.
215 #define BasedPtr( type, ptr, ofs ) (type)( (DWORD)(ptr) + (DWORD)(ofs) )
218 PE_Debug :: PE_Debug()
220 // Init file mapping cache
222 hFile = INVALID_HANDLE_VALUE ;
229 PE_Debug :: ~PE_Debug()
235 void PE_Debug :: ClearReport()
237 latestModule[ 0 ] = 0 ;
238 latestFile[ 0 ] = 0 ;
242 void PE_Debug :: ClearDebugPtrs()
245 COFFDebugInfo = NULL ;
246 COFFSymbolTable = NULL ;
247 COFFSymbolCount = 0 ;
252 void PE_Debug :: ClearFileCache()
256 UnmapViewOfFile( fileBase ) ;
259 if( hFileMapping != 0 )
261 CloseHandle( hFileMapping ) ;
264 if( hFile != INVALID_HANDLE_VALUE )
266 CloseHandle( hFile ) ;
267 hFile = INVALID_HANDLE_VALUE ;
272 void PE_Debug :: DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress )
274 PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo,
275 COFFDebugInfo->LvaToFirstLinenumber ) ;
276 DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ;
277 const DWORD none = (DWORD)-1 ;
279 DWORD lineNum = none ;
280 for( DWORD i=0; i < lineCount; i++ )
282 if( line->Linenumber != 0 ) // A regular line number
284 // look for line with bigger address <= relativeAddress
285 if( line->Type.VirtualAddress <= relativeAddress &&
286 line->Type.VirtualAddress > maxAddr )
288 maxAddr = line->Type.VirtualAddress ;
289 lineNum = line->Linenumber ;
294 if( lineNum != none )
295 dumpBuffer.Printf( " line %d\r\n", lineNum ) ;
297 // dumpBuffer.Printf( " line <unknown>\r\n" ) ;
301 const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym )
303 const int NAME_MAX_LEN = 64 ;
304 static char buf[ NAME_MAX_LEN ] ;
305 if( sym->N.Name.Short != 0 )
307 strncpy( buf, (const char*)sym->N.ShortName, 8 ) ;
312 strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ;
313 buf[ NAME_MAX_LEN - 1 ] = 0 ;
318 void unmangle(char *dst, const char *src)
320 //strcpy( dst, src );
324 while( (*src) && (*src!=' ') && (*src!='@') ) {
332 typedef struct MemSymbol {
347 Symbols = (MemSymbol *)malloc(Max_symbols*sizeof(MemSymbol));
353 void Add_Symbol( int section, int offset, const char *name, char *module )
355 if ( Num_symbols >= Max_symbols ) {
359 MemSymbol * sym = &Symbols[Num_symbols++];
361 sym->section = section;
362 sym->offset = offset;
365 strcpy( sym->name, name );
366 strcat( sym->name, "(" );
367 strcat( sym->name, module );
368 strcat( sym->name, ")" );
372 int Sym_compare( const void *arg1, const void *arg2 )
374 MemSymbol * sym1 = (MemSymbol *)arg1;
375 MemSymbol * sym2 = (MemSymbol *)arg2;
377 if ( sym1->section < sym2->section ) {
379 } else if ( sym1->section > sym2->section ) {
382 if ( sym1->offset > sym2->offset ) {
390 int Sym_compare1( const void *arg1, const void *arg2 )
392 MemSymbol * sym1 = (MemSymbol *)arg1;
393 MemSymbol * sym2 = (MemSymbol *)arg2;
395 if ( sym1->size < sym2->size ) {
397 } else if ( sym1->size > sym2->size ) {
408 qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare );
410 for (i=0;i<Num_symbols; i++ ) {
411 MemSymbol * sym1 = &Symbols[i];
412 MemSymbol * sym2 = &Symbols[i+1];
413 if ( (i<Num_symbols-1) && (sym1->section == sym2->section) ) {
414 sym1->size = sym2->offset-sym1->offset;
420 qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 );
423 FILE *fp = fopen( "dump", "wt" );
425 fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" );
428 for (i=0;i<Num_symbols; i++ ) {
429 MemSymbol * sym = &Symbols[i];
431 total_size += sym->size;
432 fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size );
443 void PE_Debug::DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress )
445 // Variables to keep track of function symbols
446 PIMAGE_SYMBOL currentSym = COFFSymbolTable ;
447 PIMAGE_SYMBOL fnSymbol = NULL ;
448 DWORD maxFnAddress = 0 ;
454 // Variables to keep track of file symbols
455 PIMAGE_SYMBOL fileSymbol = NULL ;
456 PIMAGE_SYMBOL latestFileSymbol = NULL ;
457 for ( int i = 0; i < COFFSymbolCount; i++ ) {
459 // Look for .text section where relativeAddress belongs to.
460 // Keep track of the filename the .text section belongs to.
461 if ( currentSym->StorageClass == IMAGE_SYM_CLASS_FILE ) {
462 latestFileSymbol = currentSym;
465 // Borland uses "CODE" instead of the standard ".text" entry
466 // Microsoft uses sections that only _begin_ with .text
467 const char* symName = GetSymbolName( currentSym ) ;
469 if ( SDL_strncasecmp( symName, ".text", 5 ) == 0 || strcmpi( symName, "CODE" ) == 0 ) {
470 if ( currentSym->Value <= relativeAddress ) {
471 PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1) ;
472 if ( currentSym->Value + auxSym->Section.Length >= relativeAddress ) {
473 fileSymbol = latestFileSymbol ;
479 // Look for the function with biggest address <= relativeAddress
480 BOOL isFunction = ISFCN( currentSym->Type ); // Type == 0x20, See WINNT.H
481 if ( isFunction && ( currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC ) ) {
483 if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) {
484 maxFnAddress = currentSym->Value ;
485 fnSymbol = currentSym ;
490 if ( !isFunction && (currentSym->SectionNumber>-1) ) {
491 if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) {
493 char pretty_module[1024];
496 const char* auxSym = (const char*)(latestFileSymbol + 1) ;
497 char tmpFile[ VA_MAX_FILENAME_LEN ] ;
498 strcpy( tmpFile, auxSym ) ;
499 strcpy( pretty_module, tmpFile );
500 char *p = pretty_module+strlen(pretty_module)-1;
501 // Move p to point to first letter of EXE filename
502 while( (*p!='\\') && (*p!='/') && (*p!=':') )
505 if ( strlen(p) < 1 ) {
506 strcpy( pretty_module, "<unknown>" );
508 memmove( pretty_module, p, strlen(p)+1 );
511 strcpy( pretty_module, "" );
514 Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module );
519 // Advance counters, skip aux symbols
520 i += currentSym->NumberOfAuxSymbols ;
521 currentSym += currentSym->NumberOfAuxSymbols ;
529 // dump symbolic info if found
531 const char* auxSym = (const char*)(fileSymbol + 1) ;
533 if( strcmpi( latestFile, auxSym ) ) {
534 strcpy( latestFile, auxSym ) ;
535 //JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ;
538 latestFile[ 0 ] = 0 ;
539 //JAS dumpBuffer.Printf( " file: unknown\r\n" ) ;
544 unmangle(tmp_name, GetSymbolName( fnSymbol ) );
545 dumpBuffer.Printf( " %s()", tmp_name ) ;
547 dumpBuffer.Printf( " <unknown>" ) ;
552 PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name )
554 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ;
555 for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ )
557 if( SDL_strncasecmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 )
566 PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader()
568 // Some files have a wrong entry in the COFF header, so
569 // first check if the debug info exists at all
570 if( NT_Header->FileHeader.PointerToSymbolTable == 0 )
572 DWORD debugDirRVA = NT_Header->OptionalHeader.
573 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].
575 if( debugDirRVA == 0 )
578 // The following values must be calculated differently for MS/Borland files
579 PIMAGE_DEBUG_DIRECTORY debugDir ;
582 // Borland files have the debug directory at the beginning of a .debug section
583 PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName( ".debug" ) ;
584 if( debugHeader && debugHeader->VirtualAddress == debugDirRVA )
586 debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase) ;
587 size = NT_Header->OptionalHeader.
588 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size *
589 sizeof( IMAGE_DEBUG_DIRECTORY ) ;
592 // Microsoft debug directory is in the .rdata section
594 debugHeader = SectionHeaderFromName( ".rdata" ) ;
595 if( debugHeader == 0 )
597 size = NT_Header->OptionalHeader.
598 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size ;
599 DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress ;
600 debugDir = BasedPtr( PIMAGE_DEBUG_DIRECTORY, fileBase,
601 debugHeader->PointerToRawData + offsetInto_rdata ) ;
604 // look for COFF debug info
605 DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ;
606 for( DWORD i = 0; i < debugFormats; i++ )
608 if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF )
609 return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ;
617 void PE_Debug :: FindDebugInfo()
620 // Put everything into a try/catch in case the file has wrong fields
623 // Verify that fileBase is a valid pointer to a DOS header
624 if( fileBase->e_magic == IMAGE_DOS_SIGNATURE )
626 // Get a pointer to the PE header
627 NT_Header = BasedPtr( PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew ) ;
628 // Verify that NT_Header is a valid pointer to a NT header
629 if( NT_Header->Signature == IMAGE_NT_SIGNATURE )
631 // Get a pointer to the debug header if any
632 COFFDebugInfo = GetDebugHeader() ;
633 // Get a pointer to the symbol table and retrieve the number of symbols
634 if( NT_Header->FileHeader.PointerToSymbolTable )
636 BasedPtr( PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable ) ;
637 COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols ;
638 // The string table starts right after the symbol table
639 stringTable = (const char*)(COFFSymbolTable + COFFSymbolCount) ;
645 // Header wrong, do nothing
650 void PE_Debug :: MapFileInMemory( const char* module )
653 hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL,
654 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
655 if( hFile != INVALID_HANDLE_VALUE )
657 hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ;
658 if( hFileMapping != 0 )
659 fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ) ;
661 // NB: open files/mapping are closed later in ClearFileCache
665 int PE_Debug::DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance )
667 // Avoid to open, map and looking for debug header/symbol table
668 // by caching the latest and comparing the actual module with
670 static char module[ MAX_MODULENAME_LEN ] ;
671 GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ;
674 if( strcmpi( latestModule, module ) ) {
675 strcpy( latestModule, module );
676 //JAS dumpBuffer.Printf( "Module: %s\r\n", module );
677 MapFileInMemory( module );
681 char pretty_module[1024];
683 strcpy( pretty_module, module );
684 char *p = pretty_module+strlen(pretty_module)-1;
685 // Move p to point to first letter of EXE filename
686 while( (*p!='\\') && (*p!='/') && (*p!=':') )
689 if ( strlen(p) < 1 ) {
690 strcpy( pretty_module, "<unknown>" );
692 memmove( pretty_module, p, strlen(p)+1 );
696 // Put everything into a try/catch in case the file has wrong fields
698 DWORD relativeAddress = caller - (BYTE*)hInstance ;
699 // Dump symbolic information and line number if available
700 if( COFFSymbolCount != 0 && COFFSymbolTable != NULL ) {
701 DumpSymbolInfo( dumpBuffer, relativeAddress ) ;
703 DumpLineNumber( dumpBuffer, relativeAddress ) ;
706 //dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this module.\r\n" ) ;
707 //JAS dumpBuffer.Printf( " no debug information\r\n" ) ;
708 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
712 // Header wrong, do nothing
716 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
717 //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ;
718 //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ;
727 void DumpCallsStack( DumpBuffer& dumpBuffer )
729 const char* separator = "------------------------------------------------------------------\r\n" ;
730 static PE_Debug PE_debug ;
732 dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ;
733 dumpBuffer.Printf( separator ) ;
735 // The structure of the stack frames is the following:
736 // EBP -> parent stack frame EBP
737 // return address for this call ( = caller )
738 // The chain can be navigated iteratively, after the
739 // initial value of EBP is loaded from the register
740 DWORD parentEBP, retval;
741 MEMORY_BASIC_INFORMATION mbi ;
746 __asm MOV parentEBP, EBP
753 if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) ) {
756 parentEBP = *(DWORD*)parentEBP ;
758 BYTE **NextCaller = ((BYTE**)parentEBP + 1);
760 if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) {
764 BYTE* caller = *NextCaller; // Error sometimes!!!
766 // Skip the first EBP as it points to AssertionFailed, which is
767 // uninteresting for the user
771 // Get the instance handle of the module where caller belongs to
772 retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ;
774 // The instance handle is equal to the allocation base in Win32
775 hInstance = (HINSTANCE)mbi.AllocationBase ;
777 if( ( retval == sizeof( mbi ) ) && hInstance ) {
778 if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) {
782 break ; // End of the call chain
788 dumpBuffer.Printf( separator ) ;
789 PE_debug.ClearReport() ; // Prepare for future calls
793 // This ought to be local to VerboseAssert, but it
794 // causes problems in Visual C++ (in the CRTL init phase)
795 static DumpBuffer dumpBuffer ;
797 #endif //SHOW_CALL_STACK
800 char AssertText1[1024];
801 char AssertText2[1024];
803 uint flags = MB_TASKMODAL|MB_SETFOREGROUND;
805 extern void gr_force_windowed();
807 void dump_text_to_clipboard(char *text)
809 int len = strlen(text)+1024;
811 HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len );
812 if ( !h_text ) return;
813 char *ptr = (char *)GlobalLock(h_text);
816 // copy then, if you find any \n's without \r's, then add in the \r.
819 if ( (*text == '\n') && (last_char != '\r') ) {
827 GlobalUnlock(h_text);
830 SetClipboardData(CF_TEXT, h_text);
835 void _cdecl WinAssert(char * text, char * filename, int linenum )
842 sprintf( AssertText1, "SDL_assert: %s\r\nFile: %s\r\nLine: %d", text, filename, linenum );
844 #ifdef SHOW_CALL_STACK
846 dumpBuffer.Printf( AssertText1 );
847 dumpBuffer.Printf( "\r\n" );
848 DumpCallsStack( dumpBuffer ) ;
849 dump_text_to_clipboard(dumpBuffer.buffer);
851 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
852 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n");
854 val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags );
856 val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags );
872 void __cdecl Error( char * filename, int line, char * format, ... )
879 va_start(args, format);
880 vsprintf(AssertText1,format,args);
882 sprintf(AssertText2,"Error: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
884 #ifdef SHOW_CALL_STACK
886 dumpBuffer.Printf( AssertText2 );
887 dumpBuffer.Printf( "\r\n" );
888 DumpCallsStack( dumpBuffer ) ;
889 dump_text_to_clipboard(dumpBuffer.buffer);
891 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
892 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
894 val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_OKCANCEL );
896 strcat(AssertText2,"\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
897 val = MessageBox(NULL, AssertText2, "Error!", flags|MB_OKCANCEL );
900 if (val == IDCANCEL ) {
911 void __cdecl Warning( char * filename, int line, char * format, ... )
921 va_start(args, format);
922 vsprintf(AssertText1,format,args);
924 sprintf(AssertText2,"Warning: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
927 #ifdef SHOW_CALL_STACK
929 dumpBuffer.Printf( AssertText2 );
930 dumpBuffer.Printf( "\r\n" );
931 DumpCallsStack( dumpBuffer ) ;
932 dump_text_to_clipboard(dumpBuffer.buffer);
934 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
935 dumpBuffer.Printf("\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
937 id = MessageBox(NULL, dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL|flags );
939 strcat(AssertText2,"\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
940 id = MessageBox(NULL, AssertText2, "Warning!", MB_YESNOCANCEL|flags );
942 if ( id == IDCANCEL )
944 else if ( id == IDYES ) {
956 //================= memory stuff
958 char *format_mem( DWORD num )
961 sprintf( tmp_mem, "%d bytes", num );
962 } else if ( num < 1024*1024 ) {
963 sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f );
965 sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f) );
975 MEMORY_BASIC_INFORMATION mbi ;
980 ms.dwLength = sizeof(MEMORYSTATUS);
981 GlobalMemoryStatus(&ms);
983 printf( "Percent of memory in use: %d%%\n", ms.dwMemoryLoad );
984 printf( "Bytes of physical memory: %s\n", format_mem(ms.dwTotalPhys) );
985 printf( "Free physical memory bytes: %s\n", format_mem(ms.dwAvailPhys) ); //
986 printf( "Bytes of paging file: %s\n", format_mem(ms.dwTotalPageFile) ); // bytes of paging file
987 printf( "Free bytes of paging file: %s\n", format_mem(ms.dwAvailPageFile) ); // free bytes of paging file
988 printf( "User bytes of address space: %s\n", format_mem(ms.dwTotalVirtual) ); //
989 printf( "Free user bytes: %s\n", format_mem(ms.dwAvailVirtual) ); // free user bytes
992 // Get the instance handle of the module where caller belongs to
993 retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ;
995 // The instance handle is equal to the allocation base in Win32
996 hInstance = (HINSTANCE)mbi.AllocationBase ;
998 if( ( retval == sizeof( mbi ) ) && hInstance ) {
999 printf( "Virtual Query succeeded...\n" );
1001 printf( "Virtual Query failed...\n" );
1013 #define nNoMansLandSize 4
1015 typedef struct _CrtMemBlockHeader
1017 struct _CrtMemBlockHeader * pBlockHeaderNext;
1018 struct _CrtMemBlockHeader * pBlockHeaderPrev;
1024 unsigned char gap[nNoMansLandSize];
1026 * unsigned char data[nDataSize];
1027 * unsigned char anotherGap[nNoMansLandSize];
1029 } _CrtMemBlockHeader;
1031 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1033 int __cdecl MyAllocHook(
1039 const char * szFileName,
1043 // char *operation[] = { "", "allocating", "re-allocating", "freeing" };
1044 // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };
1046 if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations
1049 _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
1050 _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );
1052 if ( nAllocType == 3 ) {
1053 _CrtMemBlockHeader *phd = pHdr(pvData);
1054 nSize = phd->nDataSize;
1057 // mprintf(( "Total RAM = %d\n", TotalRam ));
1059 /* mprintf(( "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n",
1060 szFileName, nLine, operation[nAllocType], nSize,
1061 blockType[nBlockUse], lRequest ));
1062 if ( pvData != NULL )
1063 mprintf(( " at %X", pvData ));
1068 return( TRUE ); // Allow the memory operation to proceed
1071 void windebug_memwatch_init()
1073 //_CrtSetAllocHook(MyAllocHook);
1082 int Watch_malloc = 0;
1083 DCF_BOOL(watch_malloc, Watch_malloc );
1087 #define HEAP_FLAG HEAP_NO_SERIALIZE
1088 // #define HEAP_FLAG HEAP_GENERATE_EXCEPTIONS
1090 // Returns 0 if not enough RAM.
1091 int vm_init(int min_heap_size)
1098 Heap = HeapCreate( HEAP_FLAG, min_heap_size, 0 );
1099 if ( Heap == NULL ) {
1106 char *clean_filename(char *name)
1108 char *p = name+strlen(name)-1;
1109 // Move p to point to first letter of EXE filename
1110 while( (*p!='\\') && (*p!='/') && (*p!=':') )
1118 void *vm_malloc( int size, char *filename, int line )
1120 void *vm_malloc( int size )
1127 return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ );
1131 void *ptr = HeapAlloc(Heap, HEAP_FLAG, size );
1133 if ( ptr == NULL ) {
1134 mprintf(( "HeapAlloc failed!!!!!!!!!!!!!!!!!!!\n" ));
1136 Error(LOCATION, "Out of memory. Try closing down other applications, increasing your\n"
1137 "virtual memory size, or installing more physical RAM.\n");
1141 int actual_size = HeapSize(Heap, HEAP_FLAG, ptr);
1142 if ( Watch_malloc ) {
1143 mprintf(( "Malloc %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line ));
1145 TotalRam += actual_size;
1151 char *vm_strdup( const char *ptr, char *filename, int line )
1153 char *vm_strdup( const char *ptr )
1157 int len = strlen(ptr);
1159 dst = (char *)vm_malloc( len+1, filename, line );
1161 dst = (char *)vm_malloc( len+1 );
1168 void vm_free( void *ptr, char *filename, int line )
1170 void vm_free( void *ptr )
1175 mprintf(("Why are you trying to free a NULL pointer? [%s(%d)]\n", clean_filename(filename), line));
1177 mprintf(("Why are you trying to free a NULL pointer?\n"));
1185 _CrtMemBlockHeader *phd = pHdr(ptr);
1186 int nSize = phd->nDataSize;
1191 _free_dbg(ptr,_NORMAL_BLOCK);
1197 int actual_size = HeapSize(Heap, HEAP_FLAG, ptr);
1198 if ( Watch_malloc ) {
1199 mprintf(( "Free %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line ));
1201 TotalRam -= actual_size;
1203 HeapFree( Heap, HEAP_FLAG, ptr );
1204 HeapCompact(Heap, HEAP_FLAG);