]> icculus.org git repositories - taylor/freespace2.git/blob - src/globalincs/windebug.cpp
Initial revision
[taylor/freespace2.git] / src / globalincs / windebug.cpp
1 /*
2  * $Logfile: /Freespace2/code/GlobalIncs/WinDebug.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Debug stuff
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 4     12/01/98 4:46p Dave
15  * Put in targa bitmap support (16 bit).
16  * 
17  * 3     11/30/98 5:31p Dave
18  * Fixed up Fred support for software mode.
19  * 
20  * 2     10/07/98 10:52a Dave
21  * Initial checkin.
22  * 
23  * 1     10/07/98 10:48a Dave
24  * 
25  * 32    5/25/98 1:39p John
26  * added code to give error and abort if malloc fails.
27  * 
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.
31  * 
32  * 30    4/30/98 12:02a Lawrance
33  * compile out Warnings() on NDEBUG builds
34  * 
35  * 29    4/22/98 5:15p Lawrance
36  * fix bug in strdup
37  * 
38  * 28    4/21/98 1:02p John
39  * fixed bug where the clipboard text dumped wasn't null terminated.
40  * 
41  * 27    4/20/98 3:22p John
42  * fixed bug that displayed wrong amount of allocated ram.
43  * 
44  * 26    4/17/98 3:27p Allender
45  * 
46  * 25    4/17/98 1:41p Allender
47  * made to compile with NDEBUG defined
48  * 
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.
52  * 
53  * 23    4/01/98 9:21p John
54  * Made NDEBUG, optimized build with no warnings or errors.
55  * 
56  * 22    3/31/98 8:17p John
57  * Added code to dump memory contents
58  * 
59  * 21    3/31/98 11:17a Allender
60  * fix bug with INTERPLAYQA Int3's and Warning dialogs
61  * 
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.
65  * 
66  * 19    3/14/98 3:35p John
67  * cleaned up call stack code.  Exited out on not properly aligned EBP's.
68  * 
69  * 18    3/14/98 3:25p John
70  * Added code to check the parentEBP pointer for validity before
71  * derefrencing.
72  * 
73  * 17    3/11/98 5:34p Lawrance
74  * Fix typo in error dialog box
75  * 
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
79  * 
80  * 15    3/05/98 3:04p John
81  * Made Errors, Assert, Warning info paste to clipboard.
82  * 
83  * 14    3/05/98 9:17a John
84  * Limited stack dump depth to 16
85  * 
86  * 13    3/05/98 9:14a John
87  * Added in a simple function name unmangler
88  * 
89  * 12    3/04/98 7:08p John
90  * Made Freespace generate COFF files.   Made Assert,Error, and Warning
91  * display the call stack.
92  * 
93  * 11    2/22/98 2:48p John
94  * More String Externalization Classification
95  *
96  * $NoKeywords: $
97  */
98
99 // Nothing in this module should be externalized!!!
100 //XSTR:OFF
101
102 //#define DUMPRAM       // This dumps all symbol sizes. See John for more info
103
104 #include <windows.h>
105 #include <windowsx.h>
106 #include <stdio.h>
107 #include <crtdbg.h>
108
109 #include "pstypes.h"
110
111 //Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors
112 #define SHOW_CALL_STACK
113
114 #ifdef SHOW_CALL_STACK
115
116 class DumpBuffer
117   {
118   public :
119     enum { BUFFER_SIZE = 32000 } ;
120     DumpBuffer() ;
121     void Clear() ;
122     void Printf( const char* format, ... ) ;
123     void SetWindowText( HWND hWnd ) const ;
124     char buffer[ BUFFER_SIZE ] ;
125   private :
126     char* current ;
127   } ;
128
129
130
131 DumpBuffer :: DumpBuffer()
132   {
133   Clear() ;
134   }
135
136
137 void DumpBuffer :: Clear()
138   {
139   current = buffer ;
140   }
141
142
143 void DumpBuffer :: Printf( const char* format, ... )
144   {
145   // protect against obvious buffer overflow
146   if( current - buffer < BUFFER_SIZE )
147     {
148     va_list argPtr ;
149     va_start( argPtr, format ) ;
150     int count = vsprintf( current, format, argPtr ) ;
151     va_end( argPtr ) ;
152     current += count ;
153     }
154   }
155
156
157 void DumpBuffer :: SetWindowText( HWND hWnd ) const
158   {
159   SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ;
160   }
161
162
163
164
165
166 class PE_Debug
167   {
168   public :
169     PE_Debug() ;
170     ~PE_Debug() ;
171     void ClearReport() ;
172     int DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ;
173     void Display() ;
174   private :
175     // Report data
176     enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ;
177     char latestModule[ MAX_MODULENAME_LEN ] ;
178     char latestFile[ VA_MAX_FILENAME_LEN ] ;
179     // File mapping data
180     HANDLE hFile ;
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 ;
189
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 ) ;
199   } ;
200
201
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) )
205
206
207 PE_Debug :: PE_Debug()
208   {
209   // Init file mapping cache
210   hFileMapping = 0 ;
211   hFile = INVALID_HANDLE_VALUE ;
212   fileBase = 0 ;
213   ClearDebugPtrs() ;
214   ClearReport() ;
215   }
216
217
218 PE_Debug :: ~PE_Debug()
219   {
220   ClearFileCache() ;
221   }
222
223
224 void PE_Debug :: ClearReport()
225   {
226   latestModule[ 0 ] = 0 ;
227   latestFile[ 0 ] = 0 ;
228   }
229
230
231 void PE_Debug :: ClearDebugPtrs()
232   {
233   NT_Header = NULL ;
234   COFFDebugInfo = NULL ;
235   COFFSymbolTable = NULL ;
236   COFFSymbolCount = 0 ;
237   stringTable = NULL ;
238   }
239
240
241 void PE_Debug :: ClearFileCache()
242   {
243   if( fileBase )
244     {
245     UnmapViewOfFile( fileBase ) ;
246     fileBase = 0 ;
247     }
248   if( hFileMapping != 0 )
249     {
250     CloseHandle( hFileMapping ) ;
251     hFileMapping = 0 ;
252     }
253   if( hFile != INVALID_HANDLE_VALUE )
254     {
255     CloseHandle( hFile ) ;
256     hFile = INVALID_HANDLE_VALUE ;
257     }
258   }
259
260
261 void PE_Debug :: DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress )
262   {
263   PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo,
264                                      COFFDebugInfo->LvaToFirstLinenumber ) ;
265   DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ;
266   const DWORD none = (DWORD)-1 ;
267   DWORD maxAddr = 0 ;
268   DWORD lineNum = none ;
269   for( DWORD i=0; i < lineCount; i++ )
270     {
271     if( line->Linenumber != 0 )  // A regular line number
272       {
273       // look for line with bigger address <= relativeAddress
274       if( line->Type.VirtualAddress <= relativeAddress &&
275           line->Type.VirtualAddress > maxAddr )
276         {
277         maxAddr = line->Type.VirtualAddress ;
278         lineNum = line->Linenumber ;
279         }
280       }
281     line++ ;
282     }
283   if( lineNum != none )
284     dumpBuffer.Printf( "  line %d\r\n", lineNum ) ;
285 //  else
286 //  dumpBuffer.Printf( "  line <unknown>\r\n" ) ;
287   }
288
289
290 const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym )
291   {
292   const int NAME_MAX_LEN = 64 ;
293   static char buf[ NAME_MAX_LEN ] ;
294   if( sym->N.Name.Short != 0 )
295     {
296     strncpy( buf, (const char*)sym->N.ShortName, 8 ) ;
297     buf[ 8 ] = 0 ;
298     }
299   else
300     {
301     strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ;
302     buf[ NAME_MAX_LEN - 1 ] = 0 ;
303     }
304   return( buf ) ;
305   }
306
307 void unmangle(char *dst, const char *src)
308 {
309         //strcpy( dst, src );
310         //return;
311
312         src++;
313         while( (*src) && (*src!=' ') && (*src!='@') )   {
314                 *dst++ = *src++;
315         }
316         *dst++ = 0;
317 }
318
319 #ifdef DUMPRAM
320
321 typedef struct MemSymbol {
322         int     section;
323         int     offset;
324         int     size;
325         char    name[132];
326 } MemSymbol;
327
328 int Num_symbols = 0;
329 int Max_symbols = 0;
330 MemSymbol *Symbols;
331
332 void InitSymbols()
333 {
334         Num_symbols = 0;
335         Max_symbols = 5000;
336         Symbols = (MemSymbol *)malloc(Max_symbols*sizeof(MemSymbol));
337         if ( !Symbols ) {
338                 Max_symbols = 0;
339         }
340 }
341
342 void Add_Symbol( int section, int offset, const char *name, char *module )
343 {
344         if ( Num_symbols >= Max_symbols ) {
345                 return;
346         }
347
348         MemSymbol * sym = &Symbols[Num_symbols++];
349         
350         sym->section = section;
351         sym->offset = offset;
352         sym->size = -1;
353         
354         strcpy( sym->name, name );      
355         strcat( sym->name, "(" );       
356         strcat( sym->name, module );    
357         strcat( sym->name, ")" );       
358
359 }
360
361 int Sym_compare( const void *arg1, const void *arg2 )
362 {
363         MemSymbol * sym1 = (MemSymbol *)arg1;
364         MemSymbol * sym2 = (MemSymbol *)arg2;
365
366         if ( sym1->section < sym2->section )    {
367                 return -1;
368         } else if ( sym1->section > sym2->section ) {
369                 return 1;
370         } else {
371                 if ( sym1->offset > sym2->offset )      {
372                         return 1;
373                 } else {
374                         return -1;
375                 }
376         }
377 }
378
379 int Sym_compare1( const void *arg1, const void *arg2 )
380 {
381         MemSymbol * sym1 = (MemSymbol *)arg1;
382         MemSymbol * sym2 = (MemSymbol *)arg2;
383
384         if ( sym1->size < sym2->size )  {
385                 return 1;
386         } else if ( sym1->size > sym2->size ) {
387                 return -1;
388         } else {
389                 return 0;
390         }
391 }
392
393 void DumpSymbols()
394 {
395         int i;
396
397         qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare );
398         
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;
404                 } else {
405                         sym1->size = -1;
406                 }
407         }
408
409         qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 );
410
411
412         FILE *fp = fopen( "dump", "wt" );
413
414         fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" );
415
416         int total_size = 0;
417         for (i=0;i<Num_symbols; i++ )   {
418                 MemSymbol * sym = &Symbols[i];
419                 if ( sym->size > 0 )
420                         total_size += sym->size;
421                 fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size );
422         }
423
424         fclose(fp);
425         
426         free( Symbols );
427         Symbols = NULL;
428         _asm int 3
429 }
430 #endif
431
432 void PE_Debug::DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress )
433 {
434         // Variables to keep track of function symbols
435         PIMAGE_SYMBOL currentSym = COFFSymbolTable ;
436         PIMAGE_SYMBOL fnSymbol = NULL ;
437         DWORD maxFnAddress = 0 ;
438
439         #ifdef DUMPRAM
440         InitSymbols();
441         #endif
442
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++ )     {
447
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;
452                 }
453
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 ) ;
457
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 ;
463                                 }
464                         }
465                 }
466
467
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 ) )     {
471
472                         if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) {
473                                 maxFnAddress = currentSym->Value ;
474                                 fnSymbol = currentSym ;
475                         }
476                 }
477
478         #ifdef DUMPRAM
479                 if ( !isFunction && (currentSym->SectionNumber>-1) )    {
480                         if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) {
481
482                                 char pretty_module[1024];
483
484                                 if ( fileSymbol )       {
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!=':') )
492                                                 p--;
493                                         p++;    
494                                         if ( strlen(p) < 1 ) {
495                                                 strcpy( pretty_module, "<unknown>" );
496                                         } else {
497                                                 memmove( pretty_module, p, strlen(p)+1 );
498                                         }
499                                 } else {
500                                         strcpy( pretty_module, "" );
501                                 }
502
503                                 Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module );
504                         }
505                 }
506         #endif
507
508                 // Advance counters, skip aux symbols
509                 i += currentSym->NumberOfAuxSymbols ;
510                 currentSym += currentSym->NumberOfAuxSymbols ;
511                 currentSym++ ;
512         }
513
514         #ifdef DUMPRAM
515         DumpSymbols();
516         #endif
517         
518         // dump symbolic info if found
519         if ( fileSymbol )       {
520                 const char* auxSym = (const char*)(fileSymbol + 1) ;
521
522                 if( strcmpi( latestFile, auxSym ) )     {
523                         strcpy( latestFile, auxSym ) ;
524                         //JAS      dumpBuffer.Printf( "  file: %s\r\n", auxSym ) ;    
525                 }
526         } else {
527                 latestFile[ 0 ] = 0 ;
528                 //JAS    dumpBuffer.Printf( "  file: unknown\r\n" ) ;    
529         }
530         
531         if ( fnSymbol ) {
532                 char tmp_name[1024];
533                 unmangle(tmp_name, GetSymbolName( fnSymbol ) );
534                 dumpBuffer.Printf( "    %s()", tmp_name ) ;
535         } else {
536                 dumpBuffer.Printf( "    <unknown>" ) ;
537         }
538 }
539
540
541 PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name )
542   {
543   PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ;
544   for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ )
545     {
546     if( strnicmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 )
547       return( section ) ;
548     else
549       section++ ;
550     }
551   return 0;
552   }
553
554
555 PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader()
556   {
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 )
560     return( 0 ) ;
561   DWORD debugDirRVA = NT_Header->OptionalHeader.
562                       DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].
563                       VirtualAddress;
564   if( debugDirRVA == 0 )
565     return( 0 ) ;
566
567   // The following values must be calculated differently for MS/Borland files
568   PIMAGE_DEBUG_DIRECTORY debugDir ;
569   DWORD size ;
570
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 )
574     {
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 ) ;
579     }
580   else
581   // Microsoft debug directory is in the .rdata section
582     {
583     debugHeader = SectionHeaderFromName( ".rdata" ) ;
584     if( debugHeader == 0 )
585       return( 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 ) ;
591     }
592
593   // look for COFF debug info
594   DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ;
595   for( DWORD i = 0; i < debugFormats; i++ )
596     {
597     if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF )
598       return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ;
599     else
600       debugDir++ ;
601     }
602   return( NULL ) ;
603   }
604
605
606 void PE_Debug :: FindDebugInfo()
607   {
608   ClearDebugPtrs() ;
609   // Put everything into a try/catch in case the file has wrong fields
610   try
611     {
612     // Verify that fileBase is a valid pointer to a DOS header
613     if( fileBase->e_magic == IMAGE_DOS_SIGNATURE )
614       {
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 )
619         {
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 )
624           COFFSymbolTable = 
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) ; 
629         }
630       }
631     }
632   catch( ... )
633     {
634     // Header wrong, do nothing
635     }
636   }
637
638
639 void PE_Debug :: MapFileInMemory( const char* module )
640   {
641   ClearFileCache() ;
642   hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL,
643                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
644   if( hFile != INVALID_HANDLE_VALUE )
645     {
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 ) ;
649     }
650   // NB: open files/mapping are closed later in ClearFileCache
651   }
652
653
654 int PE_Debug::DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance )
655 {
656         // Avoid to open, map and looking for debug header/symbol table
657         // by caching the latest and comparing the actual module with
658         // the latest one.
659         static char module[ MAX_MODULENAME_LEN ] ;
660         GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ;
661
662         // New module
663         if( strcmpi( latestModule, module ) )   {
664                 strcpy( latestModule, module );
665                 //JAS    dumpBuffer.Printf( "Module: %s\r\n", module );
666                 MapFileInMemory( module );
667                 FindDebugInfo();
668         }
669
670         char pretty_module[1024];
671
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!=':') )
676                 p--;
677         p++;    
678         if ( strlen(p) < 1 ) {
679                 strcpy( pretty_module, "<unknown>" );
680         } else {
681                 memmove( pretty_module, p, strlen(p)+1 );
682         }
683
684         if ( fileBase ) {
685                 // Put everything into a try/catch in case the file has wrong fields
686                 try     {
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 ) ;
691                                 if( COFFDebugInfo )
692                                         DumpLineNumber( dumpBuffer, relativeAddress ) ;
693                                 return 1;
694                         } else {
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 ) ;
698                                 return 0;
699                         }
700                 } catch( ... )  {
701                         // Header wrong, do nothing
702                         return 0;
703       }
704         } else  {
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 ) ;
708                 return 0;
709         }
710
711         Int3();
712
713 }
714
715
716 void DumpCallsStack( DumpBuffer& dumpBuffer )
717 {
718         const char* separator = "------------------------------------------------------------------\r\n" ;
719         static PE_Debug PE_debug ;
720
721         dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ;
722         dumpBuffer.Printf( separator ) ;
723
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 ;
731         HINSTANCE hInstance;
732
733         int depth = 0;
734
735         __asm MOV parentEBP, EBP
736
737         do      {
738                 depth++;
739                 if ( depth > 16 ) 
740                         break;
741
742                 if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) )        {
743                         break;
744                 }
745                 parentEBP = *(DWORD*)parentEBP ;
746
747                 BYTE **NextCaller = ((BYTE**)parentEBP + 1);
748
749                 if (IsBadReadPtr(NextCaller, sizeof(BYTE *)))   {
750                         break;
751                 }
752
753                 BYTE* caller = *NextCaller;             // Error sometimes!!!
754
755                 // Skip the first EBP as it points to AssertionFailed, which is
756                 // uninteresting for the user
757
758                 if ( depth > 1 )        {
759
760                         // Get the instance handle of the module where caller belongs to
761                         retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ;
762
763                         // The instance handle is equal to the allocation base in Win32
764                         hInstance = (HINSTANCE)mbi.AllocationBase ;
765
766                         if( ( retval == sizeof( mbi ) ) && hInstance )  {
767                                 if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) {
768                                         //break;
769                                 }
770                         } else {
771                                 break ; // End of the call chain
772                         }
773                 }
774         }  while( TRUE ) ;
775
776
777         dumpBuffer.Printf( separator ) ;
778         PE_debug.ClearReport() ;  // Prepare for future calls
779 }
780
781
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 ;
785
786 #endif  //SHOW_CALL_STACK
787
788
789 char AssertText1[1024];
790 char AssertText2[1024];
791
792 uint flags = MB_TASKMODAL|MB_SETFOREGROUND;
793
794 extern void gr_force_windowed();
795
796 void dump_text_to_clipboard(char *text)
797 {
798         int len = strlen(text)+1024;
799
800         HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len );
801         if ( !h_text ) return;
802         char *ptr = (char *)GlobalLock(h_text);
803         if ( !ptr ) return;
804
805         // copy then, if you find any \n's without \r's, then add in the \r.
806         char last_char = 0;
807         while( *text )  {
808                 if ( (*text == '\n') && (last_char != '\r') )   {
809                         *ptr++ = '\r';
810                 }
811                 last_char = *text;
812                 *ptr++ = last_char;
813                 text++;
814         }
815         *ptr++ = 0;
816         GlobalUnlock(h_text);
817         OpenClipboard(NULL);
818         EmptyClipboard();
819         SetClipboardData(CF_TEXT, h_text);
820         CloseClipboard();
821 }
822
823
824 void _cdecl WinAssert(char * text, char * filename, int linenum )
825 {
826         int val;
827
828         gr_force_windowed();
829
830
831         sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d", text, filename, linenum );
832
833         #ifdef SHOW_CALL_STACK
834                 dumpBuffer.Clear();
835                 dumpBuffer.Printf( AssertText1 );
836                 dumpBuffer.Printf( "\r\n" );
837                 DumpCallsStack( dumpBuffer ) ;  
838                 dump_text_to_clipboard(dumpBuffer.buffer);
839
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");
842
843                 val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags );
844         #else
845                 val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags );
846         #endif
847
848         if (val == IDCANCEL)
849                 exit(1);
850
851 #ifndef INTERPLAYQA
852         Int3();
853 #else
854         AsmInt3();
855 #endif
856
857
858
859
860 void _cdecl Error( char * filename, int line, char * format, ... )
861 {
862         int val;
863         va_list args;
864
865         gr_force_windowed();
866
867         va_start(args, format);
868         vsprintf(AssertText1,format,args);
869         va_end(args);
870         sprintf(AssertText2,"Error: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
871
872         #ifdef SHOW_CALL_STACK
873                 dumpBuffer.Clear();
874                 dumpBuffer.Printf( AssertText2 );
875                 dumpBuffer.Printf( "\r\n" );
876                 DumpCallsStack( dumpBuffer ) ;  
877                 dump_text_to_clipboard(dumpBuffer.buffer);
878
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");
881
882                 val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_OKCANCEL );
883         #else
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 );
886         #endif
887
888         if (val == IDCANCEL ) {
889                 exit(1);
890         } else {
891 #ifndef INTERPLAYQA
892                 Int3();
893 #else
894                 AsmInt3();
895 #endif
896         }
897 }
898
899 void _cdecl Warning( char * filename, int line, char * format, ... )
900 {
901 #ifndef NDEBUG
902
903         int id;
904         
905         va_list args;
906
907         gr_force_windowed();
908
909         va_start(args, format);
910         vsprintf(AssertText1,format,args);
911         va_end(args);
912         sprintf(AssertText2,"Warning: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line );
913
914
915         #ifdef SHOW_CALL_STACK
916                 dumpBuffer.Clear();
917                 dumpBuffer.Printf( AssertText2 );
918                 dumpBuffer.Printf( "\r\n" );
919                 DumpCallsStack( dumpBuffer ) ;  
920                 dump_text_to_clipboard(dumpBuffer.buffer);
921
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");
924
925                 id = MessageBox(NULL, dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL|flags );
926         #else
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 );
929         #endif
930         if ( id == IDCANCEL )
931                 exit(1);
932         else if ( id == IDYES ) {
933 #ifndef INTERPLAYQA
934                 Int3();
935 #else
936                 AsmInt3();
937 #endif
938         }
939 #endif // NDEBUG
940 }
941
942
943
944 //================= memory stuff
945 /*
946 char *format_mem( DWORD num )
947 {
948         if ( num < 1024 )       {
949                 sprintf( tmp_mem, "%d bytes", num );
950         } else if ( num < 1024*1024 )   {
951                 sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f );
952         } else  {
953                 sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f)  );
954         }
955         return tmp_mem; 
956 }
957 */
958
959 /*
960 void getmem()
961 {
962         DWORD retval;
963         MEMORY_BASIC_INFORMATION mbi ;
964         HINSTANCE hInstance;
965
966         MEMORYSTATUS ms;
967
968         ms.dwLength = sizeof(MEMORYSTATUS);
969         GlobalMemoryStatus(&ms);
970         
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 
978
979
980         // Get the instance handle of the module where caller belongs to
981         retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ;
982
983         // The instance handle is equal to the allocation base in Win32
984         hInstance = (HINSTANCE)mbi.AllocationBase ;
985
986         if( ( retval == sizeof( mbi ) ) && hInstance )  {
987                 printf( "Virtual Query succeeded...\n" );
988         } else {
989                 printf( "Virtual Query failed...\n" );
990         }
991
992 }
993 */
994
995 #ifndef NDEBUG
996
997 #include <crtdbg.h>
998
999 int TotalRam = 0;
1000
1001 #define nNoMansLandSize 4
1002
1003 typedef struct _CrtMemBlockHeader
1004 {
1005         struct _CrtMemBlockHeader * pBlockHeaderNext;
1006         struct _CrtMemBlockHeader * pBlockHeaderPrev;
1007         char *                      szFileName;
1008         int                         nLine;
1009         size_t                      nDataSize;
1010         int                         nBlockUse;
1011         long                        lRequest;
1012         unsigned char               gap[nNoMansLandSize];
1013         /* followed by:
1014          *  unsigned char           data[nDataSize];
1015          *  unsigned char           anotherGap[nNoMansLandSize];
1016          */
1017 } _CrtMemBlockHeader;
1018
1019 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1020
1021 int __cdecl MyAllocHook(
1022    int      nAllocType,
1023    void   * pvData,
1024    size_t   nSize,
1025    int      nBlockUse,
1026    long     lRequest,
1027    const char * szFileName,
1028    int      nLine
1029    )
1030 {
1031    // char *operation[] = { "", "allocating", "re-allocating", "freeing" };
1032    // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };
1033
1034    if ( nBlockUse == _CRT_BLOCK )   // Ignore internal C runtime library allocations
1035       return( TRUE );
1036
1037    _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
1038    _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );
1039
1040         if ( nAllocType == 3 )  {
1041                 _CrtMemBlockHeader *phd = pHdr(pvData);
1042                 nSize = phd->nDataSize;
1043         } 
1044
1045 //      mprintf(( "Total RAM = %d\n", TotalRam ));
1046
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 ));
1052
1053         mprintf(("\n" ));
1054 */
1055
1056    return( TRUE );         // Allow the memory operation to proceed
1057 }
1058  
1059 void windebug_memwatch_init()
1060 {
1061         //_CrtSetAllocHook(MyAllocHook);
1062         TotalRam = 0;
1063 }
1064
1065 #endif
1066
1067 #define NEW_MALLOC
1068
1069
1070 int Watch_malloc = 0;
1071 DCF_BOOL(watch_malloc, Watch_malloc );
1072
1073 HANDLE Heap = 0;
1074
1075 #define HEAP_FLAG HEAP_NO_SERIALIZE
1076 // #define HEAP_FLAG    HEAP_GENERATE_EXCEPTIONS
1077
1078 // Returns 0 if not enough RAM.
1079 int vm_init(int min_heap_size)
1080 {
1081         #ifndef NDEBUG
1082         TotalRam = 0;
1083         #endif
1084
1085         #ifdef NEW_MALLOC
1086                 Heap = HeapCreate( HEAP_FLAG, min_heap_size, 0 );
1087                 if ( Heap == NULL )     {
1088                         return 0;
1089                 }
1090         #endif
1091         return 1;
1092 }
1093
1094 char *clean_filename(char *name)
1095 {
1096         char *p = name+strlen(name)-1;
1097         // Move p to point to first letter of EXE filename
1098         while( (*p!='\\') && (*p!='/') && (*p!=':') )
1099                 p--;
1100         p++;    
1101
1102         return p;       
1103 }
1104
1105 #ifndef NDEBUG
1106 void *vm_malloc( int size, char *filename, int line )
1107 #else
1108 void *vm_malloc( int size )
1109 #endif
1110 {
1111         #ifndef NDEBUG
1112         if ( !Heap )    {
1113                 TotalRam += size;
1114
1115                 return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ );
1116         }
1117         #endif
1118  
1119         void *ptr = HeapAlloc(Heap, HEAP_FLAG, size );
1120
1121         if ( ptr == NULL )      {
1122                 mprintf(( "HeapAlloc failed!!!!!!!!!!!!!!!!!!!\n" ));
1123
1124                         Error(LOCATION, "Out of memory.  Try closing down other applications, increasing your\n"
1125                                 "virtual memory size, or installing more physical RAM.\n");
1126
1127         }
1128         #ifndef NDEBUG
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 ));
1132                 }
1133                 TotalRam += actual_size;
1134         #endif
1135         return ptr;
1136 }
1137
1138 #ifndef NDEBUG
1139 char *vm_strdup( const char *ptr, char *filename, int line )
1140 #else
1141 char *vm_strdup( const char *ptr )
1142 #endif
1143 {
1144         char *dst;
1145         int len = strlen(ptr);
1146         #ifndef NDEBUG
1147                 dst = (char *)vm_malloc( len+1, filename, line );
1148         #else
1149                 dst = (char *)vm_malloc( len+1 );
1150         #endif
1151         strcpy( dst, ptr );
1152         return dst;
1153 }
1154
1155 #ifndef NDEBUG
1156 void vm_free( void *ptr, char *filename, int line )
1157 #else
1158 void vm_free( void *ptr )
1159 #endif
1160 {
1161         if ( !ptr ) {
1162                 #ifndef NDEBUG
1163                         mprintf(("Why are you trying to free a NULL pointer?  [%s(%d)]\n", clean_filename(filename), line));
1164                 #else
1165                         mprintf(("Why are you trying to free a NULL pointer?\n"));
1166                 #endif
1167                 return;
1168         }
1169
1170         #ifndef NDEBUG
1171         if ( !Heap )    {
1172                 #ifndef NDEBUG
1173                 _CrtMemBlockHeader *phd = pHdr(ptr);
1174                 int nSize = phd->nDataSize;
1175
1176                 TotalRam -= nSize;
1177                 #endif
1178
1179                 _free_dbg(ptr,_NORMAL_BLOCK);
1180                 return;
1181         }
1182         #endif
1183
1184         #ifndef NDEBUG
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 ));
1188                 }
1189                 TotalRam -= actual_size;
1190         #endif
1191         HeapFree( Heap, HEAP_FLAG, ptr );
1192         HeapCompact(Heap, HEAP_FLAG);
1193 }
1194
1195 void vm_free_all()
1196 {
1197 }
1198
1199
1200
1201