mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
1276 lines
38 KiB
C++
1276 lines
38 KiB
C++
/*
|
|
* $Logfile: /DescentIII/Main/win32/windebug.cpp $
|
|
* $Revision: 33 $
|
|
* $Date: 9/15/01 1:46p $
|
|
* $Author: Kevin $
|
|
*
|
|
* Win32 Debugging 'kernal'
|
|
*
|
|
* $Log: /DescentIII/Main/win32/windebug.cpp $
|
|
*
|
|
* 33 9/15/01 1:46p Kevin
|
|
* Added -nocrashbox to suppress crash dialog box
|
|
*
|
|
* 32 5/19/99 2:43a Jeff
|
|
* handle some possible crashes. The UCT->Localtime doesn't seem to work,
|
|
* although it is setup correctly.
|
|
*
|
|
* 31 5/18/99 9:48p Kevin
|
|
* adjust time to time zone for Jeff
|
|
*
|
|
* 30 5/07/99 5:46p Jason
|
|
* changes for gamegauge release
|
|
*
|
|
* 29 4/18/99 1:18p Kevin
|
|
* Demo2 build changes
|
|
*
|
|
* 28 4/12/99 7:14p Samir
|
|
* added multiple pages per mono window.
|
|
*
|
|
* 27 4/01/99 11:07a Kevin
|
|
* Added some more usefull debugging info
|
|
*
|
|
* 26 4/01/99 9:40a Matt
|
|
* Fixed dump_text_to_clipboard. It used to allocate a fixed number of
|
|
* bytes to allow space for adding CRs to strings, but now I actually
|
|
* count the number needed and allocate that much.
|
|
*
|
|
* 25 3/29/99 6:50p Kevin
|
|
* Added some parameters to the log file
|
|
*
|
|
* 24 2/16/99 11:25a Kevin
|
|
* Took out testing code.
|
|
*
|
|
* 23 2/16/99 12:36a Kevin
|
|
* Fixes for release builds of OEM V3 and KAtmai
|
|
*
|
|
* 22 2/15/99 1:22p Kevin
|
|
* Changes for GameGauge
|
|
*
|
|
* 21 2/13/99 1:56p Kevin
|
|
*
|
|
* 20 1/05/99 4:28p Kevin
|
|
* Moved exception handling code to windebug.cpp
|
|
*
|
|
* 19 1/05/99 3:55p Kevin
|
|
* Added structured exception handling
|
|
*
|
|
* 18 10/18/98 8:52p Matt
|
|
* Revamped debug/error system.
|
|
*
|
|
* 17 10/12/98 10:20a Samir
|
|
* added parameter to debug init.
|
|
*
|
|
* 16 10/08/98 4:24p Kevin
|
|
* Changed code to comply with memory library usage. Always use mem_malloc
|
|
* , mem_free and mem_strdup
|
|
*
|
|
* 15 6/23/98 2:40p Matt
|
|
* Added Yes/No/Cancel type to OutrageMessageBox() and
|
|
* Debug_MesssageBox(), and changed return value from a bool to an
|
|
* integer.
|
|
*
|
|
* 14 4/21/98 2:30p Matt
|
|
* dump_text_to_clipboard() wasn't putting terminating null on text string
|
|
*
|
|
* 13 4/08/98 7:19p Samir
|
|
* Added runtime debugging option.
|
|
*
|
|
* 12 4/07/98 9:21p Samir
|
|
* Added volition's call stack and memory dump code. This will only
|
|
* happen if you run the game without the -debug argument.
|
|
*
|
|
* 11 4/03/98 5:15p Samir
|
|
* Implemented simple debug message filtering.
|
|
*
|
|
* 10 3/20/98 2:43p Samir
|
|
* Some better Int3 support.
|
|
*
|
|
* 9 1/29/98 12:24p Samir
|
|
* Added logfile support.
|
|
*
|
|
* 8 1/28/98 4:45p Samir
|
|
* Mono check stuff.
|
|
*
|
|
* 7 1/28/98 11:56a Samir
|
|
* Code to check for monochrome debug.
|
|
*
|
|
* 6 1/28/98 11:17a Samir
|
|
* Added debugbreak stuff.
|
|
*
|
|
* 5 10/15/97 3:05p Samir
|
|
* NT mono output works.
|
|
*
|
|
* 4 10/13/97 2:41p Samir
|
|
* Debug breaks now are macros to work depending on the operating system.
|
|
*
|
|
* 3 9/04/97 12:00p Matt
|
|
* Changed Debug_MessageBox() to return bool instead of int, since it was
|
|
* already being used that way.
|
|
*
|
|
* 2 7/28/97 3:46p Samir
|
|
* NT mono stubbed out.
|
|
*
|
|
* 18 6/11/97 1:10p Samir
|
|
* Fixed up function names.
|
|
*
|
|
* 17 3/31/97 3:50p Samir
|
|
* MessageBox should be task modal.
|
|
*
|
|
* 16 3/18/97 2:14p Samir
|
|
* DebugBreak called instead of Int3
|
|
*
|
|
* 15 3/17/97 8:10p Matt
|
|
* Don't print to monochrome screen unless D3_MONO evironment var is set
|
|
*
|
|
* 14 3/14/97 4:15p Matt
|
|
* Changed title of OutrageMessageBox() dialogs
|
|
*
|
|
* 13 3/10/97 12:29p Samir
|
|
* Moved mono code and altered lowlevel console interface to machine
|
|
* library.
|
|
*
|
|
* 12 3/10/97 11:13a Samir
|
|
* Changes made to reflect deletion of OSDebug class from system
|
|
*
|
|
* 11 3/04/97 7:15p Samir
|
|
* Fixed bug in message_box, caused an Int3.
|
|
*
|
|
* 10 3/04/97 1:00p Samir
|
|
* Fixed return type prototype errors.
|
|
*
|
|
* 9 3/04/97 12:55p Samir
|
|
* Added types of messageboxes.
|
|
*
|
|
* 8 3/03/97 5:07p Samir
|
|
* stupid
|
|
*
|
|
* 7 3/03/97 4:36p Samir
|
|
* Removed instances of pserror.h code. this is a lower level lib than
|
|
* that!
|
|
*
|
|
* 6 2/27/97 7:00p Samir
|
|
* Removed windows.h from oewin_os.h and moved to library.
|
|
*
|
|
* 5 2/26/97 7:04p Samir
|
|
* Added Win32 DebugBreak
|
|
*
|
|
* 4 2/18/97 7:00p Samir
|
|
* Now print out last Win32 error too.
|
|
*
|
|
* 3 1/31/97 3:31p Samir
|
|
* forgot to use OSWINDBG_MAX_STRINGS constant in array init.
|
|
*
|
|
* 2 1/30/97 6:49p Samir
|
|
* Added some winDebug code for interface with error handler
|
|
*
|
|
* 1 1/28/97 2:32p Samir
|
|
* Standard debugging functions
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "Debug.h"
|
|
#include "mono.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <winioctl.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <conio.h>
|
|
#include <stdlib.h>
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Debug_break = false;
|
|
bool Debug_NT = false;
|
|
|
|
char *Debug_DumpInfo();
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Debug_Init(bool debugger, bool con_debug) {
|
|
// initialization of debugging consoles.
|
|
|
|
DWORD version = GetVersion();
|
|
|
|
if (version & 0x80000000) {
|
|
// Are we 95 or Win32s!
|
|
if ((version & 0xff) < 4) {
|
|
MessageBox(NULL, "This application will only run on Windows 95 or Windows NT.", "Error", MB_OK);
|
|
exit(1);
|
|
}
|
|
Debug_NT = false;
|
|
} else {
|
|
if ((version & 0xff) < 4) {
|
|
MessageBox(NULL, "This application needs Windows NT 4.0 or higher.", "Error", MB_OK);
|
|
exit(1);
|
|
}
|
|
Debug_NT = true;
|
|
}
|
|
|
|
#ifndef RELEASE
|
|
|
|
Debug_break = debugger;
|
|
|
|
if (con_debug) {
|
|
Debug_ConsoleInit();
|
|
Debug_ConsoleOpen(0, 9, 1, 78, 15, "Debug Spew");
|
|
Debug_ConsoleOpen(1, 1, 1, 58, 6, "Warnings");
|
|
|
|
if (!Debug_NT) {
|
|
Debug_ConsoleOpen(2, 1, 61, 18, 6, "Stats");
|
|
mprintf((0, "Win95 system.\n"));
|
|
} else {
|
|
mprintf((0, "WinNT system.\n"));
|
|
}
|
|
}
|
|
|
|
if (Debug_break) {
|
|
mprintf((0, "Debug Break enabled.\n"));
|
|
}
|
|
|
|
#endif // ifndef RELEASE
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Message Box functions */
|
|
|
|
// Does a messagebox with a stack dump
|
|
// Messagebox shows topstring, then stack dump, then bottomstring
|
|
// Return types are as the same the Windows return values
|
|
int Debug_ErrorBox(int type, const char *title, const char *topstring, const char *bottomstring) {
|
|
int answer;
|
|
char *dumptext = Debug_DumpInfo();
|
|
HWND wnd = GetActiveWindow();
|
|
|
|
// ShowWindow(wnd, SW_SHOWMINNOACTIVE);
|
|
|
|
DWORD flags;
|
|
if (type == OSMBOX_OK)
|
|
flags = MB_OK;
|
|
else if (type == OSMBOX_YESNO)
|
|
flags = MB_YESNO;
|
|
else if (type == OSMBOX_YESNOCANCEL)
|
|
flags = MB_YESNOCANCEL;
|
|
else if (type == OSMBOX_ABORTRETRYIGNORE)
|
|
flags = MB_ABORTRETRYIGNORE;
|
|
else if (type == OSMBOX_OKCANCEL)
|
|
flags = MB_OKCANCEL;
|
|
else
|
|
debug_break();
|
|
|
|
char *tmpbuf = (char *)GlobalAlloc(
|
|
GMEM_FIXED, strlen(dumptext) + strlen(topstring) + strlen(bottomstring) +
|
|
10); // malloc(strlen(dumptext) + strlen(topstring) + strlen(bottomstring) + 10);
|
|
|
|
strcpy(tmpbuf, topstring);
|
|
if (dumptext[0]) {
|
|
strcat(tmpbuf, "\r\n\r\n");
|
|
strcat(tmpbuf, dumptext);
|
|
}
|
|
if (bottomstring[0]) {
|
|
strcat(tmpbuf, "\r\n\r\n");
|
|
strcat(tmpbuf, bottomstring);
|
|
}
|
|
|
|
wsprintf(tmpbuf, "%s\r\n\r\n%s\r\n\r\n%s", topstring, dumptext, bottomstring);
|
|
|
|
ShowCursor(TRUE);
|
|
answer = MessageBox(NULL, tmpbuf, title, flags | MB_TASKMODAL | MB_SETFOREGROUND); // what's MB_ICONHAND?
|
|
ShowCursor(FALSE);
|
|
|
|
// free(tmpbuf);
|
|
GlobalFree(tmpbuf);
|
|
|
|
// ShowWindow(wnd, SW_SHOWMAXIMIZED);
|
|
|
|
return answer;
|
|
}
|
|
|
|
// Return types are the same the Windows return values
|
|
int Debug_MessageBox(int type, const char *title, const char *str) {
|
|
DWORD flags;
|
|
int answer;
|
|
HWND wnd = GetActiveWindow();
|
|
|
|
if (type == OSMBOX_OK)
|
|
flags = MB_OK;
|
|
else if (type == OSMBOX_YESNO)
|
|
flags = MB_YESNO;
|
|
else if (type == OSMBOX_YESNOCANCEL)
|
|
flags = MB_YESNOCANCEL;
|
|
else if (type == OSMBOX_ABORTRETRYIGNORE)
|
|
flags = MB_ABORTRETRYIGNORE;
|
|
else
|
|
DebugBreak();
|
|
|
|
ShowCursor(TRUE);
|
|
// ShowWindow(wnd, SW_SHOWMINNOACTIVE);
|
|
answer = MessageBox(NULL, str, title, flags | MB_TASKMODAL | MB_SETFOREGROUND);
|
|
ShowCursor(FALSE);
|
|
// ShowWindow(wnd, SW_SHOWMAXIMIZED);
|
|
|
|
return answer;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// This CODE SHOULD ONLY BE USED IN A DEBUGGABLE VERSION.
|
|
|
|
#define SHOW_CALL_STACK // Uncomment SHOW_CALL_STACK to show the call stack in terminal cases
|
|
//#define DUMPRAM
|
|
|
|
#ifdef SHOW_CALL_STACK
|
|
|
|
class DumpBuffer {
|
|
public:
|
|
enum { BUFFER_SIZE = 32000 };
|
|
DumpBuffer();
|
|
void Clear();
|
|
void Printf(const char *format, ...);
|
|
void SetWindowText(HWND hWnd) const;
|
|
char buffer[BUFFER_SIZE];
|
|
|
|
private:
|
|
char *current;
|
|
};
|
|
|
|
class PE_Debug {
|
|
public:
|
|
PE_Debug();
|
|
~PE_Debug();
|
|
void ClearReport();
|
|
int DumpDebugInfo(DumpBuffer &dumpBuffer, const BYTE *caller, HINSTANCE hInstance);
|
|
void Display();
|
|
|
|
private:
|
|
// Report data
|
|
enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 };
|
|
char latestModule[MAX_MODULENAME_LEN];
|
|
char latestFile[VA_MAX_FILENAME_LEN];
|
|
// File mapping data
|
|
HANDLE hFile;
|
|
HANDLE hFileMapping;
|
|
PIMAGE_DOS_HEADER fileBase;
|
|
// Pointers to debug information
|
|
PIMAGE_NT_HEADERS NT_Header;
|
|
PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo;
|
|
PIMAGE_SYMBOL COFFSymbolTable;
|
|
int COFFSymbolCount;
|
|
const char *stringTable;
|
|
|
|
void ClearFileCache();
|
|
void ClearDebugPtrs();
|
|
void MapFileInMemory(const char *module);
|
|
void FindDebugInfo();
|
|
void DumpSymbolInfo(DumpBuffer &dumpBuffer, DWORD relativeAddress);
|
|
void DumpLineNumber(DumpBuffer &dumpBuffer, DWORD relativeAddress);
|
|
PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader();
|
|
PIMAGE_SECTION_HEADER SectionHeaderFromName(const char *name);
|
|
const char *GetSymbolName(PIMAGE_SYMBOL sym);
|
|
};
|
|
|
|
DumpBuffer ::DumpBuffer() { Clear(); }
|
|
|
|
void DumpBuffer ::Clear() { current = buffer; }
|
|
|
|
void DumpBuffer ::Printf(const char *format, ...) {
|
|
// protect against obvious buffer overflow
|
|
if (current - buffer < BUFFER_SIZE) {
|
|
va_list argPtr;
|
|
va_start(argPtr, format);
|
|
int count = wvsprintf(current, format, argPtr);
|
|
va_end(argPtr);
|
|
current += count;
|
|
}
|
|
}
|
|
|
|
void DumpBuffer ::SetWindowText(HWND hWnd) const { SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)buffer); }
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Add an offset to a pointer and cast to a given type; may be
|
|
// implemented as a template function but Visual C++ has some problems.
|
|
#define BasedPtr(type, ptr, ofs) (type)((DWORD)(ptr) + (DWORD)(ofs))
|
|
|
|
PE_Debug ::PE_Debug() {
|
|
// Init file mapping cache
|
|
hFileMapping = 0;
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
fileBase = 0;
|
|
ClearDebugPtrs();
|
|
ClearReport();
|
|
}
|
|
|
|
PE_Debug ::~PE_Debug() { ClearFileCache(); }
|
|
|
|
void PE_Debug ::ClearReport() {
|
|
latestModule[0] = 0;
|
|
latestFile[0] = 0;
|
|
}
|
|
|
|
void PE_Debug ::ClearDebugPtrs() {
|
|
NT_Header = NULL;
|
|
COFFDebugInfo = NULL;
|
|
COFFSymbolTable = NULL;
|
|
COFFSymbolCount = 0;
|
|
stringTable = NULL;
|
|
}
|
|
|
|
void PE_Debug ::ClearFileCache() {
|
|
if (fileBase) {
|
|
UnmapViewOfFile(fileBase);
|
|
fileBase = 0;
|
|
}
|
|
if (hFileMapping != 0) {
|
|
CloseHandle(hFileMapping);
|
|
hFileMapping = 0;
|
|
}
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
void PE_Debug ::DumpLineNumber(DumpBuffer &dumpBuffer, DWORD relativeAddress) {
|
|
PIMAGE_LINENUMBER line = BasedPtr(PIMAGE_LINENUMBER, COFFDebugInfo, COFFDebugInfo->LvaToFirstLinenumber);
|
|
DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers;
|
|
const DWORD none = (DWORD)-1;
|
|
DWORD maxAddr = 0;
|
|
DWORD lineNum = none;
|
|
for (DWORD i = 0; i < lineCount; i++) {
|
|
if (line->Linenumber != 0) // A regular line number
|
|
{
|
|
// look for line with bigger address <= relativeAddress
|
|
if (line->Type.VirtualAddress <= relativeAddress && line->Type.VirtualAddress > maxAddr) {
|
|
maxAddr = line->Type.VirtualAddress;
|
|
lineNum = line->Linenumber;
|
|
}
|
|
}
|
|
line++;
|
|
}
|
|
if (lineNum != none)
|
|
dumpBuffer.Printf(" line %d\r\n", lineNum);
|
|
// else
|
|
// dumpBuffer.Printf( " line <unknown>\r\n" ) ;
|
|
}
|
|
|
|
const char *PE_Debug ::GetSymbolName(PIMAGE_SYMBOL sym) {
|
|
const int NAME_MAX_LEN = 64;
|
|
static char buf[NAME_MAX_LEN];
|
|
if (sym->N.Name.Short != 0) {
|
|
strncpy(buf, (const char *)sym->N.ShortName, 8);
|
|
buf[8] = 0;
|
|
} else {
|
|
strncpy(buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN);
|
|
buf[NAME_MAX_LEN - 1] = 0;
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
void unmangle(char *dst, const char *src) {
|
|
// strcpy( dst, src );
|
|
// return;
|
|
|
|
src++;
|
|
while ((*src) && (*src != ' ') && (*src != '@')) {
|
|
*dst++ = *src++;
|
|
}
|
|
*dst++ = 0;
|
|
}
|
|
|
|
#ifdef DUMPRAM
|
|
|
|
typedef struct MemSymbol {
|
|
int section;
|
|
int offset;
|
|
int size;
|
|
char name[132];
|
|
} MemSymbol;
|
|
|
|
int Num_symbols = 0;
|
|
int Max_symbols = 0;
|
|
MemSymbol *Symbols;
|
|
|
|
void InitSymbols() {
|
|
Num_symbols = 0;
|
|
Max_symbols = 5000;
|
|
Symbols = (MemSymbol *)mem_malloc(Max_symbols * sizeof(MemSymbol));
|
|
if (!Symbols) {
|
|
Max_symbols = 0;
|
|
}
|
|
}
|
|
|
|
void Add_Symbol(int section, int offset, const char *name, char *module) {
|
|
if (Num_symbols >= Max_symbols) {
|
|
return;
|
|
}
|
|
|
|
MemSymbol *sym = &Symbols[Num_symbols++];
|
|
|
|
sym->section = section;
|
|
sym->offset = offset;
|
|
sym->size = -1;
|
|
|
|
strcpy(sym->name, name);
|
|
strcat(sym->name, "(");
|
|
strcat(sym->name, module);
|
|
strcat(sym->name, ")");
|
|
}
|
|
|
|
int Sym_compare(const void *arg1, const void *arg2) {
|
|
MemSymbol *sym1 = (MemSymbol *)arg1;
|
|
MemSymbol *sym2 = (MemSymbol *)arg2;
|
|
|
|
if (sym1->section < sym2->section) {
|
|
return -1;
|
|
} else if (sym1->section > sym2->section) {
|
|
return 1;
|
|
} else {
|
|
if (sym1->offset > sym2->offset) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Sym_compare1(const void *arg1, const void *arg2) {
|
|
MemSymbol *sym1 = (MemSymbol *)arg1;
|
|
MemSymbol *sym2 = (MemSymbol *)arg2;
|
|
|
|
if (sym1->size < sym2->size) {
|
|
return 1;
|
|
} else if (sym1->size > sym2->size) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void DumpSymbols() {
|
|
int i;
|
|
|
|
qsort(Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare);
|
|
|
|
for (i = 0; i < Num_symbols; i++) {
|
|
MemSymbol *sym1 = &Symbols[i];
|
|
MemSymbol *sym2 = &Symbols[i + 1];
|
|
if ((i < Num_symbols - 1) && (sym1->section == sym2->section)) {
|
|
sym1->size = sym2->offset - sym1->offset;
|
|
} else {
|
|
sym1->size = -1;
|
|
}
|
|
}
|
|
|
|
qsort(Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1);
|
|
|
|
FILE *fp = fopen("memory.out", "wt");
|
|
|
|
fprintf(fp, "%-60s %8s %8s\n", "Name", "Size", "Total");
|
|
|
|
int total_size = 0;
|
|
for (i = 0; i < Num_symbols; i++) {
|
|
MemSymbol *sym = &Symbols[i];
|
|
if (sym->size > 0)
|
|
total_size += sym->size;
|
|
fprintf(fp, "%-60s %8d %8d\n", sym->name, sym->size, total_size);
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
mem_free(Symbols);
|
|
Symbols = NULL;
|
|
_asm int 3
|
|
}
|
|
#endif
|
|
|
|
void PE_Debug::DumpSymbolInfo(DumpBuffer &dumpBuffer, DWORD relativeAddress) {
|
|
// Variables to keep track of function symbols
|
|
PIMAGE_SYMBOL currentSym = COFFSymbolTable;
|
|
PIMAGE_SYMBOL fnSymbol = NULL;
|
|
DWORD maxFnAddress = 0;
|
|
|
|
#ifdef DUMPRAM
|
|
InitSymbols();
|
|
#endif
|
|
|
|
// Variables to keep track of file symbols
|
|
PIMAGE_SYMBOL fileSymbol = NULL;
|
|
PIMAGE_SYMBOL latestFileSymbol = NULL;
|
|
for (int i = 0; i < COFFSymbolCount; i++) {
|
|
|
|
// Look for .text section where relativeAddress belongs to.
|
|
// Keep track of the filename the .text section belongs to.
|
|
if (currentSym->StorageClass == IMAGE_SYM_CLASS_FILE) {
|
|
latestFileSymbol = currentSym;
|
|
}
|
|
|
|
// Borland uses "CODE" instead of the standard ".text" entry
|
|
// Microsoft uses sections that only _begin_ with .text
|
|
const char *symName = GetSymbolName(currentSym);
|
|
|
|
if (strnicmp(symName, ".text", 5) == 0 || strcmpi(symName, "CODE") == 0) {
|
|
if (currentSym->Value <= relativeAddress) {
|
|
PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1);
|
|
if (currentSym->Value + auxSym->Section.Length >= relativeAddress) {
|
|
fileSymbol = latestFileSymbol;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Look for the function with biggest address <= relativeAddress
|
|
BOOL isFunction = ISFCN(currentSym->Type); // Type == 0x20, See WINNT.H
|
|
if (isFunction &&
|
|
(currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC)) {
|
|
|
|
if (currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress) {
|
|
maxFnAddress = currentSym->Value;
|
|
fnSymbol = currentSym;
|
|
}
|
|
}
|
|
|
|
#ifdef DUMPRAM
|
|
if (!isFunction && (currentSym->SectionNumber > -1)) {
|
|
if ((symName[0] == '_' && symName[1] != '$') || (symName[0] == '?')) {
|
|
|
|
char pretty_module[1024];
|
|
|
|
if (fileSymbol) {
|
|
const char *auxSym = (const char *)(latestFileSymbol + 1);
|
|
char tmpFile[VA_MAX_FILENAME_LEN];
|
|
strcpy(tmpFile, auxSym);
|
|
strcpy(pretty_module, tmpFile);
|
|
char *p = pretty_module + strlen(pretty_module) - 1;
|
|
// Move p to point to first letter of EXE filename
|
|
while ((*p != '\\') && (*p != '/') && (*p != ':'))
|
|
p--;
|
|
p++;
|
|
if (strlen(p) < 1) {
|
|
strcpy(pretty_module, "<unknown>");
|
|
} else {
|
|
memmove(pretty_module, p, strlen(p) + 1);
|
|
}
|
|
} else {
|
|
strcpy(pretty_module, "");
|
|
}
|
|
|
|
Add_Symbol(currentSym->SectionNumber, currentSym->Value, symName, pretty_module);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Advance counters, skip aux symbols
|
|
i += currentSym->NumberOfAuxSymbols;
|
|
currentSym += currentSym->NumberOfAuxSymbols;
|
|
currentSym++;
|
|
}
|
|
|
|
#ifdef DUMPRAM
|
|
DumpSymbols();
|
|
#endif
|
|
|
|
// dump symbolic info if found
|
|
if (fileSymbol) {
|
|
const char *auxSym = (const char *)(fileSymbol + 1);
|
|
|
|
if (strcmpi(latestFile, auxSym)) {
|
|
strcpy(latestFile, auxSym);
|
|
// JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ;
|
|
}
|
|
} else {
|
|
latestFile[0] = 0;
|
|
// JAS dumpBuffer.Printf( " file: unknown\r\n" ) ;
|
|
}
|
|
|
|
if (fnSymbol) {
|
|
char tmp_name[1024];
|
|
unmangle(tmp_name, GetSymbolName(fnSymbol));
|
|
dumpBuffer.Printf(" %s()", tmp_name);
|
|
} else {
|
|
dumpBuffer.Printf(" <unknown>");
|
|
}
|
|
}
|
|
|
|
PIMAGE_SECTION_HEADER PE_Debug ::SectionHeaderFromName(const char *name) {
|
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(NT_Header);
|
|
for (unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++) {
|
|
if (strnicmp((const char *)section->Name, name, IMAGE_SIZEOF_SHORT_NAME) == 0)
|
|
return (section);
|
|
else
|
|
section++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PIMAGE_COFF_SYMBOLS_HEADER PE_Debug ::GetDebugHeader() {
|
|
// Some files have a wrong entry in the COFF header, so
|
|
// first check if the debug info exists at all
|
|
if (NT_Header->FileHeader.PointerToSymbolTable == 0)
|
|
return (0);
|
|
DWORD debugDirRVA = NT_Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
|
if (debugDirRVA == 0)
|
|
return (0);
|
|
|
|
// The following values must be calculated differently for MS/Borland files
|
|
PIMAGE_DEBUG_DIRECTORY debugDir;
|
|
DWORD size;
|
|
|
|
// Borland files have the debug directory at the beginning of a .debug section
|
|
PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName(".debug");
|
|
if (debugHeader && debugHeader->VirtualAddress == debugDirRVA) {
|
|
debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase);
|
|
size = NT_Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size * sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
} else
|
|
// Microsoft debug directory is in the .rdata section
|
|
{
|
|
debugHeader = SectionHeaderFromName(".rdata");
|
|
if (debugHeader == 0)
|
|
return (0);
|
|
size = NT_Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
|
|
DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress;
|
|
debugDir = BasedPtr(PIMAGE_DEBUG_DIRECTORY, fileBase, debugHeader->PointerToRawData + offsetInto_rdata);
|
|
}
|
|
|
|
// look for COFF debug info
|
|
DWORD debugFormats = size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
for (DWORD i = 0; i < debugFormats; i++) {
|
|
if (debugDir->Type == IMAGE_DEBUG_TYPE_COFF)
|
|
return ((PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData));
|
|
else
|
|
debugDir++;
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
void PE_Debug ::FindDebugInfo() {
|
|
ClearDebugPtrs();
|
|
// Put everything into a try/catch in case the file has wrong fields
|
|
try {
|
|
// Verify that fileBase is a valid pointer to a DOS header
|
|
if (fileBase && fileBase->e_magic == IMAGE_DOS_SIGNATURE) {
|
|
// Get a pointer to the PE header
|
|
NT_Header = BasedPtr(PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew);
|
|
|
|
// Verify that NT_Header is a valid pointer to a NT header
|
|
if (NT_Header && NT_Header->Signature == IMAGE_NT_SIGNATURE) {
|
|
// Get a pointer to the debug header if any
|
|
COFFDebugInfo = GetDebugHeader();
|
|
|
|
// Get a pointer to the symbol table and retrieve the number of symbols
|
|
if (NT_Header->FileHeader.PointerToSymbolTable)
|
|
COFFSymbolTable = BasedPtr(PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable);
|
|
COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols;
|
|
|
|
// The string table starts right after the symbol table
|
|
stringTable = (const char *)(COFFSymbolTable + COFFSymbolCount);
|
|
}
|
|
}
|
|
} catch (...) {
|
|
// Header wrong, do nothing
|
|
}
|
|
}
|
|
|
|
void PE_Debug ::MapFileInMemory(const char *module) {
|
|
ClearFileCache();
|
|
hFile = CreateFile(module, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hFileMapping != 0)
|
|
fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
|
|
}
|
|
// NB: open files/mapping are closed later in ClearFileCache
|
|
}
|
|
|
|
int PE_Debug::DumpDebugInfo(DumpBuffer &dumpBuffer, const BYTE *caller, HINSTANCE hInstance) {
|
|
// Avoid to open, map and looking for debug header/symbol table
|
|
// by caching the latest and comparing the actual module with
|
|
// the latest one.
|
|
static char module[MAX_MODULENAME_LEN];
|
|
GetModuleFileName(hInstance, module, MAX_MODULENAME_LEN);
|
|
|
|
// New module
|
|
if (strcmpi(latestModule, module)) {
|
|
strcpy(latestModule, module);
|
|
// JAS dumpBuffer.Printf( "Module: %s\r\n", module );
|
|
MapFileInMemory(module);
|
|
FindDebugInfo();
|
|
}
|
|
|
|
char pretty_module[1024];
|
|
|
|
strcpy(pretty_module, module);
|
|
char *p = pretty_module + strlen(pretty_module) - 1;
|
|
// Move p to point to first letter of EXE filename
|
|
while ((*p != '\\') && (*p != '/') && (*p != ':'))
|
|
p--;
|
|
p++;
|
|
if (strlen(p) < 1) {
|
|
strcpy(pretty_module, "<unknown>");
|
|
} else {
|
|
memmove(pretty_module, p, strlen(p) + 1);
|
|
}
|
|
|
|
if (fileBase) {
|
|
// Put everything into a try/catch in case the file has wrong fields
|
|
try {
|
|
DWORD relativeAddress = caller - (BYTE *)hInstance;
|
|
// Dump symbolic information and line number if available
|
|
if (COFFSymbolCount != 0 && COFFSymbolTable != NULL) {
|
|
DumpSymbolInfo(dumpBuffer, relativeAddress);
|
|
if (COFFDebugInfo)
|
|
DumpLineNumber(dumpBuffer, relativeAddress);
|
|
return 1;
|
|
} else {
|
|
// dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this
|
|
// module.\r\n" ) ; JAS dumpBuffer.Printf( " no debug information\r\n" ) ;
|
|
dumpBuffer.Printf(" %s %08x()\r\n", pretty_module, caller);
|
|
return 0;
|
|
}
|
|
} catch (...) {
|
|
// Header wrong, do nothing
|
|
return 0;
|
|
}
|
|
} else {
|
|
dumpBuffer.Printf(" %s %08x()\r\n", pretty_module, caller);
|
|
// JAS dumpBuffer.Printf( " module not accessible\r\n" ) ;
|
|
// JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void DumpCallsStack(DumpBuffer &dumpBuffer) {
|
|
const char *separator = "------------------------------------------------------------------\r\n";
|
|
static PE_Debug PE_debug;
|
|
|
|
dumpBuffer.Printf("\r\nCall stack:\r\n");
|
|
dumpBuffer.Printf(separator);
|
|
|
|
// The structure of the stack frames is the following:
|
|
// EBP -> parent stack frame EBP
|
|
// return address for this call ( = caller )
|
|
// The chain can be navigated iteratively, after the
|
|
// initial value of EBP is loaded from the register
|
|
DWORD parentEBP, retval;
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
HINSTANCE hInstance;
|
|
|
|
int depth = 0;
|
|
|
|
__asm MOV parentEBP, EBP
|
|
|
|
do {
|
|
depth++;
|
|
if (depth > 16)
|
|
break;
|
|
|
|
if ((parentEBP & 3) || IsBadReadPtr((DWORD *)parentEBP, sizeof(DWORD))) {
|
|
break;
|
|
}
|
|
parentEBP = *(DWORD *)parentEBP;
|
|
|
|
BYTE **NextCaller = ((BYTE **)parentEBP + 1);
|
|
|
|
if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) {
|
|
break;
|
|
}
|
|
|
|
BYTE *caller = *NextCaller; // Error sometimes!!!
|
|
|
|
// Skip the first EBP as it points to AssertionFailed, which is
|
|
// uninteresting for the user
|
|
|
|
if (depth > 1) {
|
|
|
|
// Get the instance handle of the module where caller belongs to
|
|
retval = VirtualQuery(caller, &mbi, sizeof(mbi));
|
|
|
|
// The instance handle is equal to the allocation base in Win32
|
|
hInstance = (HINSTANCE)mbi.AllocationBase;
|
|
|
|
if ((retval == sizeof(mbi)) && hInstance) {
|
|
if (!PE_debug.DumpDebugInfo(dumpBuffer, caller, hInstance)) {
|
|
// break;
|
|
}
|
|
} else {
|
|
break; // End of the call chain
|
|
}
|
|
}
|
|
}
|
|
while (TRUE)
|
|
;
|
|
|
|
dumpBuffer.Printf(separator);
|
|
PE_debug.ClearReport(); // Prepare for future calls
|
|
}
|
|
|
|
// This ought to be local to VerboseAssert, but it
|
|
// causes problems in Visual C++ (in the CRTL init phase)
|
|
static DumpBuffer dumpBuffer;
|
|
|
|
#endif // SHOW_CALL_STACK
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void dump_text_to_clipboard(char *text);
|
|
|
|
char *Debug_DumpInfo() {
|
|
#ifdef SHOW_CALL_STACK
|
|
dumpBuffer.Clear();
|
|
DumpCallsStack(dumpBuffer);
|
|
dump_text_to_clipboard(dumpBuffer.buffer);
|
|
|
|
dumpBuffer.Printf("\r\n[ This info has been copied to the clipboard for pasting to a bug report. ]\r\n");
|
|
|
|
return dumpBuffer.buffer;
|
|
#else
|
|
return ("");
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DumpTextToClipboard(char *text) {
|
|
char *ptr;
|
|
|
|
// Count number of carriage returns to be added
|
|
char last_char = 0;
|
|
int extra = 0;
|
|
ptr = text;
|
|
while (*ptr) {
|
|
if ((*ptr == '\n') && (last_char != '\r')) {
|
|
extra++;
|
|
}
|
|
last_char = *ptr++;
|
|
}
|
|
|
|
// Length of string with CRs added
|
|
int len = strlen(text) + extra + 1;
|
|
|
|
HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len);
|
|
if (!h_text)
|
|
return;
|
|
ptr = (char *)GlobalLock(h_text);
|
|
if (!ptr)
|
|
return;
|
|
|
|
// copy then, if you find any \n's without \r's, then add in the \r.
|
|
last_char = 0;
|
|
while (*text) {
|
|
if ((*text == '\n') && (last_char != '\r')) {
|
|
*ptr++ = '\r';
|
|
}
|
|
last_char = *text;
|
|
*ptr++ = last_char;
|
|
text++;
|
|
}
|
|
*ptr = 0; // terminate string
|
|
GlobalUnlock(h_text);
|
|
OpenClipboard(NULL);
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_TEXT, h_text);
|
|
CloseClipboard();
|
|
}
|
|
|
|
void dump_text_to_clipboard(char *text) { DumpTextToClipboard(text); }
|
|
|
|
// Translate the exception code into something human readable.
|
|
|
|
static const char *GetExceptionDescription(DWORD ExceptionCode) {
|
|
struct ExceptionNames {
|
|
DWORD ExceptionCode;
|
|
char *ExceptionName;
|
|
};
|
|
|
|
ExceptionNames ExceptionMap[] = {
|
|
{0x40010005, "a Control-C"},
|
|
{0x40010008, "a Control-Break"},
|
|
{0x80000002, "a Datatype Misalignment"},
|
|
{0x80000003, "a Breakpoint"},
|
|
{0xc0000005, "an Access Violation"},
|
|
{0xc0000006, "an In Page Error"},
|
|
{0xc0000017, "a No Memory"},
|
|
{0xc000001d, "an Illegal Instruction"},
|
|
{0xc0000025, "a Noncontinuable Exception"},
|
|
{0xc0000026, "an Invalid Disposition"},
|
|
{0xc000008c, "a Array Bounds Exceeded"},
|
|
{0xc000008d, "a Float Denormal Operand"},
|
|
{0xc000008e, "a Float Divide by Zero"},
|
|
{0xc000008f, "a Float Inexact Result"},
|
|
{0xc0000090, "a Float Invalid Operation"},
|
|
{0xc0000091, "a Float Overflow"},
|
|
{0xc0000092, "a Float Stack Check"},
|
|
{0xc0000093, "a Float Underflow"},
|
|
{0xc0000094, "an Integer Divide by Zero"},
|
|
{0xc0000095, "an Integer Overflow"},
|
|
{0xc0000096, "a Privileged Instruction"},
|
|
{0xc00000fD, "a Stack Overflow"},
|
|
{0xc0000142, "a DLL Initialization Failed"},
|
|
{0xe06d7363, "a Microsoft C++ Exception"},
|
|
};
|
|
|
|
for (int i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++)
|
|
if (ExceptionCode == ExceptionMap[i].ExceptionCode)
|
|
return ExceptionMap[i].ExceptionName;
|
|
|
|
return "Unknown exception type";
|
|
}
|
|
|
|
static void PrintFileTime(char *sztime, FILETIME ftime) {
|
|
SYSTEMTIME systime, fixtime;
|
|
*sztime = NULL;
|
|
|
|
if (FileTimeToSystemTime(&ftime, &fixtime)) {
|
|
if (SystemTimeToTzSpecificLocalTime(NULL, &fixtime, &systime)) {
|
|
wsprintf(sztime, "%d/%d/%d %02d:%02d:%02d", systime.wMonth, systime.wDay, systime.wYear, systime.wHour,
|
|
systime.wMinute, systime.wSecond);
|
|
} else {
|
|
wsprintf(sztime, "Unknown Time");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print information about a code module (DLL or EXE) such as its size,
|
|
// location, time stamp, etc.
|
|
|
|
static void ShowModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle) {
|
|
char FmtString[2000];
|
|
unsigned long bytesout;
|
|
char ModName[MAX_PATH];
|
|
__try {
|
|
if (GetModuleFileName(ModuleHandle, ModName, sizeof(ModName)) > 0) {
|
|
// If GetModuleFileName returns greater than zero then this must
|
|
// be a valid code module address. Therefore we can try to walk
|
|
// our way through its structures to find the link time stamp.
|
|
IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER *)ModuleHandle;
|
|
if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
|
|
return;
|
|
IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS *)((char *)DosHeader + DosHeader->e_lfanew);
|
|
if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
|
|
return;
|
|
// Open the code module file so that we can get its file date
|
|
// and size.
|
|
HANDLE ModuleFile =
|
|
CreateFile(ModName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
char TimeBuffer[100] = "";
|
|
DWORD FileSize = 0;
|
|
if (ModuleFile != INVALID_HANDLE_VALUE) {
|
|
FileSize = GetFileSize(ModuleFile, 0);
|
|
FILETIME LastWriteTime;
|
|
if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime)) {
|
|
PrintFileTime(TimeBuffer + lstrlen(TimeBuffer), LastWriteTime);
|
|
}
|
|
CloseHandle(ModuleFile);
|
|
}
|
|
|
|
wsprintf(FmtString, "%s,\taddr: 0x%08x\tsize:%d\t[%s]\r\n", ModName, ModuleHandle, FileSize,
|
|
TimeBuffer); // NTHeader->FileHeader.TimeDateStamp,
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
}
|
|
}
|
|
// Handle any exceptions by continuing from this point.
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
|
|
// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
|
|
// to find all the blocks of address space that were reserved or committed,
|
|
// and ShowModuleInfo will display module information if they are code
|
|
// modules.
|
|
|
|
static void RecordModuleList(HANDLE LogFile) {
|
|
char FmtString[2000];
|
|
unsigned long bytesout;
|
|
wsprintf(FmtString, "\r\n"
|
|
"Modules:\r\n");
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
SYSTEM_INFO SystemInfo;
|
|
GetSystemInfo(&SystemInfo);
|
|
const size_t PageSize = SystemInfo.dwPageSize;
|
|
// Set NumPages to the number of pages in the 4GByte address space,
|
|
// while being careful to avoid overflowing ints.
|
|
const size_t NumPages = 4 * size_t((1024 * 1024 * 1024) / PageSize);
|
|
size_t pageNum = 0;
|
|
void *LastAllocationBase = 0;
|
|
while (pageNum < NumPages) {
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo))) {
|
|
if (MemInfo.RegionSize > 0) {
|
|
// Adjust the page number to skip over this block of memory.
|
|
pageNum += MemInfo.RegionSize / PageSize;
|
|
if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase) {
|
|
// Look for new blocks of committed memory, and try
|
|
// recording their module names - this will fail
|
|
// gracefully if they aren't code modules.
|
|
LastAllocationBase = MemInfo.AllocationBase;
|
|
ShowModuleInfo(LogFile, (HINSTANCE)LastAllocationBase);
|
|
}
|
|
} else
|
|
pageNum += (64 * 1024) / PageSize;
|
|
} else
|
|
pageNum += (64 * 1024) / PageSize;
|
|
}
|
|
}
|
|
|
|
// Record information about the user's system, such as processor type, amount
|
|
// of memory, etc.
|
|
|
|
static void RecordSystemInformation(HANDLE LogFile) {
|
|
char FmtString[2000];
|
|
unsigned long bytesout;
|
|
FILETIME CurrentTime;
|
|
GetSystemTimeAsFileTime(&CurrentTime);
|
|
char TimeBuffer[100];
|
|
|
|
PrintFileTime(TimeBuffer, CurrentTime);
|
|
|
|
wsprintf(FmtString, "Error occurred at %s.\r\n", TimeBuffer);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
char ModuleName[MAX_PATH];
|
|
if (GetModuleFileName(0, ModuleName, sizeof(ModuleName)) <= 0)
|
|
lstrcpy(ModuleName, "Unknown");
|
|
|
|
wsprintf(FmtString, "Module: %s.\r\n", ModuleName);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
|
|
MEMORYSTATUS ms;
|
|
ms.dwLength = sizeof(ms);
|
|
GlobalMemoryStatus(&ms);
|
|
// Print out the amount of physical memory, rounded up.
|
|
wsprintf(FmtString, "System Memory Status:\r\n");
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
wsprintf(FmtString, "Percent of memory in use: %d\r\n", ms.dwMemoryLoad);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
wsprintf(FmtString, "Bytes of physical memory : %d\r\n", ms.dwTotalPhys);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
wsprintf(FmtString, "Free physical memory bytes : %d\r\n", ms.dwAvailPhys);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
wsprintf(FmtString, "Available virtual memory : %d\r\n\r\n", ms.dwAvailPageFile);
|
|
WriteFile(LogFile, FmtString, lstrlen(FmtString), &bytesout, 0);
|
|
}
|
|
/*
|
|
extern bool Debug_break;
|
|
extern char *Debug_DumpInfo();
|
|
*/
|
|
#define FORMAT_REG \
|
|
"Registers:\r\nEAX=%08x CS=%04x EIP=%08x EFLGS=%08x\r\nEBX=%08x SS=%04x ESP=%08x EBP=%08x\r\nECX=%08x DS=%04x " \
|
|
"ESI=%08x FS=%04x\r\nEDX=%08x ES=%04x EDI=%08x GS=%04x\r\n"
|
|
#define FORMAT_SEP "\r\n--------------------------------------------------------------------------------\r\n"
|
|
#define FORMATCRLF "\r\n"
|
|
|
|
const int NumCodeBytes = 16; // Number of code bytes to record.
|
|
const int MaxStackDump = 2048; // Maximum number of DWORDS in stack dumps.
|
|
const int StackColumns = 8; // Number of columns in stack dump.
|
|
|
|
#if (defined(RELEASE) && (!defined(DEMO)) && (!defined(GAMEGAUGE)))
|
|
#include "buildno.h"
|
|
#endif
|
|
|
|
extern int no_debug_dialog;
|
|
|
|
int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS data, const char *Message) {
|
|
static int BeenHere;
|
|
if (BeenHere) // Going recursive! That must mean this routine crashed!
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
BeenHere = true;
|
|
|
|
PEXCEPTION_RECORD Exception = data->ExceptionRecord;
|
|
PCONTEXT Context = data->ContextRecord;
|
|
const char *desc = GetExceptionDescription(Exception->ExceptionCode);
|
|
|
|
// An Int3()
|
|
if (0x80000003 == Exception->ExceptionCode) {
|
|
BeenHere = false;
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
char topmsg[500];
|
|
char tmpmsg[1000];
|
|
char bottommsg[1000] = "";
|
|
char callstack[1000] = "";
|
|
|
|
wsprintf(topmsg, "Excecution in %s was stopped by %s", Message, desc);
|
|
|
|
wsprintf(bottommsg, FORMAT_REG, Context->Eax, Context->SegCs, Context->Eip, Context->EFlags, Context->Ebx,
|
|
Context->SegSs, Context->Esp, Context->Ebp, Context->Ecx, Context->SegDs, Context->Esi, Context->SegFs,
|
|
Context->Edx, Context->SegEs, Context->Edi, Context->SegGs);
|
|
|
|
HANDLE LogFile =
|
|
CreateFile("error.log", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
|
|
if (LogFile != INVALID_HANDLE_VALUE) {
|
|
|
|
char *p = Debug_DumpInfo();
|
|
lstrcpy(callstack, p);
|
|
|
|
unsigned long NumBytes;
|
|
SetFilePointer(LogFile, 0, 0, FILE_END);
|
|
|
|
WriteFile(LogFile, topmsg, lstrlen(topmsg), &NumBytes, 0);
|
|
WriteFile(LogFile, FORMATCRLF, lstrlen(FORMATCRLF), &NumBytes, 0);
|
|
|
|
char Username[100];
|
|
unsigned long unamelen = 99;
|
|
char Machinename[200];
|
|
unsigned long cnamelen = 199;
|
|
GetUserName(Username, &unamelen);
|
|
GetComputerName(Machinename, &cnamelen);
|
|
wsprintf(callstack, "Username: %s\r\nMachineName: %s\r\n", Username, Machinename);
|
|
WriteFile(LogFile, callstack, lstrlen(callstack), &NumBytes, 0);
|
|
|
|
#if (defined(RELEASE) && (!defined(DEMO)) && (!defined(GAMEGAUGE)))
|
|
wsprintf(callstack, "Descent 3 Release build %d\r\n", D3_RELEASE_BUILD_NO);
|
|
WriteFile(LogFile, callstack, lstrlen(callstack), &NumBytes, 0);
|
|
#endif
|
|
RecordSystemInformation(LogFile);
|
|
RecordModuleList(LogFile);
|
|
|
|
WriteFile(LogFile, bottommsg, lstrlen(bottommsg), &NumBytes, 0);
|
|
WriteFile(LogFile, FORMAT_SEP, lstrlen(FORMAT_SEP), &NumBytes, 0);
|
|
|
|
DWORD *pStack = (DWORD *)Context->Esp;
|
|
DWORD *pStackTop;
|
|
__asm {
|
|
mov eax, fs:[4]
|
|
mov pStackTop, eax
|
|
}
|
|
if (pStackTop > pStack + MaxStackDump) pStackTop = pStack + MaxStackDump;
|
|
int Count = 0;
|
|
|
|
char buffer[1000] = "";
|
|
const int safetyzone = 50;
|
|
char *nearend = buffer + sizeof(buffer) - safetyzone;
|
|
char *output = buffer;
|
|
while (pStack + 1 <= pStackTop) {
|
|
if ((Count % StackColumns) == 0)
|
|
output += wsprintf(output, "%08x: ", pStack);
|
|
char *Suffix = " ";
|
|
if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
|
|
Suffix = "\r\n";
|
|
output += wsprintf(output, "%08x%s", *pStack, Suffix);
|
|
pStack++;
|
|
// Check for when the buffer is almost full, and flush it to disk.
|
|
if (output > nearend) {
|
|
wsprintf(tmpmsg, "%s", buffer);
|
|
WriteFile(LogFile, tmpmsg, lstrlen(tmpmsg), &NumBytes, 0);
|
|
buffer[0] = 0;
|
|
output = buffer;
|
|
}
|
|
}
|
|
CloseHandle(LogFile);
|
|
}
|
|
|
|
if ((!Debug_break) && (!no_debug_dialog))
|
|
Debug_ErrorBox(OSMBOX_OK, "Error", topmsg, bottommsg);
|
|
|
|
if (no_debug_dialog)
|
|
exit(0);
|
|
BeenHere = false;
|
|
if (Debug_break)
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
else
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|