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