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