debug_except.cpp 14 KB

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