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 the
6 * source.
7 *
8 */
9
10 // Nothing in this module should be externalized!!!
11 //XSTR:OFF
12
13 //#define DUMPRAM // This dumps all symbol sizes. See John for more info
14
15 /* Windows Headers */
16 #include <windows.h>
17 #include <windowsx.h>
18 #include <stdio.h>
19
20 #ifdef _MSC_VER
21 # include <crtdbg.h>
22 /* Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors */
23 # define SHOW_CALL_STACK
24 #endif // _MSC_VER
25
26 /* STL Headers */
27 #include <string>
28
29 /* SCP Headers */
30 #include "osapi/osapi.h"
31 #include "globalincs/pstypes.h"
32 #include "globalincs/systemvars.h"
33 #include "cmdline/cmdline.h"
34 #include "parse/lua.h"
35 #include "parse/parselo.h"
36
37 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
38 # include "globalincs/mspdb_callstack.h"
39 #endif
40
41 extern void gr_activate(int active);
42
43 bool Messagebox_active = false;
44
45 int Global_warning_count = 0;
46 int Global_error_count = 0;
47
48 const int Messagebox_lines = 30;
49
50 #ifndef _ASSERT
51 #ifndef _DEBUG
52 #define _ASSERT(expr) ((void)0)
53 #else
54 #define _ASSERT(expr) (assert(expr))
55 // #error _ASSERT is not defined yet for debug mode with non-MSVC compilers
56 #endif
57 #endif
58
clean_filename(const char * name)59 const char *clean_filename( const char *name)
60 {
61 const char *p = name+strlen(name)-1;
62 // Move p to point to first letter of EXE filename
63 while( (*p!='\\') && (*p!='/') && (*p!=':') && (p>= name) )
64 p--;
65 p++;
66
67 return p;
68 }
69
70 #if defined( SHOW_CALL_STACK )
71 static bool Dump_to_log = true;
72
73 class DumpBuffer
74 {
75 public :
76 enum { BUFFER_SIZE = 32000 } ;
77 DumpBuffer() ;
78 void Clear() ;
79 void Printf( const char* format, ... ) ;
80 void SetWindowText( HWND hWnd ) const ;
81 char buffer[ BUFFER_SIZE ] ;
82
83 void Append(const char* text);
84 void Truncate(size_t size);
85 void TruncateLines(int num_allowed_lines);
86 size_t Size() const;
87 private :
88 char* current ;
89 } ;
90
91
92
DumpBuffer()93 DumpBuffer :: DumpBuffer()
94 {
95 Clear() ;
96 }
97
98
Clear()99 void DumpBuffer :: Clear()
100 {
101 current = buffer ;
102 }
103
104
Append(const char * text)105 void DumpBuffer :: Append(const char* text)
106 {
107 strcat_s(buffer, text);
108 }
109
110
Truncate(size_t size)111 void DumpBuffer :: Truncate(size_t size)
112 {
113 if (size >= strlen(buffer))
114 return;
115
116 buffer[size] = 0;
117 }
118
119
120 // adapted from parselo
TruncateLines(int num_allowed_lines)121 void DumpBuffer :: TruncateLines(int num_allowed_lines)
122 {
123 Assert(num_allowed_lines > 0);
124 char *find_from = buffer;
125 char *lastch = find_from + strlen(buffer) - 6;
126
127 while (find_from < lastch)
128 {
129 if (num_allowed_lines <= 0)
130 {
131 *find_from = 0;
132 strcat_s(buffer, "[...]");
133 break;
134 }
135
136 char *p = strchr(find_from, '\n');
137 if (p == NULL)
138 break;
139
140 num_allowed_lines--;
141 find_from = p + 1;
142 }
143 }
144
145
Size() const146 size_t DumpBuffer :: Size() const
147 {
148 return strlen(buffer);
149 }
150
151
Printf(const char * format,...)152 void DumpBuffer :: Printf( const char* format, ... )
153 {
154 // protect against obvious buffer overflow
155 if( current - buffer < BUFFER_SIZE )
156 {
157 va_list argPtr ;
158 va_start( argPtr, format ) ;
159 int count = vsprintf( current, format, argPtr ) ;
160 va_end( argPtr ) ;
161 current += count ;
162 }
163 }
164
165
SetWindowText(HWND hWnd) const166 void DumpBuffer :: SetWindowText( HWND hWnd ) const
167 {
168 SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ;
169 }
170
171 /* Needed by LUA printf */
172 // This ought to be local to VerboseAssert, but it
173 // causes problems in Visual C++ (in the CRTL init phase)
174 static DumpBuffer dumpBuffer;
175 const char* Separator = "------------------------------------------------------------------\n" ;
176
177 #endif
178
179 /* MSVC2005+ callstack support
180 */
181 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
182
183 class SCP_DebugCallStack : public SCP_IDumpHandler
184 {
185 public:
ResolveSymbols()186 virtual bool ResolveSymbols( )
187 {
188 return true;
189 }
190
OnBegin()191 virtual void OnBegin( )
192 {
193 }
194
OnEnd()195 virtual void OnEnd( )
196 {
197 }
198
OnEntry(void * address,const char * module,const char * symbol)199 virtual void OnEntry( void* address, const char* module, const char* symbol )
200 {
201 UNREFERENCED_PARAMETER( address );
202
203 StackEntry entry;
204 entry.module = clean_filename( module );
205 entry.symbol = symbol;
206 m_stackFrames.push_back( entry );
207 }
208
OnError(const char * error)209 virtual void OnError( const char* error )
210 {
211 /* No error handling here! */
212 UNREFERENCED_PARAMETER( error );
213 }
214
DumpToString()215 SCP_string DumpToString( )
216 {
217 SCP_string callstack;
218 for ( size_t i = 0; i < m_stackFrames.size( ); i++ )
219 {
220 callstack += m_stackFrames[ i ].module + "! " + m_stackFrames[ i ].symbol + "\n";
221 }
222
223 return callstack; /* Inefficient, but we don't need efficient here */
224 }
225 private:
226 struct StackEntry
227 {
228 SCP_string module;
229 SCP_string symbol;
230 };
231
232 SCP_vector< StackEntry > m_stackFrames;
233 };
234
235 #elif defined( SHOW_CALL_STACK )
236
237 class PE_Debug
238 {
239 public :
240 PE_Debug() ;
241 ~PE_Debug() ;
242 void ClearReport() ;
243 int DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ;
244 void Display() ;
245 private :
246 // Report data
247 enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ;
248 char latestModule[ MAX_MODULENAME_LEN ] ;
249 char latestFile[ VA_MAX_FILENAME_LEN ] ;
250 // File mapping data
251 HANDLE hFile ;
252 HANDLE hFileMapping ;
253 PIMAGE_DOS_HEADER fileBase ;
254 // Pointers to debug information
255 PIMAGE_NT_HEADERS NT_Header ;
256 PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo ;
257 PIMAGE_SYMBOL COFFSymbolTable ;
258 int COFFSymbolCount ;
259 const char* stringTable ;
260
261 void ClearFileCache() ;
262 void ClearDebugPtrs() ;
263 void MapFileInMemory( const char* module ) ;
264 void FindDebugInfo() ;
265 void DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
266 void DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ;
267 PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader() ;
268 PIMAGE_SECTION_HEADER SectionHeaderFromName( const char* name ) ;
269 const char* GetSymbolName( PIMAGE_SYMBOL sym ) ;
270 } ;
271
272
273 // Add an offset to a pointer and cast to a given type; may be
274 // implemented as a template function but Visual C++ has some problems.
275 #define BasedPtr( type, ptr, ofs ) (type)( (DWORD)(ptr) + (DWORD)(ofs) )
276
277
PE_Debug()278 PE_Debug :: PE_Debug()
279 {
280 // Init file mapping cache
281 hFileMapping = 0 ;
282 hFile = INVALID_HANDLE_VALUE ;
283 fileBase = 0 ;
284 ClearDebugPtrs() ;
285 ClearReport() ;
286 }
287
288
~PE_Debug()289 PE_Debug :: ~PE_Debug()
290 {
291 ClearFileCache() ;
292 }
293
294
ClearReport()295 void PE_Debug :: ClearReport()
296 {
297 latestModule[ 0 ] = 0 ;
298 latestFile[ 0 ] = 0 ;
299 }
300
301
ClearDebugPtrs()302 void PE_Debug :: ClearDebugPtrs()
303 {
304 NT_Header = NULL ;
305 COFFDebugInfo = NULL ;
306 COFFSymbolTable = NULL ;
307 COFFSymbolCount = 0 ;
308 stringTable = NULL ;
309 }
310
311
ClearFileCache()312 void PE_Debug :: ClearFileCache()
313 {
314 if( fileBase )
315 {
316 UnmapViewOfFile( fileBase ) ;
317 fileBase = 0 ;
318 }
319 if( hFileMapping != 0 )
320 {
321 CloseHandle( hFileMapping ) ;
322 hFileMapping = 0 ;
323 }
324 if( hFile != INVALID_HANDLE_VALUE )
325 {
326 CloseHandle( hFile ) ;
327 hFile = INVALID_HANDLE_VALUE ;
328 }
329 }
330
331
DumpLineNumber(DumpBuffer & dumpBuffer,DWORD relativeAddress)332 void PE_Debug :: DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress )
333 {
334 PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo,
335 COFFDebugInfo->LvaToFirstLinenumber ) ;
336 DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ;
337 const DWORD none = (DWORD)-1 ;
338 DWORD maxAddr = 0 ;
339 DWORD lineNum = none ;
340 for( DWORD i=0; i < lineCount; i++ )
341 {
342 if( line->Linenumber != 0 ) // A regular line number
343 {
344 // look for line with bigger address <= relativeAddress
345 if( line->Type.VirtualAddress <= relativeAddress &&
346 line->Type.VirtualAddress > maxAddr )
347 {
348 maxAddr = line->Type.VirtualAddress ;
349 lineNum = line->Linenumber ;
350 }
351 }
352 line++ ;
353 }
354 if( lineNum != none ) {
355 dumpBuffer.Printf( " line %d\r\n", lineNum ) ;
356 if (Dump_to_log) {
357 mprintf(( " line %d\r\n", lineNum )) ;
358 }
359 }
360 // else
361 // dumpBuffer.Printf( " line <unknown>\r\n" ) ;
362 }
363
364
GetSymbolName(PIMAGE_SYMBOL sym)365 const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym )
366 {
367 const int NAME_MAX_LEN = 64 ;
368 static char buf[ NAME_MAX_LEN ] ;
369 if( sym->N.Name.Short != 0 )
370 {
371 strncpy( buf, (const char*)sym->N.ShortName, 8 ) ;
372 buf[ 8 ] = 0 ;
373 }
374 else
375 {
376 strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ;
377 buf[ NAME_MAX_LEN - 1 ] = 0 ;
378 }
379 return( buf ) ;
380 }
381
unmangle(char * dst,const char * src)382 void unmangle(char *dst, const char *src)
383 {
384 //strcpy_s( dst, src );
385 //return;
386
387 src++;
388 while( (*src) && (*src!=' ') && (*src!='@') ) {
389 *dst++ = *src++;
390 }
391 *dst++ = 0;
392 }
393
394 #ifdef DUMPRAM
395
396 typedef struct MemSymbol {
397 int section;
398 int offset;
399 int size;
400 char name[132];
401 } MemSymbol;
402
403 int Num_symbols = 0;
404 int Max_symbols = 0;
405 MemSymbol *Symbols;
406
InitSymbols()407 void InitSymbols()
408 {
409 Num_symbols = 0;
410 Max_symbols = 5000;
411 Symbols = (MemSymbol *)vm_malloc(Max_symbols*sizeof(MemSymbol));
412 if ( !Symbols ) {
413 Max_symbols = 0;
414 }
415 }
416
Add_Symbol(int section,int offset,const char * name,char * module)417 void Add_Symbol( int section, int offset, const char *name, char *module )
418 {
419 if ( Num_symbols >= Max_symbols ) {
420 return;
421 }
422
423 MemSymbol * sym = &Symbols[Num_symbols++];
424
425 sym->section = section;
426 sym->offset = offset;
427 sym->size = -1;
428
429 strcpy_s( sym->name, name );
430 strcat_s( sym->name, "(" );
431 strcat_s( sym->name, module );
432 strcat_s( sym->name, ")" );
433
434 }
435
Sym_compare(const void * arg1,const void * arg2)436 int Sym_compare( const void *arg1, const void *arg2 )
437 {
438 MemSymbol * sym1 = (MemSymbol *)arg1;
439 MemSymbol * sym2 = (MemSymbol *)arg2;
440
441 if ( sym1->section < sym2->section ) {
442 return -1;
443 } else if ( sym1->section > sym2->section ) {
444 return 1;
445 } else {
446 if ( sym1->offset > sym2->offset ) {
447 return 1;
448 } else {
449 return -1;
450 }
451 }
452 }
453
Sym_compare1(const void * arg1,const void * arg2)454 int Sym_compare1( const void *arg1, const void *arg2 )
455 {
456 MemSymbol * sym1 = (MemSymbol *)arg1;
457 MemSymbol * sym2 = (MemSymbol *)arg2;
458
459 if ( sym1->size < sym2->size ) {
460 return 1;
461 } else if ( sym1->size > sym2->size ) {
462 return -1;
463 } else {
464 return 0;
465 }
466 }
467
DumpSymbols()468 void DumpSymbols()
469 {
470 int i;
471
472 insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare );
473
474 for (i=0;i<Num_symbols; i++ ) {
475 MemSymbol * sym1 = &Symbols[i];
476 MemSymbol * sym2 = &Symbols[i+1];
477 if ( (i<Num_symbols-1) && (sym1->section == sym2->section) ) {
478 sym1->size = sym2->offset-sym1->offset;
479 } else {
480 sym1->size = -1;
481 }
482 }
483
484 insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 );
485
486
487 FILE *fp = fopen( "dump", "wt" );
488
489 fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" );
490
491 int total_size = 0;
492 for (i=0;i<Num_symbols; i++ ) {
493 MemSymbol * sym = &Symbols[i];
494 if ( sym->size > 0 )
495 total_size += sym->size;
496 fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size );
497 }
498
499 fclose(fp);
500
501 vm_free( Symbols );
502 Symbols = NULL;
503 _asm int 3
504 }
505 #endif
506
DumpSymbolInfo(DumpBuffer & dumpBuffer,DWORD relativeAddress)507 void PE_Debug::DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress )
508 {
509 // Variables to keep track of function symbols
510 PIMAGE_SYMBOL currentSym = COFFSymbolTable ;
511 PIMAGE_SYMBOL fnSymbol = NULL ;
512 DWORD maxFnAddress = 0 ;
513
514 #ifdef DUMPRAM
515 InitSymbols();
516 #endif
517
518 // Variables to keep track of file symbols
519 PIMAGE_SYMBOL fileSymbol = NULL ;
520 PIMAGE_SYMBOL latestFileSymbol = NULL ;
521 for ( int i = 0; i < COFFSymbolCount; i++ ) {
522
523 // Look for .text section where relativeAddress belongs to.
524 // Keep track of the filename the .text section belongs to.
525 if ( currentSym->StorageClass == IMAGE_SYM_CLASS_FILE ) {
526 latestFileSymbol = currentSym;
527 }
528
529 // Borland uses "CODE" instead of the standard ".text" entry
530 // Microsoft uses sections that only _begin_ with .text
531 const char* symName = GetSymbolName( currentSym ) ;
532
533 if ( strnicmp( symName, ".text", 5 ) == 0 || strcmpi( symName, "CODE" ) == 0 ) {
534 if ( currentSym->Value <= relativeAddress ) {
535 PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1) ;
536 if ( currentSym->Value + auxSym->Section.Length >= relativeAddress ) {
537 fileSymbol = latestFileSymbol ;
538 }
539 }
540 }
541
542
543 // Look for the function with biggest address <= relativeAddress
544 BOOL isFunction = ISFCN( currentSym->Type ); // Type == 0x20, See WINNT.H
545 if ( isFunction && ( currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC ) ) {
546
547 if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) {
548 maxFnAddress = currentSym->Value ;
549 fnSymbol = currentSym ;
550 }
551 }
552
553 #ifdef DUMPRAM
554 if ( !isFunction && (currentSym->SectionNumber >= 0) ) {
555 if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) {
556
557 char pretty_module[1024];
558
559 if ( fileSymbol ) {
560 const char* auxSym = (const char*)(latestFileSymbol + 1) ;
561 char tmpFile[ VA_MAX_FILENAME_LEN ] ;
562 strcpy_s( tmpFile, auxSym ) ;
563 strcpy_s( pretty_module, tmpFile );
564 char *p = pretty_module+strlen(pretty_module)-1;
565 // Move p to point to first letter of EXE filename
566 while( (*p!='\\') && (*p!='/') && (*p!=':') )
567 p--;
568 p++;
569 if ( strlen(p) < 1 ) {
570 strcpy_s( pretty_module, "<unknown>" );
571 } else {
572 memmove( pretty_module, p, strlen(p)+1 );
573 }
574 } else {
575 strcpy_s( pretty_module, "" );
576 }
577
578 Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module );
579 }
580 }
581 #endif
582
583 // Advance counters, skip aux symbols
584 i += currentSym->NumberOfAuxSymbols ;
585 currentSym += currentSym->NumberOfAuxSymbols ;
586 currentSym++ ;
587 }
588
589 #ifdef DUMPRAM
590 DumpSymbols();
591 #endif
592
593 // dump symbolic info if found
594 if ( fileSymbol ) {
595 const char* auxSym = (const char*)(fileSymbol + 1) ;
596
597 if( strcmpi( latestFile, auxSym ) ) {
598 strcpy_s( latestFile, auxSym ) ;
599 //JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ;
600 }
601 } else {
602 latestFile[ 0 ] = 0 ;
603 //JAS dumpBuffer.Printf( " file: unknown\r\n" ) ;
604 }
605
606 if ( fnSymbol ) {
607 char tmp_name[1024];
608 unmangle(tmp_name, GetSymbolName( fnSymbol ) );
609 dumpBuffer.Printf( " %s()", tmp_name ) ;
610 if (Dump_to_log) {
611 mprintf((" %s()", tmp_name )) ;
612 }
613 } else {
614 dumpBuffer.Printf( " <unknown>" ) ;
615 if (Dump_to_log) {
616 mprintf((" <unknown>" )) ;
617 }
618 }
619 }
620
621
SectionHeaderFromName(const char * name)622 PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name )
623 {
624 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ;
625 for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ )
626 {
627 if( strnicmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 )
628 return( section ) ;
629 else
630 section++ ;
631 }
632 return 0;
633 }
634
635
GetDebugHeader()636 PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader()
637 {
638 // Some files have a wrong entry in the COFF header, so
639 // first check if the debug info exists at all
640 if( NT_Header->FileHeader.PointerToSymbolTable == 0 )
641 return( 0 ) ;
642 DWORD debugDirRVA = NT_Header->OptionalHeader.
643 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].
644 VirtualAddress;
645 if( debugDirRVA == 0 )
646 return( 0 ) ;
647
648 // The following values must be calculated differently for MS/Borland files
649 PIMAGE_DEBUG_DIRECTORY debugDir ;
650 DWORD size ;
651
652 // Borland files have the debug directory at the beginning of a .debug section
653 PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName( ".debug" ) ;
654 if( debugHeader && debugHeader->VirtualAddress == debugDirRVA )
655 {
656 debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase) ;
657 size = NT_Header->OptionalHeader.
658 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size *
659 sizeof( IMAGE_DEBUG_DIRECTORY ) ;
660 }
661 else
662 // Microsoft debug directory is in the .rdata section
663 {
664 debugHeader = SectionHeaderFromName( ".rdata" ) ;
665 if( debugHeader == 0 )
666 return( 0 ) ;
667 size = NT_Header->OptionalHeader.
668 DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size ;
669 DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress ;
670 debugDir = BasedPtr( PIMAGE_DEBUG_DIRECTORY, fileBase,
671 debugHeader->PointerToRawData + offsetInto_rdata ) ;
672 }
673
674 // look for COFF debug info
675 DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ;
676 for( DWORD i = 0; i < debugFormats; i++ )
677 {
678 if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF )
679 return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ;
680 else
681 debugDir++ ;
682 }
683 return( NULL ) ;
684 }
685
686
FindDebugInfo()687 void PE_Debug :: FindDebugInfo()
688 {
689 ClearDebugPtrs() ;
690 // Put everything into a try/catch in case the file has wrong fields
691 try
692 {
693 // Verify that fileBase is a valid pointer to a DOS header
694 if( fileBase->e_magic == IMAGE_DOS_SIGNATURE )
695 {
696 // Get a pointer to the PE header
697 NT_Header = BasedPtr( PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew ) ;
698 // Verify that NT_Header is a valid pointer to a NT header
699 if( NT_Header->Signature == IMAGE_NT_SIGNATURE )
700 {
701 // Get a pointer to the debug header if any
702 COFFDebugInfo = GetDebugHeader() ;
703 // Get a pointer to the symbol table and retrieve the number of symbols
704 if( NT_Header->FileHeader.PointerToSymbolTable )
705 COFFSymbolTable =
706 BasedPtr( PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable ) ;
707 COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols ;
708 // The string table starts right after the symbol table
709 stringTable = (const char*)(COFFSymbolTable + COFFSymbolCount) ;
710 }
711 }
712 }
713 catch( ... )
714 {
715 // Header wrong, do nothing
716 }
717 }
718
719
MapFileInMemory(const char * module)720 void PE_Debug :: MapFileInMemory( const char* module )
721 {
722 ClearFileCache() ;
723 hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL,
724 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
725 if( hFile != INVALID_HANDLE_VALUE )
726 {
727 hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ;
728 if( hFileMapping != 0 )
729 fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ) ;
730 }
731 // NB: open files/mapping are closed later in ClearFileCache
732 }
733
734
DumpDebugInfo(DumpBuffer & dumpBuffer,const BYTE * caller,HINSTANCE hInstance)735 int PE_Debug::DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance )
736 {
737 // Avoid to open, map and looking for debug header/symbol table
738 // by caching the latest and comparing the actual module with
739 // the latest one.
740 static char module[ MAX_MODULENAME_LEN ] ;
741 GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ;
742
743 // New module
744 if( strcmpi( latestModule, module ) ) {
745 strcpy_s( latestModule, module );
746 //JAS dumpBuffer.Printf( "Module: %s\r\n", module );
747 MapFileInMemory( module );
748 FindDebugInfo();
749 }
750
751 char pretty_module[1024];
752
753 strcpy_s( pretty_module, module );
754 char *p = pretty_module+strlen(pretty_module)-1;
755 // Move p to point to first letter of EXE filename
756 while( (*p!='\\') && (*p!='/') && (*p!=':') )
757 p--;
758 p++;
759 if ( strlen(p) < 1 ) {
760 strcpy_s( pretty_module, "<unknown>" );
761 } else {
762 memmove( pretty_module, p, strlen(p)+1 );
763 }
764
765 if ( fileBase ) {
766 // Put everything into a try/catch in case the file has wrong fields
767 try {
768 DWORD relativeAddress = caller - (BYTE*)hInstance ;
769 // Dump symbolic information and line number if available
770 if( COFFSymbolCount != 0 && COFFSymbolTable != NULL ) {
771 DumpSymbolInfo( dumpBuffer, relativeAddress ) ;
772 if( COFFDebugInfo )
773 DumpLineNumber( dumpBuffer, relativeAddress ) ;
774 return 1;
775 } else {
776 //dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this module.\r\n" ) ;
777 //JAS dumpBuffer.Printf( " no debug information\r\n" ) ;
778 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
779 if (Dump_to_log) {
780 mprintf((" %s %08x()\r\n", pretty_module, caller )) ;
781 }
782 return 0;
783 }
784 } catch( ... ) {
785 // Header wrong, do nothing
786 return 0;
787 }
788 } else {
789 dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ;
790 if (Dump_to_log) {
791 mprintf(( " %s %08x()\r\n", pretty_module, caller )) ;
792 }
793 //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ;
794 //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ;
795 return 0;
796 }
797
798 Int3();
799
800 }
801
DumpCallsStack(DumpBuffer & dumpBuffer)802 void DumpCallsStack( DumpBuffer& dumpBuffer )
803 {
804 static PE_Debug PE_debug ;
805
806 dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ;
807 dumpBuffer.Printf( Separator ) ;
808
809 // The structure of the stack frames is the following:
810 // EBP -> parent stack frame EBP
811 // return address for this call ( = caller )
812 // The chain can be navigated iteratively, after the
813 // initial value of EBP is loaded from the register
814 DWORD parentEBP, retval;
815 MEMORY_BASIC_INFORMATION mbi ;
816 HINSTANCE hInstance;
817
818 int depth = 0;
819
820 __asm MOV parentEBP, EBP
821
822 do {
823 depth++;
824 if ( depth > 16 )
825 break;
826
827 if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) ) {
828 break;
829 }
830 parentEBP = *(DWORD*)parentEBP ;
831
832 BYTE **NextCaller = ((BYTE**)parentEBP + 1);
833
834 if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) {
835 break;
836 }
837
838 BYTE* caller = *NextCaller; // Error sometimes!!!
839
840 // Skip the first EBP as it points to AssertionFailed, which is
841 // uninteresting for the user
842
843 if ( depth > 1 ) {
844
845 // Get the instance handle of the module where caller belongs to
846 retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ;
847
848 // The instance handle is equal to the allocation base in Win32
849 hInstance = (HINSTANCE)mbi.AllocationBase ;
850
851 if( ( retval == sizeof( mbi ) ) && hInstance ) {
852 if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) {
853 //break;
854 }
855 } else {
856 break ; // End of the call chain
857 }
858 }
859 } while( TRUE ) ;
860
861
862 dumpBuffer.Printf( Separator ) ;
863 PE_debug.ClearReport() ; // Prepare for future calls
864 }
865
866 #endif //SHOW_CALL_STACK
867
868
869 char AssertText1[2048];
870 char AssertText2[1024];
871
872 uint flags = MB_SYSTEMMODAL|MB_SETFOREGROUND;
873 //uint flags = MB_SYSTEMMODAL;
874
875 extern void gr_force_windowed();
876
dump_text_to_clipboard(const char * text)877 void dump_text_to_clipboard( const char *text )
878 {
879 int len = strlen(text)+1024;
880
881 HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len );
882 if ( !h_text ) return;
883 char *ptr = (char *)GlobalLock(h_text);
884 if ( !ptr ) return;
885
886 // copy then, if you find any \n's without \r's, then add in the \r.
887 char last_char = 0;
888 while( *text ) {
889 if ( (*text == '\n') && (last_char != '\r') ) {
890 *ptr++ = '\r';
891 }
892 last_char = *text;
893 *ptr++ = last_char;
894 text++;
895 }
896 *ptr++ = 0;
897 GlobalUnlock(h_text);
898 OpenClipboard(NULL);
899 EmptyClipboard();
900 SetClipboardData(CF_TEXT, h_text);
901 CloseClipboard();
902 }
903
904
WinAssert(char * text,char * filename,int linenum)905 void _cdecl WinAssert(char * text, char * filename, int linenum )
906 {
907 int val;
908
909 // this stuff migt be really useful for solving bug reports and user errors. We should output it!
910 mprintf(("ASSERTION: \"%s\" at %s:%d\n", text, strrchr(filename, '\\')+1, linenum ));
911
912 #ifdef Allow_NoWarn
913 if (Cmdline_nowarn) {
914 return;
915 }
916 #endif
917
918 Messagebox_active = true;
919
920 gr_activate(0);
921
922 filename = strrchr(filename, '\\')+1;
923 sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d\r\n", text, filename, linenum );
924
925 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
926 /* Dump the callstack */
927 SCP_DebugCallStack callStack;
928 SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) );
929
930 /* Format the string */
931 SCP_string assertString( AssertText1 );
932 assertString += "\n";
933 assertString += callStack.DumpToString( );
934
935 /* Copy to the clipboard */
936 dump_text_to_clipboard( assertString.c_str( ) );
937
938 // truncate text
939 truncate_message_lines(assertString, Messagebox_lines);
940
941 assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n";
942 assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n";
943 val = MessageBox( NULL, assertString.c_str( ), "Assertion Failed!", MB_OKCANCEL | flags );
944
945 #elif defined( SHOW_CALL_STACK )
946 dumpBuffer.Clear();
947 dumpBuffer.Printf( AssertText1 );
948 dumpBuffer.Printf( "\r\n" );
949 DumpCallsStack( dumpBuffer ) ;
950 dump_text_to_clipboard(dumpBuffer.buffer);
951
952 // truncate text
953 dumpBuffer.TruncateLines(Messagebox_lines);
954
955 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
956 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n");
957
958 val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags );
959 #else
960 val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags );
961 #endif
962
963 if (val == IDCANCEL)
964 exit(1);
965
966 Int3();
967
968 gr_activate(1);
969
970 Messagebox_active = false;
971 }
972
WinAssert(char * text,char * filename,int linenum,const char * format,...)973 void _cdecl WinAssert(char * text, char * filename, int linenum, const char * format, ... )
974 {
975 int val;
976
977 va_list args;
978
979 va_start(args, format);
980 vsprintf(AssertText2, format, args);
981 va_end(args);
982
983 // this stuff migt be really useful for solving bug reports and user errors. We should output it!
984 mprintf(("ASSERTION: \"%s\" at %s:%d\n %s\n", text, strrchr(filename, '\\')+1, linenum, AssertText2 ));
985
986 #ifdef Allow_NoWarn
987 if (Cmdline_nowarn) {
988 return;
989 }
990 #endif
991
992 Messagebox_active = true;
993
994 gr_activate(0);
995
996 filename = strrchr(filename, '\\')+1;
997 sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d\r\n%s\r\n", text, filename, linenum, AssertText2 );
998
999 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
1000 /* Dump the callstack */
1001 SCP_DebugCallStack callStack;
1002 SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) );
1003
1004 /* Format the string */
1005 SCP_string assertString( AssertText1 );
1006 assertString += "\n";
1007 assertString += callStack.DumpToString( );
1008
1009 /* Copy to the clipboard */
1010 dump_text_to_clipboard( assertString.c_str( ) );
1011
1012 // truncate text
1013 truncate_message_lines(assertString, Messagebox_lines);
1014
1015 assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n";
1016 assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n";
1017 val = MessageBox( NULL, assertString.c_str( ), "Assertion Failed!", MB_OKCANCEL | flags );
1018
1019 #elif defined ( SHOW_CALL_STACK )
1020 dumpBuffer.Clear();
1021 dumpBuffer.Printf( AssertText1 );
1022 dumpBuffer.Printf( "\r\n" );
1023 DumpCallsStack( dumpBuffer ) ;
1024 dump_text_to_clipboard(dumpBuffer.buffer);
1025
1026 // truncate text
1027 dumpBuffer.TruncateLines(Messagebox_lines);
1028
1029 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
1030 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n");
1031
1032 val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags );
1033 #else
1034 val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags );
1035 #endif
1036
1037 if (val == IDCANCEL)
1038 exit(1);
1039
1040 Int3();
1041
1042 gr_activate(1);
1043
1044 Messagebox_active = false;
1045 }
1046
LuaDebugPrint(lua_Debug & ar)1047 void LuaDebugPrint(lua_Debug &ar)
1048 {
1049 dumpBuffer.Printf( "Name:\t\t%s\r\n", ar.name);
1050 dumpBuffer.Printf( "Name of:\t%s\r\n", ar.namewhat);
1051 dumpBuffer.Printf( "Function type:\t%s\r\n", ar.what);
1052 dumpBuffer.Printf( "Defined on:\t%d\r\n", ar.linedefined);
1053 dumpBuffer.Printf( "Upvalues:\t%d\r\n", ar.nups);
1054 dumpBuffer.Printf( "\r\n" );
1055 dumpBuffer.Printf( "Source:\t\t%s\r\n", ar.source);
1056 dumpBuffer.Printf( "Short source:\t%s\r\n", ar.short_src);
1057 dumpBuffer.Printf( "Current line:\t%d\r\n", ar.currentline);
1058 dumpBuffer.Printf( "- Function line:\t%d\r\n", (ar.linedefined ? (1 + ar.currentline - ar.linedefined) : 0));
1059 }
1060
1061 extern lua_Debug Ade_debug_info;
1062 extern char debug_stack[4][32];
LuaError(struct lua_State * L,char * format,...)1063 void LuaError(struct lua_State *L, char *format, ...)
1064 {
1065 int val;
1066
1067 Messagebox_active = true;
1068
1069 gr_activate(0);
1070
1071 /*
1072 va_start(args, format);
1073 vsprintf(AssertText1,format,args);
1074 va_end(args);
1075 */
1076
1077 //filename = strrchr(filename, '\\')+1;
1078 //sprintf(AssertText2,"LuaError: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line );
1079
1080 dumpBuffer.Clear();
1081 //WMC - if format is set to NULL, assume this is acting as an
1082 //error handler for Lua.
1083 if(format == NULL)
1084 {
1085 dumpBuffer.Printf("LUA ERROR: %s", lua_tostring(L, -1));
1086 lua_pop(L, -1);
1087 }
1088 else
1089 {
1090 va_list args;
1091 va_start(args, format);
1092 vsprintf(AssertText1,format,args);
1093 dumpBuffer.Printf(AssertText1);
1094 va_end(args);
1095 }
1096
1097 dumpBuffer.Printf( "\r\n" );
1098 dumpBuffer.Printf( "\r\n" );
1099
1100 //WMC - This is virtually worthless.
1101 /*
1102 dumpBuffer.Printf(Separator);
1103 dumpBuffer.Printf( "LUA Debug:" );
1104 dumpBuffer.Printf( "\r\n" );
1105 dumpBuffer.Printf(Separator);
1106
1107 lua_Debug ar;
1108 if(lua_getstack(L, 0, &ar))
1109 {
1110 lua_getinfo(L, "nSlu", &ar);
1111 LuaDebugPrint(ar);
1112 }
1113 else
1114 {
1115 dumpBuffer.Printf("(No stack debug info)\r\n");
1116 }
1117 */
1118 // TEST CODE
1119
1120 dumpBuffer.Printf(Separator);
1121 dumpBuffer.Printf( "ADE Debug:" );
1122 dumpBuffer.Printf( "\r\n" );
1123 dumpBuffer.Printf(Separator);
1124 LuaDebugPrint(Ade_debug_info);
1125 dumpBuffer.Printf(Separator);
1126
1127 dumpBuffer.Printf( "\r\n" );
1128 dumpBuffer.Printf( "\r\n" );
1129
1130 AssertText2[0] = '\0';
1131 dumpBuffer.Printf(Separator);
1132
1133 // Get the stack via the debug.traceback() function
1134 lua_getglobal(L, LUA_DBLIBNAME);
1135
1136 if (!lua_isnil(L, -1))
1137 {
1138 dumpBuffer.Printf( "\r\n" );
1139 lua_getfield(L, -1, "traceback");
1140 lua_remove(L, -2);
1141
1142 if (lua_pcall(L, 0, 1, 0) != 0)
1143 dumpBuffer.Printf("Error while retrieving stack: %s", lua_tostring(L, -1));
1144 else
1145 dumpBuffer.Printf(lua_tostring(L, -1));
1146
1147 lua_pop(L, 1);
1148 }
1149 else
1150 {
1151 // If the debug library is nil then fall back to the default debug stack
1152 dumpBuffer.Printf("LUA Stack:\r\n");
1153 int i;
1154 for (i = 0; i < 4; i++) {
1155 if (debug_stack[i][0] != '\0')
1156 dumpBuffer.Printf("\t%s\r\n", debug_stack[i]);
1157 }
1158 }
1159 dumpBuffer.Printf( "\r\n" );
1160
1161 dumpBuffer.Printf(Separator);
1162 ade_stackdump(L, AssertText2);
1163 dumpBuffer.Printf( AssertText2 );
1164 dumpBuffer.Printf( "\r\n" );
1165 dumpBuffer.Printf(Separator);
1166
1167 dump_text_to_clipboard(dumpBuffer.buffer);
1168
1169 // truncate text
1170 dumpBuffer.TruncateLines(Messagebox_lines);
1171
1172 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
1173 dumpBuffer.Printf( "\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
1174
1175 val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_YESNOCANCEL );
1176
1177 if (val == IDCANCEL ) {
1178 exit(1);
1179 } else if(val == IDYES) {
1180 Int3();
1181 }
1182
1183 gr_activate(1);
1184
1185 Messagebox_active = false;
1186 }
1187
Error(const char * filename,int line,const char * format,...)1188 void _cdecl Error( const char * filename, int line, const char * format, ... )
1189 {
1190 Global_error_count++;
1191
1192 int val;
1193 va_list args;
1194
1195 va_start(args, format);
1196 vsprintf(AssertText1, format, args);
1197 va_end(args);
1198
1199 filename = strrchr(filename, '\\')+1;
1200 sprintf(AssertText2, "Error: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line);
1201 mprintf(("ERROR: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line));
1202
1203 Messagebox_active = true;
1204
1205 gr_activate(0);
1206
1207 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
1208 /* Dump the callstack */
1209 SCP_DebugCallStack callStack;
1210 SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) );
1211
1212 /* Format the string */
1213 SCP_string assertString( AssertText1 );
1214 assertString += "\n";
1215 assertString += callStack.DumpToString( );
1216
1217 /* Copy to the clipboard */
1218 dump_text_to_clipboard( assertString.c_str( ) );
1219
1220 // truncate text
1221 truncate_message_lines(assertString, Messagebox_lines);
1222
1223 assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n";
1224 assertString += "\n\nUse Ok to break into Debugger, Cancel to exit.\n";
1225 val = MessageBox( NULL, assertString.c_str( ), "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL );
1226
1227 #elif defined( SHOW_CALL_STACK )
1228 dumpBuffer.Clear();
1229 dumpBuffer.Printf( AssertText2 );
1230 dumpBuffer.Printf( "\r\n" );
1231 DumpCallsStack( dumpBuffer ) ;
1232 dump_text_to_clipboard(dumpBuffer.buffer);
1233
1234 // truncate text
1235 dumpBuffer.TruncateLines(Messagebox_lines);
1236
1237 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
1238 dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
1239
1240 val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL );
1241 #else
1242 strcat_s(AssertText2,"\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n");
1243
1244 val = MessageBox(NULL, AssertText2, "Error!", flags | MB_DEFBUTTON2 | MB_OKCANCEL );
1245 #endif
1246
1247 switch (val)
1248 {
1249 case IDCANCEL:
1250 exit(1);
1251
1252 default:
1253 Int3();
1254 break;
1255 }
1256
1257 gr_activate(1);
1258
1259 Messagebox_active = false;
1260 }
1261
WarningEx(char * filename,int line,const char * format,...)1262 void _cdecl WarningEx( char *filename, int line, const char *format, ... )
1263 {
1264 #ifndef NDEBUG
1265 if (Cmdline_extra_warn) {
1266 char msg[sizeof(AssertText1)];
1267 va_list args;
1268 va_start(args, format);
1269 vsprintf(msg, format, args);
1270 va_end(args);
1271 Warning(filename, line, msg);
1272 }
1273 #endif
1274 }
1275
Warning(char * filename,int line,const char * format,...)1276 void _cdecl Warning( char *filename, int line, const char *format, ... )
1277 {
1278 Global_warning_count++;
1279
1280 #ifndef NDEBUG
1281 va_list args;
1282 int result;
1283 int i;
1284 int slen = 0;
1285
1286 // output to the debug log before anything else (so that we have a complete record)
1287
1288 memset( AssertText1, 0, sizeof(AssertText1) );
1289 memset( AssertText2, 0, sizeof(AssertText2) );
1290
1291 va_start(args, format);
1292 vsprintf(AssertText1, format, args);
1293 va_end(args);
1294
1295 slen = strlen(AssertText1);
1296
1297 // strip out the newline char so the output looks better
1298 for (i = 0; i < slen; i++){
1299 if (AssertText1[i] == (char)0x0a) {
1300 AssertText2[i] = ' ';
1301 } else {
1302 AssertText2[i] = AssertText1[i];
1303 }
1304 }
1305
1306 // kill off extra white space at end
1307 if (AssertText2[slen-1] == (char)0x20) {
1308 AssertText2[slen-1] = '\0';
1309 } else {
1310 // just being careful
1311 AssertText2[slen] = '\0';
1312 }
1313
1314 mprintf(("WARNING: \"%s\" at %s:%d\n", AssertText2, strrchr(filename, '\\')+1, line));
1315
1316 // now go for the additional popup window, if we want it ...
1317 #ifdef Allow_NoWarn
1318 if (Cmdline_nowarn) {
1319 return;
1320 }
1321 #endif
1322
1323 filename = strrchr(filename, '\\')+1;
1324 sprintf(AssertText2, "Warning: %s\r\nFile: %s\r\nLine: %d\r\n", AssertText1, filename, line );
1325
1326 Messagebox_active = true;
1327
1328 gr_activate(0);
1329
1330 #if defined( SHOW_CALL_STACK ) && defined( PDB_DEBUGGING )
1331 /* Dump the callstack */
1332 SCP_DebugCallStack callStack;
1333 SCP_DumpStack( dynamic_cast< SCP_IDumpHandler* >( &callStack ) );
1334
1335 /* Format the string */
1336 SCP_string assertString( AssertText1 );
1337 assertString += "\n";
1338 assertString += callStack.DumpToString( );
1339
1340 /* Copy to the clipboard */
1341 dump_text_to_clipboard( assertString.c_str( ) );
1342
1343 // truncate text
1344 truncate_message_lines(assertString, Messagebox_lines);
1345
1346 assertString += "\n[ This info is in the clipboard so you can paste it somewhere now ]\n";
1347 assertString += "\n\nUse Yes to break into Debugger, No to continue.\nand Cancel to Quit\n";
1348 result = MessageBox( NULL, assertString.c_str( ), "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags );
1349
1350 #elif defined ( SHOW_CALL_STACK )
1351 //we don't want to dump the call stack for every single warning
1352 Dump_to_log = false;
1353
1354 dumpBuffer.Clear();
1355 dumpBuffer.Printf( AssertText2 );
1356 dumpBuffer.Printf( "\r\n" );
1357 DumpCallsStack( dumpBuffer ) ;
1358 dump_text_to_clipboard(dumpBuffer.buffer);
1359
1360 // truncate text
1361 dumpBuffer.TruncateLines(Messagebox_lines);
1362
1363 dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" );
1364 dumpBuffer.Printf("\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
1365
1366 result = MessageBox((HWND)os_get_window(), dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags );
1367
1368 Dump_to_log = true;
1369
1370 #else
1371 strcat_s(AssertText2,"\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit");
1372 result = MessageBox((HWND)os_get_window(), AssertText2, "Warning!", MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING | flags );
1373 #endif
1374
1375 switch (result)
1376 {
1377 case IDYES:
1378 Int3();
1379 break;
1380
1381 case IDNO:
1382 break;
1383
1384 case IDCANCEL:
1385 exit(1);
1386 }
1387
1388 gr_activate(1);
1389
1390 Messagebox_active = false;
1391 #endif // !NDEBUG
1392 }
1393
1394
1395
1396 //================= memory stuff
1397 /*
1398 char *format_mem( DWORD num )
1399 {
1400 if ( num < 1024 ) {
1401 sprintf( tmp_mem, "%d bytes", num );
1402 } else if ( num < 1024*1024 ) {
1403 sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f );
1404 } else {
1405 sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f) );
1406 }
1407 return tmp_mem;
1408 }
1409 */
1410
1411 /*
1412 void getmem()
1413 {
1414 DWORD retval;
1415 MEMORY_BASIC_INFORMATION mbi ;
1416 HINSTANCE hInstance;
1417
1418 MEMORYSTATUS ms;
1419
1420 ms.dwLength = sizeof(MEMORYSTATUS);
1421 GlobalMemoryStatus(&ms);
1422
1423 printf( "Percent of memory in use: %d%%\n", ms.dwMemoryLoad );
1424 printf( "Bytes of physical memory: %s\n", format_mem(ms.dwTotalPhys) );
1425 printf( "Free physical memory bytes: %s\n", format_mem(ms.dwAvailPhys) ); //
1426 printf( "Bytes of paging file: %s\n", format_mem(ms.dwTotalPageFile) ); // bytes of paging file
1427 printf( "Free bytes of paging file: %s\n", format_mem(ms.dwAvailPageFile) ); // free bytes of paging file
1428 printf( "User bytes of address space: %s\n", format_mem(ms.dwTotalVirtual) ); //
1429 printf( "Free user bytes: %s\n", format_mem(ms.dwAvailVirtual) ); // free user bytes
1430
1431
1432 // Get the instance handle of the module where caller belongs to
1433 retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ;
1434
1435 // The instance handle is equal to the allocation base in Win32
1436 hInstance = (HINSTANCE)mbi.AllocationBase ;
1437
1438 if( ( retval == sizeof( mbi ) ) && hInstance ) {
1439 printf( "Virtual Query succeeded...\n" );
1440 } else {
1441 printf( "Virtual Query failed...\n" );
1442 }
1443
1444 }
1445 */
1446
1447
1448
1449 #ifndef NDEBUG
1450
1451 int TotalRam = 0;
1452 #define nNoMansLandSize 4
1453
1454 typedef struct _CrtMemBlockHeader
1455 {
1456 struct _CrtMemBlockHeader * pBlockHeaderNext;
1457 struct _CrtMemBlockHeader * pBlockHeaderPrev;
1458 char * szFileName;
1459 int nLine;
1460 size_t nDataSize;
1461 int nBlockUse;
1462 long lRequest;
1463 unsigned char gap[nNoMansLandSize];
1464 /* followed by:
1465 * unsigned char data[nDataSize];
1466 * unsigned char anotherGap[nNoMansLandSize];
1467 */
1468 } _CrtMemBlockHeader;
1469
1470 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
1471
1472
1473 // this block of code is never referenced...
1474 #if 0
1475 int __cdecl MyAllocHook(
1476 int nAllocType,
1477 void * pvData,
1478 size_t nSize,
1479 int nBlockUse,
1480 long lRequest,
1481 const char * szFileName,
1482 int nLine
1483 )
1484 {
1485 // char *operation[] = { "", "allocating", "re-allocating", "freeing" };
1486 // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };
1487
1488 if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations
1489 return( TRUE );
1490
1491 _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
1492 _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );
1493
1494 if ( nAllocType == 3 ) {
1495 _CrtMemBlockHeader *phd = pHdr(pvData);
1496 nSize = phd->nDataSize;
1497 }
1498
1499 // mprintf(( "Total RAM = %d\n", TotalRam ));
1500
1501 mprintf(( "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n",
1502 szFileName, nLine, operation[nAllocType], nSize,
1503 blockType[nBlockUse], lRequest ));
1504 if ( pvData != NULL )
1505 mprintf(( " at %X", pvData ));
1506
1507 mprintf(("\n" ));
1508
1509
1510 return( TRUE ); // Allow the memory operation to proceed
1511 }
1512 #endif // #if 0
1513
1514
1515
windebug_memwatch_init()1516 void windebug_memwatch_init()
1517 {
1518 //_CrtSetAllocHook(MyAllocHook);
1519 TotalRam = 0;
1520 }
1521
1522 #endif
1523
1524 int Watch_malloc = 0;
DCF_BOOL(watch_malloc,Watch_malloc)1525 DCF_BOOL(watch_malloc, Watch_malloc )
1526
1527 // Returns 0 if not enough RAM.
1528 int vm_init(int min_heap_size)
1529 {
1530 #ifndef NDEBUG
1531 TotalRam = 0;
1532 #endif
1533
1534 return 1;
1535 }
1536
1537
1538 #ifdef _DEBUG
1539
1540 #ifdef _REPORT_MEM_LEAKS
1541 const int MAX_MEM_POINTERS = 50000;
1542
1543 typedef struct
1544 {
1545 char filename[33];
1546 int size;
1547 int line;
1548 void *ptr;
1549 } MemPtrInfo;
1550
1551 MemPtrInfo mem_ptr_list[MAX_MEM_POINTERS];
1552
1553 #endif
1554
1555 const int MAX_MEM_MODULES = 600;
1556
1557 typedef struct
1558 {
1559 char filename[33];
1560 int size;
1561 int magic_num1;
1562 int magic_num2;
1563 bool in_use;
1564
1565 } MemBlockInfo;
1566
1567 MemBlockInfo mem_block_list[MAX_MEM_MODULES];
1568
memblockinfo_sort_compare(const void * arg1,const void * arg2)1569 int memblockinfo_sort_compare( const void *arg1, const void *arg2 )
1570 {
1571 MemBlockInfo *mbi1 = (MemBlockInfo *) arg1;
1572 MemBlockInfo *mbi2 = (MemBlockInfo *) arg2;
1573
1574 if (mbi1->size > mbi2->size)
1575 return -1;
1576
1577 if (mbi1->size < mbi2->size)
1578 return 1;
1579
1580 return 0;
1581 }
1582
memblockinfo_sort()1583 void memblockinfo_sort()
1584 {
1585 insertion_sort(mem_block_list, MAX_MEM_MODULES, sizeof(MemBlockInfo), memblockinfo_sort_compare );
1586 }
1587
memblockinfo_sort_get_entry(int index,char * filename,int * size)1588 void memblockinfo_sort_get_entry(int index, char *filename, int *size)
1589 {
1590 Assert(index < MAX_MEM_MODULES);
1591
1592 strcpy(filename, mem_block_list[index].filename);
1593 *size = mem_block_list[index].size;
1594 }
1595
1596 static bool first_time = true;
1597
register_malloc(int size,char * filename,int line,void * ptr)1598 void register_malloc( int size, char *filename, int line, void *ptr)
1599 {
1600 if(first_time == true)
1601 {
1602 ZeroMemory(mem_block_list, MAX_MEM_MODULES * sizeof(MemBlockInfo) );
1603 first_time = false;
1604
1605 // Get current flag
1606 int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
1607
1608 // Turn on leak-checking bit
1609 tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
1610
1611 // Set flag to the new value
1612 _CrtSetDbgFlag( tmpFlag );
1613
1614 #ifdef _REPORT_MEM_LEAKS
1615 ZeroMemory(mem_ptr_list, MAX_MEM_POINTERS * sizeof(MemPtrInfo));
1616 #endif
1617 }
1618
1619 char *temp = strrchr(filename, '\\');
1620 if(temp)
1621 filename = temp + 1;
1622
1623 // calculate magic numbers
1624 int magic1, magic2, len = strlen(filename);
1625
1626 magic1 = magic2 = 0;
1627
1628 for(int c = 0; c < len; c++)
1629 {
1630 magic1 += filename[c];
1631
1632 if(c % 2)
1633 magic2 += filename[c];
1634 else
1635 magic2 -= filename[c];
1636 }
1637
1638 for(int i = 0; i < MAX_MEM_MODULES; i++)
1639 {
1640 // Found the first empty entry, fill it
1641 if(mem_block_list[i].in_use == false)
1642 {
1643 strcpy_s(mem_block_list[i].filename, filename);
1644 mem_block_list[i].size = size;
1645 mem_block_list[i].magic_num1 = magic1;
1646 mem_block_list[i].magic_num2 = magic2;
1647 mem_block_list[i].in_use = true;
1648 break;
1649 }
1650
1651 // Found a matching entry, update it
1652 if( mem_block_list[i].magic_num1 == magic1 &&
1653 mem_block_list[i].magic_num2 == magic2 &&
1654 stricmp(mem_block_list[i].filename, filename) == 0)
1655 {
1656 mem_block_list[i].size += size;
1657 break;
1658 }
1659 }
1660
1661 // Now if system is compiled register it with the fuller system
1662 #ifdef _REPORT_MEM_LEAKS
1663
1664 // Find empty slot
1665 int count = 0;
1666 while(mem_ptr_list[count].ptr != NULL)
1667 {
1668 count++;
1669 // If you hit this just increase MAX_MEM_POINTERS
1670 Assert(count < MAX_MEM_POINTERS);
1671 }
1672 mem_ptr_list[count].ptr = ptr;
1673 mem_ptr_list[count].line = line;
1674 mem_ptr_list[count].size = size;
1675 strcpy_s(mem_ptr_list[count].filename, filename);
1676
1677 #endif
1678 }
1679
memblockinfo_output_memleak()1680 void memblockinfo_output_memleak()
1681 {
1682 if(!Cmdline_show_mem_usage) return;
1683
1684 if(TotalRam == 0)
1685 return;
1686
1687 if(TotalRam < 0) {
1688 _RPT1(_CRT_WARN, "TotalRam bad value!",TotalRam);
1689 return;
1690 }
1691
1692 _RPT1(_CRT_WARN, "malloc memory leak of %d\n",TotalRam);
1693
1694 // Now if system is compiled register it with the fuller system
1695 #ifdef _REPORT_MEM_LEAKS
1696
1697 int total = 0;
1698 // Find empty slot
1699 for(int f = 0; f < MAX_MEM_POINTERS; f++)
1700 {
1701 if(mem_ptr_list[f].ptr)
1702 {
1703 _RPT3(_CRT_WARN, "Memory leaks: (%s line %d) of %d bytes\n", mem_ptr_list[f].filename, mem_ptr_list[f].line, mem_ptr_list[f].size);
1704 total += mem_ptr_list[f].size;
1705 }
1706 }
1707
1708 Assert(TotalRam == total);
1709
1710 #else
1711
1712 for(int i = 0; i < MAX_MEM_MODULES; i++)
1713 {
1714 // Found the first empty entry, fill it
1715 if(mem_block_list[i].size > 0)
1716 {
1717 // Oh... bad code... making assumsions...
1718 _RPT2(_CRT_WARN, "Possible memory leaks: %s %d\n", mem_block_list[i].filename, mem_block_list[i].size);
1719 }
1720 }
1721 #endif
1722 }
1723
unregister_malloc(char * filename,int size,void * ptr)1724 void unregister_malloc(char *filename, int size, void *ptr)
1725 {
1726 // calculate magic numbers
1727 int magic1, magic2, len;
1728
1729 char *temp = strrchr(filename, '\\');
1730 if(temp)
1731 filename = temp + 1;
1732
1733 len = strlen(filename);
1734
1735 magic1 = magic2 = 0;
1736
1737 for(int c = 0; c < len; c++)
1738 {
1739 magic1 += filename[c];
1740
1741 if(c % 2)
1742 magic2 += filename[c];
1743 else
1744 magic2 -= filename[c];
1745 }
1746
1747 // Now if system is compiled register it with the fuller system
1748 #ifdef _REPORT_MEM_LEAKS
1749
1750 // Find empty slot
1751 for(int f = 0; f < MAX_MEM_POINTERS; f++)
1752 {
1753 if(mem_ptr_list[f].ptr == ptr) {
1754 mem_ptr_list[f].ptr = NULL;
1755 break;
1756 }
1757 }
1758
1759 #endif
1760
1761 for(int i = 0; i < MAX_MEM_MODULES; i++)
1762 {
1763 // Found a matching entry, update it
1764 if( mem_block_list[i].magic_num1 == magic1 &&
1765 mem_block_list[i].magic_num2 == magic2 &&
1766 stricmp(mem_block_list[i].filename, filename) == 0)
1767 {
1768 mem_block_list[i].size -= size;
1769 return;
1770 }
1771 }
1772 }
1773
1774 #endif
1775
1776 #ifndef NDEBUG
_vm_malloc(int size,char * filename,int line,int quiet)1777 void *_vm_malloc( int size, char *filename, int line, int quiet )
1778 #else
1779 void *_vm_malloc( int size, int quiet )
1780 #endif
1781 {
1782 void *ptr = NULL;
1783
1784 ptr = _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ );
1785
1786 if (ptr == NULL)
1787 {
1788 mprintf(( "Malloc failed!!!!!!!!!!!!!!!!!!!\n" ));
1789
1790 if (quiet) {
1791 return NULL;
1792 }
1793
1794 Error(LOCATION, "Malloc Failed!\n");
1795 }
1796 #ifndef NDEBUG
1797 TotalRam += size;
1798
1799 if(Cmdline_show_mem_usage)
1800 register_malloc(size, filename, line, ptr);
1801 #endif
1802 return ptr;
1803 }
1804
1805 #ifndef NDEBUG
_vm_strdup(const char * ptr,char * filename,int line)1806 char *_vm_strdup( const char *ptr, char *filename, int line )
1807 #else
1808 char *_vm_strdup( const char *ptr )
1809 #endif
1810 {
1811 char *dst;
1812 int len = strlen(ptr);
1813
1814 dst = (char *)vm_malloc( len+1 );
1815
1816 if (!dst)
1817 return NULL;
1818
1819 strcpy_s( dst, len + 1, ptr );
1820 return dst;
1821 }
1822
1823 #ifndef NDEBUG
_vm_strndup(const char * ptr,int size,char * filename,int line)1824 char *_vm_strndup( const char *ptr, int size, char *filename, int line )
1825 #else
1826 char *_vm_strndup( const char *ptr, int size )
1827 #endif
1828 {
1829 char *dst;
1830
1831 dst = (char *)vm_malloc( size+1 );
1832
1833 if (!dst)
1834 return NULL;
1835
1836 strncpy( dst, ptr, size );
1837 // make sure it has a NULL terminiator
1838 dst[size] = '\0';
1839
1840 return dst;
1841 }
1842
1843 #ifndef NDEBUG
_vm_free(void * ptr,char * filename,int line)1844 void _vm_free( void *ptr, char *filename, int line )
1845 #else
1846 void _vm_free( void *ptr )
1847 #endif
1848 {
1849 if ( !ptr ) {
1850 return;
1851 }
1852
1853
1854
1855 #ifndef NDEBUG
1856 _CrtMemBlockHeader *phd = pHdr(ptr);
1857 int nSize = phd->nDataSize;
1858
1859 TotalRam -= nSize;
1860 if(Cmdline_show_mem_usage)
1861 unregister_malloc(filename, nSize, ptr);
1862 #endif
1863
1864 _free_dbg(ptr,_NORMAL_BLOCK);
1865 }
1866
vm_free_all()1867 void vm_free_all()
1868 {
1869 }
1870
1871 #ifndef NDEBUG
_vm_realloc(void * ptr,int size,char * filename,int line,int quiet)1872 void *_vm_realloc( void *ptr, int size, char *filename, int line, int quiet )
1873 #else
1874 void *_vm_realloc( void *ptr, int size, int quiet )
1875 #endif
1876 {
1877 // if this is the first time it's used then we need to malloc it first
1878 if ( ptr == NULL )
1879 return vm_malloc(size);
1880
1881 void *ret_ptr = NULL;
1882
1883 #ifndef NDEBUG
1884 // Unregistered the previous allocation
1885 _CrtMemBlockHeader *phd = pHdr(ptr);
1886 int nSize = phd->nDataSize;
1887
1888 TotalRam -= nSize;
1889 if(Cmdline_show_mem_usage)
1890 unregister_malloc(filename, nSize, ptr);
1891 #endif
1892
1893 ret_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, __FILE__, __LINE__ );
1894
1895 if (ret_ptr == NULL) {
1896 mprintf(( "realloc failed!!!!!!!!!!!!!!!!!!!\n" ));
1897
1898 if (quiet && (size > 0) && (ptr != NULL)) {
1899 // realloc doesn't touch the original ptr in the case of failure so we could still use it
1900 return NULL;
1901 }
1902
1903 Error(LOCATION, "Out of memory. Try closing down other applications, increasing your\n"
1904 "virtual memory size, or installing more physical RAM.\n");
1905 }
1906 #ifndef NDEBUG
1907 TotalRam += size;
1908
1909 // register this allocation
1910 if(Cmdline_show_mem_usage)
1911 register_malloc(size, filename, line, ret_ptr);
1912 #endif
1913 return ret_ptr;
1914 }
1915