/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* $Archive:: /Commando/Code/wwlib/Except.cpp $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 2/07/02 12:28p $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* *
* *
* *
* *
* *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* *
* Exception_Proc -- Windows dialog callback for the exception dialog *
* Exception_Dialog -- Brings up the exception options dialog. *
* Add_Txt -- Add the given text to the machine state dump buffer. *
* Dump_Exception_Info -- Dump machine state information into a buffer *
* Exception_Handler -- Exception handler filter function *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef _MSC_VER
#include "always.h"
#include
#include "assert.h"
#include "cpudetect.h"
#include "except.h"
//#include "debug.h"
#include "mpu.h"
//#include "commando\nat.h"
#include "thread.h"
#include "wwdebug.h"
#include "wwmemlog.h"
#include
#include
#include
#include
#ifdef WWDEBUG
#define DebugString WWDebug_Printf
#else
void DebugString(char const *, ...){};
#endif //WWDEBUG
/*
** Enable this define to get the 'demo timed out' message on a crash or assert failure.
*/
//#define DEMO_TIME_OUT
/*
** Buffer to dump machine state information to. We don't want to allocate this at run-time
** in case the exception was caused by a malfunction in the memory system.
*/
static char ExceptionText [65536];
bool SymbolsAvailable = false;
HINSTANCE ImageHelp = (HINSTANCE) -1;
void (*AppCallback)(void) = NULL;
char *(*AppVersionCallback)(void) = NULL;
/*
** Flag to indicate we should exit when an exception occurs.
*/
bool ExitOnException = false;
bool TryingToExit = false;
/*
** Register dump variables. These are used to allow the game to restart from an arbitrary
** position after an exception occurs.
*/
unsigned long ExceptionReturnStack = 0;
unsigned long ExceptionReturnAddress = 0;
unsigned long ExceptionReturnFrame = 0;
/*
** Number of times the exception handler has recursed. Recursions are bad.
*/
int ExceptionRecursions = -1;
/*
** List of threads that the exception handler knows about.
*/
DynamicVectorClass ThreadList;
/*
** Definitions to allow run-time linking to the Imagehlp.dll functions.
**
*/
typedef BOOL (WINAPI *SymCleanupType) (HANDLE hProcess);
typedef BOOL (WINAPI *SymGetSymFromAddrType) (HANDLE hProcess, DWORD Address, LPDWORD Displacement, PIMAGEHLP_SYMBOL Symbol);
typedef BOOL (WINAPI *SymInitializeType) (HANDLE hProcess, LPSTR UserSearchPath, BOOL fInvadeProcess);
typedef BOOL (WINAPI *SymLoadModuleType) (HANDLE hProcess, HANDLE hFile, LPSTR ImageName, LPSTR ModuleName, DWORD BaseOfDll, DWORD SizeOfDll);
typedef DWORD (WINAPI *SymSetOptionsType) (DWORD SymOptions);
typedef BOOL (WINAPI *SymUnloadModuleType) (HANDLE hProcess, DWORD BaseOfDll);
typedef BOOL (WINAPI *StackWalkType) (DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, LPVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);
typedef LPVOID (WINAPI *SymFunctionTableAccessType) (HANDLE hProcess, DWORD AddrBase);
typedef DWORD (WINAPI *SymGetModuleBaseType) (HANDLE hProcess, DWORD dwAddr);
static SymCleanupType _SymCleanup = NULL;
static SymGetSymFromAddrType _SymGetSymFromAddr = NULL;
static SymInitializeType _SymInitialize = NULL;
static SymLoadModuleType _SymLoadModule = NULL;
static SymSetOptionsType _SymSetOptions = NULL;
static SymUnloadModuleType _SymUnloadModule = NULL;
static StackWalkType _StackWalk = NULL;
static SymFunctionTableAccessType _SymFunctionTableAccess = NULL;
static SymGetModuleBaseType _SymGetModuleBase = NULL;
static char const *ImagehelpFunctionNames[] =
{
"SymCleanup",
"SymGetSymFromAddr",
"SymInitialize",
"SymLoadModule",
"SymSetOptions",
"SymUnloadModule",
"StackWalk",
"SymFunctionTableAccess",
"SymGetModuleBaseType",
NULL
};
/***********************************************************************************************
* _purecall -- This function overrides the C library Pure Virtual Function Call error *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: 0 = no error *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/22/00 11:42AM ST : Created *
*=============================================================================================*/
int __cdecl _purecall(void)
{
int return_code = 0;
#ifdef WWDEBUG
/*
** Use int3 to cause an exception.
*/
WWDEBUG_SAY(("Pure Virtual Function call. Oh No!\n"));
_asm int 0x03;
#endif //_DEBUG_ASSERT
return(return_code);
}
/***********************************************************************************************
* Last_Error_Text -- Get the system error text for GetLastError *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Ptr to error string *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/14/98 11:11AM ST : Created *
*=============================================================================================*/
char const * Last_Error_Text(void)
{
static char message_buffer[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &message_buffer[0], 256, NULL);
return (message_buffer);
}
/***********************************************************************************************
* Add_Txt -- Add the given text to the machine state dump buffer. *
* *
* *
* *
* INPUT: Text *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 7/22/97 12:21PM ST : Created *
*=============================================================================================*/
static void Add_Txt (char const *txt)
{
if (strlen(ExceptionText) + strlen(txt) < 65535) {
strcat(ExceptionText, txt);
}
#if (0)
/*
** Log to debug output too.
*/
static char _debug_output_txt[512];
const char *in = txt;
char *out = _debug_output_txt;
bool done = false;
if (strlen(txt) < sizeof(_debug_output_txt)) {
for (int i=0 ; iContextRecord;
/*
** The following are set for access violation only
*/
int access_read_write=-1;
unsigned long access_address = 0;
if (e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
DebugString("Exception Handler: Exception is access violation\n");
access_read_write = e_info->ExceptionRecord->ExceptionInformation[0]; // 0=read, 1=write
access_address = e_info->ExceptionRecord->ExceptionInformation[1];
} else {
DebugString ("Exception Handler: Exception code is %d\n", e_info->ExceptionRecord->ExceptionCode);
}
/*
** Match the exception type with the error string and print it out
*/
for (int i=0 ; _codes[i] != 0xffffffff ; i++) {
if (_codes[i] == e_info->ExceptionRecord->ExceptionCode) {
DebugString("Exception Handler: Found exception description\n");
break;
}
}
Add_Txt(_code_txt[i]);
Add_Txt("\r\n");
/*
** For access violations, print out the violation address and if it was read or write.
*/
if (e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
sprintf(scrap, "Access address:%08X ", access_address);
Add_Txt(scrap);
if (access_read_write) {
Add_Txt("was written to.\r\n");
} else {
Add_Txt("was read from.\r\n");
}
}
/*
** If symbols are available, print out the exception eip address and the name of the
** function it represents.
*/
memset(symptr, 0, sizeof (IMAGEHLP_SYMBOL));
symptr->SizeOfStruct = sizeof (IMAGEHLP_SYMBOL);
symptr->MaxNameLength = 256-sizeof (IMAGEHLP_SYMBOL);
symptr->Size = 0;
symptr->Address = context->Eip;
if (!IsBadCodePtr((FARPROC)context->Eip)) {
if (_SymGetSymFromAddr != NULL && _SymGetSymFromAddr (GetCurrentProcess(), context->Eip, &displacement, symptr)) {
sprintf (scrap, "Exception occurred at %08X - %s + %08X\r\n", context->Eip, symptr->Name, displacement);
} else {
DebugString ("Exception Handler: Failed to get symbol for EIP\r\n");
if (_SymGetSymFromAddr != NULL) {
DebugString ("Exception Handler: SymGetSymFromAddr failed with code %d - %s\n", GetLastError(), Last_Error_Text());
}
sprintf (scrap, "Exception occurred at %08X\r\n", context->Eip);
}
} else {
DebugString ("Exception Handler: context->Eip is bad code pointer\n");
}
Add_Txt (scrap);
/*
** Try to walk the stack. It might work....
*/
DebugString("Stack walk...\n");
Add_Txt("\r\n Stack walk...\r\n");
unsigned long return_addresses[256];
int num_addresses = Stack_Walk(return_addresses, 256, context);
if (num_addresses) {
for (int s=0 ; sSizeOfStruct = sizeof(symbol);
symptr->MaxNameLength = 128;
symptr->Size = 0;
symptr->Address = temp_addr;
if (_SymGetSymFromAddr != NULL && _SymGetSymFromAddr (GetCurrentProcess(), temp_addr, &displacement, symptr)) {
char symbuf[256];
sprintf(symbuf, "%s + %08X\r\n", symptr->Name, displacement);
Add_Txt(symbuf);
}
} else {
char symbuf[256];
sprintf(symbuf, "%08x\r\n", temp_addr);
Add_Txt(symbuf);
}
}
Add_Txt("\r\n\r\n");
} else {
DebugString("Stack walk failed!\n");
Add_Txt("Stack walk failed!\r\n");
}
#if (0) //Don't have this info yet for Renegade.
/*
** Add in the version info.
*/
sprintf(scrap, "\r\nVersion %s\r\n", Version_Name());
Add_Txt(scrap);
sprintf(scrap, "Internal Version %s\r\n", VerNum.Version_Name());
Add_Txt(scrap);
char buildinfo[128];
buildinfo[0] = 0;
Build_Date_String(buildinfo, 128);
char build_number[128];
char build_name[128];
#endif //(0)
if (AppVersionCallback) {
sprintf(scrap, "%s\r\n\r\n", AppVersionCallback());
Add_Txt(scrap);
}
/*
** Thread list.
*/
Add_Txt("Thread list\r\n");
/*
** Get the thread info from ThreadClass.
*/
for (int thread = 0 ; thread < ThreadList.Count() ; thread++) {
sprintf(scrap, " ID: %08X - %s", ThreadList[thread]->ThreadID, ThreadList[thread]->ThreadName);
Add_Txt(scrap);
if (GetCurrentThreadId() == ThreadList[thread]->ThreadID) {
Add_Txt(" ***CURRENT THREAD***");
}
Add_Txt("\r\n");
}
/*
** CPU type
*/
sprintf(scrap, "\r\nCPU %s, %d Mhz, Vendor: %s\r\n", (char*)CPUDetectClass::Get_Processor_String(), Get_RDTSC_CPU_Speed(), (char*)CPUDetectClass::Get_Processor_Manufacturer_Name());
Add_Txt(scrap);
Add_Txt("\r\nDetails:\r\n");
DebugString("Register dump...\n");
/*
** Dump the registers.
*/
sprintf(scrap, "Eip:%08X\tEsp:%08X\tEbp:%08X\r\n", context->Eip, context->Esp, context->Ebp);
Add_Txt(scrap);
sprintf(scrap, "Eax:%08X\tEbx:%08X\tEcx:%08X\r\n", context->Eax, context->Ebx, context->Ecx);
Add_Txt(scrap);
sprintf(scrap, "Edx:%08X\tEsi:%08X\tEdi:%08X\r\n", context->Edx, context->Esi, context->Edi);
Add_Txt(scrap);
sprintf(scrap, "EFlags:%08X \r\n", context->EFlags);
Add_Txt(scrap);
sprintf(scrap, "CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x\r\n", context->SegCs, context->SegSs, context->SegDs, context->SegEs, context->SegFs, context->SegGs);
Add_Txt(scrap);
/*
** Now the FP registers.
*/
Add_Txt("\r\nFloating point status\r\n");
sprintf(scrap, " Control word: %08x\r\n", context->FloatSave.ControlWord);
Add_Txt(scrap);
sprintf(scrap, " Status word: %08x\r\n", context->FloatSave.StatusWord);
Add_Txt(scrap);
sprintf(scrap, " Tag word: %08x\r\n", context->FloatSave.TagWord);
Add_Txt(scrap);
sprintf(scrap, " Error Offset: %08x\r\n", context->FloatSave.ErrorOffset);
Add_Txt(scrap);
sprintf(scrap, " Error Selector: %08x\r\n", context->FloatSave.ErrorSelector);
Add_Txt(scrap);
sprintf(scrap, " Data Offset: %08x\r\n", context->FloatSave.DataOffset);
Add_Txt(scrap);
sprintf(scrap, " Data Selector: %08x\r\n", context->FloatSave.DataSelector);
Add_Txt(scrap);
sprintf(scrap, " Cr0NpxState: %08x\r\n", context->FloatSave.Cr0NpxState);
Add_Txt(scrap);
for (int fp=0 ; fpFloatSave.RegisterArea[(fp*10) + b]);
Add_Txt(scrap);
}
void *fp_data_ptr = (void*)(&context->FloatSave.RegisterArea[fp*10]);
double fp_value;
/*
** Convert FP dump from temporary real value (10 bytes) to double (8 bytes).
*/
_asm {
push eax
mov eax,fp_data_ptr
fld tbyte ptr [eax]
fstp qword ptr [fp_value]
pop eax
}
sprintf(scrap, " %+#.17e\r\n", fp_value);
Add_Txt(scrap);
}
/*
** Dump the bytes at EIP. This will make it easier to match the crash address with later versions of the game.
*/
DebugString("EIP bytes dump...\n");
sprintf(scrap, "\r\nBytes at CS:EIP (%08X) : ", context->Eip);
unsigned char *eip_ptr = (unsigned char *) (context->Eip);
char bytestr[32];
for (int c = 0 ; c < 32 ; c++) {
if (IsBadReadPtr(eip_ptr, 1)) {
strcat(scrap, "?? ");
} else {
sprintf(bytestr, "%02X ", *eip_ptr);
strcat(scrap, bytestr);
}
eip_ptr++;
}
strcat(scrap, "\r\n\r\n");
Add_Txt(scrap);
/*
** Dump out the values on the stack.
*/
DebugString("Stack dump...\n");
Add_Txt("Stack dump (* indicates possible code address) :\r\n");
unsigned long *stackptr = (unsigned long*) context->Esp;
for (int j=0 ; j<2048 ; j++) {
if (IsBadReadPtr(stackptr, 4)) {
/*
** The stack contents cannot be read so just print up question marks.
*/
sprintf(scrap, "%08X: ", stackptr);
strcat(scrap, "????????\r\n");
} else {
/*
** If this stack address is in our memory space then try to match it with a code symbol.
*/
if (IsBadCodePtr((FARPROC)*stackptr)) {
sprintf(scrap, "%08X: %08X ", stackptr, *stackptr);
strcat(scrap, "DATA_PTR\r\n");
} else {
sprintf(scrap, "%08X: %08X", stackptr, *stackptr);
if (symbols_available) {
symptr->SizeOfStruct = sizeof(symbol);
symptr->MaxNameLength = 128;
symptr->Size = 0;
symptr->Address = *stackptr;
if (_SymGetSymFromAddr != NULL && _SymGetSymFromAddr (GetCurrentProcess(), *stackptr, &displacement, symptr)) {
char symbuf[256];
sprintf(symbuf, " - %s + %08X", symptr->Name, displacement);
strcat(scrap, symbuf);
}
} else {
strcat (scrap, " *");
}
strcat (scrap, "\r\n");
}
}
Add_Txt(scrap);
stackptr++;
}
/*
** Unload the symbols.
*/
if (symbols_available) {
if (_SymCleanup != NULL) {
_SymCleanup (GetCurrentProcess());
}
if (symload) {
if (_SymUnloadModule != NULL) {
_SymUnloadModule(GetCurrentProcess(), NULL);
}
}
}
if (imagehelp) {
FreeLibrary(imagehelp);
}
Add_Txt ("\r\n\r\n");
}
/***********************************************************************************************
* Exception_Handler -- Exception handler filter function *
* *
* *
* *
* INPUT: exception code *
* pointer to exception information pointers *
* *
* OUTPUT: EXCEPTION_EXECUTE_HANDLER -- Excecute the body of the __except construct *
* or EXCEPTION_CONTINUE_SEARCH -- Pass this exception down to the debugger *
* or EXCEPTION_CONTINUE_EXECUTION -- Continue to execute at the fault address *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 7/22/97 12:29PM ST : Created *
*=============================================================================================*/
int Exception_Handler(int exception_code, EXCEPTION_POINTERS *e_info)
{
DebugString("Exception!\n");
#ifdef DEMO_TIME_OUT
if ( !WindowedMode ) {
Load_Title_Page("TITLE.PCX", true);
MouseCursor->Release_Mouse();
MessageBox(MainWindow, "This demo has timed out. Thank you for playing Red Alert 2.","Byeee!", MB_ICONEXCLAMATION|MB_OK);
return (EXCEPTION_EXECUTE_HANDLER);
}
#endif //DEMO_TIME_OUT
/*
** If we were trying to quit and we got another exception then just shut down the whole shooting match right here.
*/
if (TryingToExit) {
ExitProcess(0);
}
/*
** Track recursions because we need to know if something here is failing.
*/
ExceptionRecursions++;
if (ExceptionRecursions > 1) {
return (EXCEPTION_CONTINUE_SEARCH);
}
/*
** If there was a breakpoint then chances are it was set by a debugger. In _DEBUG mode
** we probably should ignore breakpoints. Breakpoints become more significant in release
** mode since there probably isn't a debugger present.
*/
#ifdef _DEBUG
if (exception_code == EXCEPTION_BREAKPOINT) {
return (EXCEPTION_CONTINUE_SEARCH);
}
#else
exception_code = exception_code;
#endif //_DEBUG
#ifdef WWDEBUG
//CONTEXT *context;
#endif WWDEBUG
if (ExceptionRecursions == 0) {
/*
** Create a dump of the exception info.
*/
Dump_Exception_Info(e_info);
/*
** Log the machine state to disk
*/
HANDLE debug_file;
DWORD actual;
debug_file = CreateFile("_except.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (debug_file != INVALID_HANDLE_VALUE){
WriteFile(debug_file, ExceptionText, strlen(ExceptionText), &actual, NULL);
CloseHandle (debug_file);
#if (0)
#ifdef _DEBUG_PRINT
#ifndef _DEBUG
/*
** Copy the exception debug file to the network. No point in doing this for the debug version
** since symbols are not normally available.
*/
DebugString ("About to copy debug file\n");
char filename[512];
if (Get_Global_Output_File_Name ("EXCEPT", filename, 512)) {
DebugString ("Copying DEBUG.TXT to %s\n", filename);
int result = CopyFile("debug.txt", filename, false);
if (result == 0) {
DebugString ("CopyFile failed with error code %d - %s\n", GetLastError(), Last_Error_Text());
}
}
DebugString ("Debug file copied\n");
#endif //_DEBUG
#endif //_DEBUG_PRINT
#endif //(0)
}
}
/*
** Call the apps callback function.
*/
if (AppCallback) {
AppCallback();
}
/*
** If an exit is required then turn of memory leak reporting (there will be lots of them) and use
** EXCEPTION_EXECUTE_HANDLER to let us fall out of winmain.
*/
if (ExitOnException) {
#ifdef _DEBUG
_CrtSetDbgFlag(0);
#endif //_DEBUG
TryingToExit = true;
unsigned long id = Get_Main_Thread_ID();
if (id != GetCurrentThreadId()) {
DebugString("Exiting due to exception in sub thread\n");
ExitProcess(EXIT_SUCCESS);
}
return(EXCEPTION_EXECUTE_HANDLER);
}
return (EXCEPTION_CONTINUE_SEARCH);
}
/***********************************************************************************************
* Register_Thread_ID -- Let the exception handler know about a thread *
* *
* *
* *
* INPUT: Thread ID *
* Thread name *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/30/2001 3:04PM ST : Created *
*=============================================================================================*/
void Register_Thread_ID(unsigned long thread_id, char *thread_name, bool main_thread)
{
WWMEMLOG(MEM_GAMEDATA);
if (thread_name) {
/*
** See if we already know about this thread. Maybe just the thread_id changed.
*/
for (int i=0 ; iThreadName) == 0) {
ThreadList[i]->ThreadID = thread_id;
return;
}
}
ThreadInfoType *thread = new ThreadInfoType;
thread->ThreadID = thread_id;
strcpy(thread->ThreadName, thread_name);
thread->Main = main_thread;
thread->ThreadHandle = INVALID_HANDLE_VALUE;
ThreadList.Add(thread);
}
}
#if (0)
/***********************************************************************************************
* Register_Thread_Handle -- Keep a copy of the thread handle that matches this thread ID *
* *
* *
* *
* INPUT: Thread ID *
* Thread handle *
* *
* OUTPUT: True if thread ID was matched *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/6/2002 9:40PM ST : Created *
*=============================================================================================*/
bool Register_Thread_Handle(unsigned long thread_id, HANDLE thread_handle)
{
for (int i=0 ; iThreadID == thread_id) {
ThreadList[i]->ThreadHandle = thread_handle;
return(true);
break;
}
}
return(false);
}
/***********************************************************************************************
* Get_Num_Threads -- Get the number of threads being tracked. *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Number of threads we know about *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/6/2002 9:43PM ST : Created *
*=============================================================================================*/
int Get_Num_Threads(void)
{
return(ThreadList.Count());
}
/***********************************************************************************************
* Get_Thread_Handle -- Get the handle for the given thread index *
* *
* *
* *
* INPUT: Thread index *
* *
* OUTPUT: Thread handle *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/6/2002 9:46PM ST : Created *
*=============================================================================================*/
HANDLE Get_Thread_Handle(int thread_index)
{
if (thread_index < ThreadList.Count()) {
return(ThreadList[thread_index]->ThreadHandle);
}
}
#endif //(0)
/***********************************************************************************************
* Unregister_Thread_ID -- Remove a thread entry from the thread list *
* *
* *
* *
* INPUT: Thread ID *
* Thread name *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 8/30/2001 3:10PM ST : Created *
*=============================================================================================*/
void Unregister_Thread_ID(unsigned long thread_id, char *thread_name)
{
for (int i=0 ; iThreadName) == 0) {
assert(ThreadList[i]->ThreadID == thread_id);
delete ThreadList[i];
ThreadList.Delete(i);
return;
}
}
}
/***********************************************************************************************
* Get_Main_Thread_ID -- Get the ID of the processes main thread *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Thread ID *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 12/6/2001 12:20PM ST : Created *
*=============================================================================================*/
unsigned long Get_Main_Thread_ID(void)
{
for (int i=0 ; iMain) {
return(ThreadList[i]->ThreadID);
}
}
return(0);
}
/***********************************************************************************************
* Load_Image_Helper -- Load imagehlp.dll and retrieve the programs symbols *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/12/2001 4:27PM ST : Created *
*=============================================================================================*/
void Load_Image_Helper(void)
{
/*
** If this is the first time through then fix up the imagehelp function pointers since imagehlp.dll
** can't be statically linked.
*/
if (ImageHelp == (HINSTANCE)-1) {
ImageHelp = LoadLibrary("IMAGEHLP.DLL");
if (ImageHelp != NULL) {
char const *function_name = NULL;
unsigned long *fptr = (unsigned long *) &_SymCleanup;
int count = 0;
do {
function_name = ImagehelpFunctionNames[count];
if (function_name) {
*fptr = (unsigned long) GetProcAddress(ImageHelp, function_name);
fptr++;
count++;
}
}
while (function_name);
}
/*
** Retrieve the programs symbols if they are available. This can be a .pdb or a .dbg file.
*/
if (_SymSetOptions != NULL) {
_SymSetOptions(SYMOPT_DEFERRED_LOADS);
}
int symload = 0;
if (_SymInitialize != NULL && _SymInitialize(GetCurrentProcess(), NULL, FALSE)) {
if (_SymSetOptions != NULL) {
_SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
}
char exe_name[_MAX_PATH];
GetModuleFileName(NULL, exe_name, sizeof(exe_name));
if (_SymLoadModule != NULL) {
symload = _SymLoadModule(GetCurrentProcess(), NULL, exe_name, NULL, 0, 0);
}
if (symload) {
SymbolsAvailable = true;
} else {
//assert (_SymLoadModule != NULL);
//DebugString ("SymLoad failed for module %s with code %d - %s\n", szModuleName, GetLastError(), Last_Error_Text());
}
}
}
}
/***********************************************************************************************
* Lookup_Symbol -- Get the symbol for a given code address *
* *
* *
* *
* INPUT: Address of code to get symbol for *
* Ptr to buffer to return symbol in *
* Reference to int to return displacement *
* *
* OUTPUT: True if symbol found *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/12/2001 4:47PM ST : Created *
*=============================================================================================*/
bool Lookup_Symbol(void *code_ptr, char *symbol, int &displacement)
{
/*
** Locals.
*/
char symbol_struct_buf[1024];
IMAGEHLP_SYMBOL *symbol_struct_ptr = (IMAGEHLP_SYMBOL *)symbol_struct_buf;
/*
** Set default values in case of early exit.
*/
displacement = 0;
*symbol = '\0';
/*
** Make sure symbols are available.
*/
if (!SymbolsAvailable || _SymGetSymFromAddr == NULL) {
return(false);
}
/*
** If it's a bad code pointer then there is no point in trying to match it with a symbol.
*/
if (IsBadCodePtr((FARPROC)code_ptr)) {
strcpy(symbol, "Bad code pointer");
return(false);
}
/*
** Set up the parameters for the call to SymGetSymFromAddr
*/
memset (symbol_struct_ptr, 0, sizeof (symbol_struct_buf));
symbol_struct_ptr->SizeOfStruct = sizeof (symbol_struct_buf);
symbol_struct_ptr->MaxNameLength = sizeof(symbol_struct_buf)-sizeof (IMAGEHLP_SYMBOL);
symbol_struct_ptr->Size = 0;
symbol_struct_ptr->Address = (unsigned long)code_ptr;
/*
** See if we have the symbol for that address.
*/
if (_SymGetSymFromAddr(GetCurrentProcess(), (unsigned long)code_ptr, (unsigned long *)&displacement, symbol_struct_ptr)) {
/*
** Copy it back into the buffer provided.
*/
strcpy(symbol, symbol_struct_ptr->Name);
return(true);
}
return(false);
}
/***********************************************************************************************
* Stack_Walk -- Walk the stack and get the last n return addresses *
* *
* *
* *
* INPUT: Ptr to return address list *
* Number of return addresses to fetch *
* Ptr to optional context. NULL means use current *
* *
* OUTPUT: Number of return addresses found *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/12/2001 11:57AM ST : Created *
*=============================================================================================*/
int Stack_Walk(unsigned long *return_addresses, int num_addresses, CONTEXT *context)
{
static HINSTANCE _imagehelp = (HINSTANCE) -1;
/*
** If this is the first time through then fix up the imagehelp function pointers since imagehlp.dll
** can't be statically linked.
*/
if (ImageHelp == (HINSTANCE)-1) {
Load_Image_Helper();
}
/*
** If there is no debug support .dll available then we can't walk the stack.
*/
if (ImageHelp == NULL) {
return(0);
}
/*
** Set up the stack frame structure for the start point of the stack walk (i.e. here).
*/
STACKFRAME stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
unsigned long reg_eip, reg_ebp, reg_esp;
__asm {
here:
lea eax,here
mov reg_eip,eax
mov reg_ebp,ebp
mov reg_esp,esp
}
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = reg_eip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = reg_esp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = reg_ebp;
/*
** Use the context struct if it was provided.
*/
if (context) {
stack_frame.AddrPC.Offset = context->Eip;
stack_frame.AddrStack.Offset = context->Esp;
stack_frame.AddrFrame.Offset = context->Ebp;
}
int pointer_index = 0;
/*
** Walk the stack by the requested number of return address iterations.
*/
for (int i = 0; i < num_addresses + 1; i++) {
if (_StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &stack_frame, NULL, NULL, _SymFunctionTableAccess, _SymGetModuleBase, NULL)) {
/*
** First result will always be the return address we were called from.
*/
if (i==0 && context == NULL) {
continue;
}
unsigned long return_address = stack_frame.AddrReturn.Offset;
return_addresses[pointer_index++] = return_address;
} else {
break;
}
}
return(pointer_index);
}
void Register_Application_Exception_Callback(void (*app_callback)(void))
{
AppCallback = app_callback;
}
void Register_Application_Version_Callback(char *(*app_ver_callback)(void))
{
AppVersionCallback = app_ver_callback;
}
void Set_Exit_On_Exception(bool set)
{
ExitOnException = true;
}
bool Is_Trying_To_Exit(void)
{
return(TryingToExit);
}
#endif //_MSC_VER