| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- /*
- ** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
- */
- /////////////////////////////////////////////////////////////////////////EA-V1
- // $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_except.cpp $
- // $Author: mhoffe $
- // $Revision: #1 $
- // $DateTime: 2003/07/03 11:55:26 $
- //
- // ©2003 Electronic Arts
- //
- // Unhandled exception handler
- //////////////////////////////////////////////////////////////////////////////
- #include "_pch.h"
- #include <commctrl.h>
- #pragma comment (lib,"comctl32")
- DebugExceptionhandler::DebugExceptionhandler(void)
- {
- // don't do anything here!
- }
- const char *DebugExceptionhandler::GetExceptionType(struct _EXCEPTION_POINTERS *exptr, char *explanation)
- {
- #define EX(code,text) \
- case EXCEPTION_##code: strcpy(explanation,text); return "EXCEPTION_" #code;
- switch(exptr->ExceptionRecord->ExceptionCode)
- {
- case EXCEPTION_ACCESS_VIOLATION:
- wsprintf(explanation,
- "The thread tried to read from or write to a virtual\n"
- "address for which it does not have the appropriate access.\n"
- "Access address 0x%08x was %s.",
- exptr->ExceptionRecord->ExceptionInformation[1],
- exptr->ExceptionRecord->ExceptionInformation[0]?"written to":"read from");
- return "EXCEPTION_ACCESS_VIOLATION";
- EX(ARRAY_BOUNDS_EXCEEDED,"The thread tried to access an array element that\n"
- "is out of bounds and the underlying hardware\n"
- "supports bounds checking.")
- EX(BREAKPOINT,"A breakpoint was encountered.")
- EX(DATATYPE_MISALIGNMENT,"The thread tried to read or write data that is\n"
- "misaligned on hardware that does not provide alignment.\n"
- "For example, 16-bit values must be aligned on\n"
- "2-byte boundaries; 32-bit values on 4-byte\n"
- "boundaries, and so on.")
- EX(FLT_DENORMAL_OPERAND,"One of the operands in a floating-point operation is\n"
- "denormal. A denormal value is one that is too small\n"
- "to represent as a standard floating-point value.")
- EX(FLT_DIVIDE_BY_ZERO,"The thread tried to divide a floating-point\n"
- "value by a floating-point divisor of zero.")
- EX(FLT_INEXACT_RESULT,"The result of a floating-point operation\n"
- "cannot be represented exactly as a decimal fraction.")
- EX(FLT_INVALID_OPERATION,"Some strange unknown floating point operation was attempted.")
- EX(FLT_OVERFLOW,"The exponent of a floating-point operation is greater\n"
- "than the magnitude allowed by the corresponding type.")
- EX(FLT_STACK_CHECK,"The stack overflowed or underflowed as the result\n"
- "of a floating-point operation.")
- EX(FLT_UNDERFLOW,"The exponent of a floating-point operation is less\n"
- "than the magnitude allowed by the corresponding type.")
- EX(GUARD_PAGE,"A guard page was accessed.")
- EX(ILLEGAL_INSTRUCTION,"The thread tried to execute an invalid instruction.")
- EX(IN_PAGE_ERROR,"The thread tried to access a page that was not\n"
- "present, and the system was unable to load the page.\n"
- "For example, this exception might occur if a network "
- "connection is lost while running a program over the network.")
- EX(INT_DIVIDE_BY_ZERO,"The thread tried to divide an integer value by\n"
- "an integer divisor of zero.")
- EX(INT_OVERFLOW,"The result of an integer operation caused a carry\n"
- "out of the most significant bit of the result.")
- EX(INVALID_DISPOSITION,"An exception handler returned an invalid disposition\n"
- "to the exception dispatcher. Programmers using a\n"
- "high-level language such as C should never encounter\n"
- "this exception.")
- EX(INVALID_HANDLE,"An invalid Windows handle was used.")
- EX(NONCONTINUABLE_EXCEPTION,"The thread tried to continue execution after\n"
- "a noncontinuable exception occurred.")
- EX(PRIV_INSTRUCTION,"The thread tried to execute an instruction whose\n"
- "operation is not allowed in the current machine mode.")
- EX(SINGLE_STEP,"A trace trap or other single-instruction mechanism\n"
- "signaled that one instruction has been executed.")
- EX(STACK_OVERFLOW,"The thread used up its stack.")
- case 0xE06D7363: strcpy(explanation,"Microsoft C++ Exception"); return "EXCEPTION_MS";
- default:
- wsprintf(explanation,"Unknown exception code 0x%08x",exptr->ExceptionRecord->ExceptionCode);
- return "EXCEPTION_UNKNOWN";
- }
- #undef EX
- }
- void DebugExceptionhandler::LogExceptionLocation(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
- {
- struct _CONTEXT &ctx=*exptr->ContextRecord;
- char buf[512];
- DebugStackwalk::Signature::GetSymbol(ctx.Eip,buf,sizeof(buf));
- dbg << "Exception occured at\n" << buf << ".";
- }
- void DebugExceptionhandler::LogRegisters(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
- {
- struct _CONTEXT &ctx=*exptr->ContextRecord;
- dbg << Debug::FillChar('0')
- << Debug::Hex()
- << "EAX:" << Debug::Width(8) << ctx.Eax
- << " EBX:" << Debug::Width(8) << ctx.Ebx
- << " ECX:" << Debug::Width(8) << ctx.Ecx << "\n"
- << "EDX:" << Debug::Width(8) << ctx.Edx
- << " ESI:" << Debug::Width(8) << ctx.Esi
- << " EDI:" << Debug::Width(8) << ctx.Edi << "\n"
- << "EIP:" << Debug::Width(8) << ctx.Eip
- << " ESP:" << Debug::Width(8) << ctx.Esp
- << " EBP:" << Debug::Width(8) << ctx.Ebp << "\n"
- << "Flags:" << Debug::Bin() << Debug::Width(32) << ctx.EFlags << Debug::Hex() << "\n"
- << "CS:" << Debug::Width(4) << ctx.SegCs
- << " DS:" << Debug::Width(4) << ctx.SegDs
- << " SS:" << Debug::Width(4) << ctx.SegSs
- << "\nES:" << Debug::Width(4) << ctx.SegEs
- << " FS:" << Debug::Width(4) << ctx.SegFs
- << " GS:" << Debug::Width(4) << ctx.SegGs << "\n" << Debug::FillChar() << Debug::Dec();
- }
- void DebugExceptionhandler::LogFPURegisters(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
- {
- struct _CONTEXT &ctx=*exptr->ContextRecord;
- if (!(ctx.ContextFlags&CONTEXT_FLOATING_POINT))
- {
- dbg << "FP registers not available\n";
- return;
- }
- FLOATING_SAVE_AREA &flt=ctx.FloatSave;
- dbg << Debug::Bin() << Debug::FillChar('0')
- << "CW:" << Debug::Width(16) << (flt.ControlWord&0xffff) << "\n"
- << "SW:" << Debug::Width(16) << (flt.StatusWord&0xffff) << "\n"
- << "TW:" << Debug::Width(16) << (flt.TagWord&0xffff) << "\n"
- << Debug::Hex()
- << "ErrOfs: " << Debug::Width(8) << flt.ErrorOffset
- << " ErrSel: " << Debug::Width(8) << flt.ErrorSelector << "\n"
- << "DataOfs: " << Debug::Width(8) << flt.DataOffset
- << " DataSel: " << Debug::Width(8) << flt.DataSelector << "\n"
- << "Cr0NpxState: " << Debug::Width(8) << flt.Cr0NpxState << "\n";
- for (unsigned k=0;k<SIZE_OF_80387_REGISTERS/10;++k)
- {
- dbg << Debug::Dec() << "ST(" << k << ") ";
- dbg.SetPrefixAndRadix("",16);
- BYTE *value=flt.RegisterArea+k*10;
- for (unsigned i=0;i<10;i++)
- dbg << Debug::Width(2) << value[i];
- double fpVal;
- // convert from temporary real (10 byte) to double
- _asm
- {
- mov eax,value
- fld tbyte ptr [eax]
- fstp qword ptr [fpVal]
- }
- dbg << " " << fpVal << "\n";
- }
- dbg << Debug::FillChar() << Debug::Dec();
- }
- // include exception dialog box
- #include "rc_exception.inl"
- // stupid dialog box function needs this
- static struct _EXCEPTION_POINTERS *exPtrs;
- // and this saves us from re-generating register/version info again...
- static char regInfo[1024],verInfo[256];
- // and this saves us from doing a stack walk twice
- static DebugStackwalk::Signature sig;
- static BOOL CALLBACK ExceptionDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- switch(uMsg)
- {
- case WM_INITDIALOG:
- break;
- case WM_COMMAND:
- if (LOWORD(wParam)==IDOK)
- EndDialog(hWnd,IDOK);
- default:
- return FALSE;
- }
- // init dialog box
- // version
- SendDlgItemMessage(hWnd,103,WM_SETTEXT,0,(LPARAM)verInfo);
- // registers
- char *p=regInfo;
- for (char *q=p;;q++)
- {
- if (!*q||*q=='\n')
- {
- bool quit=!*q; *q=0;
- SendDlgItemMessage(hWnd,105,LB_ADDSTRING,0,(LPARAM)p);
- if (quit)
- break;
- p=q+1;
- }
- }
- // yes, this generates a GDI leak but we're crashing anyway
- SendDlgItemMessage(hWnd,105,WM_SETFONT,(WPARAM)CreateFont(13,0,0,0,FW_NORMAL,
- FALSE,FALSE,FALSE,ANSI_CHARSET,
- OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
- DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,NULL),MAKELPARAM(TRUE,0));
- // exception type
- SendDlgItemMessage(hWnd,100,WM_SETTEXT,0,(LPARAM)
- DebugExceptionhandler::GetExceptionType(exPtrs,regInfo));
- SendDlgItemMessage(hWnd,101,WM_SETTEXT,0,(LPARAM)regInfo);
- // address
- struct _CONTEXT &ctx=*exPtrs->ContextRecord;
- DebugStackwalk::Signature::GetSymbol(ctx.Eip,regInfo,sizeof(regInfo));
- SendDlgItemMessage(hWnd,102,WM_SETTEXT,0,(LPARAM)regInfo);
- // stack
- // (this code is a little messy because we're dealing with a raw list control)
- HWND list;
- list=GetDlgItem(hWnd,104);
- if (!sig.Size())
- {
- LVCOLUMN c;
- c.mask=LVCF_TEXT|LVCF_WIDTH;
- c.pszText="";
- c.cx=690;
- ListView_InsertColumn(list,0,&c);
- LVITEM item;
- item.iItem=0;
- item.iSubItem=0;
- item.mask=LVIF_TEXT;
- item.pszText="No stack data available - check for dbghelp.dll";
- item.iItem=ListView_InsertItem(list,&item);
- }
- else
- {
- // add columns first
- LVCOLUMN c;
- c.mask=LVCF_TEXT|LVCF_WIDTH;
- c.pszText="";
- c.cx=0; // first column is empty (can't right-align 1st column)
- ListView_InsertColumn(list,0,&c);
- c.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_FMT;
- c.pszText="Address";
- c.cx=60;
- c.fmt=LVCFMT_RIGHT;
- ListView_InsertColumn(list,1,&c);
- c.mask=LVCF_TEXT|LVCF_WIDTH;
- c.pszText="Module";
- c.cx=120;
- ListView_InsertColumn(list,2,&c);
- c.pszText="Symbol";
- c.cx=300;
- ListView_InsertColumn(list,3,&c);
- c.pszText="File";
- c.cx=130;
- ListView_InsertColumn(list,4,&c);
- c.pszText="Line";
- c.cx=80;
- ListView_InsertColumn(list,5,&c);
- // now add stack walk lines
- for (unsigned k=0;k<sig.Size();k++)
- {
- DebugStackwalk::Signature::GetSymbol(sig.GetAddress(k),regInfo,sizeof(regInfo));
-
- LVITEM item;
- item.iItem=k;
- item.iSubItem=0;
- item.mask=0;
- item.iItem=ListView_InsertItem(list,&item);
- item.mask=LVIF_TEXT;
- item.iSubItem++;
- item.pszText=strtok(regInfo," ");
- ListView_SetItem(list,&item);
- item.iSubItem++;
- item.pszText=strtok(NULL,",");
- ListView_SetItem(list,&item);
- item.iSubItem++;
- item.pszText=strtok(NULL,",");
- ListView_SetItem(list,&item);
- item.iSubItem++;
- item.pszText=strtok(NULL,":");
- ListView_SetItem(list,&item);
-
- item.iSubItem++;
- item.pszText=strtok(NULL,"");
- ListView_SetItem(list,&item);
- }
- }
- return TRUE;
- }
- #include <stdio.h>
- LONG __stdcall DebugExceptionhandler::ExceptionFilter(struct _EXCEPTION_POINTERS* pExPtrs)
- {
- // we should not be calling ourselves!
- static bool inExceptionFilter;
- if (inExceptionFilter)
- {
- MessageBox(NULL,"Exception in exception handler","Fatal error",MB_OK);
- return EXCEPTION_CONTINUE_SEARCH;
- }
- inExceptionFilter=true;
- if (pExPtrs->ExceptionRecord->ExceptionCode==EXCEPTION_STACK_OVERFLOW)
- {
- // almost everything we are about to do will generate a second
- // stack overflow... double fault... so give at least a little warning
- OutputDebugString("EA/DEBUG: EXCEPTION_STACK_OVERFLOW\n");
- }
- // Let's log some info
- Debug &dbg=Debug::Instance;
- // we're logging an exception
- ++dbg.disableAssertsEtc;
- if (dbg.curType!=DebugIOInterface::StringType::MAX)
- dbg.FlushOutput();
- dbg.StartOutput(DebugIOInterface::StringType::Exception,"");
- // start off with the exception type & location
- dbg << "\n" << Debug::RepeatChar('=',80) << "\n";
- dbg << GetExceptionType(pExPtrs,regInfo) << ":\n" << regInfo << "\n\n";
- LogExceptionLocation(dbg,pExPtrs); dbg << "\n\n";
- // build info must be saved off for dialog...
- unsigned curOfs=dbg.ioBuffer[DebugIOInterface::Exception].used;
- dbg.WriteBuildInfo();
- unsigned len=dbg.ioBuffer[DebugIOInterface::Exception].used-curOfs;
- if (len>=sizeof(verInfo))
- len=sizeof(verInfo)-1;
- memcpy(verInfo,dbg.ioBuffer[DebugIOInterface::Exception].buffer+curOfs,len);
- verInfo[len]=0;
- dbg << "\n\n";
- // save off register info as well...
- curOfs=dbg.ioBuffer[DebugIOInterface::Exception].used;
- LogRegisters(dbg,pExPtrs); dbg << "\n";
- LogFPURegisters(dbg,pExPtrs); dbg << "\n";
- len=dbg.ioBuffer[DebugIOInterface::Exception].used-curOfs;
- if (len>=sizeof(regInfo))
- len=sizeof(regInfo)-1;
- memcpy(regInfo,dbg.ioBuffer[DebugIOInterface::Exception].buffer+curOfs,len);
- regInfo[len]=0;
- // now finally add stack & EIP dump
- dbg.m_stackWalk.StackWalk(sig,pExPtrs->ContextRecord);
- dbg << sig << "\n";
- dbg << "Bytes around EIP:" << Debug::MemDump::Char(((char *)(pExPtrs->ContextRecord->Eip))-32,80);
- dbg.FlushOutput();
- // shut down real Debug module now
- // (atexit code never gets called in exception case)
- Debug::StaticExit();
- // Show a dialog box
- InitCommonControls();
- exPtrs=pExPtrs;
- DialogBoxIndirect(NULL,(LPDLGTEMPLATE)rcException,NULL,ExceptionDlgProc);
- // Now die
- return EXCEPTION_EXECUTE_HANDLER;
- }
|