Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
debug_except.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_except.cpp $
21// $Author: mhoffe $
22// $Revision: #1 $
23// $DateTime: 2003/07/03 11:55:26 $
24//
25// ©2003 Electronic Arts
26//
27// Unhandled exception handler
29#include "_pch.h"
30#include <commctrl.h>
31
32#pragma comment (lib,"comctl32")
33
34DebugExceptionhandler::DebugExceptionhandler(void)
35{
36 // don't do anything here!
37}
38
39const char *DebugExceptionhandler::GetExceptionType(struct _EXCEPTION_POINTERS *exptr, char *explanation)
40{
41 #define EX(code,text) \
42 case EXCEPTION_##code: strcpy(explanation,text); return "EXCEPTION_" #code;
43
44 switch(exptr->ExceptionRecord->ExceptionCode)
45 {
46 case EXCEPTION_ACCESS_VIOLATION:
47 wsprintf(explanation,
48 "The thread tried to read from or write to a virtual\n"
49 "address for which it does not have the appropriate access.\n"
50 "Access address 0x%08x was %s.",
51 exptr->ExceptionRecord->ExceptionInformation[1],
52 exptr->ExceptionRecord->ExceptionInformation[0]?"written to":"read from");
53 return "EXCEPTION_ACCESS_VIOLATION";
54 EX(ARRAY_BOUNDS_EXCEEDED,"The thread tried to access an array element that\n"
55 "is out of bounds and the underlying hardware\n"
56 "supports bounds checking.")
57 EX(BREAKPOINT,"A breakpoint was encountered.")
58 EX(DATATYPE_MISALIGNMENT,"The thread tried to read or write data that is\n"
59 "misaligned on hardware that does not provide alignment.\n"
60 "For example, 16-bit values must be aligned on\n"
61 "2-byte boundaries; 32-bit values on 4-byte\n"
62 "boundaries, and so on.")
63 EX(FLT_DENORMAL_OPERAND,"One of the operands in a floating-point operation is\n"
64 "denormal. A denormal value is one that is too small\n"
65 "to represent as a standard floating-point value.")
66 EX(FLT_DIVIDE_BY_ZERO,"The thread tried to divide a floating-point\n"
67 "value by a floating-point divisor of zero.")
68 EX(FLT_INEXACT_RESULT,"The result of a floating-point operation\n"
69 "cannot be represented exactly as a decimal fraction.")
70 EX(FLT_INVALID_OPERATION,"Some strange unknown floating point operation was attempted.")
71 EX(FLT_OVERFLOW,"The exponent of a floating-point operation is greater\n"
72 "than the magnitude allowed by the corresponding type.")
73 EX(FLT_STACK_CHECK,"The stack overflowed or underflowed as the result\n"
74 "of a floating-point operation.")
75 EX(FLT_UNDERFLOW,"The exponent of a floating-point operation is less\n"
76 "than the magnitude allowed by the corresponding type.")
77 EX(GUARD_PAGE,"A guard page was accessed.")
78 EX(ILLEGAL_INSTRUCTION,"The thread tried to execute an invalid instruction.")
79 EX(IN_PAGE_ERROR,"The thread tried to access a page that was not\n"
80 "present, and the system was unable to load the page.\n"
81 "For example, this exception might occur if a network "
82 "connection is lost while running a program over the network.")
83 EX(INT_DIVIDE_BY_ZERO,"The thread tried to divide an integer value by\n"
84 "an integer divisor of zero.")
85 EX(INT_OVERFLOW,"The result of an integer operation caused a carry\n"
86 "out of the most significant bit of the result.")
87 EX(INVALID_DISPOSITION,"An exception handler returned an invalid disposition\n"
88 "to the exception dispatcher. Programmers using a\n"
89 "high-level language such as C should never encounter\n"
90 "this exception.")
91 EX(INVALID_HANDLE,"An invalid Windows handle was used.")
92 EX(NONCONTINUABLE_EXCEPTION,"The thread tried to continue execution after\n"
93 "a noncontinuable exception occurred.")
94 EX(PRIV_INSTRUCTION,"The thread tried to execute an instruction whose\n"
95 "operation is not allowed in the current machine mode.")
96 EX(SINGLE_STEP,"A trace trap or other single-instruction mechanism\n"
97 "signaled that one instruction has been executed.")
98 EX(STACK_OVERFLOW,"The thread used up its stack.")
99 case 0xE06D7363: strcpy(explanation,"Microsoft C++ Exception"); return "EXCEPTION_MS";
100 default:
101 wsprintf(explanation,"Unknown exception code 0x%08x",exptr->ExceptionRecord->ExceptionCode);
102 return "EXCEPTION_UNKNOWN";
103 }
104
105 #undef EX
106}
107
108void DebugExceptionhandler::LogExceptionLocation(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
109{
110 struct _CONTEXT &ctx=*exptr->ContextRecord;
111
112 char buf[512];
113 DebugStackwalk::Signature::GetSymbol(ctx.Eip,buf,sizeof(buf));
114 dbg << "Exception occured at\n" << buf << ".";
115}
116
117void DebugExceptionhandler::LogRegisters(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
118{
119 struct _CONTEXT &ctx=*exptr->ContextRecord;
120
121 dbg << Debug::FillChar('0')
122 << Debug::Hex()
123 << "EAX:" << Debug::Width(8) << ctx.Eax
124 << " EBX:" << Debug::Width(8) << ctx.Ebx
125 << " ECX:" << Debug::Width(8) << ctx.Ecx << "\n"
126 << "EDX:" << Debug::Width(8) << ctx.Edx
127 << " ESI:" << Debug::Width(8) << ctx.Esi
128 << " EDI:" << Debug::Width(8) << ctx.Edi << "\n"
129 << "EIP:" << Debug::Width(8) << ctx.Eip
130 << " ESP:" << Debug::Width(8) << ctx.Esp
131 << " EBP:" << Debug::Width(8) << ctx.Ebp << "\n"
132 << "Flags:" << Debug::Bin() << Debug::Width(32) << ctx.EFlags << Debug::Hex() << "\n"
133 << "CS:" << Debug::Width(4) << ctx.SegCs
134 << " DS:" << Debug::Width(4) << ctx.SegDs
135 << " SS:" << Debug::Width(4) << ctx.SegSs
136 << "\nES:" << Debug::Width(4) << ctx.SegEs
137 << " FS:" << Debug::Width(4) << ctx.SegFs
138 << " GS:" << Debug::Width(4) << ctx.SegGs << "\n" << Debug::FillChar() << Debug::Dec();
139}
140
141void DebugExceptionhandler::LogFPURegisters(Debug &dbg, struct _EXCEPTION_POINTERS *exptr)
142{
143 struct _CONTEXT &ctx=*exptr->ContextRecord;
144
145 if (!(ctx.ContextFlags&CONTEXT_FLOATING_POINT))
146 {
147 dbg << "FP registers not available\n";
148 return;
149 }
150
151 FLOATING_SAVE_AREA &flt=ctx.FloatSave;
152 dbg << Debug::Bin() << Debug::FillChar('0')
153 << "CW:" << Debug::Width(16) << (flt.ControlWord&0xffff) << "\n"
154 << "SW:" << Debug::Width(16) << (flt.StatusWord&0xffff) << "\n"
155 << "TW:" << Debug::Width(16) << (flt.TagWord&0xffff) << "\n"
156 << Debug::Hex()
157 << "ErrOfs: " << Debug::Width(8) << flt.ErrorOffset
158 << " ErrSel: " << Debug::Width(8) << flt.ErrorSelector << "\n"
159 << "DataOfs: " << Debug::Width(8) << flt.DataOffset
160 << " DataSel: " << Debug::Width(8) << flt.DataSelector << "\n"
161 << "Cr0NpxState: " << Debug::Width(8) << flt.Cr0NpxState << "\n";
162
163 for (unsigned k=0;k<SIZE_OF_80387_REGISTERS/10;++k)
164 {
165 dbg << Debug::Dec() << "ST(" << k << ") ";
166 dbg.SetPrefixAndRadix("",16);
167
168 BYTE *value=flt.RegisterArea+k*10;
169 for (unsigned i=0;i<10;i++)
170 dbg << Debug::Width(2) << value[i];
171
172 double fpVal;
173
174 // convert from temporary real (10 byte) to double
175 _asm
176 {
177 mov eax,value
178 fld tbyte ptr [eax]
179 fstp qword ptr [fpVal]
180 }
181
182 dbg << " " << fpVal << "\n";
183 }
184 dbg << Debug::FillChar() << Debug::Dec();
185}
186
187// include exception dialog box
188#include "rc_exception.inl"
189
190// stupid dialog box function needs this
191static struct _EXCEPTION_POINTERS *exPtrs;
192
193// and this saves us from re-generating register/version info again...
194static char regInfo[1024],verInfo[256];
195
196// and this saves us from doing a stack walk twice
198
199static BOOL CALLBACK ExceptionDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
200{
201 switch(uMsg)
202 {
203 case WM_INITDIALOG:
204 break;
205 case WM_COMMAND:
206 if (LOWORD(wParam)==IDOK)
207 EndDialog(hWnd,IDOK);
208 default:
209 return FALSE;
210 }
211
212 // init dialog box
213
214 // version
215 SendDlgItemMessage(hWnd,103,WM_SETTEXT,0,(LPARAM)verInfo);
216
217 // registers
218 char *p=regInfo;
219 for (char *q=p;;q++)
220 {
221 if (!*q||*q=='\n')
222 {
223 bool quit=!*q; *q=0;
224 SendDlgItemMessage(hWnd,105,LB_ADDSTRING,0,(LPARAM)p);
225 if (quit)
226 break;
227 p=q+1;
228 }
229 }
230
231 // yes, this generates a GDI leak but we're crashing anyway
232 SendDlgItemMessage(hWnd,105,WM_SETFONT,(WPARAM)CreateFont(13,0,0,0,FW_NORMAL,
233 FALSE,FALSE,FALSE,ANSI_CHARSET,
234 OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
235 DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,NULL),MAKELPARAM(TRUE,0));
236
237 // exception type
238 SendDlgItemMessage(hWnd,100,WM_SETTEXT,0,(LPARAM)
240 SendDlgItemMessage(hWnd,101,WM_SETTEXT,0,(LPARAM)regInfo);
241
242 // address
243 struct _CONTEXT &ctx=*exPtrs->ContextRecord;
244 DebugStackwalk::Signature::GetSymbol(ctx.Eip,regInfo,sizeof(regInfo));
245 SendDlgItemMessage(hWnd,102,WM_SETTEXT,0,(LPARAM)regInfo);
246
247 // stack
248 // (this code is a little messy because we're dealing with a raw list control)
249 HWND list;
250 list=GetDlgItem(hWnd,104);
251 if (!sig.Size())
252 {
253 LVCOLUMN c;
254 c.mask=LVCF_TEXT|LVCF_WIDTH;
255 c.pszText="";
256 c.cx=690;
257 ListView_InsertColumn(list,0,&c);
258
259 LVITEM item;
260 item.iItem=0;
261 item.iSubItem=0;
262 item.mask=LVIF_TEXT;
263 item.pszText="No stack data available - check for dbghelp.dll";
264
265 item.iItem=ListView_InsertItem(list,&item);
266 }
267 else
268 {
269 // add columns first
270 LVCOLUMN c;
271 c.mask=LVCF_TEXT|LVCF_WIDTH;
272 c.pszText="";
273 c.cx=0; // first column is empty (can't right-align 1st column)
274 ListView_InsertColumn(list,0,&c);
275
276 c.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_FMT;
277 c.pszText="Address";
278 c.cx=60;
279 c.fmt=LVCFMT_RIGHT;
280 ListView_InsertColumn(list,1,&c);
281
282 c.mask=LVCF_TEXT|LVCF_WIDTH;
283 c.pszText="Module";
284 c.cx=120;
285 ListView_InsertColumn(list,2,&c);
286
287 c.pszText="Symbol";
288 c.cx=300;
289 ListView_InsertColumn(list,3,&c);
290
291 c.pszText="File";
292 c.cx=130;
293 ListView_InsertColumn(list,4,&c);
294
295 c.pszText="Line";
296 c.cx=80;
297 ListView_InsertColumn(list,5,&c);
298
299 // now add stack walk lines
300 for (unsigned k=0;k<sig.Size();k++)
301 {
302 DebugStackwalk::Signature::GetSymbol(sig.GetAddress(k),regInfo,sizeof(regInfo));
303
304 LVITEM item;
305 item.iItem=k;
306 item.iSubItem=0;
307 item.mask=0;
308 item.iItem=ListView_InsertItem(list,&item);
309 item.mask=LVIF_TEXT;
310
311 item.iSubItem++;
312 item.pszText=strtok(regInfo," ");
313 ListView_SetItem(list,&item);
314
315 item.iSubItem++;
316 item.pszText=strtok(NULL,",");
317 ListView_SetItem(list,&item);
318
319 item.iSubItem++;
320 item.pszText=strtok(NULL,",");
321 ListView_SetItem(list,&item);
322
323 item.iSubItem++;
324 item.pszText=strtok(NULL,":");
325 ListView_SetItem(list,&item);
326
327 item.iSubItem++;
328 item.pszText=strtok(NULL,"");
329 ListView_SetItem(list,&item);
330 }
331 }
332
333 return TRUE;
334}
335
336#include <stdio.h>
337
338LONG __stdcall DebugExceptionhandler::ExceptionFilter(struct _EXCEPTION_POINTERS* pExPtrs)
339{
340 // we should not be calling ourselves!
341 static bool inExceptionFilter;
342 if (inExceptionFilter)
343 {
344 MessageBox(NULL,"Exception in exception handler","Fatal error",MB_OK);
345 return EXCEPTION_CONTINUE_SEARCH;
346 }
347 inExceptionFilter=true;
348
349 if (pExPtrs->ExceptionRecord->ExceptionCode==EXCEPTION_STACK_OVERFLOW)
350 {
351 // almost everything we are about to do will generate a second
352 // stack overflow... double fault... so give at least a little warning
353 OutputDebugString("EA/DEBUG: EXCEPTION_STACK_OVERFLOW\n");
354 }
355
356 // Let's log some info
357 Debug &dbg=Debug::Instance;
358
359 // we're logging an exception
360 ++dbg.disableAssertsEtc;
361 if (dbg.curType!=DebugIOInterface::StringType::MAX)
362 dbg.FlushOutput();
363 dbg.StartOutput(DebugIOInterface::StringType::Exception,"");
364
365 // start off with the exception type & location
366 dbg << "\n" << Debug::RepeatChar('=',80) << "\n";
367 dbg << GetExceptionType(pExPtrs,regInfo) << ":\n" << regInfo << "\n\n";
368 LogExceptionLocation(dbg,pExPtrs); dbg << "\n\n";
369
370 // build info must be saved off for dialog...
371 unsigned curOfs=dbg.ioBuffer[DebugIOInterface::Exception].used;
372 dbg.WriteBuildInfo();
373 unsigned len=dbg.ioBuffer[DebugIOInterface::Exception].used-curOfs;
374 if (len>=sizeof(verInfo))
375 len=sizeof(verInfo)-1;
376 memcpy(verInfo,dbg.ioBuffer[DebugIOInterface::Exception].buffer+curOfs,len);
377 verInfo[len]=0;
378 dbg << "\n\n";
379
380 // save off register info as well...
381 curOfs=dbg.ioBuffer[DebugIOInterface::Exception].used;
382 LogRegisters(dbg,pExPtrs); dbg << "\n";
383 LogFPURegisters(dbg,pExPtrs); dbg << "\n";
384 len=dbg.ioBuffer[DebugIOInterface::Exception].used-curOfs;
385 if (len>=sizeof(regInfo))
386 len=sizeof(regInfo)-1;
387 memcpy(regInfo,dbg.ioBuffer[DebugIOInterface::Exception].buffer+curOfs,len);
388 regInfo[len]=0;
389
390 // now finally add stack & EIP dump
391 dbg.m_stackWalk.StackWalk(sig,pExPtrs->ContextRecord);
392 dbg << sig << "\n";
393
394 dbg << "Bytes around EIP:" << Debug::MemDump::Char(((char *)(pExPtrs->ContextRecord->Eip))-32,80);
395
396 dbg.FlushOutput();
397
398 // shut down real Debug module now
399 // (atexit code never gets called in exception case)
400 Debug::StaticExit();
401
402 // Show a dialog box
403 InitCommonControls();
404 exPtrs=pExPtrs;
405 DialogBoxIndirect(NULL,(LPDLGTEMPLATE)rcException,NULL,ExceptionDlgProc);
406
407 // Now die
408 return EXCEPTION_EXECUTE_HANDLER;
409}
#define NULL
Definition BaseType.h:92
#define TRUE
Definition BaseType.h:109
#define FALSE
Definition BaseType.h:113
void const char * value
unsigned int UINT
Definition bittype.h:63
unsigned char BYTE
Definition bittype.h:59
#define BOOL
Definition Wnd_File.h:57
static MemDump Char(const void *startPtr, unsigned numItems, unsigned bytePerItem=1)
Repeats a given character N times.
static const char * GetExceptionType(struct _EXCEPTION_POINTERS *exptr, char *explanation)
static long __stdcall ExceptionFilter(struct _EXCEPTION_POINTERS *pExPtrs)
Debug module main class (singleton).
Definition debug_debug.h:45
void WriteBuildInfo(void)
Write build information into log.
unsigned used
used buffer size
void SetPrefixAndRadix(const char *prefix, int radix)
char * buffer
buffer
@ Exception
Exception.
Definition debug_io.h:81
a stack trace signature
Definition debug_stack.h:51
static void GetSymbol(unsigned addr, char *buf, unsigned bufSize)
Determines symbol for given address.
static int StackWalk(Signature &sig, struct _CONTEXT *ctx=0)
Walks the stack from the given address.
#define EX(code, text)
int q
Definition test1.cpp:94